1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
import Players from "@/services/players";
import { osIcon, osId } from "@/utils/system";
import Slider from "@/widgets/slider";
import { bind, GLib, monitorFile, Variable } from "astal";
import { Gtk } from "astal/gtk3";
import AstalMpris from "gi://AstalMpris";
import Notifications from "./modules/notifications";
const lengthStr = (length: number) =>
`${Math.floor(length / 60)}:${Math.floor(length % 60)
.toString()
.padStart(2, "0")}`;
const FaceFallback = () => (
<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 = "";
self.xalign = 0.44;
}
}}
/>
);
const User = () => {
const uptime = Variable("").poll(5000, "uptime -p");
const hasFace = Variable(GLib.file_test(HOME + "/.face", GLib.FileTest.EXISTS));
monitorFile(HOME + "/.face", () => hasFace.set(GLib.file_test(HOME + "/.face", GLib.FileTest.EXISTS)));
return (
<box className="user">
<box
homogeneous
className="face"
setup={self => {
self.css = `background-image: url("${HOME}/.face");`;
monitorFile(HOME + "/.face", () => (self.css = `background-image: url("${HOME}/.face");`));
}}
onDestroy={() => hasFace.drop()}
>
{bind(hasFace).as(h => (h ? <box visible={false} /> : <FaceFallback />))}
</box>
<box vertical hexpand valign={Gtk.Align.CENTER} className="details">
<label xalign={0} className="name" label={`${osIcon} ${GLib.get_user_name()}`} />
<label xalign={0} label={(GLib.getenv("XDG_CURRENT_DESKTOP") ?? osId).toUpperCase()} />
<label truncate xalign={0} className="uptime" label={bind(uptime)} onDestroy={() => uptime.drop()} />
</box>
</box>
);
};
const QuickToggles = () => <box></box>;
const Media = ({ player }: { player: AstalMpris.Player }) => {
const position = Variable.derive([bind(player, "position"), bind(player, "length")], (p, l) => p / l);
return (
<box className="media" onDestroy={() => position.drop()}>
<box
homogeneous
className="cover-art"
css={bind(player, "coverArt").as(a => `background-image: url("${a}");`)}
>
{bind(player, "coverArt").as(a => (a ? <box visible={false} /> : <label xalign={0.31} label="" />))}
</box>
<box vertical className="details">
<label truncate className="title" label={bind(player, "title")} />
<label truncate className="artist" label={bind(player, "artist")} />
<box hexpand className="controls">
<button
hexpand
sensitive={bind(player, "canGoPrevious")}
cursor="pointer"
onClicked={() => player.next()}
label=""
/>
<button
hexpand
sensitive={bind(player, "canControl")}
cursor="pointer"
onClicked={() => player.play_pause()}
label={bind(player, "playbackStatus").as(s =>
s === AstalMpris.PlaybackStatus.PLAYING ? "" : ""
)}
/>
<button
hexpand
sensitive={bind(player, "canGoNext")}
cursor="pointer"
onClicked={() => player.next()}
label=""
/>
</box>
<Slider value={bind(position)} />
<box className="time">
<label label={bind(player, "position").as(lengthStr)} />
<box hexpand />
<label label={bind(player, "length").as(lengthStr)} />
</box>
</box>
</box>
);
};
const Today = () => <box></box>;
export default () => (
<box vertical className="pane dashboard" name="dashboard">
<User />
<box className="separator" />
{bind(Players.get_default(), "lastPlayer").as(p => (
<Media player={p} />
))}
<box className="separator" />
<Notifications />
</box>
);
|