cleanup: remove popdown stuff
Popdowns were replaced with the sidebar
This commit is contained in:
parent
dcc3c1fd4c
commit
e9eba2e291
19 changed files with 0 additions and 1612 deletions
|
|
@ -1,51 +0,0 @@
|
||||||
@use "sass:color";
|
|
||||||
@use "../scheme";
|
|
||||||
@use "../lib";
|
|
||||||
@use "../font";
|
|
||||||
|
|
||||||
$-accent: scheme.$rosewater;
|
|
||||||
|
|
||||||
.bluetooth-devices {
|
|
||||||
@include lib.popdown-window($-accent);
|
|
||||||
|
|
||||||
min-width: lib.s(500);
|
|
||||||
min-height: lib.s(400);
|
|
||||||
|
|
||||||
.list {
|
|
||||||
@include lib.spacing($vertical: true);
|
|
||||||
|
|
||||||
color: scheme.$text;
|
|
||||||
|
|
||||||
.device {
|
|
||||||
@include lib.spacing(8);
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
font-size: lib.s(18);
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
@include lib.rounded(5);
|
|
||||||
@include lib.element-decel;
|
|
||||||
|
|
||||||
padding: lib.s(3) lib.s(8);
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
background-color: scheme.$surface0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
background-color: scheme.$surface1;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:disabled {
|
|
||||||
color: scheme.$subtext0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
color: $-accent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
@forward "notifications";
|
|
||||||
@forward "updates";
|
|
||||||
@forward "bluetoothdevices";
|
|
||||||
@forward "networks";
|
|
||||||
@forward "media";
|
|
||||||
@forward "sideright";
|
|
||||||
@forward "sideleft";
|
|
||||||
|
|
@ -1,113 +0,0 @@
|
||||||
@use "sass:color";
|
|
||||||
@use "../scheme";
|
|
||||||
@use "../lib";
|
|
||||||
@use "../font";
|
|
||||||
|
|
||||||
$-accent: scheme.$lavender;
|
|
||||||
$-accent2: scheme.$pink;
|
|
||||||
|
|
||||||
.media {
|
|
||||||
@include lib.rounded(8);
|
|
||||||
@include lib.border($-accent, 0.4, 2);
|
|
||||||
@include lib.shadow;
|
|
||||||
@include font.mono;
|
|
||||||
|
|
||||||
background-color: scheme.$mantle;
|
|
||||||
color: $-accent;
|
|
||||||
padding: lib.s(12);
|
|
||||||
font-size: lib.s(14);
|
|
||||||
min-width: lib.s(500);
|
|
||||||
min-height: lib.s(220);
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
font-size: lib.s(32);
|
|
||||||
}
|
|
||||||
|
|
||||||
.overlay {
|
|
||||||
@include lib.rounded(5);
|
|
||||||
|
|
||||||
background-color: color.change(scheme.$mantle, $alpha: 0.8);
|
|
||||||
}
|
|
||||||
|
|
||||||
.background {
|
|
||||||
@include lib.rounded(5);
|
|
||||||
|
|
||||||
transition: background 300ms ease-in-out;
|
|
||||||
background-color: scheme.$surface0;
|
|
||||||
background-position: center;
|
|
||||||
background-size: cover;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
.player {
|
|
||||||
padding: lib.s(35) lib.s(12);
|
|
||||||
font-weight: bold;
|
|
||||||
|
|
||||||
@include lib.spacing(10, true);
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: lib.s(18);
|
|
||||||
}
|
|
||||||
|
|
||||||
.artist {
|
|
||||||
color: scheme.$green;
|
|
||||||
}
|
|
||||||
|
|
||||||
.album {
|
|
||||||
color: $-accent2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.controls {
|
|
||||||
margin-top: lib.s(3);
|
|
||||||
|
|
||||||
@include lib.spacing(10);
|
|
||||||
|
|
||||||
button {
|
|
||||||
@include lib.rounded(4);
|
|
||||||
@include lib.element-decel;
|
|
||||||
@include lib.border(scheme.$base, 0.7);
|
|
||||||
|
|
||||||
padding: lib.s(5) lib.s(12);
|
|
||||||
font-size: lib.s(18);
|
|
||||||
background-color: $-accent;
|
|
||||||
color: scheme.$base;
|
|
||||||
|
|
||||||
&:disabled {
|
|
||||||
background-color: scheme.$overlay2;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
background-color: color.change($-accent, $alpha: 0.8);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
background-color: color.change($-accent, $alpha: 0.6);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Progress bar
|
|
||||||
trough {
|
|
||||||
@include lib.rounded(3);
|
|
||||||
|
|
||||||
margin-top: lib.s(10);
|
|
||||||
background-color: scheme.$surface0;
|
|
||||||
|
|
||||||
highlight {
|
|
||||||
@include lib.rounded(3);
|
|
||||||
|
|
||||||
background-color: $-accent2;
|
|
||||||
padding: lib.s(3) 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
slider {
|
|
||||||
@include lib.rounded(3);
|
|
||||||
|
|
||||||
background-color: scheme.$overlay0;
|
|
||||||
min-width: lib.s(15);
|
|
||||||
min-height: lib.s(15);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
@use "sass:color";
|
|
||||||
@use "../scheme";
|
|
||||||
@use "../lib";
|
|
||||||
@use "../font";
|
|
||||||
|
|
||||||
$-accent: scheme.$rosewater;
|
|
||||||
|
|
||||||
.networks {
|
|
||||||
@include lib.popdown-window($-accent);
|
|
||||||
|
|
||||||
min-width: lib.s(500);
|
|
||||||
min-height: lib.s(400);
|
|
||||||
|
|
||||||
.list {
|
|
||||||
@include lib.spacing($vertical: true);
|
|
||||||
|
|
||||||
color: scheme.$text;
|
|
||||||
|
|
||||||
.network {
|
|
||||||
@include lib.spacing(8);
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
font-size: lib.s(18);
|
|
||||||
}
|
|
||||||
|
|
||||||
button {
|
|
||||||
@include lib.rounded(5);
|
|
||||||
@include lib.element-decel;
|
|
||||||
|
|
||||||
padding: lib.s(3) lib.s(8);
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
background-color: scheme.$surface0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
background-color: scheme.$surface1;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:disabled {
|
|
||||||
color: scheme.$subtext0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
color: $-accent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
@use "sass:color";
|
|
||||||
@use "../scheme";
|
|
||||||
@use "../lib";
|
|
||||||
@use "../font";
|
|
||||||
|
|
||||||
@mixin popup($accent) {
|
|
||||||
.separator {
|
|
||||||
background-color: $accent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image {
|
|
||||||
@include lib.border($accent, 0.05);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.notifications {
|
|
||||||
@include lib.popdown-window(scheme.$mauve);
|
|
||||||
|
|
||||||
min-width: lib.s(400);
|
|
||||||
min-height: lib.s(600);
|
|
||||||
|
|
||||||
.notification {
|
|
||||||
.wrapper {
|
|
||||||
padding-bottom: lib.s(10);
|
|
||||||
}
|
|
||||||
|
|
||||||
.inner {
|
|
||||||
background-color: color.mix(scheme.$base, scheme.$surface0, 70%);
|
|
||||||
|
|
||||||
&.low {
|
|
||||||
@include popup(scheme.$overlay0);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.normal {
|
|
||||||
@include lib.border(scheme.$primary, 0.3);
|
|
||||||
@include popup(scheme.$primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.critical {
|
|
||||||
@include lib.border(scheme.$error, 0.8);
|
|
||||||
@include popup(scheme.$error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.actions > * {
|
|
||||||
background-color: scheme.$surface0;
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
background-color: scheme.$surface1;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
background-color: scheme.$surface2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,131 +0,0 @@
|
||||||
@use "sass:color";
|
|
||||||
@use "../scheme";
|
|
||||||
@use "../lib";
|
|
||||||
@use "../font";
|
|
||||||
|
|
||||||
$-accent: scheme.$yellow;
|
|
||||||
|
|
||||||
.sideleft {
|
|
||||||
@include lib.rounded(8);
|
|
||||||
@include lib.border($-accent, 0.4, 2);
|
|
||||||
@include lib.shadow;
|
|
||||||
@include font.mono;
|
|
||||||
|
|
||||||
background-color: scheme.$crust;
|
|
||||||
color: $-accent;
|
|
||||||
padding: lib.s(12);
|
|
||||||
font-size: lib.s(14);
|
|
||||||
|
|
||||||
@include lib.spacing(10, true);
|
|
||||||
|
|
||||||
& > * {
|
|
||||||
@include lib.rounded(5);
|
|
||||||
|
|
||||||
background-color: scheme.$base;
|
|
||||||
padding: lib.s(16) lib.s(20);
|
|
||||||
}
|
|
||||||
|
|
||||||
.user {
|
|
||||||
@include lib.spacing(15);
|
|
||||||
|
|
||||||
.face {
|
|
||||||
@include lib.rounded(1000);
|
|
||||||
|
|
||||||
background-position: center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: cover;
|
|
||||||
min-width: lib.s(48);
|
|
||||||
min-height: lib.s(48);
|
|
||||||
font-size: lib.s(24);
|
|
||||||
}
|
|
||||||
|
|
||||||
.details {
|
|
||||||
font-size: lib.s(14);
|
|
||||||
|
|
||||||
@include lib.spacing(3, true);
|
|
||||||
|
|
||||||
.name {
|
|
||||||
font-size: lib.s(18);
|
|
||||||
color: scheme.$text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.power {
|
|
||||||
@include lib.element-decel;
|
|
||||||
@include font.icon;
|
|
||||||
|
|
||||||
font-size: lib.s(24);
|
|
||||||
color: scheme.$red;
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
color: color.mix(scheme.$red, scheme.$base, 80%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
color: color.mix(scheme.$red, scheme.$base, 60%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.locations {
|
|
||||||
@include lib.spacing(30);
|
|
||||||
|
|
||||||
& > * {
|
|
||||||
@include lib.spacing(8, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
$-colours: scheme.$pink, scheme.$yellow, scheme.$maroon, scheme.$green, scheme.$mauve, scheme.$lavender;
|
|
||||||
@for $i from 1 through 6 {
|
|
||||||
.loc#{$i} {
|
|
||||||
@include lib.element-decel;
|
|
||||||
|
|
||||||
color: nth($-colours, $i);
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
color: color.mix(nth($-colours, $i), scheme.$base, 80%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
color: color.mix(nth($-colours, $i), scheme.$base, 60%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.hw-resources {
|
|
||||||
@include lib.spacing(10, true);
|
|
||||||
|
|
||||||
.resource {
|
|
||||||
font-size: lib.s(13);
|
|
||||||
|
|
||||||
.inner {
|
|
||||||
font-size: lib.s(16);
|
|
||||||
|
|
||||||
@include lib.spacing(20);
|
|
||||||
}
|
|
||||||
|
|
||||||
.slider {
|
|
||||||
@include lib.rounded(5);
|
|
||||||
@include lib.fluent-decel(1000ms);
|
|
||||||
|
|
||||||
min-height: lib.s(10);
|
|
||||||
background-color: scheme.$surface0;
|
|
||||||
color: scheme.$red;
|
|
||||||
}
|
|
||||||
|
|
||||||
$-names: gpu, cpu, memory, storage;
|
|
||||||
$-colours: scheme.$green, scheme.$maroon, scheme.$yellow, scheme.$pink;
|
|
||||||
@for $i from 1 through length($-names) {
|
|
||||||
&.#{nth($-names, $i)} {
|
|
||||||
color: nth($-colours, $i);
|
|
||||||
|
|
||||||
.slider {
|
|
||||||
color: nth($-colours, $i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,148 +0,0 @@
|
||||||
@use "sass:color";
|
|
||||||
@use "../scheme";
|
|
||||||
@use "../lib";
|
|
||||||
@use "../font";
|
|
||||||
|
|
||||||
$-accent: scheme.$peach;
|
|
||||||
$-accent2: scheme.$blue;
|
|
||||||
|
|
||||||
.sideright {
|
|
||||||
@include lib.rounded(8);
|
|
||||||
@include lib.border($-accent, 0.4, 2);
|
|
||||||
@include lib.shadow;
|
|
||||||
@include font.mono;
|
|
||||||
|
|
||||||
background-color: scheme.$crust;
|
|
||||||
color: $-accent;
|
|
||||||
padding: lib.s(12);
|
|
||||||
font-size: lib.s(14);
|
|
||||||
|
|
||||||
@include lib.spacing(10, true);
|
|
||||||
|
|
||||||
& > * {
|
|
||||||
@include lib.rounded(5);
|
|
||||||
|
|
||||||
background-color: scheme.$base;
|
|
||||||
}
|
|
||||||
|
|
||||||
.time {
|
|
||||||
padding: lib.s(12) lib.s(8);
|
|
||||||
font-size: lib.s(48);
|
|
||||||
font-weight: bold;
|
|
||||||
|
|
||||||
& > * {
|
|
||||||
@include lib.spacing(3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ampm {
|
|
||||||
font-size: lib.s(24);
|
|
||||||
font-weight: normal;
|
|
||||||
margin-top: lib.s(18);
|
|
||||||
color: $-accent2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.calendar {
|
|
||||||
padding: lib.s(5) lib.s(8) 0 lib.s(8);
|
|
||||||
min-width: lib.s(350);
|
|
||||||
color: scheme.$text;
|
|
||||||
|
|
||||||
calendar {
|
|
||||||
padding: lib.s(8); // Padding for each day
|
|
||||||
|
|
||||||
// Month and year
|
|
||||||
&.header {
|
|
||||||
color: $-accent2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change month/year buttons
|
|
||||||
&.button {
|
|
||||||
&:hover {
|
|
||||||
color: scheme.$subtext0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:disabled {
|
|
||||||
color: color.mix(scheme.$text, scheme.$base, 65%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Weekday labels
|
|
||||||
&.highlight {
|
|
||||||
color: $-accent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Days in other month
|
|
||||||
&:indeterminate {
|
|
||||||
color: color.mix(scheme.$text, scheme.$base, 65%);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Current day
|
|
||||||
&:selected {
|
|
||||||
$base: color.change(scheme.$base, $alpha: 1);
|
|
||||||
box-shadow: inset 0 lib.s(-12) 0 0 $-accent, inset 0 lib.s(-9) 0 0 $base,
|
|
||||||
inset lib.s(-10) lib.s(-3) 0 0 $base, inset lib.s(10) 0 0 lib.s(0.1) $base;
|
|
||||||
color: $-accent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.weather {
|
|
||||||
padding: lib.s(5) lib.s(15);
|
|
||||||
|
|
||||||
@include lib.spacing(10, true);
|
|
||||||
|
|
||||||
.current {
|
|
||||||
@include lib.spacing(20);
|
|
||||||
|
|
||||||
.status-icon {
|
|
||||||
font-size: lib.s(64);
|
|
||||||
padding: lib.s(10) lib.s(20) 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status {
|
|
||||||
color: $-accent2;
|
|
||||||
font-size: lib.s(16);
|
|
||||||
|
|
||||||
@include lib.spacing($vertical: true);
|
|
||||||
|
|
||||||
.temperature {
|
|
||||||
font-size: lib.s(28);
|
|
||||||
font-weight: bold;
|
|
||||||
color: scheme.$text;
|
|
||||||
|
|
||||||
@include lib.spacing;
|
|
||||||
|
|
||||||
.temp-icon {
|
|
||||||
font-size: lib.s(30);
|
|
||||||
|
|
||||||
$-temps: freezing, cold, normal, hot, burning;
|
|
||||||
$-colours: scheme.$sky, scheme.$blue, scheme.$green, scheme.$yellow, scheme.$red;
|
|
||||||
@for $i from 1 through length($-temps) {
|
|
||||||
&.#{nth($-temps, $i)} {
|
|
||||||
color: nth($-colours, $i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.separator {
|
|
||||||
background-color: scheme.$surface0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.forecast {
|
|
||||||
padding: lib.s(10) 0 lib.s(15) 0;
|
|
||||||
|
|
||||||
@include lib.spacing(10);
|
|
||||||
|
|
||||||
.hour {
|
|
||||||
@include lib.spacing($vertical: true);
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
font-size: lib.s(32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
@use "sass:color";
|
|
||||||
@use "../scheme";
|
|
||||||
@use "../lib";
|
|
||||||
@use "../font";
|
|
||||||
|
|
||||||
$-accent: scheme.$blue;
|
|
||||||
|
|
||||||
.updates {
|
|
||||||
@include lib.popdown-window($-accent);
|
|
||||||
|
|
||||||
min-width: lib.s(550);
|
|
||||||
min-height: lib.s(800);
|
|
||||||
|
|
||||||
.wrapper {
|
|
||||||
@include lib.element-decel;
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
color: color.mix($-accent, scheme.$base, 80%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
color: color.mix($-accent, scheme.$base, 60%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.repos {
|
|
||||||
@include lib.spacing($vertical: true);
|
|
||||||
|
|
||||||
.repo {
|
|
||||||
.header {
|
|
||||||
font-size: lib.s(16);
|
|
||||||
}
|
|
||||||
|
|
||||||
.list,
|
|
||||||
.news {
|
|
||||||
color: scheme.$text;
|
|
||||||
margin-left: lib.s(12);
|
|
||||||
}
|
|
||||||
|
|
||||||
.list {
|
|
||||||
@include lib.spacing($vertical: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
.news {
|
|
||||||
margin-bottom: lib.s(5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -102,20 +102,6 @@ export default {
|
||||||
sidebar: {
|
sidebar: {
|
||||||
showOnStartup: false,
|
showOnStartup: false,
|
||||||
},
|
},
|
||||||
sideleft: {
|
|
||||||
directories: {
|
|
||||||
left: {
|
|
||||||
top: " Downloads",
|
|
||||||
middle: " Documents",
|
|
||||||
bottom: " Music",
|
|
||||||
},
|
|
||||||
right: {
|
|
||||||
top: " Pictures",
|
|
||||||
middle: " Videos",
|
|
||||||
bottom: " Home",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Services
|
// Services
|
||||||
math: {
|
math: {
|
||||||
maxHistory: 100,
|
maxHistory: 100,
|
||||||
|
|
|
||||||
|
|
@ -54,13 +54,6 @@ export default {
|
||||||
"osds.lock.num.hideDelay": NUM,
|
"osds.lock.num.hideDelay": NUM,
|
||||||
// Sidebar
|
// Sidebar
|
||||||
"sidebar.showOnStartup": BOOL,
|
"sidebar.showOnStartup": BOOL,
|
||||||
// Sideleft
|
|
||||||
"sideleft.directories.left.top": STR,
|
|
||||||
"sideleft.directories.left.middle": STR,
|
|
||||||
"sideleft.directories.left.bottom": STR,
|
|
||||||
"sideleft.directories.right.top": STR,
|
|
||||||
"sideleft.directories.right.middle": STR,
|
|
||||||
"sideleft.directories.right.bottom": STR,
|
|
||||||
// Services
|
// Services
|
||||||
"math.maxHistory": NUM,
|
"math.maxHistory": NUM,
|
||||||
"updates.interval": NUM,
|
"updates.interval": NUM,
|
||||||
|
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
import { bind, Variable } from "astal";
|
|
||||||
import { Astal, Gtk } from "astal/gtk3";
|
|
||||||
import AstalBluetooth from "gi://AstalBluetooth";
|
|
||||||
import PopdownWindow from "../../widgets/popdownwindow";
|
|
||||||
|
|
||||||
const BluetoothDevice = (device: AstalBluetooth.Device) => (
|
|
||||||
<box className={bind(device, "connected").as(c => `device ${c ? "active" : ""}`)}>
|
|
||||||
<icon
|
|
||||||
className="icon"
|
|
||||||
icon={bind(device, "icon").as(i =>
|
|
||||||
Astal.Icon.lookup_icon(`${i}-symbolic`) ? `${i}-symbolic` : "bluetooth-symbolic"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
truncate
|
|
||||||
xalign={0}
|
|
||||||
setup={self => {
|
|
||||||
const update = () =>
|
|
||||||
(self.label = `${device.alias}${
|
|
||||||
device.connected || device.paired ? ` (${device.connected ? "Connected" : "Paired"})` : ""
|
|
||||||
}`);
|
|
||||||
self.hook(device, "notify::alias", update);
|
|
||||||
self.hook(device, "notify::connected", update);
|
|
||||||
self.hook(device, "notify::paired", update);
|
|
||||||
update();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<box hexpand />
|
|
||||||
<button
|
|
||||||
cursor="pointer"
|
|
||||||
onClicked={self => {
|
|
||||||
if (device.connected)
|
|
||||||
device.disconnect_device((_, res) => {
|
|
||||||
self.sensitive = true;
|
|
||||||
device.disconnect_device_finish(res);
|
|
||||||
});
|
|
||||||
else
|
|
||||||
device.connect_device((_, res) => {
|
|
||||||
self.sensitive = true;
|
|
||||||
device.connect_device_finish(res);
|
|
||||||
});
|
|
||||||
self.sensitive = false;
|
|
||||||
}}
|
|
||||||
label={bind(device, "connected").as(c => (c ? "Disconnect" : "Connect"))}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
cursor="pointer"
|
|
||||||
onClicked={() => AstalBluetooth.get_default().adapter.remove_device(device)}
|
|
||||||
label="Remove"
|
|
||||||
/>
|
|
||||||
</box>
|
|
||||||
);
|
|
||||||
|
|
||||||
const List = () => (
|
|
||||||
<box vertical valign={Gtk.Align.START} className="list">
|
|
||||||
{bind(AstalBluetooth.get_default(), "devices").as(d => d.map(BluetoothDevice))}
|
|
||||||
</box>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default () => {
|
|
||||||
const bluetooth = AstalBluetooth.get_default();
|
|
||||||
const label = Variable("");
|
|
||||||
|
|
||||||
const update = () => {
|
|
||||||
const devices = bluetooth.get_devices();
|
|
||||||
const connected = devices.filter(d => d.connected).length;
|
|
||||||
label.set(`${connected} connected device${connected === 1 ? "" : "s"} (${devices.length} available)`);
|
|
||||||
};
|
|
||||||
bluetooth.get_devices().forEach(d => d.connect("notify::connected", update));
|
|
||||||
bluetooth.connect("device-added", (_, device) => device.connect("notify::connected", update));
|
|
||||||
bluetooth.connect("notify::devices", update);
|
|
||||||
update();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PopdownWindow
|
|
||||||
name="bluetooth-devices"
|
|
||||||
count={bind(bluetooth, "devices").as(n => n.length)}
|
|
||||||
countLabel={bind(label)}
|
|
||||||
headerButtons={[
|
|
||||||
{
|
|
||||||
label: bind(bluetooth, "isPowered").as(p => (p ? "Disable" : "Enable")),
|
|
||||||
onClicked: () => bluetooth.toggle(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Discovery",
|
|
||||||
onClicked: () => {
|
|
||||||
if (bluetooth.adapter.discovering) bluetooth.adapter.start_discovery();
|
|
||||||
else bluetooth.adapter.stop_discovery();
|
|
||||||
},
|
|
||||||
enabled: bind(bluetooth.adapter, "discovering"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
emptyIcon="bluetooth_disabled"
|
|
||||||
emptyLabel="No Bluetooth devices"
|
|
||||||
list={<List />}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
import BluetoothDevices from "./bluetoothdevices";
|
|
||||||
import Media from "./media";
|
|
||||||
import Networks from "./networks";
|
|
||||||
import Notifications from "./notifications";
|
|
||||||
import SideLeft from "./sideleft";
|
|
||||||
import SideRight from "./sideright";
|
|
||||||
import Updates from "./updates";
|
|
||||||
|
|
||||||
export default () => {
|
|
||||||
<Notifications />;
|
|
||||||
<Updates />;
|
|
||||||
<BluetoothDevices />;
|
|
||||||
<Networks />;
|
|
||||||
<Media />;
|
|
||||||
<SideRight />;
|
|
||||||
<SideLeft />;
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
@ -1,152 +0,0 @@
|
||||||
import Players from "@/services/players";
|
|
||||||
import { isRealPlayer } from "@/utils/mpris";
|
|
||||||
import PopupWindow from "@/widgets/popupwindow";
|
|
||||||
import { bind, Variable } from "astal";
|
|
||||||
import { Astal, Gtk } from "astal/gtk3";
|
|
||||||
import AstalMpris from "gi://AstalMpris";
|
|
||||||
|
|
||||||
const shuffleToIcon = (s: AstalMpris.Shuffle) =>
|
|
||||||
`caelestia-${s === AstalMpris.Shuffle.ON ? "shuffle" : "consecutive"}-symbolic`;
|
|
||||||
|
|
||||||
const playbackToIcon = (s: AstalMpris.PlaybackStatus) =>
|
|
||||||
`caelestia-${s === AstalMpris.PlaybackStatus.PLAYING ? "pause" : "play"}-symbolic`;
|
|
||||||
|
|
||||||
const loopToIcon = (s: AstalMpris.Loop) => {
|
|
||||||
if (s === AstalMpris.Loop.PLAYLIST) return "caelestia-repeat-symbolic";
|
|
||||||
if (s === AstalMpris.Loop.TRACK) return "caelestia-repeat-one-symbolic";
|
|
||||||
return "caelestia-no-repeat-symbolic";
|
|
||||||
};
|
|
||||||
|
|
||||||
const lengthStr = (length: number) =>
|
|
||||||
`${Math.floor(length / 60)}:${Math.floor(length % 60)
|
|
||||||
.toString()
|
|
||||||
.padStart(2, "0")}`;
|
|
||||||
|
|
||||||
const Player = ({ player }: { player: AstalMpris.Player }) => {
|
|
||||||
const background = (
|
|
||||||
<box className="background" css={bind(player, "coverArt").as(u => `background-image: url("${u}");`)} />
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<overlay
|
|
||||||
overlays={[
|
|
||||||
<box className="overlay" />,
|
|
||||||
<box
|
|
||||||
vertical
|
|
||||||
className="player"
|
|
||||||
setup={self =>
|
|
||||||
self.connect("size-allocate", () =>
|
|
||||||
background.set_size_request(self.get_preferred_width()[1], self.get_preferred_height()[1])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<label className="title" label={bind(player, "title").as(t => t ?? "")} />
|
|
||||||
<label className="artist" label={bind(player, "artist").as(a => a ?? "")} />
|
|
||||||
<label className="album" label={bind(player, "album").as(a => a ?? "")} />
|
|
||||||
<box className="controls" halign={Gtk.Align.CENTER}>
|
|
||||||
<button
|
|
||||||
cursor="pointer"
|
|
||||||
onClicked={() => player.shuffle()}
|
|
||||||
sensitive={bind(player, "canControl")}
|
|
||||||
>
|
|
||||||
<icon icon={bind(player, "shuffleStatus").as(shuffleToIcon)} />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
cursor="pointer"
|
|
||||||
onClicked={() => player.previous()}
|
|
||||||
sensitive={bind(player, "canGoPrevious")}
|
|
||||||
>
|
|
||||||
<icon icon="caelestia-skip-previous-symbolic" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
cursor="pointer"
|
|
||||||
onClicked={() => player.play_pause()}
|
|
||||||
sensitive={bind(player, "canControl")}
|
|
||||||
>
|
|
||||||
<icon icon={bind(player, "playbackStatus").as(playbackToIcon)} />
|
|
||||||
</button>
|
|
||||||
<button cursor="pointer" onClicked={() => player.next()} sensitive={bind(player, "canGoNext")}>
|
|
||||||
<icon icon="caelestia-skip-next-symbolic" />
|
|
||||||
</button>
|
|
||||||
<button cursor="pointer" onClicked={() => player.loop()} sensitive={bind(player, "canControl")}>
|
|
||||||
<icon icon={bind(player, "loopStatus").as(loopToIcon)} />
|
|
||||||
</button>
|
|
||||||
</box>
|
|
||||||
<slider
|
|
||||||
hexpand
|
|
||||||
onDragged={self => player.set_position(self.value * player.length)}
|
|
||||||
setup={self => {
|
|
||||||
const update = () => {
|
|
||||||
self.set_tooltip_text(`${lengthStr(player.position)}/${lengthStr(player.length)}`);
|
|
||||||
self.set_value(player.position / player.length);
|
|
||||||
};
|
|
||||||
self.hook(player, "notify::position", update);
|
|
||||||
self.hook(player, "notify::length", update);
|
|
||||||
update();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</box>,
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{background}
|
|
||||||
</overlay>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default () => {
|
|
||||||
const shown = Variable("none");
|
|
||||||
return (
|
|
||||||
<PopupWindow name="media">
|
|
||||||
<box className="media">
|
|
||||||
<eventbox
|
|
||||||
onClick={(_, event) => {
|
|
||||||
if (event.button === Astal.MouseButton.SECONDARY) {
|
|
||||||
const current = Players.get_default().list.find(p => p.busName === shown.get());
|
|
||||||
if (current) Players.get_default().makeCurrent(current);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onScroll={(_, event) => {
|
|
||||||
const players = AstalMpris.get_default().players.filter(isRealPlayer);
|
|
||||||
const idx = players.findIndex(p => p.busName === shown.get());
|
|
||||||
if (idx === -1) return;
|
|
||||||
if (event.delta_y < 0) {
|
|
||||||
if (idx > 0) shown.set(players[idx - 1].busName);
|
|
||||||
} else if (idx < players.length - 1) shown.set(players[idx + 1].busName);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<stack
|
|
||||||
expand
|
|
||||||
transitionType={Gtk.StackTransitionType.SLIDE_LEFT_RIGHT}
|
|
||||||
transitionDuration={150}
|
|
||||||
shown={bind(shown)}
|
|
||||||
setup={self => {
|
|
||||||
const players = new Map<string, JSX.Element>();
|
|
||||||
|
|
||||||
const addPlayer = (player: AstalMpris.Player) => {
|
|
||||||
if (!isRealPlayer(player) || players.has(player.busName)) return;
|
|
||||||
const widget = <Player player={player} />;
|
|
||||||
self.add_named(widget, player.busName);
|
|
||||||
if (players.size === 0) shown.set(player.busName);
|
|
||||||
players.set(player.busName, widget);
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const player of Players.get_default().list) addPlayer(player);
|
|
||||||
|
|
||||||
self.hook(AstalMpris.get_default(), "player-added", (_, player) => addPlayer(player));
|
|
||||||
self.hook(AstalMpris.get_default(), "player-closed", (_, player) => {
|
|
||||||
players.get(player.busName)?.destroy();
|
|
||||||
players.delete(player.busName);
|
|
||||||
if (shown.get() === player.busName)
|
|
||||||
shown.set(AstalMpris.get_default().players.find(isRealPlayer)?.busName ?? "none");
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<box vertical valign={Gtk.Align.CENTER} name="none">
|
|
||||||
<label className="icon" label="music_note" />
|
|
||||||
<label label="No media playing" />
|
|
||||||
</box>
|
|
||||||
</stack>
|
|
||||||
</eventbox>
|
|
||||||
</box>
|
|
||||||
</PopupWindow>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -1,101 +0,0 @@
|
||||||
import { bind, execAsync, Variable } from "astal";
|
|
||||||
import { Gtk } from "astal/gtk3";
|
|
||||||
import AstalNetwork from "gi://AstalNetwork";
|
|
||||||
import PopdownWindow from "../../widgets/popdownwindow";
|
|
||||||
|
|
||||||
const Network = (accessPoint: AstalNetwork.AccessPoint) => (
|
|
||||||
<box
|
|
||||||
className={bind(AstalNetwork.get_default().wifi, "activeAccessPoint").as(
|
|
||||||
a => `network ${a === accessPoint ? "active" : ""}`
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<icon className="icon" icon={bind(accessPoint, "iconName")} />
|
|
||||||
<label
|
|
||||||
truncate
|
|
||||||
xalign={0}
|
|
||||||
setup={self => {
|
|
||||||
const update = () =>
|
|
||||||
(self.label = `${accessPoint.ssid}${` (${accessPoint.frequency > 5000 ? 5 : 2.4}GHz | ${
|
|
||||||
accessPoint.strength
|
|
||||||
}/100)`}`);
|
|
||||||
self.hook(accessPoint, "notify::ssid", update);
|
|
||||||
self.hook(accessPoint, "notify::frequency", update);
|
|
||||||
self.hook(accessPoint, "notify::strength", update);
|
|
||||||
update();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<box hexpand />
|
|
||||||
<button
|
|
||||||
cursor="pointer"
|
|
||||||
onClicked={self => {
|
|
||||||
const cmd =
|
|
||||||
AstalNetwork.get_default().wifi.activeAccessPoint === accessPoint ? "c down id" : "d wifi connect";
|
|
||||||
execAsync(`nmcli ${cmd} '${accessPoint.ssid}'`)
|
|
||||||
.then(() => (self.sensitive = true))
|
|
||||||
.catch(console.error);
|
|
||||||
self.sensitive = false;
|
|
||||||
}}
|
|
||||||
label={bind(AstalNetwork.get_default().wifi, "activeAccessPoint").as(a =>
|
|
||||||
a === accessPoint ? "Disconnect" : "Connect"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
cursor="pointer"
|
|
||||||
onClicked={() => execAsync(`nmcli c delete id '${accessPoint.ssid}'`).catch(() => {})}
|
|
||||||
label="Forget"
|
|
||||||
/>
|
|
||||||
</box>
|
|
||||||
);
|
|
||||||
|
|
||||||
const List = () => {
|
|
||||||
const { wifi } = AstalNetwork.get_default();
|
|
||||||
const children = Variable.derive([bind(wifi, "accessPoints"), bind(wifi, "activeAccessPoint")], (aps, ac) =>
|
|
||||||
aps
|
|
||||||
.filter(a => a.ssid)
|
|
||||||
.sort((a, b) => (a === ac ? -1 : b.strength - a.strength))
|
|
||||||
.map(Network)
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<box vertical valign={Gtk.Align.START} className="list" onDestroy={() => children.drop()}>
|
|
||||||
{bind(children)}
|
|
||||||
</box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default () => {
|
|
||||||
const network = AstalNetwork.get_default();
|
|
||||||
const label = Variable("");
|
|
||||||
|
|
||||||
const update = () => {
|
|
||||||
if (network.primary === AstalNetwork.Primary.WIFI) label.set(network.wifi.ssid ?? "Disconnected");
|
|
||||||
else if (network.primary === AstalNetwork.Primary.WIRED) label.set(`Ethernet (${network.wired.speed})`);
|
|
||||||
else label.set("No Wifi");
|
|
||||||
};
|
|
||||||
network.connect("notify::primary", update);
|
|
||||||
network.get_wifi()?.connect("notify::ssid", update);
|
|
||||||
network.get_wired()?.connect("notify::speed", update);
|
|
||||||
update();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PopdownWindow
|
|
||||||
name="networks"
|
|
||||||
count={bind(network.wifi, "accessPoints").as(a => a.length)}
|
|
||||||
countLabel={bind(label)}
|
|
||||||
headerButtons={[
|
|
||||||
{
|
|
||||||
label: bind(network.wifi, "enabled").as(e => (e ? "Disable" : "Enable")),
|
|
||||||
onClicked: () => (network.wifi.enabled = !network.wifi.enabled),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: bind(network.wifi, "scanning").as(s => (s ? "Scanning" : "Scan")),
|
|
||||||
onClicked: () => network.wifi.scan(),
|
|
||||||
enabled: bind(network.wifi, "scanning"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
emptyIcon="wifi_off"
|
|
||||||
emptyLabel={bind(network.wifi, "enabled").as(p => (p ? "No available networks" : "Wifi is off"))}
|
|
||||||
list={<List />}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
import { bind } from "astal";
|
|
||||||
import { Astal, Gtk } from "astal/gtk3";
|
|
||||||
import AstalNotifd from "gi://AstalNotifd";
|
|
||||||
import Notification from "../../widgets/notification";
|
|
||||||
import PopdownWindow from "../../widgets/popdownwindow";
|
|
||||||
|
|
||||||
const List = () => (
|
|
||||||
<box
|
|
||||||
vertical
|
|
||||||
valign={Gtk.Align.START}
|
|
||||||
className="list"
|
|
||||||
setup={self => {
|
|
||||||
const notifd = AstalNotifd.get_default();
|
|
||||||
const map = new Map<number, Notification>();
|
|
||||||
|
|
||||||
const addNotification = (notification: AstalNotifd.Notification) => {
|
|
||||||
const notif = (<Notification notification={notification} />) as Notification;
|
|
||||||
notif.connect("destroy", () => map.get(notification.id) === notif && map.delete(notification.id));
|
|
||||||
map.get(notification.id)?.destroyWithAnims();
|
|
||||||
map.set(notification.id, notif);
|
|
||||||
|
|
||||||
self.pack_end(
|
|
||||||
<eventbox
|
|
||||||
// Dismiss on middle click
|
|
||||||
onClick={(_, event) => event.button === Astal.MouseButton.MIDDLE && notification.dismiss()}
|
|
||||||
>
|
|
||||||
{notif}
|
|
||||||
</eventbox>,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
notifd
|
|
||||||
.get_notifications()
|
|
||||||
.sort((a, b) => a.time - b.time)
|
|
||||||
.forEach(addNotification);
|
|
||||||
|
|
||||||
self.hook(notifd, "notified", (_, id) => addNotification(notifd.get_notification(id)));
|
|
||||||
self.hook(notifd, "resolved", (_, id) => map.get(id)?.destroyWithAnims());
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default () => (
|
|
||||||
<PopdownWindow
|
|
||||||
name="notifications"
|
|
||||||
count={bind(AstalNotifd.get_default(), "notifications").as(n => n.length)}
|
|
||||||
headerButtons={[
|
|
||||||
{
|
|
||||||
label: "Silence",
|
|
||||||
onClicked: () => (AstalNotifd.get_default().dontDisturb = !AstalNotifd.get_default().dontDisturb),
|
|
||||||
enabled: bind(AstalNotifd.get_default(), "dontDisturb"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Clear",
|
|
||||||
onClicked: () => AstalNotifd.get_default().notifications.forEach(n => n.dismiss()),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
emptyIcon="notifications_active"
|
|
||||||
emptyLabel="All caught up!"
|
|
||||||
list={<List />}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
@ -1,205 +0,0 @@
|
||||||
import Cpu from "@/services/cpu";
|
|
||||||
import Gpu from "@/services/gpu";
|
|
||||||
import Memory from "@/services/memory";
|
|
||||||
import Storage from "@/services/storage";
|
|
||||||
import { osId } from "@/utils/system";
|
|
||||||
import PopupWindow from "@/widgets/popupwindow";
|
|
||||||
import { bind, execAsync, GLib, type Binding } from "astal";
|
|
||||||
import { App, Gtk, type Widget } from "astal/gtk3";
|
|
||||||
import type cairo from "cairo";
|
|
||||||
import { sideleft } from "config";
|
|
||||||
|
|
||||||
const fmt = (bytes: number, pow: number) => +(bytes / 1024 ** pow).toFixed(2);
|
|
||||||
const format = ({ total, used }: { total: number; used: number }) => {
|
|
||||||
if (total >= 1024 ** 4) return `${fmt(used, 4)}/${fmt(total, 4)} TiB`;
|
|
||||||
if (total >= 1024 ** 3) return `${fmt(used, 3)}/${fmt(total, 3)} GiB`;
|
|
||||||
if (total >= 1024 ** 2) return `${fmt(used, 2)}/${fmt(total, 2)} MiB`;
|
|
||||||
if (total >= 1024) return `${fmt(used, 1)}/${fmt(total, 1)} KiB`;
|
|
||||||
return `${used}/${total} B`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const User = () => (
|
|
||||||
<box className="user">
|
|
||||||
<box
|
|
||||||
valign={Gtk.Align.CENTER}
|
|
||||||
className="face"
|
|
||||||
css={`
|
|
||||||
background-image: url("${HOME}/.face");
|
|
||||||
`}
|
|
||||||
>
|
|
||||||
{!GLib.file_test(HOME + "/.face", GLib.FileTest.EXISTS) && (
|
|
||||||
<label
|
|
||||||
setup={self => {
|
|
||||||
const name = GLib.get_real_name();
|
|
||||||
if (name !== "Unknown")
|
|
||||||
self.label = name
|
|
||||||
.split(" ")
|
|
||||||
.map(s => s[0].toUpperCase())
|
|
||||||
.join("");
|
|
||||||
else self.label = "";
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</box>
|
|
||||||
<box vertical hexpand valign={Gtk.Align.CENTER} className="details">
|
|
||||||
<label xalign={0} className="name" label={GLib.get_user_name()} />
|
|
||||||
<label xalign={0} label={(GLib.getenv("XDG_CURRENT_DESKTOP") ?? osId).toUpperCase()} />
|
|
||||||
</box>
|
|
||||||
<button
|
|
||||||
valign={Gtk.Align.CENTER}
|
|
||||||
className="power"
|
|
||||||
cursor="pointer"
|
|
||||||
onClicked={() => App.toggle_window("session")}
|
|
||||||
label="power_settings_new"
|
|
||||||
/>
|
|
||||||
</box>
|
|
||||||
);
|
|
||||||
|
|
||||||
const QuickLaunch = () => (
|
|
||||||
<box className="quick-launch">
|
|
||||||
<box vertical>
|
|
||||||
<box>{/* <button */}</box> // TODO
|
|
||||||
</box>
|
|
||||||
</box>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Location = ({ label, num }: { label: Binding<string>; num: number }) => (
|
|
||||||
<button
|
|
||||||
className={"loc" + num}
|
|
||||||
cursor="pointer"
|
|
||||||
onClicked={self => {
|
|
||||||
self.get_toplevel().hide();
|
|
||||||
const dir = label.get().split(" ").at(-1);
|
|
||||||
execAsync(`xdg-open ${HOME}/${dir?.toLowerCase() === "home" ? "" : `${dir}/`}`).catch(console.error);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<label xalign={0} label={label} />
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Locations = () => (
|
|
||||||
<box className="locations">
|
|
||||||
<box vertical>
|
|
||||||
<Location label={bind(sideleft.directories.left.top)} num={1} />
|
|
||||||
<Location label={bind(sideleft.directories.left.middle)} num={2} />
|
|
||||||
<Location label={bind(sideleft.directories.left.bottom)} num={3} />
|
|
||||||
</box>
|
|
||||||
<box vertical>
|
|
||||||
<Location label={bind(sideleft.directories.right.top)} num={4} />
|
|
||||||
<Location label={bind(sideleft.directories.right.middle)} num={5} />
|
|
||||||
<Location label={bind(sideleft.directories.right.bottom)} num={6} />
|
|
||||||
</box>
|
|
||||||
</box>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Slider = ({ value }: { value: Binding<number> }) => (
|
|
||||||
<drawingarea
|
|
||||||
hexpand
|
|
||||||
valign={Gtk.Align.CENTER}
|
|
||||||
className="slider"
|
|
||||||
css={bind(value).as(v => `font-size: ${v}px;`)}
|
|
||||||
setup={self => {
|
|
||||||
const halfPi = Math.PI / 2;
|
|
||||||
|
|
||||||
const styleContext = self.get_style_context();
|
|
||||||
self.set_size_request(-1, styleContext.get_property("min-height", Gtk.StateFlags.NORMAL) as number);
|
|
||||||
|
|
||||||
self.connect("draw", (_, cr: cairo.Context) => {
|
|
||||||
const styleContext = self.get_style_context();
|
|
||||||
|
|
||||||
const width = self.get_allocated_width();
|
|
||||||
const height = styleContext.get_property("min-height", Gtk.StateFlags.NORMAL) as number;
|
|
||||||
self.set_size_request(-1, height);
|
|
||||||
|
|
||||||
const progressValue = styleContext.get_property("font-size", Gtk.StateFlags.NORMAL) as number;
|
|
||||||
let radius = styleContext.get_property("border-radius", Gtk.StateFlags.NORMAL) as number;
|
|
||||||
|
|
||||||
const bg = styleContext.get_background_color(Gtk.StateFlags.NORMAL);
|
|
||||||
cr.setSourceRGBA(bg.red, bg.green, bg.blue, bg.alpha);
|
|
||||||
|
|
||||||
// Background
|
|
||||||
cr.arc(radius, radius, radius, -Math.PI, -halfPi); // Top left
|
|
||||||
cr.arc(width - radius, radius, radius, -halfPi, 0); // Top right
|
|
||||||
cr.arc(width - radius, height - radius, radius, 0, halfPi); // Bottom right
|
|
||||||
cr.arc(radius, height - radius, radius, halfPi, Math.PI); // Bottom left
|
|
||||||
cr.fill();
|
|
||||||
|
|
||||||
// Flatten when near 0
|
|
||||||
radius = Math.min(radius, Math.min(width * progressValue, height) / 2);
|
|
||||||
|
|
||||||
const progressPosition = width * progressValue - radius;
|
|
||||||
const fg = styleContext.get_color(Gtk.StateFlags.NORMAL);
|
|
||||||
cr.setSourceRGBA(fg.red, fg.green, fg.blue, fg.alpha);
|
|
||||||
|
|
||||||
// Foreground
|
|
||||||
cr.arc(radius, radius, radius, -Math.PI, -halfPi); // Top left
|
|
||||||
cr.arc(progressPosition, radius, radius, -halfPi, 0); // Top right
|
|
||||||
cr.arc(progressPosition, height - radius, radius, 0, halfPi); // Bottom right
|
|
||||||
cr.arc(radius, height - radius, radius, halfPi, Math.PI); // Bottom left
|
|
||||||
cr.fill();
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Resource = ({
|
|
||||||
icon,
|
|
||||||
name,
|
|
||||||
value,
|
|
||||||
labelSetup,
|
|
||||||
}: {
|
|
||||||
icon: string;
|
|
||||||
name: string;
|
|
||||||
value: Binding<number>;
|
|
||||||
labelSetup?: (self: Widget.Label) => void;
|
|
||||||
}) => (
|
|
||||||
<box vertical className={`resource ${name}`}>
|
|
||||||
<box className="inner">
|
|
||||||
<label label={icon} />
|
|
||||||
<Slider value={value.as(v => v / 100)} />
|
|
||||||
</box>
|
|
||||||
<label halign={Gtk.Align.END} label={labelSetup ? "" : value.as(v => `${+v.toFixed(2)}%`)} setup={labelSetup} />
|
|
||||||
</box>
|
|
||||||
);
|
|
||||||
|
|
||||||
const HwResources = () => (
|
|
||||||
<box vertical className="hw-resources">
|
|
||||||
{Gpu.get_default().available && <Resource icon="" name="gpu" value={bind(Gpu.get_default(), "usage")} />}
|
|
||||||
<Resource icon="" name="cpu" value={bind(Cpu.get_default(), "usage")} />
|
|
||||||
<Resource
|
|
||||||
icon=""
|
|
||||||
name="memory"
|
|
||||||
value={bind(Memory.get_default(), "usage")}
|
|
||||||
labelSetup={self => {
|
|
||||||
const mem = Memory.get_default();
|
|
||||||
const update = () => (self.label = format(mem));
|
|
||||||
self.hook(mem, "notify::used", update);
|
|
||||||
self.hook(mem, "notify::total", update);
|
|
||||||
update();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Resource
|
|
||||||
icon=""
|
|
||||||
name="storage"
|
|
||||||
value={bind(Storage.get_default(), "usage")}
|
|
||||||
labelSetup={self => {
|
|
||||||
const storage = Storage.get_default();
|
|
||||||
const update = () => (self.label = format(storage));
|
|
||||||
self.hook(storage, "notify::used", update);
|
|
||||||
self.hook(storage, "notify::total", update);
|
|
||||||
update();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</box>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default () => (
|
|
||||||
<PopupWindow name="sideleft">
|
|
||||||
<box vertical className="sideleft">
|
|
||||||
<User />
|
|
||||||
{/* <QuickLaunch /> */}
|
|
||||||
<Locations />
|
|
||||||
<HwResources />
|
|
||||||
</box>
|
|
||||||
</PopupWindow>
|
|
||||||
);
|
|
||||||
|
|
@ -1,146 +0,0 @@
|
||||||
import SWeather, { type WeatherData } from "@/services/weather";
|
|
||||||
import { ellipsize } from "@/utils/strings";
|
|
||||||
import { bindCurrentTime } from "@/utils/system";
|
|
||||||
import { setupCustomTooltip, Calendar as WCal } from "@/utils/widgets";
|
|
||||||
import PopupWindow from "@/widgets/popupwindow";
|
|
||||||
import { bind, timeout } from "astal";
|
|
||||||
import { Astal, Gtk, type Gdk } from "astal/gtk3";
|
|
||||||
|
|
||||||
const getHoursFromUpdate = (data: WeatherData, hours: number) => {
|
|
||||||
const updateTime = data.location.localtime_epoch;
|
|
||||||
const updateHourStart = updateTime - (updateTime % 3600);
|
|
||||||
|
|
||||||
let nextHour = new Date((updateHourStart + hours * 3600) * 1000).getHours();
|
|
||||||
if (nextHour >= 24) nextHour -= 24;
|
|
||||||
|
|
||||||
return nextHour;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Time = () => (
|
|
||||||
<box className="time">
|
|
||||||
<box hexpand halign={Gtk.Align.CENTER}>
|
|
||||||
<label label={bindCurrentTime("%I:%M:%S")} />
|
|
||||||
<label className="ampm" label={bindCurrentTime("%p", c => (c.get_hour() < 12 ? "AM" : "PM"))} />
|
|
||||||
</box>
|
|
||||||
</box>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Calendar = () => (
|
|
||||||
<box className="calendar">
|
|
||||||
<eventbox
|
|
||||||
setup={self => {
|
|
||||||
self.connect("button-press-event", (_, event: Gdk.Event) => {
|
|
||||||
if (event.get_button()[1] === Astal.MouseButton.MIDDLE) {
|
|
||||||
const now = new Date();
|
|
||||||
const child = self.get_child() as WCal | null;
|
|
||||||
if (!child) return;
|
|
||||||
child.select_month(now.getMonth(), now.getFullYear());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<WCal
|
|
||||||
hexpand
|
|
||||||
showDetails={false}
|
|
||||||
setup={self => {
|
|
||||||
const update = () =>
|
|
||||||
timeout(0.1, () => {
|
|
||||||
const now = new Date();
|
|
||||||
if (self.month === now.getMonth() && self.year === now.getFullYear())
|
|
||||||
self.day = now.getDate();
|
|
||||||
else self.day = 0;
|
|
||||||
});
|
|
||||||
self.connect("month-changed", update);
|
|
||||||
self.connect("next-month", update);
|
|
||||||
self.connect("prev-month", update);
|
|
||||||
self.connect("next-year", update);
|
|
||||||
self.connect("prev-year", update);
|
|
||||||
update();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</eventbox>
|
|
||||||
</box>
|
|
||||||
);
|
|
||||||
|
|
||||||
const Weather = () => {
|
|
||||||
const weather = SWeather.get_default();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<box vertical className="weather">
|
|
||||||
<centerbox className="current">
|
|
||||||
<label
|
|
||||||
halign={Gtk.Align.START}
|
|
||||||
valign={Gtk.Align.CENTER}
|
|
||||||
className="status-icon"
|
|
||||||
label={bind(weather, "icon")}
|
|
||||||
/>
|
|
||||||
<box vertical halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER} className="status">
|
|
||||||
<box halign={Gtk.Align.CENTER} className="temperature">
|
|
||||||
<label label={bind(weather, "temperature")} />
|
|
||||||
<label
|
|
||||||
className={bind(weather, "tempColour").as(c => `temp-icon ${c}`)}
|
|
||||||
label={bind(weather, "tempIcon")}
|
|
||||||
/>
|
|
||||||
</box>
|
|
||||||
<label label={bind(weather, "condition").as(c => ellipsize(c, 16))} />
|
|
||||||
</box>
|
|
||||||
<box vertical halign={Gtk.Align.END} valign={Gtk.Align.CENTER} className="other-data">
|
|
||||||
<label
|
|
||||||
xalign={0}
|
|
||||||
label={bind(weather, "wind").as(w => ` ${w}`)}
|
|
||||||
setup={self =>
|
|
||||||
setupCustomTooltip(
|
|
||||||
self,
|
|
||||||
bind(weather, "wind").as(w => `${w} wind speed`)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
xalign={0}
|
|
||||||
label={bind(weather, "rainChance").as(r => ` ${r}`)}
|
|
||||||
setup={self =>
|
|
||||||
setupCustomTooltip(
|
|
||||||
self,
|
|
||||||
bind(weather, "rainChance").as(r => `${r} chance of rain`)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</box>
|
|
||||||
</centerbox>
|
|
||||||
<box className="separator" />
|
|
||||||
<box className="forecast">
|
|
||||||
{Array.from({ length: 4 }).map((_, i) => (
|
|
||||||
<box vertical hexpand className="hour">
|
|
||||||
<label
|
|
||||||
label={bind(weather, "raw").as(r => {
|
|
||||||
const hour = getHoursFromUpdate(r, i + 1);
|
|
||||||
return `${hour % 12 || 12}${hour < 12 ? "AM" : "PM"}`;
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
className="icon"
|
|
||||||
label={bind(weather, "raw").as(r =>
|
|
||||||
weather.getIcon(weather.forecast[getHoursFromUpdate(r, i + 1)].condition.text)
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
label={bind(weather, "raw").as(r =>
|
|
||||||
weather.getTemp(weather.forecast[getHoursFromUpdate(r, i + 1)])
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</box>
|
|
||||||
))}
|
|
||||||
</box>
|
|
||||||
</box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default () => (
|
|
||||||
<PopupWindow name="sideright">
|
|
||||||
<box vertical className="sideright">
|
|
||||||
<Time />
|
|
||||||
<Calendar />
|
|
||||||
<Weather />
|
|
||||||
</box>
|
|
||||||
</PopupWindow>
|
|
||||||
);
|
|
||||||
|
|
@ -1,145 +0,0 @@
|
||||||
import Palette from "@/services/palette";
|
|
||||||
import Updates, { Repo as IRepo, Update as IUpdate } from "@/services/updates";
|
|
||||||
import { MenuItem } from "@/utils/widgets";
|
|
||||||
import PopdownWindow from "@/widgets/popdownwindow";
|
|
||||||
import { bind, execAsync, GLib, Variable } from "astal";
|
|
||||||
import { App, Astal, Gtk } from "astal/gtk3";
|
|
||||||
|
|
||||||
const constructItem = (label: string, exec: string, quiet = true) =>
|
|
||||||
new MenuItem({
|
|
||||||
label,
|
|
||||||
onActivate() {
|
|
||||||
App.get_window("updates")?.hide();
|
|
||||||
execAsync(exec).catch(e => !quiet && console.error(e));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const Update = (update: IUpdate) => {
|
|
||||||
const menu = new Gtk.Menu();
|
|
||||||
menu.append(constructItem("Open info in browser", `xdg-open '${update.url}'`, false));
|
|
||||||
menu.append(constructItem("Open info in terminal", `uwsm app -- foot -H pacman -Qi ${update.name}`));
|
|
||||||
menu.append(new Gtk.SeparatorMenuItem({ visible: true }));
|
|
||||||
menu.append(constructItem("Reinstall", `uwsm app -T -- yay -S ${update.name}`));
|
|
||||||
menu.append(constructItem("Remove with dependencies", `uwsm app -T -- yay -Rns ${update.name}`));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
onClick={(_, event) => event.button === Astal.MouseButton.SECONDARY && menu.popup_at_pointer(null)}
|
|
||||||
onDestroy={() => menu.destroy()}
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
truncate
|
|
||||||
useMarkup
|
|
||||||
xalign={0}
|
|
||||||
label={bind(Palette.get_default(), "colours").as(
|
|
||||||
c =>
|
|
||||||
`${update.name} <span foreground="${c.teal}">(${update.version.old} -> ${
|
|
||||||
update.version.new
|
|
||||||
})</span>\n <span foreground="${c.subtext0}">${GLib.markup_escape_text(
|
|
||||||
update.description,
|
|
||||||
update.description.length
|
|
||||||
)}</span>`
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const Repo = ({ repo, first }: { repo: IRepo; first?: boolean }) => {
|
|
||||||
const expanded = Variable(first);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<box vertical className="repo">
|
|
||||||
<button className="wrapper" cursor="pointer" onClicked={() => expanded.set(!expanded.get())}>
|
|
||||||
<box className="header">
|
|
||||||
<label className="icon" label={repo.icon} />
|
|
||||||
<label label={`${repo.name} (${repo.updates.length})`} />
|
|
||||||
<box hexpand />
|
|
||||||
<label className="icon" label={bind(expanded).as(e => (e ? "expand_less" : "expand_more"))} />
|
|
||||||
</box>
|
|
||||||
</button>
|
|
||||||
<revealer
|
|
||||||
revealChild={bind(expanded)}
|
|
||||||
transitionType={Gtk.RevealerTransitionType.SLIDE_DOWN}
|
|
||||||
transitionDuration={200}
|
|
||||||
>
|
|
||||||
<box vertical className="list">
|
|
||||||
{repo.updates.map(Update)}
|
|
||||||
</box>
|
|
||||||
</revealer>
|
|
||||||
</box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const News = ({ news }: { news: string }) => {
|
|
||||||
const expanded = Variable(true);
|
|
||||||
|
|
||||||
news = news
|
|
||||||
.replace(/^([0-9]{4}-[0-9]{2}-[0-9]{2} .+)$/gm, "<b>$1</b>") // Make titles bold
|
|
||||||
.replaceAll("\n\x1b[0m\n<b>", "\n\n<b>") // Remove unopened \x1b[0m after each piece of news
|
|
||||||
.slice(0, -5) // Remove last unopened \x1b[0m
|
|
||||||
.replaceAll("\x1b[0m", "</span>"); // Replace reset code with end span
|
|
||||||
|
|
||||||
return (
|
|
||||||
<box vertical className="repo">
|
|
||||||
<button className="wrapper" cursor="pointer" onClicked={() => expanded.set(!expanded.get())}>
|
|
||||||
<box className="header">
|
|
||||||
<label className="icon" label="newspaper" />
|
|
||||||
<label label="News" />
|
|
||||||
<box hexpand />
|
|
||||||
<label className="icon" label={bind(expanded).as(e => (e ? "expand_less" : "expand_more"))} />
|
|
||||||
</box>
|
|
||||||
</button>
|
|
||||||
<revealer
|
|
||||||
revealChild={bind(expanded)}
|
|
||||||
transitionType={Gtk.RevealerTransitionType.SLIDE_DOWN}
|
|
||||||
transitionDuration={200}
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
wrap
|
|
||||||
useMarkup
|
|
||||||
xalign={0}
|
|
||||||
className="news"
|
|
||||||
label={bind(Palette.get_default(), "teal").as(
|
|
||||||
c => news.replaceAll("\x1b[36m", `<span foreground="${c}">`) // Replace color codes with html spans
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</revealer>
|
|
||||||
</box>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const List = () => (
|
|
||||||
<box vertical valign={Gtk.Align.START} className="repos">
|
|
||||||
{bind(Updates.get_default(), "updateData").as(d =>
|
|
||||||
d.news
|
|
||||||
? [<News news={d.news} />, ...d.repos.map(r => <Repo repo={r} />)]
|
|
||||||
: d.repos.map((r, i) => <Repo repo={r} first={i === 0} />)
|
|
||||||
)}
|
|
||||||
</box>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default () => (
|
|
||||||
<PopdownWindow
|
|
||||||
name="updates"
|
|
||||||
count={bind(Updates.get_default(), "numUpdates")}
|
|
||||||
headerButtons={[
|
|
||||||
{
|
|
||||||
label: "Update all",
|
|
||||||
onClicked: () =>
|
|
||||||
execAsync("uwsm app -T -- yay")
|
|
||||||
.then(() => Updates.get_default().getUpdates())
|
|
||||||
// Ignore errors
|
|
||||||
.catch(() => {}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: bind(Updates.get_default(), "loading").as(l => (l ? "Loading" : "Reload")),
|
|
||||||
onClicked: () => Updates.get_default().getUpdates(),
|
|
||||||
enabled: bind(Updates.get_default(), "loading"),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
emptyIcon="deployed_code_history"
|
|
||||||
emptyLabel="All packages up to date!"
|
|
||||||
list={<List />}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
import type { Binding } from "astal";
|
|
||||||
import { Gtk } from "astal/gtk3";
|
|
||||||
import PopupWindow from "./popupwindow";
|
|
||||||
|
|
||||||
export default ({
|
|
||||||
name,
|
|
||||||
count,
|
|
||||||
countLabel = count.as(c => `${c} ${name.slice(0, -1)}${c === 1 ? "" : "s"}`),
|
|
||||||
headerButtons,
|
|
||||||
emptyIcon,
|
|
||||||
emptyLabel,
|
|
||||||
list,
|
|
||||||
}: {
|
|
||||||
name: string;
|
|
||||||
count: Binding<number>;
|
|
||||||
countLabel?: Binding<string>;
|
|
||||||
headerButtons: { label: string | Binding<string>; onClicked: () => void; enabled?: Binding<boolean> }[];
|
|
||||||
emptyIcon: string;
|
|
||||||
emptyLabel: string | Binding<string>;
|
|
||||||
list: JSX.Element;
|
|
||||||
}) => (
|
|
||||||
<PopupWindow name={name}>
|
|
||||||
<box vertical className={name}>
|
|
||||||
<box className="header">
|
|
||||||
<label label={countLabel} />
|
|
||||||
<box hexpand />
|
|
||||||
{headerButtons.map(({ label, onClicked, enabled }) => (
|
|
||||||
<button
|
|
||||||
cursor="pointer"
|
|
||||||
onClicked={onClicked}
|
|
||||||
label={label}
|
|
||||||
className={enabled?.as(d => (d ? "enabled" : ""))}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</box>
|
|
||||||
<stack
|
|
||||||
transitionType={Gtk.StackTransitionType.CROSSFADE}
|
|
||||||
transitionDuration={150}
|
|
||||||
shown={count.as(c => (c > 0 ? "list" : "empty"))}
|
|
||||||
>
|
|
||||||
<box vertical valign={Gtk.Align.CENTER} name="empty">
|
|
||||||
<label className="icon" label={emptyIcon} />
|
|
||||||
<label label={emptyLabel} />
|
|
||||||
</box>
|
|
||||||
<scrollable expand hscroll={Gtk.PolicyType.NEVER} name="list">
|
|
||||||
{list}
|
|
||||||
</scrollable>
|
|
||||||
</stack>
|
|
||||||
</box>
|
|
||||||
</PopupWindow>
|
|
||||||
);
|
|
||||||
Loading…
Add table
Reference in a new issue