Implemented strict linting standards and prettier formatting config. (#248)
* Implemented strict linting standards and prettier formatting config. * More linter fixes and type updates. * More linter updates and type fixes * Remove noisy comments * Linter and type updates * Linter, formatting and type updates. * Linter updates * Type updates * Type updates * fixed all linter errors * Fixed all linting, formatting and type issues. * Resolve merge conflicts.
This commit is contained in:
@@ -1,25 +1,34 @@
|
||||
const hyprland = await Service.import("hyprland");
|
||||
import { DropdownMenuProps } from "lib/types/dropdownmenu";
|
||||
import { Exclusivity } from "lib/types/widget";
|
||||
import { bash } from "lib/utils";
|
||||
import { Monitor } from "types/service/hyprland";
|
||||
const hyprland = await Service.import('hyprland');
|
||||
import { DropdownMenuProps } from 'lib/types/dropdownmenu';
|
||||
import { Attribute, Child, Exclusivity, GtkWidget } from 'lib/types/widget';
|
||||
import { bash } from 'lib/utils';
|
||||
import { Widget as TWidget } from 'types/@girs/gtk-3.0/gtk-3.0.cjs';
|
||||
import { Monitor } from 'types/service/hyprland';
|
||||
import Box from 'types/widgets/box';
|
||||
import EventBox from 'types/widgets/eventbox';
|
||||
import Revealer from 'types/widgets/revealer';
|
||||
import Window from 'types/widgets/window';
|
||||
|
||||
export const Padding = (name: string) =>
|
||||
type NestedRevealer = Revealer<Box<TWidget, unknown>, unknown>;
|
||||
type NestedBox = Box<NestedRevealer, unknown>;
|
||||
type NestedEventBox = EventBox<NestedBox, unknown>;
|
||||
|
||||
export const Padding = (name: string): EventBox<Box<GtkWidget, Attribute>, Attribute> =>
|
||||
Widget.EventBox({
|
||||
hexpand: true,
|
||||
vexpand: true,
|
||||
can_focus: true,
|
||||
child: Widget.Box(),
|
||||
setup: (w) => w.on("button-press-event", () => App.toggleWindow(name)),
|
||||
setup: (w) => w.on('button-press-event', () => App.toggleWindow(name)),
|
||||
});
|
||||
|
||||
const moveBoxToCursor = (self: any, fixed: boolean) => {
|
||||
const moveBoxToCursor = <T extends NestedEventBox>(self: T, fixed: boolean): void => {
|
||||
if (fixed) {
|
||||
return;
|
||||
}
|
||||
|
||||
globalMousePos.connect("changed", async ({ value }) => {
|
||||
const curHyprlandMonitor = hyprland.monitors.find(m => m.id === hyprland.active.monitor.id);
|
||||
globalMousePos.connect('changed', async ({ value }) => {
|
||||
const curHyprlandMonitor = hyprland.monitors.find((m) => m.id === hyprland.active.monitor.id);
|
||||
const dropdownWidth = self.child.get_allocation().width;
|
||||
|
||||
let hyprScaling = 1;
|
||||
@@ -27,8 +36,8 @@ const moveBoxToCursor = (self: any, fixed: boolean) => {
|
||||
const monitorInfo = await bash('hyprctl monitors -j');
|
||||
const parsedMonitorInfo = JSON.parse(monitorInfo);
|
||||
|
||||
const foundMonitor = parsedMonitorInfo.find((monitor: Monitor) =>
|
||||
monitor.id === hyprland.active.monitor.id
|
||||
const foundMonitor = parsedMonitorInfo.find(
|
||||
(monitor: Monitor) => monitor.id === hyprland.active.monitor.id,
|
||||
);
|
||||
hyprScaling = foundMonitor?.scale || 1;
|
||||
} catch (error) {
|
||||
@@ -58,9 +67,7 @@ const moveBoxToCursor = (self: any, fixed: boolean) => {
|
||||
}
|
||||
|
||||
// If monitor is vertical (transform = 1 || 3) swap height and width
|
||||
const isVertical = curHyprlandMonitor?.transform !== undefined
|
||||
? curHyprlandMonitor.transform % 2 !== 0
|
||||
: false;
|
||||
const isVertical = curHyprlandMonitor?.transform !== undefined ? curHyprlandMonitor.transform % 2 !== 0 : false;
|
||||
|
||||
if (isVertical) {
|
||||
[monWidth, monHeight] = [monHeight, monWidth];
|
||||
@@ -100,58 +107,55 @@ setTimeout(() => {
|
||||
initRender.value = false;
|
||||
}, 2000);
|
||||
|
||||
export default (
|
||||
{
|
||||
name,
|
||||
child,
|
||||
layout = "center",
|
||||
transition,
|
||||
exclusivity = "ignore" as Exclusivity,
|
||||
fixed = false,
|
||||
...props
|
||||
}: DropdownMenuProps
|
||||
) =>
|
||||
export default ({
|
||||
name,
|
||||
child,
|
||||
transition,
|
||||
exclusivity = 'ignore' as Exclusivity,
|
||||
fixed = false,
|
||||
...props
|
||||
}: DropdownMenuProps): Window<Child, Attribute> =>
|
||||
Widget.Window({
|
||||
name,
|
||||
class_names: [name, "dropdown-menu"],
|
||||
setup: (w) => w.keybind("Escape", () => App.closeWindow(name)),
|
||||
visible: initRender.bind("value"),
|
||||
keymode: "on-demand",
|
||||
class_names: [name, 'dropdown-menu'],
|
||||
setup: (w) => w.keybind('Escape', () => App.closeWindow(name)),
|
||||
visible: initRender.bind('value'),
|
||||
keymode: 'on-demand',
|
||||
exclusivity,
|
||||
layer: "top",
|
||||
anchor: ["top", "left"],
|
||||
layer: 'top',
|
||||
anchor: ['top', 'left'],
|
||||
child: Widget.EventBox({
|
||||
class_name: "parent-event",
|
||||
class_name: 'parent-event',
|
||||
on_primary_click: () => App.closeWindow(name),
|
||||
on_secondary_click: () => App.closeWindow(name),
|
||||
child: Widget.Box({
|
||||
class_name: "top-eb",
|
||||
class_name: 'top-eb',
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.EventBox({
|
||||
class_name: "mid-eb event-top-padding-static",
|
||||
class_name: 'mid-eb event-top-padding-static',
|
||||
hexpand: true,
|
||||
vexpand: false,
|
||||
can_focus: false,
|
||||
child: Widget.Box(),
|
||||
setup: (w) => {
|
||||
w.on("button-press-event", () => App.toggleWindow(name));
|
||||
w.on('button-press-event', () => App.toggleWindow(name));
|
||||
w.set_margin_top(1);
|
||||
},
|
||||
}),
|
||||
Widget.EventBox({
|
||||
class_name: "mid-eb event-top-padding",
|
||||
class_name: 'mid-eb event-top-padding',
|
||||
hexpand: true,
|
||||
vexpand: false,
|
||||
can_focus: false,
|
||||
child: Widget.Box(),
|
||||
setup: (w) => {
|
||||
w.on("button-press-event", () => App.toggleWindow(name));
|
||||
w.on('button-press-event', () => App.toggleWindow(name));
|
||||
w.set_margin_top(1);
|
||||
},
|
||||
}),
|
||||
Widget.EventBox({
|
||||
class_name: "in-eb menu-event-box",
|
||||
class_name: 'in-eb menu-event-box',
|
||||
on_primary_click: () => {
|
||||
return true;
|
||||
},
|
||||
@@ -162,18 +166,18 @@ export default (
|
||||
moveBoxToCursor(self, fixed);
|
||||
},
|
||||
child: Widget.Box({
|
||||
class_name: "dropdown-menu-container",
|
||||
css: "padding: 1px; margin: -1px;",
|
||||
class_name: 'dropdown-menu-container',
|
||||
css: 'padding: 1px; margin: -1px;',
|
||||
child: Widget.Revealer({
|
||||
revealChild: false,
|
||||
setup: (self) =>
|
||||
self.hook(App, (_, wname, visible) => {
|
||||
if (wname === name) self.reveal_child = visible;
|
||||
}),
|
||||
transition: "crossfade",
|
||||
transition,
|
||||
transitionDuration: 350,
|
||||
child: Widget.Box({
|
||||
class_name: "dropdown-menu-container",
|
||||
class_name: 'dropdown-menu-container',
|
||||
can_focus: true,
|
||||
children: [child],
|
||||
}),
|
||||
|
||||
@@ -1,25 +1,32 @@
|
||||
import { WINDOW_LAYOUTS } from "globals/window";
|
||||
import { LayoutFunction, Layouts, PopupWindowProps } from "lib/types/popupwindow";
|
||||
import { Exclusivity, Transition } from "lib/types/widget";
|
||||
import { WINDOW_LAYOUTS } from 'globals/window';
|
||||
import { LayoutFunction, Layouts, PopupWindowProps } from 'lib/types/popupwindow';
|
||||
import { Attribute, Child, Exclusivity, GtkWidget, Transition } from 'lib/types/widget';
|
||||
import Box from 'types/widgets/box';
|
||||
import EventBox from 'types/widgets/eventbox';
|
||||
import Window from 'types/widgets/window';
|
||||
|
||||
type Opts = {
|
||||
className: string
|
||||
vexpand: boolean
|
||||
}
|
||||
className: string;
|
||||
vexpand: boolean;
|
||||
};
|
||||
|
||||
export const Padding = (name: string, opts: Opts) =>
|
||||
export const Padding = (name: string, opts: Opts): EventBox<Box<GtkWidget, Attribute>, unknown> =>
|
||||
Widget.EventBox({
|
||||
class_name: opts?.className || "",
|
||||
class_name: opts?.className || '',
|
||||
hexpand: true,
|
||||
vexpand: typeof opts?.vexpand === "boolean" ? opts.vexpand : true,
|
||||
vexpand: typeof opts?.vexpand === 'boolean' ? opts.vexpand : true,
|
||||
can_focus: false,
|
||||
child: Widget.Box(),
|
||||
setup: (w) => w.on("button-press-event", () => App.toggleWindow(name)),
|
||||
setup: (w) => w.on('button-press-event', () => App.toggleWindow(name)),
|
||||
});
|
||||
|
||||
const PopupRevealer = (name: string, child: any, transition = "slide_down" as Transition) =>
|
||||
const PopupRevealer = (
|
||||
name: string,
|
||||
child: GtkWidget,
|
||||
transition = 'slide_down' as Transition,
|
||||
): Box<Child, Attribute> =>
|
||||
Widget.Box(
|
||||
{ css: "padding: 1px;" },
|
||||
{ css: 'padding: 1px;' },
|
||||
Widget.Revealer({
|
||||
transition,
|
||||
child: Widget.Box({
|
||||
@@ -34,7 +41,7 @@ const PopupRevealer = (name: string, child: any, transition = "slide_down" as Tr
|
||||
}),
|
||||
);
|
||||
|
||||
const Layout: LayoutFunction = (name: string, child: any, transition: Transition) => ({
|
||||
const Layout: LayoutFunction = (name: string, child: GtkWidget, transition: Transition) => ({
|
||||
center: () =>
|
||||
Widget.CenterBox(
|
||||
{},
|
||||
@@ -51,14 +58,10 @@ const Layout: LayoutFunction = (name: string, child: any, transition: Transition
|
||||
Widget.CenterBox(
|
||||
{},
|
||||
Padding(name, {} as Opts),
|
||||
Widget.Box(
|
||||
{ vertical: true },
|
||||
PopupRevealer(name, child, transition),
|
||||
Padding(name, {} as Opts),
|
||||
),
|
||||
Widget.Box({ vertical: true }, PopupRevealer(name, child, transition), Padding(name, {} as Opts)),
|
||||
Padding(name, {} as Opts),
|
||||
),
|
||||
"top-right": () =>
|
||||
'top-right': () =>
|
||||
Widget.Box(
|
||||
{},
|
||||
Padding(name, {} as Opts),
|
||||
@@ -69,13 +72,13 @@ const Layout: LayoutFunction = (name: string, child: any, transition: Transition
|
||||
},
|
||||
Padding(name, {
|
||||
vexpand: false,
|
||||
className: "event-top-padding",
|
||||
className: 'event-top-padding',
|
||||
}),
|
||||
PopupRevealer(name, child, transition),
|
||||
Padding(name, {} as Opts),
|
||||
),
|
||||
),
|
||||
"top-center": () =>
|
||||
'top-center': () =>
|
||||
Widget.Box(
|
||||
{},
|
||||
Padding(name, {} as Opts),
|
||||
@@ -86,14 +89,14 @@ const Layout: LayoutFunction = (name: string, child: any, transition: Transition
|
||||
},
|
||||
Padding(name, {
|
||||
vexpand: false,
|
||||
className: "event-top-padding",
|
||||
className: 'event-top-padding',
|
||||
}),
|
||||
PopupRevealer(name, child, transition),
|
||||
Padding(name, {} as Opts),
|
||||
),
|
||||
Padding(name, {} as Opts),
|
||||
),
|
||||
"top-left": () =>
|
||||
'top-left': () =>
|
||||
Widget.Box(
|
||||
{},
|
||||
Widget.Box(
|
||||
@@ -103,14 +106,14 @@ const Layout: LayoutFunction = (name: string, child: any, transition: Transition
|
||||
},
|
||||
Padding(name, {
|
||||
vexpand: false,
|
||||
className: "event-top-padding",
|
||||
className: 'event-top-padding',
|
||||
}),
|
||||
PopupRevealer(name, child, transition),
|
||||
Padding(name, {} as Opts),
|
||||
),
|
||||
Padding(name, {} as Opts),
|
||||
),
|
||||
"bottom-left": () =>
|
||||
'bottom-left': () =>
|
||||
Widget.Box(
|
||||
{},
|
||||
Widget.Box(
|
||||
@@ -123,7 +126,7 @@ const Layout: LayoutFunction = (name: string, child: any, transition: Transition
|
||||
),
|
||||
Padding(name, {} as Opts),
|
||||
),
|
||||
"bottom-center": () =>
|
||||
'bottom-center': () =>
|
||||
Widget.Box(
|
||||
{},
|
||||
Padding(name, {} as Opts),
|
||||
@@ -137,7 +140,7 @@ const Layout: LayoutFunction = (name: string, child: any, transition: Transition
|
||||
),
|
||||
Padding(name, {} as Opts),
|
||||
),
|
||||
"bottom-right": () =>
|
||||
'bottom-right': () =>
|
||||
Widget.Box(
|
||||
{},
|
||||
Padding(name, {} as Opts),
|
||||
@@ -159,25 +162,25 @@ const isValidLayout = (layout: string): layout is Layouts => {
|
||||
export default ({
|
||||
name,
|
||||
child,
|
||||
layout = "center",
|
||||
layout = 'center',
|
||||
transition,
|
||||
exclusivity = "ignore" as Exclusivity,
|
||||
exclusivity = 'ignore' as Exclusivity,
|
||||
...props
|
||||
}: PopupWindowProps) => {
|
||||
const layoutFn = isValidLayout(layout) ? layout : "center";
|
||||
}: PopupWindowProps): Window<Child, Attribute> => {
|
||||
const layoutFn = isValidLayout(layout) ? layout : 'center';
|
||||
|
||||
const layoutWidget = Layout(name, child, transition)[layoutFn]();
|
||||
|
||||
return Widget.Window({
|
||||
name,
|
||||
class_names: [name, "popup-window"],
|
||||
setup: (w) => w.keybind("Escape", () => App.closeWindow(name)),
|
||||
class_names: [name, 'popup-window'],
|
||||
setup: (w) => w.keybind('Escape', () => App.closeWindow(name)),
|
||||
visible: false,
|
||||
keymode: "on-demand",
|
||||
keymode: 'on-demand',
|
||||
exclusivity,
|
||||
layer: "top",
|
||||
anchor: ["top", "bottom", "right", "left"],
|
||||
layer: 'top',
|
||||
anchor: ['top', 'bottom', 'right', 'left'],
|
||||
child: layoutWidget,
|
||||
...props,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,31 +1,36 @@
|
||||
const audio = await Service.import("audio");
|
||||
const audio = await Service.import('audio');
|
||||
import { BarBoxChild } from 'lib/types/bar.js';
|
||||
import { getIcon } from '../utils.js';
|
||||
|
||||
const renderActiveInput = () => {
|
||||
const renderActiveInput = (): BarBoxChild => {
|
||||
return [
|
||||
Widget.Box({
|
||||
class_name: "menu-slider-container input",
|
||||
class_name: 'menu-slider-container input',
|
||||
children: [
|
||||
Widget.Button({
|
||||
vexpand: false,
|
||||
vpack: "end",
|
||||
vpack: 'end',
|
||||
setup: (self) => {
|
||||
self.hook(audio, () => {
|
||||
const mic = audio.microphone;
|
||||
const className = `menu-active-button input ${mic.is_muted ? "muted" : ""}`;
|
||||
const className = `menu-active-button input ${mic.is_muted ? 'muted' : ''}`;
|
||||
return (self.class_name = className);
|
||||
});
|
||||
},
|
||||
on_primary_click: () =>
|
||||
(audio.microphone.is_muted = !audio.microphone.is_muted),
|
||||
on_primary_click: () => (audio.microphone.is_muted = !audio.microphone.is_muted),
|
||||
child: Widget.Icon({
|
||||
class_name: "menu-active-icon input",
|
||||
class_name: 'menu-active-icon input',
|
||||
setup: (self) => {
|
||||
self.hook(audio, () => {
|
||||
self.icon = getIcon(
|
||||
audio.microphone.volume,
|
||||
audio.microphone.is_muted,
|
||||
)["mic"];
|
||||
const isMicMuted =
|
||||
audio.microphone.is_muted !== null ? audio.microphone.is_muted : true;
|
||||
|
||||
if (audio.microphone.volume > 0) {
|
||||
self.icon = getIcon(audio.microphone.volume, isMicMuted)['mic'];
|
||||
return;
|
||||
}
|
||||
|
||||
self.icon = getIcon(100, false)['mic'];
|
||||
});
|
||||
},
|
||||
}),
|
||||
@@ -34,15 +39,17 @@ const renderActiveInput = () => {
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: "menu-active input",
|
||||
hpack: "start",
|
||||
truncate: "end",
|
||||
class_name: 'menu-active input',
|
||||
hpack: 'start',
|
||||
truncate: 'end',
|
||||
wrap: true,
|
||||
label: audio.bind("microphone").as((v) => v.description === null ? "No input device found..." : v.description),
|
||||
label: audio
|
||||
.bind('microphone')
|
||||
.as((v) => (v.description === null ? 'No input device found...' : v.description)),
|
||||
}),
|
||||
Widget.Slider({
|
||||
value: audio.microphone.bind("volume").as((v) => v),
|
||||
class_name: "menu-active-slider menu-slider inputs",
|
||||
value: audio.microphone.bind('volume').as((v) => v),
|
||||
class_name: 'menu-active-slider menu-slider inputs',
|
||||
draw_value: false,
|
||||
hexpand: true,
|
||||
min: 0,
|
||||
@@ -52,11 +59,9 @@ const renderActiveInput = () => {
|
||||
],
|
||||
}),
|
||||
Widget.Label({
|
||||
class_name: "menu-active-percentage input",
|
||||
vpack: "end",
|
||||
label: audio.microphone
|
||||
.bind("volume")
|
||||
.as((v) => `${Math.round(v * 100)}%`),
|
||||
class_name: 'menu-active-percentage input',
|
||||
vpack: 'end',
|
||||
label: audio.microphone.bind('volume').as((v) => `${Math.round(v * 100)}%`),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
|
||||
@@ -1,31 +1,29 @@
|
||||
const audio = await Service.import("audio");
|
||||
import { getIcon } from "../utils.js";
|
||||
const audio = await Service.import('audio');
|
||||
import { BarBoxChild } from 'lib/types/bar.js';
|
||||
import { getIcon } from '../utils.js';
|
||||
|
||||
const renderActivePlayback = () => {
|
||||
const renderActivePlayback = (): BarBoxChild => {
|
||||
return [
|
||||
Widget.Box({
|
||||
class_name: "menu-slider-container playback",
|
||||
class_name: 'menu-slider-container playback',
|
||||
children: [
|
||||
Widget.Button({
|
||||
vexpand: false,
|
||||
vpack: "end",
|
||||
vpack: 'end',
|
||||
setup: (self) => {
|
||||
self.hook(audio, () => {
|
||||
const spkr = audio.speaker;
|
||||
const className = `menu-active-button playback ${spkr.is_muted ? "muted" : ""}`;
|
||||
const className = `menu-active-button playback ${spkr.is_muted ? 'muted' : ''}`;
|
||||
return (self.class_name = className);
|
||||
});
|
||||
},
|
||||
on_primary_click: () =>
|
||||
(audio.speaker.is_muted = !audio.speaker.is_muted),
|
||||
on_primary_click: () => (audio.speaker.is_muted = !audio.speaker.is_muted),
|
||||
child: Widget.Icon({
|
||||
class_name: "menu-active-icon playback",
|
||||
class_name: 'menu-active-icon playback',
|
||||
setup: (self) => {
|
||||
self.hook(audio, () => {
|
||||
self.icon = getIcon(
|
||||
audio.speaker.volume,
|
||||
audio.speaker.is_muted,
|
||||
)["spkr"];
|
||||
const isSpeakerMuted = audio.speaker.is_muted !== null ? audio.speaker.is_muted : true;
|
||||
self.icon = getIcon(audio.speaker.volume, isSpeakerMuted)['spkr'];
|
||||
});
|
||||
},
|
||||
}),
|
||||
@@ -34,16 +32,16 @@ const renderActivePlayback = () => {
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: "menu-active playback",
|
||||
hpack: "start",
|
||||
truncate: "end",
|
||||
class_name: 'menu-active playback',
|
||||
hpack: 'start',
|
||||
truncate: 'end',
|
||||
expand: true,
|
||||
wrap: true,
|
||||
label: audio.bind("speaker").as((v) => v.description || ""),
|
||||
label: audio.bind('speaker').as((v) => v.description || ''),
|
||||
}),
|
||||
Widget.Slider({
|
||||
value: audio["speaker"].bind("volume"),
|
||||
class_name: "menu-active-slider menu-slider playback",
|
||||
value: audio['speaker'].bind('volume'),
|
||||
class_name: 'menu-active-slider menu-slider playback',
|
||||
draw_value: false,
|
||||
hexpand: true,
|
||||
min: 0,
|
||||
@@ -53,11 +51,9 @@ const renderActivePlayback = () => {
|
||||
],
|
||||
}),
|
||||
Widget.Label({
|
||||
vpack: "end",
|
||||
class_name: "menu-active-percentage playback",
|
||||
label: audio.speaker
|
||||
.bind("volume")
|
||||
.as((v) => `${Math.round(v * 100)}%`),
|
||||
vpack: 'end',
|
||||
class_name: 'menu-active-percentage playback',
|
||||
label: audio.speaker.bind('volume').as((v) => `${Math.round(v * 100)}%`),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
|
||||
@@ -1,39 +1,40 @@
|
||||
import { renderActiveInput } from "./SelectedInput.js";
|
||||
import { renderActivePlayback } from "./SelectedPlayback.js";
|
||||
import { BarBoxChild } from 'lib/types/bar.js';
|
||||
import { renderActiveInput } from './SelectedInput.js';
|
||||
import { renderActivePlayback } from './SelectedPlayback.js';
|
||||
|
||||
const activeDevices = () => {
|
||||
return Widget.Box({
|
||||
class_name: "menu-section-container volume",
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "menu-label-container volume selected",
|
||||
hpack: "fill",
|
||||
child: Widget.Label({
|
||||
class_name: "menu-label audio volume",
|
||||
hexpand: true,
|
||||
hpack: "start",
|
||||
label: "Volume",
|
||||
}),
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "menu-items-section selected",
|
||||
const activeDevices = (): BarBoxChild => {
|
||||
return Widget.Box({
|
||||
class_name: 'menu-section-container volume',
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "menu-active-container playback",
|
||||
vertical: true,
|
||||
children: renderActivePlayback(),
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "menu-active-container input",
|
||||
vertical: true,
|
||||
children: renderActiveInput(),
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: 'menu-label-container volume selected',
|
||||
hpack: 'fill',
|
||||
child: Widget.Label({
|
||||
class_name: 'menu-label audio volume',
|
||||
hexpand: true,
|
||||
hpack: 'start',
|
||||
label: 'Volume',
|
||||
}),
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: 'menu-items-section selected',
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: 'menu-active-container playback',
|
||||
vertical: true,
|
||||
children: renderActivePlayback(),
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: 'menu-active-container input',
|
||||
vertical: true,
|
||||
children: renderActiveInput(),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export { activeDevices };
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
const audio = await Service.import("audio");
|
||||
import { Stream } from "types/service/audio";
|
||||
const audio = await Service.import('audio');
|
||||
import { InputDevices } from 'lib/types/audio';
|
||||
import { Stream } from 'types/service/audio';
|
||||
|
||||
const renderInputDevices = (inputDevices: Stream[]) => {
|
||||
const renderInputDevices = (inputDevices: Stream[]): InputDevices => {
|
||||
if (inputDevices.length === 0) {
|
||||
return [
|
||||
Widget.Button({
|
||||
@@ -9,11 +10,11 @@ const renderInputDevices = (inputDevices: Stream[]) => {
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
Widget.Box({
|
||||
hpack: "start",
|
||||
hpack: 'start',
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: "menu-button-name input",
|
||||
label: "No input devices found...",
|
||||
class_name: 'menu-button-name input',
|
||||
label: 'No input devices found...',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
@@ -29,28 +30,28 @@ const renderInputDevices = (inputDevices: Stream[]) => {
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
Widget.Box({
|
||||
hpack: "start",
|
||||
hpack: 'start',
|
||||
children: [
|
||||
Widget.Label({
|
||||
wrap: true,
|
||||
class_name: audio.microphone
|
||||
.bind("description")
|
||||
.bind('description')
|
||||
.as((v) =>
|
||||
device.description === v
|
||||
? "menu-button-icon active input txt-icon"
|
||||
: "menu-button-icon input txt-icon",
|
||||
? 'menu-button-icon active input txt-icon'
|
||||
: 'menu-button-icon input txt-icon',
|
||||
),
|
||||
label: "",
|
||||
label: '',
|
||||
}),
|
||||
Widget.Label({
|
||||
truncate: "end",
|
||||
truncate: 'end',
|
||||
wrap: true,
|
||||
class_name: audio.microphone
|
||||
.bind("description")
|
||||
.bind('description')
|
||||
.as((v) =>
|
||||
device.description === v
|
||||
? "menu-button-name active input"
|
||||
: "menu-button-name input",
|
||||
? 'menu-button-name active input'
|
||||
: 'menu-button-name input',
|
||||
),
|
||||
label: device.description,
|
||||
}),
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
const audio = await Service.import("audio");
|
||||
import { Stream } from "types/service/audio";
|
||||
const audio = await Service.import('audio');
|
||||
import { PlaybackDevices } from 'lib/types/audio';
|
||||
import { Stream } from 'types/service/audio';
|
||||
|
||||
const renderPlaybacks = (playbackDevices: Stream[]) => {
|
||||
const renderPlaybacks = (playbackDevices: Stream[]): PlaybackDevices => {
|
||||
return playbackDevices.map((device) => {
|
||||
if (device.description === "Dummy Output") {
|
||||
if (device.description === 'Dummy Output') {
|
||||
return Widget.Box({
|
||||
class_name: "menu-unfound-button playback",
|
||||
class_name: 'menu-unfound-button playback',
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: "menu-button-name playback",
|
||||
label: "No playback devices found...",
|
||||
class_name: 'menu-button-name playback',
|
||||
label: 'No playback devices found...',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
@@ -22,29 +23,29 @@ const renderPlaybacks = (playbackDevices: Stream[]) => {
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
Widget.Box({
|
||||
hpack: "start",
|
||||
hpack: 'start',
|
||||
children: [
|
||||
Widget.Label({
|
||||
truncate: "end",
|
||||
truncate: 'end',
|
||||
wrap: true,
|
||||
class_name: audio.speaker
|
||||
.bind("description")
|
||||
.bind('description')
|
||||
.as((v) =>
|
||||
device.description === v
|
||||
? "menu-button-icon active playback txt-icon"
|
||||
: "menu-button-icon playback txt-icon",
|
||||
? 'menu-button-icon active playback txt-icon'
|
||||
: 'menu-button-icon playback txt-icon',
|
||||
),
|
||||
label: "",
|
||||
label: '',
|
||||
}),
|
||||
Widget.Label({
|
||||
truncate: "end",
|
||||
truncate: 'end',
|
||||
wrap: true,
|
||||
class_name: audio.speaker
|
||||
.bind("description")
|
||||
.bind('description')
|
||||
.as((v) =>
|
||||
device.description === v
|
||||
? "menu-button-name active playback"
|
||||
: "menu-button-name playback",
|
||||
? 'menu-button-name active playback'
|
||||
: 'menu-button-name playback',
|
||||
),
|
||||
label: device.description,
|
||||
}),
|
||||
|
||||
@@ -1,66 +1,63 @@
|
||||
const audio = await Service.import("audio");
|
||||
import { renderInputDevices } from "./InputDevices.js";
|
||||
import { renderPlaybacks } from "./PlaybackDevices.js";
|
||||
const audio = await Service.import('audio');
|
||||
import { BoxWidget } from 'lib/types/widget.js';
|
||||
import { renderInputDevices } from './InputDevices.js';
|
||||
import { renderPlaybacks } from './PlaybackDevices.js';
|
||||
|
||||
const availableDevices = () => {
|
||||
const availableDevices = (): BoxWidget => {
|
||||
return Widget.Box({
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "menu-section-container playback",
|
||||
class_name: 'menu-section-container playback',
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "menu-label-container playback",
|
||||
hpack: "fill",
|
||||
class_name: 'menu-label-container playback',
|
||||
hpack: 'fill',
|
||||
child: Widget.Label({
|
||||
class_name: "menu-label audio playback",
|
||||
class_name: 'menu-label audio playback',
|
||||
hexpand: true,
|
||||
hpack: "start",
|
||||
label: "Playback Devices",
|
||||
hpack: 'start',
|
||||
label: 'Playback Devices',
|
||||
}),
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "menu-items-section playback",
|
||||
class_name: 'menu-items-section playback',
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "menu-container playback",
|
||||
class_name: 'menu-container playback',
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
children: audio
|
||||
.bind("speakers")
|
||||
.as((v) => renderPlaybacks(v)),
|
||||
children: audio.bind('speakers').as((v) => renderPlaybacks(v)),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "menu-label-container input",
|
||||
hpack: "fill",
|
||||
class_name: 'menu-label-container input',
|
||||
hpack: 'fill',
|
||||
child: Widget.Label({
|
||||
class_name: "menu-label audio input",
|
||||
class_name: 'menu-label audio input',
|
||||
hexpand: true,
|
||||
hpack: "start",
|
||||
label: "Input Devices",
|
||||
hpack: 'start',
|
||||
label: 'Input Devices',
|
||||
}),
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "menu-items-section input",
|
||||
class_name: 'menu-items-section input',
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "menu-container input",
|
||||
class_name: 'menu-container input',
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
children: audio
|
||||
.bind("microphones")
|
||||
.as((v) => renderInputDevices(v)),
|
||||
children: audio.bind('microphones').as((v) => renderInputDevices(v)),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
import DropdownMenu from "../DropdownMenu.js";
|
||||
import { activeDevices } from "./active/index.js";
|
||||
import { availableDevices } from "./available/index.js";
|
||||
import Window from 'types/widgets/window.js';
|
||||
import DropdownMenu from '../DropdownMenu.js';
|
||||
import { activeDevices } from './active/index.js';
|
||||
import { availableDevices } from './available/index.js';
|
||||
import { Attribute, Child } from 'lib/types/widget.js';
|
||||
|
||||
export default () => {
|
||||
export default (): Window<Child, Attribute> => {
|
||||
return DropdownMenu({
|
||||
name: "audiomenu",
|
||||
transition: "crossfade",
|
||||
name: 'audiomenu',
|
||||
transition: 'crossfade',
|
||||
child: Widget.Box({
|
||||
class_name: "menu-items audio",
|
||||
hpack: "fill",
|
||||
class_name: 'menu-items audio',
|
||||
hpack: 'fill',
|
||||
hexpand: true,
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
hpack: "fill",
|
||||
hpack: 'fill',
|
||||
hexpand: true,
|
||||
class_name: "menu-items-container audio",
|
||||
children: [
|
||||
activeDevices(),
|
||||
availableDevices(),
|
||||
],
|
||||
class_name: 'menu-items-container audio',
|
||||
children: [activeDevices(), availableDevices()],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -1,26 +1,24 @@
|
||||
const speakerIcons = {
|
||||
101: "audio-volume-overamplified-symbolic",
|
||||
66: "audio-volume-high-symbolic",
|
||||
34: "audio-volume-medium-symbolic",
|
||||
1: "audio-volume-low-symbolic",
|
||||
0: "audio-volume-muted-symbolic",
|
||||
101: 'audio-volume-overamplified-symbolic',
|
||||
66: 'audio-volume-high-symbolic',
|
||||
34: 'audio-volume-medium-symbolic',
|
||||
1: 'audio-volume-low-symbolic',
|
||||
0: 'audio-volume-muted-symbolic',
|
||||
} as const;
|
||||
|
||||
const inputIcons = {
|
||||
101: "microphone-sensitivity-high-symbolic",
|
||||
66: "microphone-sensitivity-high-symbolic",
|
||||
34: "microphone-sensitivity-medium-symbolic",
|
||||
1: "microphone-sensitivity-low-symbolic",
|
||||
0: "microphone-disabled-symbolic",
|
||||
101: 'microphone-sensitivity-high-symbolic',
|
||||
66: 'microphone-sensitivity-high-symbolic',
|
||||
34: 'microphone-sensitivity-medium-symbolic',
|
||||
1: 'microphone-sensitivity-low-symbolic',
|
||||
0: 'microphone-disabled-symbolic',
|
||||
};
|
||||
|
||||
type IconVolumes = keyof typeof speakerIcons;
|
||||
|
||||
const getIcon = (audioVol: IconVolumes, isMuted: boolean) => {
|
||||
const getIcon = (audioVol: number, isMuted: boolean): Record<string, string> => {
|
||||
const thresholds: IconVolumes[] = [101, 66, 34, 1, 0];
|
||||
const icon = isMuted
|
||||
? 0
|
||||
: thresholds.find((threshold) => threshold <= audioVol * 100) || 0;
|
||||
const icon = isMuted ? 0 : thresholds.find((threshold) => threshold <= audioVol * 100) || 0;
|
||||
|
||||
return {
|
||||
spkr: speakerIcons[icon],
|
||||
|
||||
@@ -1,74 +1,67 @@
|
||||
import { BluetoothDevice } from "types/service/bluetooth";
|
||||
import { BoxWidget } from 'lib/types/widget';
|
||||
import { BluetoothDevice } from 'types/service/bluetooth';
|
||||
|
||||
const connectedControls = (dev: BluetoothDevice, connectedDevices: BluetoothDevice[]) => {
|
||||
const connectedControls = (dev: BluetoothDevice, connectedDevices: BluetoothDevice[]): BoxWidget => {
|
||||
if (!connectedDevices.includes(dev.address)) {
|
||||
return Widget.Box({});
|
||||
}
|
||||
|
||||
return Widget.Box({
|
||||
vpack: "start",
|
||||
class_name: "bluetooth-controls",
|
||||
vpack: 'start',
|
||||
class_name: 'bluetooth-controls',
|
||||
children: [
|
||||
Widget.Button({
|
||||
class_name: "menu-icon-button unpair bluetooth",
|
||||
class_name: 'menu-icon-button unpair bluetooth',
|
||||
child: Widget.Label({
|
||||
tooltip_text: dev.paired ? "Unpair" : "Pair",
|
||||
class_name: "menu-icon-button-label unpair bluetooth txt-icon",
|
||||
label: dev.paired ? "" : "",
|
||||
tooltip_text: dev.paired ? 'Unpair' : 'Pair',
|
||||
class_name: 'menu-icon-button-label unpair bluetooth txt-icon',
|
||||
label: dev.paired ? '' : '',
|
||||
}),
|
||||
on_primary_click: () =>
|
||||
Utils.execAsync([
|
||||
"bash",
|
||||
"-c",
|
||||
`bluetoothctl ${dev.paired ? "unpair" : "pair"} ${dev.address}`,
|
||||
'bash',
|
||||
'-c',
|
||||
`bluetoothctl ${dev.paired ? 'unpair' : 'pair'} ${dev.address}`,
|
||||
]).catch((err) =>
|
||||
console.error(
|
||||
`bluetoothctl ${dev.paired ? "unpair" : "pair"} ${dev.address}`,
|
||||
err,
|
||||
),
|
||||
console.error(`bluetoothctl ${dev.paired ? 'unpair' : 'pair'} ${dev.address}`, err),
|
||||
),
|
||||
}),
|
||||
Widget.Button({
|
||||
class_name: "menu-icon-button disconnect bluetooth",
|
||||
class_name: 'menu-icon-button disconnect bluetooth',
|
||||
child: Widget.Label({
|
||||
tooltip_text: dev.connected ? "Disconnect" : "Connect",
|
||||
class_name: "menu-icon-button-label disconnect bluetooth txt-icon",
|
||||
label: dev.connected ? "" : "",
|
||||
tooltip_text: dev.connected ? 'Disconnect' : 'Connect',
|
||||
class_name: 'menu-icon-button-label disconnect bluetooth txt-icon',
|
||||
label: dev.connected ? '' : '',
|
||||
}),
|
||||
on_primary_click: () => dev.setConnection(!dev.connected),
|
||||
}),
|
||||
Widget.Button({
|
||||
class_name: "menu-icon-button untrust bluetooth",
|
||||
class_name: 'menu-icon-button untrust bluetooth',
|
||||
child: Widget.Label({
|
||||
tooltip_text: dev.trusted ? "Untrust" : "Trust",
|
||||
class_name: "menu-icon-button-label untrust bluetooth txt-icon",
|
||||
label: dev.trusted ? "" : "",
|
||||
tooltip_text: dev.trusted ? 'Untrust' : 'Trust',
|
||||
class_name: 'menu-icon-button-label untrust bluetooth txt-icon',
|
||||
label: dev.trusted ? '' : '',
|
||||
}),
|
||||
on_primary_click: () =>
|
||||
Utils.execAsync([
|
||||
"bash",
|
||||
"-c",
|
||||
`bluetoothctl ${dev.trusted ? "untrust" : "trust"} ${dev.address}`,
|
||||
'bash',
|
||||
'-c',
|
||||
`bluetoothctl ${dev.trusted ? 'untrust' : 'trust'} ${dev.address}`,
|
||||
]).catch((err) =>
|
||||
console.error(
|
||||
`bluetoothctl ${dev.trusted ? "untrust" : "trust"} ${dev.address}`,
|
||||
err,
|
||||
),
|
||||
console.error(`bluetoothctl ${dev.trusted ? 'untrust' : 'trust'} ${dev.address}`, err),
|
||||
),
|
||||
}),
|
||||
Widget.Button({
|
||||
class_name: "menu-icon-button delete bluetooth",
|
||||
class_name: 'menu-icon-button delete bluetooth',
|
||||
child: Widget.Label({
|
||||
tooltip_text: "Forget",
|
||||
class_name: "menu-icon-button-label delete bluetooth txt-icon",
|
||||
label: "",
|
||||
tooltip_text: 'Forget',
|
||||
class_name: 'menu-icon-button-label delete bluetooth txt-icon',
|
||||
label: '',
|
||||
}),
|
||||
on_primary_click: () => {
|
||||
Utils.execAsync([
|
||||
"bash",
|
||||
"-c",
|
||||
`bluetoothctl remove ${dev.address}`,
|
||||
]).catch((err) => console.error("Bluetooth Remove", err));
|
||||
Utils.execAsync(['bash', '-c', `bluetoothctl remove ${dev.address}`]).catch((err) =>
|
||||
console.error('Bluetooth Remove', err),
|
||||
);
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -1,32 +1,31 @@
|
||||
import { Bluetooth } from "types/service/bluetooth.js";
|
||||
import Box from "types/widgets/box.js";
|
||||
import { connectedControls } from "./connectedControls.js";
|
||||
import { getBluetoothIcon } from "../utils.js";
|
||||
import Gtk from "types/@girs/gtk-3.0/gtk-3.0.js";
|
||||
import { Bluetooth } from 'types/service/bluetooth.js';
|
||||
import Box from 'types/widgets/box.js';
|
||||
import { connectedControls } from './connectedControls.js';
|
||||
import { getBluetoothIcon } from '../utils.js';
|
||||
import Gtk from 'types/@girs/gtk-3.0/gtk-3.0.js';
|
||||
import { Attribute, Child } from 'lib/types/widget.js';
|
||||
|
||||
const devices = (bluetooth: Bluetooth, self: Box<Gtk.Widget, unknown>) => {
|
||||
const devices = (bluetooth: Bluetooth, self: Box<Gtk.Widget, unknown>): Box<Child, Attribute> => {
|
||||
return self.hook(bluetooth, () => {
|
||||
if (!bluetooth.enabled) {
|
||||
return (self.child = Widget.Box({
|
||||
class_name: "bluetooth-items",
|
||||
class_name: 'bluetooth-items',
|
||||
vertical: true,
|
||||
expand: true,
|
||||
vpack: "center",
|
||||
hpack: "center",
|
||||
vpack: 'center',
|
||||
hpack: 'center',
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: "bluetooth-disabled dim",
|
||||
class_name: 'bluetooth-disabled dim',
|
||||
hexpand: true,
|
||||
label: "Bluetooth is disabled",
|
||||
label: 'Bluetooth is disabled',
|
||||
}),
|
||||
],
|
||||
}));
|
||||
}
|
||||
|
||||
const availableDevices = bluetooth.devices
|
||||
.filter(
|
||||
(btDev) => btDev.name !== null,
|
||||
)
|
||||
.filter((btDev) => btDev.name !== null)
|
||||
.sort((a, b) => {
|
||||
if (a.connected || a.paired) {
|
||||
return -1;
|
||||
@@ -39,25 +38,23 @@ const devices = (bluetooth: Bluetooth, self: Box<Gtk.Widget, unknown>) => {
|
||||
return b.name - a.name;
|
||||
});
|
||||
|
||||
const conDevNames = availableDevices
|
||||
.filter((d) => d.connected || d.paired)
|
||||
.map((d) => d.address);
|
||||
const conDevNames = availableDevices.filter((d) => d.connected || d.paired).map((d) => d.address);
|
||||
|
||||
if (!availableDevices.length) {
|
||||
return (self.child = Widget.Box({
|
||||
class_name: "bluetooth-items",
|
||||
class_name: 'bluetooth-items',
|
||||
vertical: true,
|
||||
expand: true,
|
||||
vpack: "center",
|
||||
hpack: "center",
|
||||
vpack: 'center',
|
||||
hpack: 'center',
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: "no-bluetooth-devices dim",
|
||||
class_name: 'no-bluetooth-devices dim',
|
||||
hexpand: true,
|
||||
label: "No devices currently found",
|
||||
label: 'No devices currently found',
|
||||
}),
|
||||
Widget.Label({
|
||||
class_name: "search-bluetooth-label dim",
|
||||
class_name: 'search-bluetooth-label dim',
|
||||
hexpand: true,
|
||||
label: "Press '' to search",
|
||||
}),
|
||||
@@ -74,41 +71,40 @@ const devices = (bluetooth: Bluetooth, self: Box<Gtk.Widget, unknown>) => {
|
||||
hexpand: true,
|
||||
class_name: `bluetooth-element-item ${device}`,
|
||||
on_primary_click: () => {
|
||||
if (!conDevNames.includes(device.address))
|
||||
device.setConnection(true);
|
||||
if (!conDevNames.includes(device.address)) device.setConnection(true);
|
||||
},
|
||||
child: Widget.Box({
|
||||
hexpand: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
hexpand: true,
|
||||
hpack: "start",
|
||||
class_name: "menu-button-container",
|
||||
hpack: 'start',
|
||||
class_name: 'menu-button-container',
|
||||
children: [
|
||||
Widget.Label({
|
||||
vpack: "start",
|
||||
class_name: `menu-button-icon bluetooth ${conDevNames.includes(device.address) ? "active" : ""} txt-icon`,
|
||||
label: getBluetoothIcon(`${device["icon_name"]}-symbolic`),
|
||||
vpack: 'start',
|
||||
class_name: `menu-button-icon bluetooth ${conDevNames.includes(device.address) ? 'active' : ''} txt-icon`,
|
||||
label: getBluetoothIcon(`${device['icon_name']}-symbolic`),
|
||||
}),
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
vpack: "center",
|
||||
vpack: 'center',
|
||||
children: [
|
||||
Widget.Label({
|
||||
vpack: "center",
|
||||
hpack: "start",
|
||||
class_name: "menu-button-name bluetooth",
|
||||
truncate: "end",
|
||||
vpack: 'center',
|
||||
hpack: 'start',
|
||||
class_name: 'menu-button-name bluetooth',
|
||||
truncate: 'end',
|
||||
wrap: true,
|
||||
label: device.alias,
|
||||
}),
|
||||
Widget.Revealer({
|
||||
hpack: "start",
|
||||
hpack: 'start',
|
||||
reveal_child: device.connected || device.paired,
|
||||
child: Widget.Label({
|
||||
hpack: "start",
|
||||
class_name: "connection-status dim",
|
||||
label: device.connected ? "Connected" : "Paired",
|
||||
hpack: 'start',
|
||||
class_name: 'connection-status dim',
|
||||
label: device.connected ? 'Connected' : 'Paired',
|
||||
}),
|
||||
}),
|
||||
],
|
||||
@@ -116,14 +112,14 @@ const devices = (bluetooth: Bluetooth, self: Box<Gtk.Widget, unknown>) => {
|
||||
],
|
||||
}),
|
||||
Widget.Box({
|
||||
hpack: "end",
|
||||
hpack: 'end',
|
||||
children: device.connecting
|
||||
? [
|
||||
Widget.Spinner({
|
||||
vpack: "start",
|
||||
class_name: "spinner bluetooth",
|
||||
}),
|
||||
]
|
||||
Widget.Spinner({
|
||||
vpack: 'start',
|
||||
class_name: 'spinner bluetooth',
|
||||
}),
|
||||
]
|
||||
: [],
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
const bluetooth = await Service.import("bluetooth");
|
||||
import { label } from "./label.js";
|
||||
import { devices } from "./devicelist.js";
|
||||
const bluetooth = await Service.import('bluetooth');
|
||||
import { label } from './label.js';
|
||||
import { devices } from './devicelist.js';
|
||||
import { BoxWidget } from 'lib/types/widget.js';
|
||||
|
||||
const Devices = () => {
|
||||
const Devices = (): BoxWidget => {
|
||||
return Widget.Box({
|
||||
class_name: "menu-section-container",
|
||||
class_name: 'menu-section-container',
|
||||
vertical: true,
|
||||
children: [
|
||||
label(bluetooth),
|
||||
Widget.Box({
|
||||
class_name: "menu-items-section",
|
||||
class_name: 'menu-items-section',
|
||||
child: Widget.Box({
|
||||
class_name: "menu-content",
|
||||
class_name: 'menu-content',
|
||||
vertical: true,
|
||||
setup: (self) => {
|
||||
devices(bluetooth, self);
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { Bluetooth } from "types/service/bluetooth";
|
||||
const label = (bluetooth: Bluetooth) => {
|
||||
import { BoxWidget } from 'lib/types/widget';
|
||||
import { Bluetooth } from 'types/service/bluetooth';
|
||||
|
||||
const label = (bluetooth: Bluetooth): BoxWidget => {
|
||||
const searchInProgress = Variable(false);
|
||||
const startRotation = () => {
|
||||
|
||||
const startRotation = (): void => {
|
||||
searchInProgress.value = true;
|
||||
setTimeout(() => {
|
||||
searchInProgress.value = false;
|
||||
@@ -9,61 +12,48 @@ const label = (bluetooth: Bluetooth) => {
|
||||
};
|
||||
|
||||
return Widget.Box({
|
||||
class_name: "menu-label-container",
|
||||
hpack: "fill",
|
||||
vpack: "start",
|
||||
class_name: 'menu-label-container',
|
||||
hpack: 'fill',
|
||||
vpack: 'start',
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: "menu-label",
|
||||
vpack: "center",
|
||||
hpack: "start",
|
||||
label: "Bluetooth",
|
||||
class_name: 'menu-label',
|
||||
vpack: 'center',
|
||||
hpack: 'start',
|
||||
label: 'Bluetooth',
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "controls-container",
|
||||
vpack: "start",
|
||||
class_name: 'controls-container',
|
||||
vpack: 'start',
|
||||
children: [
|
||||
Widget.Switch({
|
||||
class_name: "menu-switch bluetooth",
|
||||
class_name: 'menu-switch bluetooth',
|
||||
hexpand: true,
|
||||
hpack: "end",
|
||||
active: bluetooth.bind("enabled"),
|
||||
hpack: 'end',
|
||||
active: bluetooth.bind('enabled'),
|
||||
on_activate: ({ active }) => {
|
||||
searchInProgress.value = false;
|
||||
Utils.execAsync([
|
||||
"bash",
|
||||
"-c",
|
||||
`bluetoothctl power ${active ? "on" : "off"}`,
|
||||
]).catch((err) =>
|
||||
console.error(
|
||||
`bluetoothctl power ${active ? "on" : "off"}`,
|
||||
err,
|
||||
),
|
||||
Utils.execAsync(['bash', '-c', `bluetoothctl power ${active ? 'on' : 'off'}`]).catch(
|
||||
(err) => console.error(`bluetoothctl power ${active ? 'on' : 'off'}`, err),
|
||||
);
|
||||
},
|
||||
}),
|
||||
Widget.Separator({
|
||||
class_name: "menu-separator bluetooth",
|
||||
class_name: 'menu-separator bluetooth',
|
||||
}),
|
||||
Widget.Button({
|
||||
vpack: "center",
|
||||
class_name: "menu-icon-button search",
|
||||
vpack: 'center',
|
||||
class_name: 'menu-icon-button search',
|
||||
on_primary_click: () => {
|
||||
startRotation();
|
||||
Utils.execAsync([
|
||||
"bash",
|
||||
"-c",
|
||||
"bluetoothctl --timeout 120 scan on",
|
||||
]).catch((err) => {
|
||||
Utils.execAsync(['bash', '-c', 'bluetoothctl --timeout 120 scan on']).catch((err) => {
|
||||
searchInProgress.value = false;
|
||||
console.error("bluetoothctl --timeout 120 scan on", err);
|
||||
console.error('bluetoothctl --timeout 120 scan on', err);
|
||||
});
|
||||
},
|
||||
child: Widget.Icon({
|
||||
class_name: searchInProgress
|
||||
.bind("value")
|
||||
.as((v) => (v ? "spinning" : "")),
|
||||
icon: "view-refresh-symbolic",
|
||||
class_name: searchInProgress.bind('value').as((v) => (v ? 'spinning' : '')),
|
||||
icon: 'view-refresh-symbolic',
|
||||
}),
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
import DropdownMenu from "../DropdownMenu.js";
|
||||
import { Devices } from "./devices/index.js";
|
||||
import Window from 'types/widgets/window.js';
|
||||
import DropdownMenu from '../DropdownMenu.js';
|
||||
import { Devices } from './devices/index.js';
|
||||
import { Attribute, Child } from 'lib/types/widget.js';
|
||||
|
||||
export default () => {
|
||||
return DropdownMenu({
|
||||
name: "bluetoothmenu",
|
||||
transition: "crossfade",
|
||||
child: Widget.Box({
|
||||
class_name: "menu-items bluetooth",
|
||||
hpack: "fill",
|
||||
hexpand: true,
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
hpack: "fill",
|
||||
hexpand: true,
|
||||
class_name: "menu-items-container bluetooth",
|
||||
child: Devices(),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
export default (): Window<Child, Attribute> => {
|
||||
return DropdownMenu({
|
||||
name: 'bluetoothmenu',
|
||||
transition: 'crossfade',
|
||||
child: Widget.Box({
|
||||
class_name: 'menu-items bluetooth',
|
||||
hpack: 'fill',
|
||||
hexpand: true,
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
hpack: 'fill',
|
||||
hexpand: true,
|
||||
class_name: 'menu-items-container bluetooth',
|
||||
child: Devices(),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,31 +1,29 @@
|
||||
const getBluetoothIcon = (iconName: string) => {
|
||||
const getBluetoothIcon = (iconName: string): string => {
|
||||
const deviceIconMap = [
|
||||
["^audio-card*", ""],
|
||||
["^audio-headphones*", ""],
|
||||
["^audio-headset*", ""],
|
||||
["^audio-input*", ""],
|
||||
["^audio-speakers*", ""],
|
||||
["^bluetooth*", ""],
|
||||
["^camera*", ""],
|
||||
["^computer*", ""],
|
||||
["^input-gaming*", ""],
|
||||
["^input-keyboard*", ""],
|
||||
["^input-mouse*", ""],
|
||||
["^input-tablet*", ""],
|
||||
["^media*", ""],
|
||||
["^modem*", ""],
|
||||
["^network*", ""],
|
||||
["^phone*", ""],
|
||||
["^printer*", ""],
|
||||
["^scanner*", ""],
|
||||
["^video-camera*", ""],
|
||||
['^audio-card*', ''],
|
||||
['^audio-headphones*', ''],
|
||||
['^audio-headset*', ''],
|
||||
['^audio-input*', ''],
|
||||
['^audio-speakers*', ''],
|
||||
['^bluetooth*', ''],
|
||||
['^camera*', ''],
|
||||
['^computer*', ''],
|
||||
['^input-gaming*', ''],
|
||||
['^input-keyboard*', ''],
|
||||
['^input-mouse*', ''],
|
||||
['^input-tablet*', ''],
|
||||
['^media*', ''],
|
||||
['^modem*', ''],
|
||||
['^network*', ''],
|
||||
['^phone*', ''],
|
||||
['^printer*', ''],
|
||||
['^scanner*', ''],
|
||||
['^video-camera*', ''],
|
||||
];
|
||||
|
||||
const foundMatch = deviceIconMap.find((icon) =>
|
||||
RegExp(icon[0]).test(iconName.toLowerCase()),
|
||||
);
|
||||
const foundMatch = deviceIconMap.find((icon) => RegExp(icon[0]).test(iconName.toLowerCase()));
|
||||
|
||||
return foundMatch ? foundMatch[1] : "";
|
||||
return foundMatch ? foundMatch[1] : '';
|
||||
};
|
||||
|
||||
export { getBluetoothIcon };
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
const CalendarWidget = () => {
|
||||
return Widget.Box({
|
||||
class_name: "calendar-menu-item-container calendar",
|
||||
hpack: "fill",
|
||||
vpack: "fill",
|
||||
expand: true,
|
||||
child: Widget.Box({
|
||||
class_name: "calendar-container-box",
|
||||
child: Widget.Calendar({
|
||||
import { BoxWidget } from 'lib/types/widget';
|
||||
|
||||
const CalendarWidget = (): BoxWidget => {
|
||||
return Widget.Box({
|
||||
class_name: 'calendar-menu-item-container calendar',
|
||||
hpack: 'fill',
|
||||
vpack: 'fill',
|
||||
expand: true,
|
||||
hpack: "fill",
|
||||
vpack: "fill",
|
||||
class_name: "calendar-menu-widget",
|
||||
showDayNames: true,
|
||||
showDetails: false,
|
||||
showHeading: true,
|
||||
}),
|
||||
}),
|
||||
});
|
||||
child: Widget.Box({
|
||||
class_name: 'calendar-container-box',
|
||||
child: Widget.Calendar({
|
||||
expand: true,
|
||||
hpack: 'fill',
|
||||
vpack: 'fill',
|
||||
class_name: 'calendar-menu-widget',
|
||||
showDayNames: true,
|
||||
showDetails: false,
|
||||
showHeading: true,
|
||||
}),
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
export { CalendarWidget };
|
||||
|
||||
@@ -1,29 +1,31 @@
|
||||
import DropdownMenu from "../DropdownMenu.js";
|
||||
import { TimeWidget } from "./time/index.js";
|
||||
import { CalendarWidget } from "./calendar.js";
|
||||
import { WeatherWidget } from "./weather/index.js";
|
||||
import DropdownMenu from '../DropdownMenu.js';
|
||||
import { TimeWidget } from './time/index.js';
|
||||
import { CalendarWidget } from './calendar.js';
|
||||
import { WeatherWidget } from './weather/index.js';
|
||||
import Window from 'types/widgets/window.js';
|
||||
import { Attribute, Child } from 'lib/types/widget.js';
|
||||
|
||||
export default () => {
|
||||
return DropdownMenu({
|
||||
name: "calendarmenu",
|
||||
transition: "crossfade",
|
||||
child: Widget.Box({
|
||||
class_name: "calendar-menu-content",
|
||||
css: "padding: 1px; margin: -1px;",
|
||||
vexpand: false,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "calendar-content-container",
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "calendar-content-items",
|
||||
vertical: true,
|
||||
children: [TimeWidget(), CalendarWidget(), WeatherWidget()],
|
||||
}),
|
||||
],
|
||||
export default (): Window<Child, Attribute> => {
|
||||
return DropdownMenu({
|
||||
name: 'calendarmenu',
|
||||
transition: 'crossfade',
|
||||
child: Widget.Box({
|
||||
class_name: 'calendar-menu-content',
|
||||
css: 'padding: 1px; margin: -1px;',
|
||||
vexpand: false,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: 'calendar-content-container',
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: 'calendar-content-items',
|
||||
vertical: true,
|
||||
children: [TimeWidget(), CalendarWidget(), WeatherWidget()],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,69 +1,70 @@
|
||||
import options from "options";
|
||||
import { BoxWidget } from 'lib/types/widget';
|
||||
import options from 'options';
|
||||
|
||||
const { military } = options.menus.clock.time;
|
||||
|
||||
const time = Variable("", {
|
||||
poll: [1000, 'date "+%I:%M:%S"'],
|
||||
const time = Variable('', {
|
||||
poll: [1000, 'date "+%I:%M:%S"'],
|
||||
});
|
||||
|
||||
const period = Variable("", {
|
||||
poll: [1000, 'date "+%p"'],
|
||||
const period = Variable('', {
|
||||
poll: [1000, 'date "+%p"'],
|
||||
});
|
||||
|
||||
const militaryTime = Variable("", {
|
||||
poll: [1000, 'date "+%H:%M:%S"'],
|
||||
const militaryTime = Variable('', {
|
||||
poll: [1000, 'date "+%H:%M:%S"'],
|
||||
});
|
||||
|
||||
const TimeWidget = () => {
|
||||
return Widget.Box({
|
||||
class_name: "calendar-menu-item-container clock",
|
||||
hexpand: true,
|
||||
vpack: "center",
|
||||
hpack: "fill",
|
||||
child: Widget.Box({
|
||||
hexpand: true,
|
||||
vpack: "center",
|
||||
hpack: "center",
|
||||
class_name: "clock-content-items",
|
||||
children: military.bind("value").as((is24hr) => {
|
||||
if (!is24hr) {
|
||||
return [
|
||||
Widget.Box({
|
||||
hpack: "center",
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: "clock-content-time",
|
||||
label: time.bind(),
|
||||
}),
|
||||
],
|
||||
const TimeWidget = (): BoxWidget => {
|
||||
return Widget.Box({
|
||||
class_name: 'calendar-menu-item-container clock',
|
||||
hexpand: true,
|
||||
vpack: 'center',
|
||||
hpack: 'fill',
|
||||
child: Widget.Box({
|
||||
hexpand: true,
|
||||
vpack: 'center',
|
||||
hpack: 'center',
|
||||
class_name: 'clock-content-items',
|
||||
children: military.bind('value').as((is24hr) => {
|
||||
if (!is24hr) {
|
||||
return [
|
||||
Widget.Box({
|
||||
hpack: 'center',
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: 'clock-content-time',
|
||||
label: time.bind(),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Box({
|
||||
hpack: 'center',
|
||||
children: [
|
||||
Widget.Label({
|
||||
vpack: 'end',
|
||||
class_name: 'clock-content-period',
|
||||
label: period.bind(),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
Widget.Box({
|
||||
hpack: 'center',
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: 'clock-content-time',
|
||||
label: militaryTime.bind(),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
];
|
||||
}),
|
||||
Widget.Box({
|
||||
hpack: "center",
|
||||
children: [
|
||||
Widget.Label({
|
||||
vpack: "end",
|
||||
class_name: "clock-content-period",
|
||||
label: period.bind(),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
Widget.Box({
|
||||
hpack: "center",
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: "clock-content-time",
|
||||
label: militaryTime.bind(),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
];
|
||||
}),
|
||||
}),
|
||||
});
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
export { TimeWidget };
|
||||
|
||||
@@ -1,49 +1,43 @@
|
||||
import { Weather, WeatherIcon, WeatherIconTitle } from "lib/types/weather.js";
|
||||
import { Variable } from "types/variable.js";
|
||||
import { weatherIcons } from "modules/icons/weather.js";
|
||||
import { isValidWeatherIconTitle } from "globals/weather";
|
||||
import { Weather, WeatherIconTitle } from 'lib/types/weather.js';
|
||||
import { Variable } from 'types/variable.js';
|
||||
import { weatherIcons } from 'modules/icons/weather.js';
|
||||
import { isValidWeatherIconTitle } from 'globals/weather';
|
||||
import { BoxWidget } from 'lib/types/widget';
|
||||
import { getNextEpoch } from '../utils';
|
||||
|
||||
export const HourlyIcon = (theWeather: Variable<Weather>, getNextEpoch: any) => {
|
||||
export const HourlyIcon = (theWeather: Variable<Weather>, hoursFromNow: number): BoxWidget => {
|
||||
const getIconQuery = (wthr: Weather): WeatherIconTitle => {
|
||||
|
||||
const nextEpoch = getNextEpoch(wthr);
|
||||
const weatherAtEpoch = wthr.forecast.forecastday[0].hour.find(
|
||||
(h) => h.time_epoch === nextEpoch,
|
||||
);
|
||||
const nextEpoch = getNextEpoch(wthr, hoursFromNow);
|
||||
const weatherAtEpoch = wthr.forecast.forecastday[0].hour.find((h) => h.time_epoch === nextEpoch);
|
||||
|
||||
if (weatherAtEpoch === undefined) {
|
||||
return "warning";
|
||||
return 'warning';
|
||||
}
|
||||
|
||||
let iconQuery = weatherAtEpoch.condition.text
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replaceAll(" ", "_");
|
||||
let iconQuery = weatherAtEpoch.condition.text.trim().toLowerCase().replaceAll(' ', '_');
|
||||
|
||||
if (!weatherAtEpoch?.is_day && iconQuery === "partly_cloudy") {
|
||||
iconQuery = "partly_cloudy_night";
|
||||
if (!weatherAtEpoch?.is_day && iconQuery === 'partly_cloudy') {
|
||||
iconQuery = 'partly_cloudy_night';
|
||||
}
|
||||
|
||||
if (isValidWeatherIconTitle(iconQuery)) {
|
||||
return iconQuery;
|
||||
} else {
|
||||
return "warning";
|
||||
return 'warning';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return Widget.Box({
|
||||
hpack: "center",
|
||||
child: theWeather.bind("value").as((w) => {
|
||||
let weatherIcn: WeatherIcon;
|
||||
|
||||
hpack: 'center',
|
||||
child: theWeather.bind('value').as((w) => {
|
||||
const iconQuery = getIconQuery(w);
|
||||
weatherIcn = weatherIcons[iconQuery] || weatherIcons["warning"];
|
||||
const weatherIcn = weatherIcons[iconQuery] || weatherIcons['warning'];
|
||||
|
||||
return Widget.Label({
|
||||
hpack: "center",
|
||||
class_name: "hourly-weather-icon txt-icon",
|
||||
hpack: 'center',
|
||||
class_name: 'hourly-weather-icon txt-icon',
|
||||
label: weatherIcn,
|
||||
});
|
||||
})
|
||||
|
||||
})
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,44 +1,25 @@
|
||||
import { Weather } from "lib/types/weather";
|
||||
import { Variable } from "types/variable";
|
||||
import { HourlyIcon } from "./icon/index.js";
|
||||
import { HourlyTemp } from "./temperature/index.js";
|
||||
import { HourlyTime } from "./time/index.js";
|
||||
import { Weather } from 'lib/types/weather';
|
||||
import { Variable } from 'types/variable';
|
||||
import { HourlyIcon } from './icon/index.js';
|
||||
import { HourlyTemp } from './temperature/index.js';
|
||||
import { HourlyTime } from './time/index.js';
|
||||
import { BoxWidget } from 'lib/types/widget.js';
|
||||
|
||||
export const Hourly = (theWeather: Variable<Weather>) => {
|
||||
export const Hourly = (theWeather: Variable<Weather>): BoxWidget => {
|
||||
return Widget.Box({
|
||||
vertical: false,
|
||||
hexpand: true,
|
||||
hpack: "fill",
|
||||
class_name: "hourly-weather-container",
|
||||
hpack: 'fill',
|
||||
class_name: 'hourly-weather-container',
|
||||
children: [1, 2, 3, 4].map((hoursFromNow) => {
|
||||
const getNextEpoch = (wthr: Weather) => {
|
||||
const currentEpoch = wthr.location.localtime_epoch;
|
||||
const epochAtHourStart = currentEpoch - (currentEpoch % 3600);
|
||||
let nextEpoch = 3600 * hoursFromNow + epochAtHourStart;
|
||||
|
||||
const curHour = new Date(currentEpoch * 1000).getHours();
|
||||
|
||||
/*
|
||||
* NOTE: Since the API is only capable of showing the current day; if
|
||||
* the hours left in the day are less than 4 (aka spilling into the next day),
|
||||
* then rewind to contain the prediction within the current day.
|
||||
*/
|
||||
if (curHour > 19) {
|
||||
const hoursToRewind = curHour - 19;
|
||||
nextEpoch =
|
||||
3600 * hoursFromNow + epochAtHourStart - hoursToRewind * 3600;
|
||||
}
|
||||
return nextEpoch;
|
||||
};
|
||||
|
||||
return Widget.Box({
|
||||
class_name: "hourly-weather-item",
|
||||
class_name: 'hourly-weather-item',
|
||||
hexpand: true,
|
||||
vertical: true,
|
||||
children: [
|
||||
HourlyTime(theWeather, getNextEpoch),
|
||||
HourlyIcon(theWeather, getNextEpoch),
|
||||
HourlyTemp(theWeather, getNextEpoch),
|
||||
HourlyTime(theWeather, hoursFromNow),
|
||||
HourlyIcon(theWeather, hoursFromNow),
|
||||
HourlyTemp(theWeather, hoursFromNow),
|
||||
],
|
||||
});
|
||||
}),
|
||||
|
||||
@@ -1,29 +1,27 @@
|
||||
import { Weather } from "lib/types/weather";
|
||||
import { Variable } from "types/variable";
|
||||
import options from "options";
|
||||
import { Weather } from 'lib/types/weather';
|
||||
import { Variable } from 'types/variable';
|
||||
import options from 'options';
|
||||
import Label from 'types/widgets/label';
|
||||
import { Child } from 'lib/types/widget';
|
||||
import { getNextEpoch } from '../utils';
|
||||
|
||||
const { unit } = options.menus.clock.weather;
|
||||
|
||||
export const HourlyTemp = (theWeather: Variable<Weather>, getNextEpoch: any) => {
|
||||
export const HourlyTemp = (theWeather: Variable<Weather>, hoursFromNow: number): Label<Child> => {
|
||||
return Widget.Label({
|
||||
class_name: "hourly-weather-temp",
|
||||
label: Utils.merge(
|
||||
[theWeather.bind("value"), unit.bind("value")],
|
||||
(wthr, unt) => {
|
||||
if (!Object.keys(wthr).length) {
|
||||
return "-";
|
||||
}
|
||||
class_name: 'hourly-weather-temp',
|
||||
label: Utils.merge([theWeather.bind('value'), unit.bind('value')], (wthr, unt) => {
|
||||
if (!Object.keys(wthr).length) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
const nextEpoch = getNextEpoch(wthr);
|
||||
const weatherAtEpoch = wthr.forecast.forecastday[0].hour.find(
|
||||
(h) => h.time_epoch === nextEpoch,
|
||||
);
|
||||
const nextEpoch = getNextEpoch(wthr, hoursFromNow);
|
||||
const weatherAtEpoch = wthr.forecast.forecastday[0].hour.find((h) => h.time_epoch === nextEpoch);
|
||||
|
||||
if (unt === "imperial") {
|
||||
return `${weatherAtEpoch ? Math.ceil(weatherAtEpoch.temp_f) : "-"}° F`;
|
||||
}
|
||||
return `${weatherAtEpoch ? Math.ceil(weatherAtEpoch.temp_c) : "-"}° C`;
|
||||
},
|
||||
),
|
||||
if (unt === 'imperial') {
|
||||
return `${weatherAtEpoch ? Math.ceil(weatherAtEpoch.temp_f) : '-'}° F`;
|
||||
}
|
||||
return `${weatherAtEpoch ? Math.ceil(weatherAtEpoch.temp_c) : '-'}° C`;
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import { Weather } from "lib/types/weather";
|
||||
import { Variable } from "types/variable";
|
||||
import { Weather } from 'lib/types/weather';
|
||||
import { Child } from 'lib/types/widget';
|
||||
import { Variable } from 'types/variable';
|
||||
import Label from 'types/widgets/label';
|
||||
import { getNextEpoch } from '../utils';
|
||||
|
||||
export const HourlyTime = (theWeather: Variable<Weather>, getNextEpoch: any) => {
|
||||
export const HourlyTime = (theWeather: Variable<Weather>, hoursFromNow: number): Label<Child> => {
|
||||
return Widget.Label({
|
||||
class_name: "hourly-weather-time",
|
||||
label: theWeather.bind("value").as((w) => {
|
||||
class_name: 'hourly-weather-time',
|
||||
label: theWeather.bind('value').as((w) => {
|
||||
if (!Object.keys(w).length) {
|
||||
return "-";
|
||||
return '-';
|
||||
}
|
||||
|
||||
const nextEpoch = getNextEpoch(w);
|
||||
const nextEpoch = getNextEpoch(w, hoursFromNow);
|
||||
const dateAtEpoch = new Date(nextEpoch * 1000);
|
||||
let hours = dateAtEpoch.getHours();
|
||||
const ampm = hours >= 12 ? "PM" : "AM";
|
||||
const ampm = hours >= 12 ? 'PM' : 'AM';
|
||||
hours = hours % 12 || 12;
|
||||
|
||||
return `${hours}${ampm}`;
|
||||
|
||||
20
modules/menus/calendar/weather/hourly/utils.ts
Normal file
20
modules/menus/calendar/weather/hourly/utils.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Weather } from 'lib/types/weather';
|
||||
|
||||
export const getNextEpoch = (wthr: Weather, hoursFromNow: number): number => {
|
||||
const currentEpoch = wthr.location.localtime_epoch;
|
||||
const epochAtHourStart = currentEpoch - (currentEpoch % 3600);
|
||||
let nextEpoch = 3600 * hoursFromNow + epochAtHourStart;
|
||||
|
||||
const curHour = new Date(currentEpoch * 1000).getHours();
|
||||
|
||||
/*
|
||||
* NOTE: Since the API is only capable of showing the current day; if
|
||||
* the hours left in the day are less than 4 (aka spilling into the next day),
|
||||
* then rewind to contain the prediction within the current day.
|
||||
*/
|
||||
if (curHour > 19) {
|
||||
const hoursToRewind = curHour - 19;
|
||||
nextEpoch = 3600 * hoursFromNow + epochAtHourStart - hoursToRewind * 3600;
|
||||
}
|
||||
return nextEpoch;
|
||||
};
|
||||
@@ -1,17 +1,18 @@
|
||||
import { Weather } from "lib/types/weather";
|
||||
import { Variable } from "types/variable";
|
||||
import { getWeatherStatusTextIcon } from "globals/weather.js";
|
||||
import { Weather } from 'lib/types/weather';
|
||||
import { Variable } from 'types/variable';
|
||||
import { getWeatherStatusTextIcon } from 'globals/weather.js';
|
||||
import { BoxWidget } from 'lib/types/widget';
|
||||
|
||||
export const TodayIcon = (theWeather: Variable<Weather>) => {
|
||||
export const TodayIcon = (theWeather: Variable<Weather>): BoxWidget => {
|
||||
return Widget.Box({
|
||||
vpack: "center",
|
||||
hpack: "start",
|
||||
class_name: "calendar-menu-weather today icon container",
|
||||
vpack: 'center',
|
||||
hpack: 'start',
|
||||
class_name: 'calendar-menu-weather today icon container',
|
||||
child: Widget.Label({
|
||||
class_name: "calendar-menu-weather today icon txt-icon",
|
||||
label: theWeather.bind("value").as((w) => {
|
||||
class_name: 'calendar-menu-weather today icon txt-icon',
|
||||
label: theWeather.bind('value').as((w) => {
|
||||
return getWeatherStatusTextIcon(w);
|
||||
}),
|
||||
})
|
||||
})
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
import { TodayIcon } from "./icon/index.js";
|
||||
import { TodayStats } from "./stats/index.js";
|
||||
import { TodayTemperature } from "./temperature/index.js";
|
||||
import { Hourly } from "./hourly/index.js";
|
||||
import { globalWeatherVar } from "globals/weather.js";
|
||||
import { TodayIcon } from './icon/index.js';
|
||||
import { TodayStats } from './stats/index.js';
|
||||
import { TodayTemperature } from './temperature/index.js';
|
||||
import { Hourly } from './hourly/index.js';
|
||||
import { globalWeatherVar } from 'globals/weather.js';
|
||||
import { BoxWidget } from 'lib/types/widget.js';
|
||||
|
||||
const WeatherWidget = () => {
|
||||
const WeatherWidget = (): BoxWidget => {
|
||||
return Widget.Box({
|
||||
class_name: "calendar-menu-item-container weather",
|
||||
class_name: 'calendar-menu-item-container weather',
|
||||
child: Widget.Box({
|
||||
class_name: "weather-container-box",
|
||||
class_name: 'weather-container-box',
|
||||
setup: (self) => {
|
||||
return (self.child = Widget.Box({
|
||||
vertical: true,
|
||||
hexpand: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "calendar-menu-weather today",
|
||||
class_name: 'calendar-menu-weather today',
|
||||
hexpand: true,
|
||||
children: [
|
||||
TodayIcon(globalWeatherVar),
|
||||
@@ -24,7 +25,7 @@ const WeatherWidget = () => {
|
||||
],
|
||||
}),
|
||||
Widget.Separator({
|
||||
class_name: "menu-separator weather",
|
||||
class_name: 'menu-separator weather',
|
||||
}),
|
||||
Hourly(globalWeatherVar),
|
||||
],
|
||||
|
||||
@@ -1,29 +1,30 @@
|
||||
import { Weather } from "lib/types/weather";
|
||||
import { Variable } from "types/variable";
|
||||
import options from "options";
|
||||
import { Unit } from "lib/types/options";
|
||||
import { getRainChance, getWindConditions } from "globals/weather";
|
||||
import { Weather } from 'lib/types/weather';
|
||||
import { Variable } from 'types/variable';
|
||||
import options from 'options';
|
||||
import { Unit } from 'lib/types/options';
|
||||
import { getRainChance, getWindConditions } from 'globals/weather';
|
||||
import { BoxWidget } from 'lib/types/widget';
|
||||
|
||||
const { unit } = options.menus.clock.weather;
|
||||
|
||||
export const TodayStats = (theWeather: Variable<Weather>) => {
|
||||
export const TodayStats = (theWeather: Variable<Weather>): BoxWidget => {
|
||||
return Widget.Box({
|
||||
class_name: "calendar-menu-weather today stats container",
|
||||
hpack: "end",
|
||||
vpack: "center",
|
||||
class_name: 'calendar-menu-weather today stats container',
|
||||
hpack: 'end',
|
||||
vpack: 'center',
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "weather wind",
|
||||
class_name: 'weather wind',
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: "weather wind icon txt-icon",
|
||||
label: "",
|
||||
class_name: 'weather wind icon txt-icon',
|
||||
label: '',
|
||||
}),
|
||||
Widget.Label({
|
||||
class_name: "weather wind label",
|
||||
class_name: 'weather wind label',
|
||||
label: Utils.merge(
|
||||
[theWeather.bind("value"), unit.bind("value")],
|
||||
[theWeather.bind('value'), unit.bind('value')],
|
||||
(wthr: Weather, unt: Unit) => {
|
||||
return getWindConditions(wthr, unt);
|
||||
},
|
||||
@@ -32,19 +33,15 @@ export const TodayStats = (theWeather: Variable<Weather>) => {
|
||||
],
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "weather precip",
|
||||
class_name: 'weather precip',
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: "weather precip icon txt-icon",
|
||||
label: "",
|
||||
class_name: 'weather precip icon txt-icon',
|
||||
label: '',
|
||||
}),
|
||||
Widget.Label({
|
||||
class_name: "weather precip label",
|
||||
label: theWeather
|
||||
.bind("value")
|
||||
.as(
|
||||
(v) => getRainChance(v),
|
||||
),
|
||||
class_name: 'weather precip label',
|
||||
label: theWeather.bind('value').as((v) => getRainChance(v)),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
|
||||
@@ -1,43 +1,41 @@
|
||||
import { Weather } from "lib/types/weather";
|
||||
import { Variable } from "types/variable";
|
||||
import options from "options";
|
||||
import { getTemperature, getWeatherIcon } from "globals/weather";
|
||||
import { Weather } from 'lib/types/weather';
|
||||
import { Variable } from 'types/variable';
|
||||
import options from 'options';
|
||||
import { getTemperature, getWeatherIcon } from 'globals/weather';
|
||||
import { BoxWidget } from 'lib/types/widget';
|
||||
const { unit } = options.menus.clock.weather;
|
||||
|
||||
export const TodayTemperature = (theWeather: Variable<Weather>) => {
|
||||
export const TodayTemperature = (theWeather: Variable<Weather>): BoxWidget => {
|
||||
return Widget.Box({
|
||||
hpack: "center",
|
||||
vpack: "center",
|
||||
hpack: 'center',
|
||||
vpack: 'center',
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
hexpand: true,
|
||||
vpack: "center",
|
||||
class_name: "calendar-menu-weather today temp container",
|
||||
vpack: 'center',
|
||||
class_name: 'calendar-menu-weather today temp container',
|
||||
vertical: false,
|
||||
children: [
|
||||
Widget.Box({
|
||||
hexpand: true,
|
||||
hpack: "center",
|
||||
hpack: 'center',
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: "calendar-menu-weather today temp label",
|
||||
label: Utils.merge(
|
||||
[theWeather.bind("value"), unit.bind("value")],
|
||||
(wthr, unt) => {
|
||||
return getTemperature(wthr, unt);
|
||||
},
|
||||
),
|
||||
class_name: 'calendar-menu-weather today temp label',
|
||||
label: Utils.merge([theWeather.bind('value'), unit.bind('value')], (wthr, unt) => {
|
||||
return getTemperature(wthr, unt);
|
||||
}),
|
||||
}),
|
||||
Widget.Label({
|
||||
class_name: theWeather
|
||||
.bind("value")
|
||||
.bind('value')
|
||||
.as(
|
||||
(v) =>
|
||||
`calendar-menu-weather today temp label icon txt-icon ${getWeatherIcon(Math.ceil(v.current.temp_f)).color}`,
|
||||
),
|
||||
label: theWeather
|
||||
.bind("value")
|
||||
.bind('value')
|
||||
.as((v) => getWeatherIcon(Math.ceil(v.current.temp_f)).icon),
|
||||
}),
|
||||
],
|
||||
@@ -45,18 +43,18 @@ export const TodayTemperature = (theWeather: Variable<Weather>) => {
|
||||
],
|
||||
}),
|
||||
Widget.Box({
|
||||
hpack: "center",
|
||||
hpack: 'center',
|
||||
child: Widget.Label({
|
||||
max_width_chars: 17,
|
||||
truncate: "end",
|
||||
truncate: 'end',
|
||||
lines: 2,
|
||||
class_name: theWeather
|
||||
.bind("value")
|
||||
.bind('value')
|
||||
.as(
|
||||
(v) =>
|
||||
`calendar-menu-weather today condition label ${getWeatherIcon(Math.ceil(v.current.temp_f)).color}`,
|
||||
),
|
||||
label: theWeather.bind("value").as((v) => v.current.condition.text),
|
||||
label: theWeather.bind('value').as((v) => v.current.condition.text),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -1,95 +1,91 @@
|
||||
const network = await Service.import("network");
|
||||
const bluetooth = await Service.import("bluetooth");
|
||||
const notifications = await Service.import("notifications");
|
||||
const audio = await Service.import("audio");
|
||||
import { BoxWidget } from 'lib/types/widget';
|
||||
|
||||
const Controls = () => {
|
||||
const network = await Service.import('network');
|
||||
const bluetooth = await Service.import('bluetooth');
|
||||
const notifications = await Service.import('notifications');
|
||||
const audio = await Service.import('audio');
|
||||
|
||||
const Controls = (): BoxWidget => {
|
||||
return Widget.Box({
|
||||
class_name: "dashboard-card controls-container",
|
||||
hpack: "fill",
|
||||
vpack: "fill",
|
||||
class_name: 'dashboard-card controls-container',
|
||||
hpack: 'fill',
|
||||
vpack: 'fill',
|
||||
expand: true,
|
||||
children: [
|
||||
Widget.Button({
|
||||
tooltip_text: "Toggle Wifi",
|
||||
tooltip_text: 'Toggle Wifi',
|
||||
expand: true,
|
||||
setup: (self) => {
|
||||
self.hook(network, () => {
|
||||
return (self.class_name = `dashboard-button wifi ${!network.wifi.enabled ? "disabled" : ""}`);
|
||||
return (self.class_name = `dashboard-button wifi ${!network.wifi.enabled ? 'disabled' : ''}`);
|
||||
});
|
||||
},
|
||||
on_primary_click: () => network.toggleWifi(),
|
||||
child: Widget.Label({
|
||||
class_name: "txt-icon",
|
||||
class_name: 'txt-icon',
|
||||
setup: (self) => {
|
||||
self.hook(network, () => {
|
||||
return (self.label = network.wifi.enabled ? "" : "");
|
||||
return (self.label = network.wifi.enabled ? '' : '');
|
||||
});
|
||||
},
|
||||
}),
|
||||
}),
|
||||
Widget.Button({
|
||||
tooltip_text: "Toggle Bluetooth",
|
||||
tooltip_text: 'Toggle Bluetooth',
|
||||
expand: true,
|
||||
class_name: bluetooth
|
||||
.bind("enabled")
|
||||
.as(
|
||||
(btOn) => `dashboard-button bluetooth ${!btOn ? "disabled" : ""}`,
|
||||
),
|
||||
.bind('enabled')
|
||||
.as((btOn) => `dashboard-button bluetooth ${!btOn ? 'disabled' : ''}`),
|
||||
on_primary_click: () => bluetooth.toggle(),
|
||||
child: Widget.Label({
|
||||
class_name: "txt-icon",
|
||||
label: bluetooth.bind("enabled").as((btOn) => (btOn ? "" : "")),
|
||||
class_name: 'txt-icon',
|
||||
label: bluetooth.bind('enabled').as((btOn) => (btOn ? '' : '')),
|
||||
}),
|
||||
}),
|
||||
Widget.Button({
|
||||
tooltip_text: "Toggle Notifications",
|
||||
tooltip_text: 'Toggle Notifications',
|
||||
expand: true,
|
||||
class_name: notifications
|
||||
.bind("dnd")
|
||||
.as(
|
||||
(dnd) => `dashboard-button notifications ${dnd ? "disabled" : ""}`,
|
||||
),
|
||||
.bind('dnd')
|
||||
.as((dnd) => `dashboard-button notifications ${dnd ? 'disabled' : ''}`),
|
||||
on_primary_click: () => (notifications.dnd = !notifications.dnd),
|
||||
child: Widget.Label({
|
||||
class_name: "txt-icon",
|
||||
label: notifications.bind("dnd").as((dnd) => (dnd ? "" : "")),
|
||||
class_name: 'txt-icon',
|
||||
label: notifications.bind('dnd').as((dnd) => (dnd ? '' : '')),
|
||||
}),
|
||||
}),
|
||||
Widget.Button({
|
||||
tooltip_text: "Toggle Mute (Playback)",
|
||||
tooltip_text: 'Toggle Mute (Playback)',
|
||||
expand: true,
|
||||
on_primary_click: () =>
|
||||
(audio.speaker.is_muted = !audio.speaker.is_muted),
|
||||
on_primary_click: () => (audio.speaker.is_muted = !audio.speaker.is_muted),
|
||||
setup: (self) => {
|
||||
self.hook(audio, () => {
|
||||
return (self.class_name = `dashboard-button playback ${audio.speaker.is_muted ? "disabled" : ""}`);
|
||||
return (self.class_name = `dashboard-button playback ${audio.speaker.is_muted ? 'disabled' : ''}`);
|
||||
});
|
||||
},
|
||||
child: Widget.Label({
|
||||
class_name: "txt-icon",
|
||||
class_name: 'txt-icon',
|
||||
setup: (self) => {
|
||||
self.hook(audio, () => {
|
||||
return (self.label = audio.speaker.is_muted ? "" : "");
|
||||
return (self.label = audio.speaker.is_muted ? '' : '');
|
||||
});
|
||||
},
|
||||
}),
|
||||
}),
|
||||
Widget.Button({
|
||||
tooltip_text: "Toggle Mute (Microphone)",
|
||||
tooltip_text: 'Toggle Mute (Microphone)',
|
||||
expand: true,
|
||||
on_primary_click: () =>
|
||||
(audio.microphone.is_muted = !audio.microphone.is_muted),
|
||||
on_primary_click: () => (audio.microphone.is_muted = !audio.microphone.is_muted),
|
||||
setup: (self) => {
|
||||
self.hook(audio, () => {
|
||||
return (self.class_name = `dashboard-button input ${audio.microphone.is_muted ? "disabled" : ""}`);
|
||||
return (self.class_name = `dashboard-button input ${audio.microphone.is_muted ? 'disabled' : ''}`);
|
||||
});
|
||||
},
|
||||
child: Widget.Label({
|
||||
class_name: "txt-icon",
|
||||
class_name: 'txt-icon',
|
||||
setup: (self) => {
|
||||
self.hook(audio, () => {
|
||||
return (self.label = audio.microphone.is_muted ? "" : "");
|
||||
return (self.label = audio.microphone.is_muted ? '' : '');
|
||||
});
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -1,68 +1,63 @@
|
||||
import options from "options";
|
||||
import { BoxWidget } from 'lib/types/widget';
|
||||
import options from 'options';
|
||||
|
||||
const { left, right } = options.menus.dashboard.directories;
|
||||
|
||||
const Directories = () => {
|
||||
const Directories = (): BoxWidget => {
|
||||
return Widget.Box({
|
||||
class_name: "dashboard-card directories-container",
|
||||
vpack: "fill",
|
||||
hpack: "fill",
|
||||
class_name: 'dashboard-card directories-container',
|
||||
vpack: 'fill',
|
||||
hpack: 'fill',
|
||||
expand: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
expand: true,
|
||||
class_name: "section right",
|
||||
class_name: 'section right',
|
||||
children: [
|
||||
Widget.Button({
|
||||
hpack: "start",
|
||||
hpack: 'start',
|
||||
expand: true,
|
||||
class_name: "directory-link left top",
|
||||
on_primary_click: left.directory1.command
|
||||
.bind("value")
|
||||
.as((cmd) => {
|
||||
return () => {
|
||||
App.closeWindow("dashboardmenu");
|
||||
Utils.execAsync(cmd);
|
||||
};
|
||||
}),
|
||||
class_name: 'directory-link left top',
|
||||
on_primary_click: left.directory1.command.bind('value').as((cmd) => {
|
||||
return () => {
|
||||
App.closeWindow('dashboardmenu');
|
||||
Utils.execAsync(cmd);
|
||||
};
|
||||
}),
|
||||
child: Widget.Label({
|
||||
hpack: "start",
|
||||
label: left.directory1.label.bind("value"),
|
||||
hpack: 'start',
|
||||
label: left.directory1.label.bind('value'),
|
||||
}),
|
||||
}),
|
||||
Widget.Button({
|
||||
expand: true,
|
||||
hpack: "start",
|
||||
class_name: "directory-link left middle",
|
||||
on_primary_click: left.directory2.command
|
||||
.bind("value")
|
||||
.as((cmd) => {
|
||||
return () => {
|
||||
App.closeWindow("dashboardmenu");
|
||||
Utils.execAsync(cmd);
|
||||
};
|
||||
}),
|
||||
hpack: 'start',
|
||||
class_name: 'directory-link left middle',
|
||||
on_primary_click: left.directory2.command.bind('value').as((cmd) => {
|
||||
return () => {
|
||||
App.closeWindow('dashboardmenu');
|
||||
Utils.execAsync(cmd);
|
||||
};
|
||||
}),
|
||||
child: Widget.Label({
|
||||
hpack: "start",
|
||||
label: left.directory2.label.bind("value"),
|
||||
hpack: 'start',
|
||||
label: left.directory2.label.bind('value'),
|
||||
}),
|
||||
}),
|
||||
Widget.Button({
|
||||
expand: true,
|
||||
hpack: "start",
|
||||
class_name: "directory-link left bottom",
|
||||
on_primary_click: left.directory3.command
|
||||
.bind("value")
|
||||
.as((cmd) => {
|
||||
return () => {
|
||||
App.closeWindow("dashboardmenu");
|
||||
Utils.execAsync(cmd);
|
||||
};
|
||||
}),
|
||||
hpack: 'start',
|
||||
class_name: 'directory-link left bottom',
|
||||
on_primary_click: left.directory3.command.bind('value').as((cmd) => {
|
||||
return () => {
|
||||
App.closeWindow('dashboardmenu');
|
||||
Utils.execAsync(cmd);
|
||||
};
|
||||
}),
|
||||
child: Widget.Label({
|
||||
hpack: "start",
|
||||
label: left.directory3.label.bind("value"),
|
||||
hpack: 'start',
|
||||
label: left.directory3.label.bind('value'),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
@@ -70,57 +65,51 @@ const Directories = () => {
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
expand: true,
|
||||
class_name: "section left",
|
||||
class_name: 'section left',
|
||||
children: [
|
||||
Widget.Button({
|
||||
hpack: "start",
|
||||
hpack: 'start',
|
||||
expand: true,
|
||||
class_name: "directory-link right top",
|
||||
on_primary_click: right.directory1.command
|
||||
.bind("value")
|
||||
.as((cmd) => {
|
||||
return () => {
|
||||
App.closeWindow("dashboardmenu");
|
||||
Utils.execAsync(cmd);
|
||||
};
|
||||
}),
|
||||
class_name: 'directory-link right top',
|
||||
on_primary_click: right.directory1.command.bind('value').as((cmd) => {
|
||||
return () => {
|
||||
App.closeWindow('dashboardmenu');
|
||||
Utils.execAsync(cmd);
|
||||
};
|
||||
}),
|
||||
child: Widget.Label({
|
||||
hpack: "start",
|
||||
label: right.directory1.label.bind("value"),
|
||||
hpack: 'start',
|
||||
label: right.directory1.label.bind('value'),
|
||||
}),
|
||||
}),
|
||||
Widget.Button({
|
||||
expand: true,
|
||||
hpack: "start",
|
||||
class_name: "directory-link right middle",
|
||||
on_primary_click: right.directory2.command
|
||||
.bind("value")
|
||||
.as((cmd) => {
|
||||
return () => {
|
||||
App.closeWindow("dashboardmenu");
|
||||
Utils.execAsync(cmd);
|
||||
};
|
||||
}),
|
||||
hpack: 'start',
|
||||
class_name: 'directory-link right middle',
|
||||
on_primary_click: right.directory2.command.bind('value').as((cmd) => {
|
||||
return () => {
|
||||
App.closeWindow('dashboardmenu');
|
||||
Utils.execAsync(cmd);
|
||||
};
|
||||
}),
|
||||
child: Widget.Label({
|
||||
hpack: "start",
|
||||
label: right.directory2.label.bind("value"),
|
||||
hpack: 'start',
|
||||
label: right.directory2.label.bind('value'),
|
||||
}),
|
||||
}),
|
||||
Widget.Button({
|
||||
expand: true,
|
||||
hpack: "start",
|
||||
class_name: "directory-link right bottom",
|
||||
on_primary_click: right.directory3.command
|
||||
.bind("value")
|
||||
.as((cmd) => {
|
||||
return () => {
|
||||
App.closeWindow("dashboardmenu");
|
||||
Utils.execAsync(cmd);
|
||||
};
|
||||
}),
|
||||
hpack: 'start',
|
||||
class_name: 'directory-link right bottom',
|
||||
on_primary_click: right.directory3.command.bind('value').as((cmd) => {
|
||||
return () => {
|
||||
App.closeWindow('dashboardmenu');
|
||||
Utils.execAsync(cmd);
|
||||
};
|
||||
}),
|
||||
child: Widget.Label({
|
||||
hpack: "start",
|
||||
label: right.directory3.label.bind("value"),
|
||||
hpack: 'start',
|
||||
label: right.directory3.label.bind('value'),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -1,37 +1,33 @@
|
||||
import DropdownMenu from "../DropdownMenu.js";
|
||||
import { Profile } from "./profile/index.js";
|
||||
import { Shortcuts } from "./shortcuts/index.js";
|
||||
import { Controls } from "./controls/index.js";
|
||||
import { Stats } from "./stats/index.js";
|
||||
import { Directories } from "./directories/index.js";
|
||||
import DropdownMenu from '../DropdownMenu.js';
|
||||
import { Profile } from './profile/index.js';
|
||||
import { Shortcuts } from './shortcuts/index.js';
|
||||
import { Controls } from './controls/index.js';
|
||||
import { Stats } from './stats/index.js';
|
||||
import { Directories } from './directories/index.js';
|
||||
import Window from 'types/widgets/window.js';
|
||||
import { Attribute, Child } from 'lib/types/widget.js';
|
||||
|
||||
export default () => {
|
||||
return DropdownMenu({
|
||||
name: "dashboardmenu",
|
||||
transition: "crossfade",
|
||||
child: Widget.Box({
|
||||
class_name: "dashboard-menu-content",
|
||||
css: "padding: 1px; margin: -1px;",
|
||||
vexpand: false,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "dashboard-content-container",
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "dashboard-content-items",
|
||||
vertical: true,
|
||||
children: [
|
||||
Profile(),
|
||||
Shortcuts(),
|
||||
Controls(),
|
||||
Directories(),
|
||||
Stats(),
|
||||
],
|
||||
}),
|
||||
],
|
||||
export default (): Window<Child, Attribute> => {
|
||||
return DropdownMenu({
|
||||
name: 'dashboardmenu',
|
||||
transition: 'crossfade',
|
||||
child: Widget.Box({
|
||||
class_name: 'dashboard-menu-content',
|
||||
css: 'padding: 1px; margin: -1px;',
|
||||
vexpand: false,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: 'dashboard-content-container',
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: 'dashboard-content-items',
|
||||
vertical: true,
|
||||
children: [Profile(), Shortcuts(), Controls(), Directories(), Stats()],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,64 +1,67 @@
|
||||
import powermenu from "../../power/helpers/actions.js";
|
||||
import { PowerOptions } from "lib/types/options.js";
|
||||
import GdkPixbuf from "gi://GdkPixbuf";
|
||||
import powermenu from '../../power/helpers/actions.js';
|
||||
import { PowerOptions } from 'lib/types/options.js';
|
||||
import GdkPixbuf from 'gi://GdkPixbuf';
|
||||
|
||||
import options from "options";
|
||||
import options from 'options';
|
||||
import { BoxWidget, Child } from 'lib/types/widget.js';
|
||||
import Label from 'types/widgets/label.js';
|
||||
const { image, name } = options.menus.dashboard.powermenu.avatar;
|
||||
const { confirmation, shutdown, logout, sleep, reboot } = options.menus.dashboard.powermenu;
|
||||
|
||||
const Profile = () => {
|
||||
const handleClick = (action: PowerOptions) => {
|
||||
const Profile = (): BoxWidget => {
|
||||
const handleClick = (action: PowerOptions): void => {
|
||||
const actions = {
|
||||
shutdown: shutdown.value,
|
||||
reboot: reboot.value,
|
||||
logout: logout.value,
|
||||
sleep: sleep.value,
|
||||
};
|
||||
App.closeWindow("dashboardmenu");
|
||||
App.closeWindow('dashboardmenu');
|
||||
|
||||
if (!confirmation.value) {
|
||||
Utils.execAsync(actions[action])
|
||||
.catch((err) => console.error(`Failed to execute ${action} command. Error: ${err}`));
|
||||
Utils.execAsync(actions[action]).catch((err) =>
|
||||
console.error(`Failed to execute ${action} command. Error: ${err}`),
|
||||
);
|
||||
} else {
|
||||
powermenu.action(action);
|
||||
}
|
||||
};
|
||||
|
||||
const getIconForButton = (txtIcon: string) => {
|
||||
const getIconForButton = (txtIcon: string): Label<Child> => {
|
||||
return Widget.Label({
|
||||
className: "txt-icon",
|
||||
label: txtIcon
|
||||
})
|
||||
}
|
||||
className: 'txt-icon',
|
||||
label: txtIcon,
|
||||
});
|
||||
};
|
||||
|
||||
return Widget.Box({
|
||||
class_name: "profiles-container",
|
||||
hpack: "fill",
|
||||
class_name: 'profiles-container',
|
||||
hpack: 'fill',
|
||||
hexpand: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "profile-picture-container dashboard-card",
|
||||
class_name: 'profile-picture-container dashboard-card',
|
||||
hexpand: true,
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
hpack: "center",
|
||||
class_name: "profile-picture",
|
||||
css: image.bind("value").as(i => {
|
||||
hpack: 'center',
|
||||
class_name: 'profile-picture',
|
||||
css: image.bind('value').as((i) => {
|
||||
try {
|
||||
GdkPixbuf.Pixbuf.new_from_file(i);
|
||||
return `background-image: url("${i}")`
|
||||
return `background-image: url("${i}")`;
|
||||
} catch {
|
||||
return `background-image: url("${App.configDir}/assets/hyprpanel.png")`
|
||||
return `background-image: url("${App.configDir}/assets/hyprpanel.png")`;
|
||||
}
|
||||
}),
|
||||
}),
|
||||
Widget.Label({
|
||||
hpack: "center",
|
||||
class_name: "profile-name",
|
||||
label: name.bind("value").as((v) => {
|
||||
if (v === "system") {
|
||||
return Utils.exec("bash -c whoami");
|
||||
hpack: 'center',
|
||||
class_name: 'profile-name',
|
||||
label: name.bind('value').as((v) => {
|
||||
if (v === 'system') {
|
||||
return Utils.exec('bash -c whoami');
|
||||
}
|
||||
return v;
|
||||
}),
|
||||
@@ -66,39 +69,39 @@ const Profile = () => {
|
||||
],
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "power-menu-container dashboard-card",
|
||||
class_name: 'power-menu-container dashboard-card',
|
||||
vertical: true,
|
||||
vexpand: true,
|
||||
children: [
|
||||
Widget.Button({
|
||||
class_name: "dashboard-button shutdown",
|
||||
on_clicked: () => handleClick("shutdown"),
|
||||
tooltip_text: "Shut Down",
|
||||
class_name: 'dashboard-button shutdown',
|
||||
on_clicked: () => handleClick('shutdown'),
|
||||
tooltip_text: 'Shut Down',
|
||||
vexpand: true,
|
||||
child: getIconForButton("")
|
||||
child: getIconForButton(''),
|
||||
}),
|
||||
Widget.Button({
|
||||
class_name: "dashboard-button restart",
|
||||
on_clicked: () => handleClick("reboot"),
|
||||
tooltip_text: "Restart",
|
||||
class_name: 'dashboard-button restart',
|
||||
on_clicked: () => handleClick('reboot'),
|
||||
tooltip_text: 'Restart',
|
||||
vexpand: true,
|
||||
child: getIconForButton("")
|
||||
child: getIconForButton(''),
|
||||
}),
|
||||
Widget.Button({
|
||||
class_name: "dashboard-button lock",
|
||||
on_clicked: () => handleClick("logout"),
|
||||
tooltip_text: "Log Out",
|
||||
class_name: 'dashboard-button lock',
|
||||
on_clicked: () => handleClick('logout'),
|
||||
tooltip_text: 'Log Out',
|
||||
vexpand: true,
|
||||
child: getIconForButton("")
|
||||
child: getIconForButton(''),
|
||||
}),
|
||||
Widget.Button({
|
||||
class_name: "dashboard-button sleep",
|
||||
on_clicked: () => handleClick("sleep"),
|
||||
tooltip_text: "Sleep",
|
||||
class_name: 'dashboard-button sleep',
|
||||
on_clicked: () => handleClick('sleep'),
|
||||
tooltip_text: 'Sleep',
|
||||
vexpand: true,
|
||||
child: getIconForButton("")
|
||||
child: getIconForButton(''),
|
||||
}),
|
||||
]
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
@@ -1,24 +1,28 @@
|
||||
const hyprland = await Service.import("hyprland");
|
||||
import options from "options";
|
||||
import { Variable as VarType } from "types/variable";
|
||||
const hyprland = await Service.import('hyprland');
|
||||
import { Attribute, BoxWidget, Child } from 'lib/types/widget';
|
||||
import options from 'options';
|
||||
import { Variable as VarType } from 'types/variable';
|
||||
import Box from 'types/widgets/box';
|
||||
import Button from 'types/widgets/button';
|
||||
import Label from 'types/widgets/label';
|
||||
|
||||
const { left, right } = options.menus.dashboard.shortcuts;
|
||||
|
||||
const Shortcuts = () => {
|
||||
const Shortcuts = (): BoxWidget => {
|
||||
const isRecording = Variable(false, {
|
||||
poll: [
|
||||
1000,
|
||||
`${App.configDir}/services/screen_record.sh status`,
|
||||
(out) => {
|
||||
if (out === "recording") {
|
||||
(out): boolean => {
|
||||
if (out === 'recording') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
],
|
||||
});
|
||||
const handleClick = (action: any, tOut: number = 250) => {
|
||||
App.closeWindow("dashboardmenu");
|
||||
const handleClick = (action: string, tOut: number = 250): void => {
|
||||
App.closeWindow('dashboardmenu');
|
||||
|
||||
setTimeout(() => {
|
||||
Utils.execAsync(action)
|
||||
@@ -30,8 +34,8 @@ const Shortcuts = () => {
|
||||
};
|
||||
|
||||
const recordingDropdown = Widget.Menu({
|
||||
class_name: "dropdown recording",
|
||||
hpack: "fill",
|
||||
class_name: 'dropdown recording',
|
||||
hpack: 'fill',
|
||||
hexpand: true,
|
||||
setup: (self) => {
|
||||
self.hook(hyprland, () => {
|
||||
@@ -39,26 +43,26 @@ const Shortcuts = () => {
|
||||
return Widget.MenuItem({
|
||||
label: `Display ${mon.name}`,
|
||||
on_activate: () => {
|
||||
App.closeWindow("dashboardmenu");
|
||||
Utils.execAsync(
|
||||
`${App.configDir}/services/screen_record.sh start ${mon.name}`,
|
||||
).catch((err) => console.error(err));
|
||||
App.closeWindow('dashboardmenu');
|
||||
Utils.execAsync(`${App.configDir}/services/screen_record.sh start ${mon.name}`).catch(
|
||||
(err) => console.error(err),
|
||||
);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// NOTE: This is disabled since window recording isn't available on wayland
|
||||
const apps = hyprland.clients.map((clt) => {
|
||||
return Widget.MenuItem({
|
||||
label: `${clt.class.charAt(0).toUpperCase() + clt.class.slice(1)} (Workspace ${clt.workspace.name})`,
|
||||
on_activate: () => {
|
||||
App.closeWindow("dashboardmenu");
|
||||
Utils.execAsync(
|
||||
`${App.configDir}/services/screen_record.sh start ${clt.focusHistoryID}`,
|
||||
).catch((err) => console.error(err));
|
||||
},
|
||||
});
|
||||
});
|
||||
// const apps = hyprland.clients.map((clt) => {
|
||||
// return Widget.MenuItem({
|
||||
// label: `${clt.class.charAt(0).toUpperCase() + clt.class.slice(1)} (Workspace ${clt.workspace.name})`,
|
||||
// on_activate: () => {
|
||||
// App.closeWindow('dashboardmenu');
|
||||
// Utils.execAsync(
|
||||
// `${App.configDir}/services/screen_record.sh start ${clt.focusHistoryID}`,
|
||||
// ).catch((err) => console.error(err));
|
||||
// },
|
||||
// });
|
||||
// });
|
||||
|
||||
return (self.children = [
|
||||
...displays,
|
||||
@@ -85,15 +89,15 @@ const Shortcuts = () => {
|
||||
|
||||
type Shortcut = ShortcutFixed | ShortcutVariable;
|
||||
|
||||
const cmdLn = (sCut: ShortcutVariable) => {
|
||||
return sCut.command.value.length > 0
|
||||
const cmdLn = (sCut: ShortcutVariable): boolean => {
|
||||
return sCut.command.value.length > 0;
|
||||
};
|
||||
|
||||
const leftCardHidden = Variable(
|
||||
!(cmdLn(left.shortcut1) || cmdLn(left.shortcut2) || cmdLn(left.shortcut3) || cmdLn(left.shortcut4))
|
||||
!(cmdLn(left.shortcut1) || cmdLn(left.shortcut2) || cmdLn(left.shortcut3) || cmdLn(left.shortcut4)),
|
||||
);
|
||||
|
||||
function createButton(shortcut: Shortcut, className: string) {
|
||||
const createButton = (shortcut: Shortcut, className: string): Button<Label<Attribute>, Attribute> => {
|
||||
if (shortcut.configurable !== false) {
|
||||
return Widget.Button({
|
||||
vexpand: true,
|
||||
@@ -101,7 +105,7 @@ const Shortcuts = () => {
|
||||
class_name: className,
|
||||
on_primary_click: () => handleClick(shortcut.command.value),
|
||||
child: Widget.Label({
|
||||
class_name: "button-label txt-icon",
|
||||
class_name: 'button-label txt-icon',
|
||||
label: shortcut.icon.value,
|
||||
}),
|
||||
});
|
||||
@@ -112,166 +116,197 @@ const Shortcuts = () => {
|
||||
tooltip_text: shortcut.tooltip,
|
||||
class_name: className,
|
||||
on_primary_click: (_, event) => {
|
||||
if (shortcut.command === "settings-dialog") {
|
||||
App.closeWindow("dashboardmenu");
|
||||
App.toggleWindow("settings-dialog");
|
||||
} else if (shortcut.command === "record") {
|
||||
if (shortcut.command === 'settings-dialog') {
|
||||
App.closeWindow('dashboardmenu');
|
||||
App.toggleWindow('settings-dialog');
|
||||
} else if (shortcut.command === 'record') {
|
||||
if (isRecording.value === true) {
|
||||
App.closeWindow("dashboardmenu");
|
||||
return Utils.execAsync(
|
||||
`${App.configDir}/services/screen_record.sh stop`,
|
||||
).catch((err) => console.error(err));
|
||||
App.closeWindow('dashboardmenu');
|
||||
return Utils.execAsync(`${App.configDir}/services/screen_record.sh stop`).catch((err) =>
|
||||
console.error(err),
|
||||
);
|
||||
} else {
|
||||
recordingDropdown.popup_at_pointer(event);
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Widget.Label({
|
||||
class_name: "button-label txt-icon",
|
||||
class_name: 'button-label txt-icon',
|
||||
label: shortcut.icon,
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function createButtonIfCommandExists(shortcut: Shortcut, className: string, command: string) {
|
||||
const createButtonIfCommandExists = (
|
||||
shortcut: Shortcut,
|
||||
className: string,
|
||||
command: string,
|
||||
): Button<Label<Attribute>, Attribute> | Box<Child, Attribute> => {
|
||||
if (command.length > 0) {
|
||||
return createButton(shortcut, className);
|
||||
}
|
||||
return Widget.Box();
|
||||
}
|
||||
};
|
||||
|
||||
return Widget.Box({
|
||||
class_name: "shortcuts-container",
|
||||
hpack: "fill",
|
||||
class_name: 'shortcuts-container',
|
||||
hpack: 'fill',
|
||||
hexpand: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
child: Utils.merge([
|
||||
left.shortcut1.command.bind("value"),
|
||||
left.shortcut2.command.bind("value"),
|
||||
left.shortcut1.tooltip.bind("value"),
|
||||
left.shortcut2.tooltip.bind("value"),
|
||||
left.shortcut1.icon.bind("value"),
|
||||
left.shortcut2.icon.bind("value"),
|
||||
left.shortcut3.command.bind("value"),
|
||||
left.shortcut4.command.bind("value"),
|
||||
left.shortcut3.tooltip.bind("value"),
|
||||
left.shortcut4.tooltip.bind("value"),
|
||||
left.shortcut3.icon.bind("value"),
|
||||
left.shortcut4.icon.bind("value")
|
||||
], () => {
|
||||
const isVisibleLeft = cmdLn(left.shortcut1) || cmdLn(left.shortcut2);
|
||||
const isVisibleRight = cmdLn(left.shortcut3) || cmdLn(left.shortcut4);
|
||||
child: Utils.merge(
|
||||
[
|
||||
left.shortcut1.command.bind('value'),
|
||||
left.shortcut2.command.bind('value'),
|
||||
left.shortcut1.tooltip.bind('value'),
|
||||
left.shortcut2.tooltip.bind('value'),
|
||||
left.shortcut1.icon.bind('value'),
|
||||
left.shortcut2.icon.bind('value'),
|
||||
left.shortcut3.command.bind('value'),
|
||||
left.shortcut4.command.bind('value'),
|
||||
left.shortcut3.tooltip.bind('value'),
|
||||
left.shortcut4.tooltip.bind('value'),
|
||||
left.shortcut3.icon.bind('value'),
|
||||
left.shortcut4.icon.bind('value'),
|
||||
],
|
||||
() => {
|
||||
const isVisibleLeft = cmdLn(left.shortcut1) || cmdLn(left.shortcut2);
|
||||
const isVisibleRight = cmdLn(left.shortcut3) || cmdLn(left.shortcut4);
|
||||
|
||||
if (!isVisibleLeft && !isVisibleRight) {
|
||||
leftCardHidden.value = true;
|
||||
return Widget.Box();
|
||||
}
|
||||
if (!isVisibleLeft && !isVisibleRight) {
|
||||
leftCardHidden.value = true;
|
||||
return Widget.Box();
|
||||
}
|
||||
|
||||
leftCardHidden.value = false;
|
||||
leftCardHidden.value = false;
|
||||
|
||||
return Widget.Box({
|
||||
class_name: "container most-used dashboard-card",
|
||||
children: [
|
||||
Widget.Box({
|
||||
className: `card-button-section-container ${isVisibleRight && isVisibleLeft ? "visible" : ""}`,
|
||||
child: isVisibleLeft ? Widget.Box({
|
||||
vertical: true,
|
||||
hexpand: true,
|
||||
vexpand: true,
|
||||
children: [
|
||||
createButtonIfCommandExists(
|
||||
left.shortcut1,
|
||||
`dashboard-button top-button ${cmdLn(left.shortcut2) ? "paired" : ""}`,
|
||||
left.shortcut1.command.value),
|
||||
createButtonIfCommandExists(
|
||||
left.shortcut2,
|
||||
"dashboard-button",
|
||||
left.shortcut2.command.value
|
||||
),
|
||||
],
|
||||
}) : Widget.Box({
|
||||
children: [],
|
||||
})
|
||||
}),
|
||||
Widget.Box({
|
||||
className: "card-button-section-container",
|
||||
child: isVisibleRight ? Widget.Box({
|
||||
vertical: true,
|
||||
hexpand: true,
|
||||
vexpand: true,
|
||||
children: [
|
||||
createButtonIfCommandExists(
|
||||
left.shortcut3,
|
||||
`dashboard-button top-button ${cmdLn(left.shortcut4) ? "paired" : ""}`,
|
||||
left.shortcut3.command.value),
|
||||
createButtonIfCommandExists(
|
||||
left.shortcut4,
|
||||
"dashboard-button",
|
||||
left.shortcut4.command.value
|
||||
),
|
||||
],
|
||||
}) : Widget.Box({
|
||||
children: [],
|
||||
return Widget.Box({
|
||||
class_name: 'container most-used dashboard-card',
|
||||
children: [
|
||||
Widget.Box({
|
||||
className: `card-button-section-container ${isVisibleRight && isVisibleLeft ? 'visible' : ''}`,
|
||||
child: isVisibleLeft
|
||||
? Widget.Box({
|
||||
vertical: true,
|
||||
hexpand: true,
|
||||
vexpand: true,
|
||||
children: [
|
||||
createButtonIfCommandExists(
|
||||
left.shortcut1,
|
||||
`dashboard-button top-button ${cmdLn(left.shortcut2) ? 'paired' : ''}`,
|
||||
left.shortcut1.command.value,
|
||||
),
|
||||
createButtonIfCommandExists(
|
||||
left.shortcut2,
|
||||
'dashboard-button',
|
||||
left.shortcut2.command.value,
|
||||
),
|
||||
],
|
||||
})
|
||||
: Widget.Box({
|
||||
children: [],
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
]
|
||||
});
|
||||
})
|
||||
Widget.Box({
|
||||
className: 'card-button-section-container',
|
||||
child: isVisibleRight
|
||||
? Widget.Box({
|
||||
vertical: true,
|
||||
hexpand: true,
|
||||
vexpand: true,
|
||||
children: [
|
||||
createButtonIfCommandExists(
|
||||
left.shortcut3,
|
||||
`dashboard-button top-button ${cmdLn(left.shortcut4) ? 'paired' : ''}`,
|
||||
left.shortcut3.command.value,
|
||||
),
|
||||
createButtonIfCommandExists(
|
||||
left.shortcut4,
|
||||
'dashboard-button',
|
||||
left.shortcut4.command.value,
|
||||
),
|
||||
],
|
||||
})
|
||||
: Widget.Box({
|
||||
children: [],
|
||||
}),
|
||||
}),
|
||||
],
|
||||
});
|
||||
},
|
||||
),
|
||||
}),
|
||||
Widget.Box({
|
||||
child: Utils.merge([
|
||||
right.shortcut1.command.bind("value"),
|
||||
right.shortcut1.tooltip.bind("value"),
|
||||
right.shortcut1.icon.bind("value"),
|
||||
right.shortcut3.command.bind("value"),
|
||||
right.shortcut3.tooltip.bind("value"),
|
||||
right.shortcut3.icon.bind("value"),
|
||||
leftCardHidden.bind("value"),
|
||||
isRecording.bind("value")
|
||||
], () => {
|
||||
return Widget.Box({
|
||||
class_name: `container utilities dashboard-card ${!leftCardHidden.value ? "paired" : ""}`,
|
||||
children: [
|
||||
Widget.Box({
|
||||
className: `card-button-section-container visible`,
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
hexpand: true,
|
||||
vexpand: true,
|
||||
children: [
|
||||
createButtonIfCommandExists(right.shortcut1, "dashboard-button top-button paired", right.shortcut1.command.value),
|
||||
createButtonIfCommandExists(
|
||||
{
|
||||
tooltip: "HyprPanel Configuration",
|
||||
command: "settings-dialog",
|
||||
icon: "",
|
||||
configurable: false
|
||||
}, "dashboard-button", "settings-dialog"),
|
||||
],
|
||||
})
|
||||
}),
|
||||
Widget.Box({
|
||||
className: "card-button-section-container",
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
hexpand: true,
|
||||
vexpand: true,
|
||||
children: [
|
||||
createButtonIfCommandExists(right.shortcut3, "dashboard-button top-button paired", right.shortcut3.command.value),
|
||||
createButtonIfCommandExists({
|
||||
tooltip: "Record Screen",
|
||||
command: "record",
|
||||
icon: "",
|
||||
configurable: false
|
||||
}, `dashboard-button record ${isRecording.value ? "active" : ""}`, "record"),
|
||||
],
|
||||
child: Utils.merge(
|
||||
[
|
||||
right.shortcut1.command.bind('value'),
|
||||
right.shortcut1.tooltip.bind('value'),
|
||||
right.shortcut1.icon.bind('value'),
|
||||
right.shortcut3.command.bind('value'),
|
||||
right.shortcut3.tooltip.bind('value'),
|
||||
right.shortcut3.icon.bind('value'),
|
||||
leftCardHidden.bind('value'),
|
||||
isRecording.bind('value'),
|
||||
],
|
||||
() => {
|
||||
return Widget.Box({
|
||||
class_name: `container utilities dashboard-card ${!leftCardHidden.value ? 'paired' : ''}`,
|
||||
children: [
|
||||
Widget.Box({
|
||||
className: `card-button-section-container visible`,
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
hexpand: true,
|
||||
vexpand: true,
|
||||
children: [
|
||||
createButtonIfCommandExists(
|
||||
right.shortcut1,
|
||||
'dashboard-button top-button paired',
|
||||
right.shortcut1.command.value,
|
||||
),
|
||||
createButtonIfCommandExists(
|
||||
{
|
||||
tooltip: 'HyprPanel Configuration',
|
||||
command: 'settings-dialog',
|
||||
icon: '',
|
||||
configurable: false,
|
||||
},
|
||||
'dashboard-button',
|
||||
'settings-dialog',
|
||||
),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
]
|
||||
});
|
||||
})
|
||||
Widget.Box({
|
||||
className: 'card-button-section-container',
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
hexpand: true,
|
||||
vexpand: true,
|
||||
children: [
|
||||
createButtonIfCommandExists(
|
||||
right.shortcut3,
|
||||
'dashboard-button top-button paired',
|
||||
right.shortcut3.command.value,
|
||||
),
|
||||
createButtonIfCommandExists(
|
||||
{
|
||||
tooltip: 'Record Screen',
|
||||
command: 'record',
|
||||
icon: '',
|
||||
configurable: false,
|
||||
},
|
||||
`dashboard-button record ${isRecording.value ? 'active' : ''}`,
|
||||
'record',
|
||||
),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
],
|
||||
});
|
||||
},
|
||||
),
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
@@ -1,32 +1,33 @@
|
||||
import options from "options";
|
||||
import { GPU_Stat } from "lib/types/gpustat";
|
||||
import { dependencies } from "lib/utils";
|
||||
import options from 'options';
|
||||
import { GPU_Stat } from 'lib/types/gpustat';
|
||||
import { dependencies } from 'lib/utils';
|
||||
import { BoxWidget } from 'lib/types/widget';
|
||||
import { GenericResourceMetrics } from 'lib/types/customModules/generic';
|
||||
|
||||
const { terminal } = options;
|
||||
const { enable_gpu } = options.menus.dashboard.stats;
|
||||
|
||||
const Stats = () => {
|
||||
const divide = ([total, free]: number[]) => free / total;
|
||||
const Stats = (): BoxWidget => {
|
||||
const divide = ([total, free]: number[]): number => free / total;
|
||||
|
||||
const formatSizeInGB = (sizeInKB: number) =>
|
||||
Number((sizeInKB / 1024 ** 2).toFixed(2));
|
||||
const formatSizeInGB = (sizeInKB: number): number => Number((sizeInKB / 1024 ** 2).toFixed(2));
|
||||
|
||||
const cpu = Variable(0, {
|
||||
poll: [
|
||||
2000,
|
||||
"top -b -n 1",
|
||||
(out) => {
|
||||
if (typeof out !== "string") {
|
||||
'top -b -n 1',
|
||||
(out): number => {
|
||||
if (typeof out !== 'string') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const cpuOut = out.split("\n").find((line) => line.includes("Cpu(s)"));
|
||||
const cpuOut = out.split('\n').find((line) => line.includes('Cpu(s)'));
|
||||
|
||||
if (cpuOut === undefined) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const freeCpu = parseFloat(cpuOut.split(/\s+/)[1].replace(",", "."));
|
||||
const freeCpu = parseFloat(cpuOut.split(/\s+/)[1].replace(',', '.'));
|
||||
return divide([100, freeCpu]);
|
||||
},
|
||||
],
|
||||
@@ -37,22 +38,19 @@ const Stats = () => {
|
||||
{
|
||||
poll: [
|
||||
2000,
|
||||
"free",
|
||||
(out) => {
|
||||
if (typeof out !== "string") {
|
||||
'free',
|
||||
(out): GenericResourceMetrics => {
|
||||
if (typeof out !== 'string') {
|
||||
return { total: 0, used: 0, percentage: 0 };
|
||||
}
|
||||
|
||||
const ramOut = out.split("\n").find((line) => line.includes("Mem:"));
|
||||
const ramOut = out.split('\n').find((line) => line.includes('Mem:'));
|
||||
|
||||
if (ramOut === undefined) {
|
||||
return { total: 0, used: 0, percentage: 0 };
|
||||
}
|
||||
|
||||
const [totalRam, usedRam] = ramOut
|
||||
.split(/\s+/)
|
||||
.splice(1, 2)
|
||||
.map(Number);
|
||||
const [totalRam, usedRam] = ramOut.split(/\s+/).splice(1, 2).map(Number);
|
||||
|
||||
return {
|
||||
percentage: divide([totalRam, usedRam]),
|
||||
@@ -67,8 +65,8 @@ const Stats = () => {
|
||||
const gpu = Variable(0);
|
||||
|
||||
const GPUStat = Widget.Box({
|
||||
child: enable_gpu.bind("value").as((gpStat) => {
|
||||
if (!gpStat || !dependencies("gpustat")) {
|
||||
child: enable_gpu.bind('value').as((gpStat) => {
|
||||
if (!gpStat || !dependencies('gpustat')) {
|
||||
return Widget.Box();
|
||||
}
|
||||
|
||||
@@ -76,19 +74,19 @@ const Stats = () => {
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "stat gpu",
|
||||
class_name: 'stat gpu',
|
||||
hexpand: true,
|
||||
vpack: "center",
|
||||
setup: self => {
|
||||
const getGpuUsage = () => {
|
||||
vpack: 'center',
|
||||
setup: (self) => {
|
||||
const getGpuUsage = (): void => {
|
||||
if (!enable_gpu.value) {
|
||||
gpu.value = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
Utils.execAsync("gpustat --json")
|
||||
Utils.execAsync('gpustat --json')
|
||||
.then((out) => {
|
||||
if (typeof out !== "string") {
|
||||
if (typeof out !== 'string') {
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
@@ -97,80 +95,79 @@ const Stats = () => {
|
||||
const totalGpu = 100;
|
||||
const usedGpu =
|
||||
data.gpus.reduce((acc: number, gpu: GPU_Stat) => {
|
||||
|
||||
return acc + gpu["utilization.gpu"]
|
||||
return acc + gpu['utilization.gpu'];
|
||||
}, 0) / data.gpus.length;
|
||||
|
||||
gpu.value = divide([totalGpu, usedGpu]);
|
||||
} catch (e) {
|
||||
console.error("Error getting GPU stats:", e);
|
||||
console.error('Error getting GPU stats:', e);
|
||||
gpu.value = 0;
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(`An error occurred while fetching GPU stats: ${err}`)
|
||||
})
|
||||
}
|
||||
console.error(`An error occurred while fetching GPU stats: ${err}`);
|
||||
});
|
||||
};
|
||||
|
||||
self.poll(2000, getGpuUsage)
|
||||
self.poll(2000, getGpuUsage);
|
||||
|
||||
Utils.merge([gpu.bind("value"), enable_gpu.bind("value")], (gpu, enableGpu) => {
|
||||
Utils.merge([gpu.bind('value'), enable_gpu.bind('value')], (gpu, enableGpu) => {
|
||||
if (!enableGpu) {
|
||||
return self.children = [];
|
||||
return (self.children = []);
|
||||
}
|
||||
|
||||
return self.children = [
|
||||
return (self.children = [
|
||||
Widget.Button({
|
||||
on_primary_click: terminal.bind("value").as(term => {
|
||||
return () => {
|
||||
App.closeWindow("dashboardmenu");
|
||||
on_primary_click: terminal.bind('value').as((term) => {
|
||||
return (): void => {
|
||||
App.closeWindow('dashboardmenu');
|
||||
Utils.execAsync(`bash -c "${term} -e btop"`).catch(
|
||||
(err) => `Failed to open btop: ${err}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
}),
|
||||
child: Widget.Label({
|
||||
class_name: "txt-icon",
|
||||
label: "",
|
||||
})
|
||||
class_name: 'txt-icon',
|
||||
label: '',
|
||||
}),
|
||||
}),
|
||||
Widget.Button({
|
||||
on_primary_click: terminal.bind("value").as(term => {
|
||||
return () => {
|
||||
App.closeWindow("dashboardmenu");
|
||||
on_primary_click: terminal.bind('value').as((term) => {
|
||||
return (): void => {
|
||||
App.closeWindow('dashboardmenu');
|
||||
Utils.execAsync(`bash -c "${term} -e btop"`).catch(
|
||||
(err) => `Failed to open btop: ${err}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
}),
|
||||
child: Widget.LevelBar({
|
||||
class_name: "stats-bar",
|
||||
class_name: 'stats-bar',
|
||||
hexpand: true,
|
||||
vpack: "center",
|
||||
vpack: 'center',
|
||||
value: gpu,
|
||||
}),
|
||||
}),
|
||||
]
|
||||
})
|
||||
]);
|
||||
});
|
||||
},
|
||||
}),
|
||||
Widget.Box({
|
||||
hpack: "end",
|
||||
children: Utils.merge([gpu.bind("value"), enable_gpu.bind("value")], (gpuUsed, enableGpu) => {
|
||||
hpack: 'end',
|
||||
children: Utils.merge([gpu.bind('value'), enable_gpu.bind('value')], (gpuUsed, enableGpu) => {
|
||||
if (!enableGpu) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
Widget.Label({
|
||||
class_name: "stat-value gpu",
|
||||
class_name: 'stat-value gpu',
|
||||
label: `${Math.floor(gpuUsed * 100)}%`,
|
||||
})
|
||||
}),
|
||||
];
|
||||
})
|
||||
})
|
||||
]
|
||||
})
|
||||
})
|
||||
}),
|
||||
}),
|
||||
],
|
||||
});
|
||||
}),
|
||||
});
|
||||
|
||||
const storage = Variable(
|
||||
@@ -178,13 +175,13 @@ const Stats = () => {
|
||||
{
|
||||
poll: [
|
||||
2000,
|
||||
"df -B1 /",
|
||||
(out) => {
|
||||
if (typeof out !== "string") {
|
||||
'df -B1 /',
|
||||
(out): GenericResourceMetrics => {
|
||||
if (typeof out !== 'string') {
|
||||
return { total: 0, used: 0, percentage: 0 };
|
||||
}
|
||||
|
||||
const dfOut = out.split("\n").find((line) => line.startsWith("/"));
|
||||
const dfOut = out.split('\n').find((line) => line.startsWith('/'));
|
||||
|
||||
if (dfOut === undefined) {
|
||||
return { total: 0, used: 0, percentage: 0 };
|
||||
@@ -208,58 +205,58 @@ const Stats = () => {
|
||||
);
|
||||
|
||||
return Widget.Box({
|
||||
class_name: "dashboard-card stats-container",
|
||||
class_name: 'dashboard-card stats-container',
|
||||
vertical: true,
|
||||
vpack: "fill",
|
||||
hpack: "fill",
|
||||
vpack: 'fill',
|
||||
hpack: 'fill',
|
||||
expand: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "stat cpu",
|
||||
class_name: 'stat cpu',
|
||||
hexpand: true,
|
||||
vpack: "center",
|
||||
vpack: 'center',
|
||||
children: [
|
||||
Widget.Button({
|
||||
on_primary_click: terminal.bind("value").as(term => {
|
||||
on_primary_click: terminal.bind('value').as((term) => {
|
||||
return () => {
|
||||
App.closeWindow("dashboardmenu");
|
||||
App.closeWindow('dashboardmenu');
|
||||
Utils.execAsync(`bash -c "${term} -e btop"`).catch(
|
||||
(err) => `Failed to open btop: ${err}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
}),
|
||||
child: Widget.Label({
|
||||
class_name: "txt-icon",
|
||||
label: "",
|
||||
})
|
||||
class_name: 'txt-icon',
|
||||
label: '',
|
||||
}),
|
||||
}),
|
||||
Widget.Button({
|
||||
on_primary_click: terminal.bind("value").as(term => {
|
||||
on_primary_click: terminal.bind('value').as((term) => {
|
||||
return () => {
|
||||
App.closeWindow("dashboardmenu");
|
||||
App.closeWindow('dashboardmenu');
|
||||
Utils.execAsync(`bash -c "${term} -e btop"`).catch(
|
||||
(err) => `Failed to open btop: ${err}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
}),
|
||||
child: Widget.LevelBar({
|
||||
class_name: "stats-bar",
|
||||
class_name: 'stats-bar',
|
||||
hexpand: true,
|
||||
vpack: "center",
|
||||
bar_mode: "continuous",
|
||||
vpack: 'center',
|
||||
bar_mode: 'continuous',
|
||||
max_value: 1,
|
||||
value: cpu.bind("value"),
|
||||
value: cpu.bind('value'),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Label({
|
||||
hpack: "end",
|
||||
class_name: "stat-value cpu",
|
||||
label: cpu.bind("value").as((v) => `${Math.floor(v * 100)}%`),
|
||||
hpack: 'end',
|
||||
class_name: 'stat-value cpu',
|
||||
label: cpu.bind('value').as((v) => `${Math.floor(v * 100)}%`),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
@@ -267,46 +264,46 @@ const Stats = () => {
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "stat ram",
|
||||
vpack: "center",
|
||||
class_name: 'stat ram',
|
||||
vpack: 'center',
|
||||
hexpand: true,
|
||||
children: [
|
||||
Widget.Button({
|
||||
on_primary_click: terminal.bind("value").as(term => {
|
||||
on_primary_click: terminal.bind('value').as((term) => {
|
||||
return () => {
|
||||
App.closeWindow("dashboardmenu");
|
||||
App.closeWindow('dashboardmenu');
|
||||
Utils.execAsync(`bash -c "${term} -e btop"`).catch(
|
||||
(err) => `Failed to open btop: ${err}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
}),
|
||||
child: Widget.Label({
|
||||
class_name: "txt-icon",
|
||||
label: "",
|
||||
})
|
||||
class_name: 'txt-icon',
|
||||
label: '',
|
||||
}),
|
||||
}),
|
||||
Widget.Button({
|
||||
on_primary_click: terminal.bind("value").as(term => {
|
||||
on_primary_click: terminal.bind('value').as((term) => {
|
||||
return () => {
|
||||
App.closeWindow("dashboardmenu");
|
||||
App.closeWindow('dashboardmenu');
|
||||
Utils.execAsync(`bash -c "${term} -e btop"`).catch(
|
||||
(err) => `Failed to open btop: ${err}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
}),
|
||||
child: Widget.LevelBar({
|
||||
class_name: "stats-bar",
|
||||
class_name: 'stats-bar',
|
||||
hexpand: true,
|
||||
vpack: "center",
|
||||
value: ram.bind("value").as((v) => v.percentage),
|
||||
vpack: 'center',
|
||||
value: ram.bind('value').as((v) => v.percentage),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Label({
|
||||
hpack: "end",
|
||||
class_name: "stat-value ram",
|
||||
label: ram.bind("value").as((v) => `${v.used}/${v.total} GB`),
|
||||
hpack: 'end',
|
||||
class_name: 'stat-value ram',
|
||||
label: ram.bind('value').as((v) => `${v.used}/${v.total} GB`),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
@@ -315,46 +312,46 @@ const Stats = () => {
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "stat storage",
|
||||
class_name: 'stat storage',
|
||||
hexpand: true,
|
||||
vpack: "center",
|
||||
vpack: 'center',
|
||||
children: [
|
||||
Widget.Button({
|
||||
on_primary_click: terminal.bind("value").as(term => {
|
||||
on_primary_click: terminal.bind('value').as((term) => {
|
||||
return () => {
|
||||
App.closeWindow("dashboardmenu");
|
||||
App.closeWindow('dashboardmenu');
|
||||
Utils.execAsync(`bash -c "${term} -e btop"`).catch(
|
||||
(err) => `Failed to open btop: ${err}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
}),
|
||||
child: Widget.Label({
|
||||
class_name: "txt-icon",
|
||||
label: "",
|
||||
})
|
||||
class_name: 'txt-icon',
|
||||
label: '',
|
||||
}),
|
||||
}),
|
||||
Widget.Button({
|
||||
on_primary_click: terminal.bind("value").as(term => {
|
||||
on_primary_click: terminal.bind('value').as((term) => {
|
||||
return () => {
|
||||
App.closeWindow("dashboardmenu");
|
||||
App.closeWindow('dashboardmenu');
|
||||
Utils.execAsync(`bash -c "${term} -e btop"`).catch(
|
||||
(err) => `Failed to open btop: ${err}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
}),
|
||||
child: Widget.LevelBar({
|
||||
class_name: "stats-bar",
|
||||
class_name: 'stats-bar',
|
||||
hexpand: true,
|
||||
vpack: "center",
|
||||
value: storage.bind("value").as((v) => v.percentage),
|
||||
vpack: 'center',
|
||||
value: storage.bind('value').as((v) => v.percentage),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Label({
|
||||
hpack: "end",
|
||||
class_name: "stat-value storage",
|
||||
label: storage.bind("value").as((v) => `${v.used}/${v.total} GB`),
|
||||
hpack: 'end',
|
||||
class_name: 'stat-value storage',
|
||||
label: storage.bind('value').as((v) => `${v.used}/${v.total} GB`),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
|
||||
@@ -1,40 +1,41 @@
|
||||
import brightness from "../../../../services/Brightness.js";
|
||||
import icons from "../../../icons/index.js";
|
||||
import { BoxWidget } from 'lib/types/widget.js';
|
||||
import brightness from '../../../../services/Brightness.js';
|
||||
import icons from '../../../icons/index.js';
|
||||
|
||||
const Brightness = () => {
|
||||
const Brightness = (): BoxWidget => {
|
||||
return Widget.Box({
|
||||
class_name: "menu-section-container brightness",
|
||||
class_name: 'menu-section-container brightness',
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "menu-label-container",
|
||||
hpack: "fill",
|
||||
class_name: 'menu-label-container',
|
||||
hpack: 'fill',
|
||||
child: Widget.Label({
|
||||
class_name: "menu-label",
|
||||
class_name: 'menu-label',
|
||||
hexpand: true,
|
||||
hpack: "start",
|
||||
label: "Brightness",
|
||||
hpack: 'start',
|
||||
label: 'Brightness',
|
||||
}),
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "menu-items-section",
|
||||
vpack: "fill",
|
||||
class_name: 'menu-items-section',
|
||||
vpack: 'fill',
|
||||
vexpand: true,
|
||||
vertical: true,
|
||||
child: Widget.Box({
|
||||
class_name: "brightness-container",
|
||||
class_name: 'brightness-container',
|
||||
children: [
|
||||
Widget.Icon({
|
||||
vexpand: true,
|
||||
vpack: "center",
|
||||
class_name: "brightness-slider-icon",
|
||||
vpack: 'center',
|
||||
class_name: 'brightness-slider-icon',
|
||||
icon: icons.brightness.screen,
|
||||
}),
|
||||
Widget.Slider({
|
||||
vpack: "center",
|
||||
vpack: 'center',
|
||||
vexpand: true,
|
||||
value: brightness.bind("screen"),
|
||||
class_name: "menu-active-slider menu-slider brightness",
|
||||
value: brightness.bind('screen'),
|
||||
class_name: 'menu-active-slider menu-slider brightness',
|
||||
draw_value: false,
|
||||
hexpand: true,
|
||||
min: 0,
|
||||
@@ -42,12 +43,10 @@ const Brightness = () => {
|
||||
onChange: ({ value }) => (brightness.screen = value),
|
||||
}),
|
||||
Widget.Label({
|
||||
vpack: "center",
|
||||
vpack: 'center',
|
||||
vexpand: true,
|
||||
class_name: "brightness-slider-label",
|
||||
label: brightness
|
||||
.bind("screen")
|
||||
.as((b) => `${Math.round(b * 100)}%`),
|
||||
class_name: 'brightness-slider-label',
|
||||
label: brightness.bind('screen').as((b) => `${Math.round(b * 100)}%`),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
import DropdownMenu from "../DropdownMenu.js";
|
||||
import { EnergyProfiles } from "./profiles/index.js";
|
||||
import { Brightness } from "./brightness/index.js";
|
||||
import DropdownMenu from '../DropdownMenu.js';
|
||||
import { EnergyProfiles } from './profiles/index.js';
|
||||
import { Brightness } from './brightness/index.js';
|
||||
import { Attribute, Child } from 'lib/types/widget.js';
|
||||
import Window from 'types/widgets/window.js';
|
||||
|
||||
export default () => {
|
||||
export default (): Window<Child, Attribute> => {
|
||||
return DropdownMenu({
|
||||
name: "energymenu",
|
||||
transition: "crossfade",
|
||||
name: 'energymenu',
|
||||
transition: 'crossfade',
|
||||
child: Widget.Box({
|
||||
class_name: "menu-items energy",
|
||||
hpack: "fill",
|
||||
class_name: 'menu-items energy',
|
||||
hpack: 'fill',
|
||||
hexpand: true,
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
hpack: "fill",
|
||||
hpack: 'fill',
|
||||
hexpand: true,
|
||||
class_name: "menu-items-container energy",
|
||||
children: [
|
||||
Brightness(),
|
||||
EnergyProfiles(),
|
||||
],
|
||||
class_name: 'menu-items-container energy',
|
||||
children: [Brightness(), EnergyProfiles()],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -1,36 +1,37 @@
|
||||
const powerProfiles = await Service.import("powerprofiles");
|
||||
import { PowerProfile, PowerProfileObject, PowerProfiles } from "lib/types/powerprofiles.js";
|
||||
import icons from "../../../icons/index.js";
|
||||
const powerProfiles = await Service.import('powerprofiles');
|
||||
import { PowerProfile, PowerProfileObject, PowerProfiles } from 'lib/types/powerprofiles.js';
|
||||
import icons from '../../../icons/index.js';
|
||||
import { BoxWidget } from 'lib/types/widget.js';
|
||||
|
||||
const EnergyProfiles = () => {
|
||||
const EnergyProfiles = (): BoxWidget => {
|
||||
const isValidProfile = (profile: string): profile is PowerProfile =>
|
||||
profile === "power-saver" || profile === "balanced" || profile === "performance";
|
||||
profile === 'power-saver' || profile === 'balanced' || profile === 'performance';
|
||||
|
||||
return Widget.Box({
|
||||
class_name: "menu-section-container energy",
|
||||
class_name: 'menu-section-container energy',
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "menu-label-container",
|
||||
hpack: "fill",
|
||||
class_name: 'menu-label-container',
|
||||
hpack: 'fill',
|
||||
child: Widget.Label({
|
||||
class_name: "menu-label",
|
||||
class_name: 'menu-label',
|
||||
hexpand: true,
|
||||
hpack: "start",
|
||||
label: "Power Profile",
|
||||
hpack: 'start',
|
||||
label: 'Power Profile',
|
||||
}),
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "menu-items-section",
|
||||
vpack: "fill",
|
||||
class_name: 'menu-items-section',
|
||||
vpack: 'fill',
|
||||
vexpand: true,
|
||||
vertical: true,
|
||||
children: powerProfiles.bind("profiles").as((profiles: PowerProfiles) => {
|
||||
children: powerProfiles.bind('profiles').as((profiles: PowerProfiles) => {
|
||||
return profiles.map((prof: PowerProfileObject) => {
|
||||
const profileLabels = {
|
||||
"power-saver": "Power Saver",
|
||||
balanced: "Balanced",
|
||||
performance: "Performance",
|
||||
'power-saver': 'Power Saver',
|
||||
balanced: 'Balanced',
|
||||
performance: 'Performance',
|
||||
};
|
||||
|
||||
const profileType = prof.Profile;
|
||||
@@ -43,17 +44,17 @@ const EnergyProfiles = () => {
|
||||
on_primary_click: () => {
|
||||
powerProfiles.active_profile = prof.Profile;
|
||||
},
|
||||
class_name: powerProfiles.bind("active_profile").as((active) => {
|
||||
return `power-profile-item ${active === prof.Profile ? "active" : ""}`;
|
||||
class_name: powerProfiles.bind('active_profile').as((active) => {
|
||||
return `power-profile-item ${active === prof.Profile ? 'active' : ''}`;
|
||||
}),
|
||||
child: Widget.Box({
|
||||
children: [
|
||||
Widget.Icon({
|
||||
class_name: "power-profile-icon",
|
||||
class_name: 'power-profile-icon',
|
||||
icon: icons.powerprofile[profileType],
|
||||
}),
|
||||
Widget.Label({
|
||||
class_name: "power-profile-label",
|
||||
class_name: 'power-profile-label',
|
||||
label: profileLabels[profileType],
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import PowerMenu from "./power/index.js";
|
||||
import Verification from "./power/verification.js";
|
||||
import AudioMenu from "./audio/index.js";
|
||||
import NetworkMenu from "./network/index.js";
|
||||
import BluetoothMenu from "./bluetooth/index.js";
|
||||
import MediaMenu from "./media/index.js";
|
||||
import NotificationsMenu from "./notifications/index.js";
|
||||
import CalendarMenu from "./calendar/index.js";
|
||||
import EnergyMenu from "./energy/index.js";
|
||||
import DashboardMenu from "./dashboard/index.js";
|
||||
import PowerDropdown from "./powerDropdown/index.js";
|
||||
import PowerMenu from './power/index.js';
|
||||
import Verification from './power/verification.js';
|
||||
import AudioMenu from './audio/index.js';
|
||||
import NetworkMenu from './network/index.js';
|
||||
import BluetoothMenu from './bluetooth/index.js';
|
||||
import MediaMenu from './media/index.js';
|
||||
import NotificationsMenu from './notifications/index.js';
|
||||
import CalendarMenu from './calendar/index.js';
|
||||
import EnergyMenu from './energy/index.js';
|
||||
import DashboardMenu from './dashboard/index.js';
|
||||
import PowerDropdown from './powerDropdown/index.js';
|
||||
|
||||
export default [
|
||||
PowerMenu(),
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
const media = await Service.import("mpris");
|
||||
import { BoxWidget } from 'lib/types/widget';
|
||||
import { Mpris, MprisPlayer } from 'types/service/mpris';
|
||||
|
||||
const Bar = (getPlayerInfo: Function) => {
|
||||
const media = await Service.import('mpris');
|
||||
|
||||
const Bar = (getPlayerInfo: (media: Mpris) => MprisPlayer): BoxWidget => {
|
||||
return Widget.Box({
|
||||
class_name: "media-indicator-current-progress-bar",
|
||||
class_name: 'media-indicator-current-progress-bar',
|
||||
hexpand: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
hexpand: true,
|
||||
child: Widget.Slider({
|
||||
hexpand: true,
|
||||
tooltip_text: "--",
|
||||
class_name: "menu-slider media progress",
|
||||
tooltip_text: '--',
|
||||
class_name: 'menu-slider media progress',
|
||||
draw_value: false,
|
||||
on_change: ({ value }) => {
|
||||
const foundPlayer = getPlayerInfo(media);
|
||||
@@ -20,7 +23,7 @@ const Bar = (getPlayerInfo: Function) => {
|
||||
return (foundPlayer.position = value * foundPlayer.length);
|
||||
},
|
||||
setup: (self) => {
|
||||
const update = () => {
|
||||
const update = (): void => {
|
||||
const foundPlayer = getPlayerInfo(media);
|
||||
if (foundPlayer !== undefined) {
|
||||
const value = foundPlayer.length ? foundPlayer.position / foundPlayer.length : 0;
|
||||
@@ -32,28 +35,25 @@ const Bar = (getPlayerInfo: Function) => {
|
||||
self.hook(media, update);
|
||||
self.poll(1000, update);
|
||||
|
||||
function updateTooltip() {
|
||||
const updateTooltip = (): void => {
|
||||
const foundPlayer = getPlayerInfo(media);
|
||||
if (foundPlayer === undefined) {
|
||||
return self.tooltip_text = '00:00'
|
||||
self.tooltip_text = '00:00';
|
||||
return;
|
||||
}
|
||||
const curHour = Math.floor(foundPlayer.position / 3600);
|
||||
const curMin = Math.floor((foundPlayer.position % 3600) / 60);
|
||||
const curSec = Math.floor(foundPlayer.position % 60);
|
||||
|
||||
if (
|
||||
typeof foundPlayer.position === "number" &&
|
||||
foundPlayer.position >= 0
|
||||
) {
|
||||
if (typeof foundPlayer.position === 'number' && foundPlayer.position >= 0) {
|
||||
// WARN: These nested ternaries are absolutely disgusting lol
|
||||
self.tooltip_text = `${curHour > 0
|
||||
? (curHour < 10 ? "0" + curHour : curHour) + ":"
|
||||
: ""
|
||||
}${curMin < 10 ? "0" + curMin : curMin}:${curSec < 10 ? "0" + curSec : curSec}`;
|
||||
self.tooltip_text = `${
|
||||
curHour > 0 ? (curHour < 10 ? '0' + curHour : curHour) + ':' : ''
|
||||
}${curMin < 10 ? '0' + curMin : curMin}:${curSec < 10 ? '0' + curSec : curSec}`;
|
||||
} else {
|
||||
self.tooltip_text = `00:00`;
|
||||
}
|
||||
}
|
||||
};
|
||||
self.poll(1000, updateTooltip);
|
||||
self.hook(media, updateTooltip);
|
||||
},
|
||||
|
||||
@@ -1,82 +1,81 @@
|
||||
import { MprisPlayer } from "types/service/mpris.js";
|
||||
import icons from "../../../icons/index.js";
|
||||
import { LoopStatus, PlaybackStatus } from "lib/types/mpris.js";
|
||||
const media = await Service.import("mpris");
|
||||
import { MprisPlayer } from 'types/service/mpris.js';
|
||||
import icons from '../../../icons/index.js';
|
||||
import { LoopStatus, PlaybackStatus } from 'lib/types/mpris.js';
|
||||
import { BoxWidget } from 'lib/types/widget.js';
|
||||
const media = await Service.import('mpris');
|
||||
|
||||
const Controls = (getPlayerInfo: Function) => {
|
||||
const isValidLoopStatus = (status: string): status is LoopStatus =>
|
||||
["none", "track", "playlist"].includes(status);
|
||||
const Controls = (getPlayerInfo: () => MprisPlayer): BoxWidget => {
|
||||
const isValidLoopStatus = (status: string): status is LoopStatus => ['none', 'track', 'playlist'].includes(status);
|
||||
|
||||
const isValidPlaybackStatus = (status: string): status is PlaybackStatus =>
|
||||
["playing", "paused", "stopped"].includes(status);
|
||||
['playing', 'paused', 'stopped'].includes(status);
|
||||
|
||||
const isLoopActive = (player: MprisPlayer) => {
|
||||
return player["loop_status"] !== null &&
|
||||
["track", "playlist"].includes(player["loop_status"].toLowerCase())
|
||||
? "active"
|
||||
: "";
|
||||
const isLoopActive = (player: MprisPlayer): string => {
|
||||
return player['loop_status'] !== null && ['track', 'playlist'].includes(player['loop_status'].toLowerCase())
|
||||
? 'active'
|
||||
: '';
|
||||
};
|
||||
|
||||
const isShuffleActive = (player: MprisPlayer) => {
|
||||
return player["shuffle_status"] !== null && player["shuffle_status"]
|
||||
? "active"
|
||||
: "";
|
||||
const isShuffleActive = (player: MprisPlayer): string => {
|
||||
return player['shuffle_status'] !== null && player['shuffle_status'] ? 'active' : '';
|
||||
};
|
||||
|
||||
return Widget.Box({
|
||||
class_name: "media-indicator-current-player-controls",
|
||||
class_name: 'media-indicator-current-player-controls',
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "media-indicator-current-controls",
|
||||
hpack: "center",
|
||||
class_name: 'media-indicator-current-controls',
|
||||
hpack: 'center',
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "media-indicator-control shuffle",
|
||||
class_name: 'media-indicator-control shuffle',
|
||||
children: [
|
||||
Widget.Button({
|
||||
hpack: "center",
|
||||
hpack: 'center',
|
||||
hasTooltip: true,
|
||||
setup: (self) => {
|
||||
self.hook(media, () => {
|
||||
const foundPlayer = getPlayerInfo();
|
||||
if (foundPlayer === undefined) {
|
||||
self.tooltip_text = "Unavailable";
|
||||
self.class_name =
|
||||
"media-indicator-control-button shuffle disabled";
|
||||
self.tooltip_text = 'Unavailable';
|
||||
self.class_name = 'media-indicator-control-button shuffle disabled';
|
||||
return;
|
||||
}
|
||||
|
||||
self.tooltip_text =
|
||||
foundPlayer.shuffle_status !== null
|
||||
? foundPlayer.shuffle_status
|
||||
? "Shuffling"
|
||||
: "Not Shuffling"
|
||||
? 'Shuffling'
|
||||
: 'Not Shuffling'
|
||||
: null;
|
||||
self.on_primary_click = () => foundPlayer.shuffle();
|
||||
self.class_name = `media-indicator-control-button shuffle ${isShuffleActive(foundPlayer)} ${foundPlayer.shuffle_status !== null ? "enabled" : "disabled"}`;
|
||||
self.on_primary_click = (): void => {
|
||||
foundPlayer.shuffle();
|
||||
};
|
||||
self.class_name = `media-indicator-control-button shuffle ${isShuffleActive(foundPlayer)} ${foundPlayer.shuffle_status !== null ? 'enabled' : 'disabled'}`;
|
||||
});
|
||||
},
|
||||
child: Widget.Icon(icons.mpris.shuffle["enabled"]),
|
||||
child: Widget.Icon(icons.mpris.shuffle['enabled']),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Box({
|
||||
children: [
|
||||
Widget.Button({
|
||||
hpack: "center",
|
||||
hpack: 'center',
|
||||
child: Widget.Icon(icons.mpris.prev),
|
||||
setup: (self) => {
|
||||
self.hook(media, () => {
|
||||
const foundPlayer = getPlayerInfo();
|
||||
if (foundPlayer === undefined) {
|
||||
self.class_name =
|
||||
"media-indicator-control-button prev disabled";
|
||||
self.class_name = 'media-indicator-control-button prev disabled';
|
||||
return;
|
||||
}
|
||||
|
||||
self.on_primary_click = () => foundPlayer.previous();
|
||||
self.class_name = `media-indicator-control-button prev ${foundPlayer.can_go_prev !== null && foundPlayer.can_go_prev ? "enabled" : "disabled"}`;
|
||||
self.on_primary_click = (): void => {
|
||||
foundPlayer.previous();
|
||||
};
|
||||
self.class_name = `media-indicator-control-button prev ${foundPlayer.can_go_prev !== null && foundPlayer.can_go_prev ? 'enabled' : 'disabled'}`;
|
||||
});
|
||||
},
|
||||
}),
|
||||
@@ -85,40 +84,35 @@ const Controls = (getPlayerInfo: Function) => {
|
||||
Widget.Box({
|
||||
children: [
|
||||
Widget.Button({
|
||||
hpack: "center",
|
||||
hpack: 'center',
|
||||
setup: (self) => {
|
||||
self.hook(media, () => {
|
||||
const foundPlayer = getPlayerInfo();
|
||||
if (foundPlayer === undefined) {
|
||||
self.class_name =
|
||||
"media-indicator-control-button play disabled";
|
||||
self.class_name = 'media-indicator-control-button play disabled';
|
||||
return;
|
||||
}
|
||||
|
||||
self.on_primary_click = () => foundPlayer.playPause();
|
||||
self.class_name = `media-indicator-control-button play ${foundPlayer.can_play !== null ? "enabled" : "disabled"}`;
|
||||
self.on_primary_click = (): void => {
|
||||
foundPlayer.playPause();
|
||||
};
|
||||
self.class_name = `media-indicator-control-button play ${foundPlayer.can_play !== null ? 'enabled' : 'disabled'}`;
|
||||
});
|
||||
},
|
||||
child: Widget.Icon({
|
||||
icon: Utils.watch(
|
||||
icons.mpris.paused,
|
||||
media,
|
||||
"changed",
|
||||
() => {
|
||||
const foundPlayer: MprisPlayer = getPlayerInfo();
|
||||
if (foundPlayer === undefined) {
|
||||
return icons.mpris["paused"];
|
||||
}
|
||||
const playbackStatus = foundPlayer.play_back_status?.toLowerCase();
|
||||
icon: Utils.watch(icons.mpris.paused, media, 'changed', () => {
|
||||
const foundPlayer: MprisPlayer = getPlayerInfo();
|
||||
if (foundPlayer === undefined) {
|
||||
return icons.mpris['paused'];
|
||||
}
|
||||
const playbackStatus = foundPlayer.play_back_status?.toLowerCase();
|
||||
|
||||
if (playbackStatus && isValidPlaybackStatus(playbackStatus)) {
|
||||
return icons.mpris[playbackStatus];
|
||||
}
|
||||
else {
|
||||
return icons.mpris["paused"];
|
||||
}
|
||||
},
|
||||
),
|
||||
if (playbackStatus && isValidPlaybackStatus(playbackStatus)) {
|
||||
return icons.mpris[playbackStatus];
|
||||
} else {
|
||||
return icons.mpris['paused'];
|
||||
}
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
@@ -127,47 +121,49 @@ const Controls = (getPlayerInfo: Function) => {
|
||||
class_name: `media-indicator-control next`,
|
||||
children: [
|
||||
Widget.Button({
|
||||
hpack: "center",
|
||||
hpack: 'center',
|
||||
child: Widget.Icon(icons.mpris.next),
|
||||
setup: (self) => {
|
||||
self.hook(media, () => {
|
||||
const foundPlayer = getPlayerInfo();
|
||||
if (foundPlayer === undefined) {
|
||||
self.class_name =
|
||||
"media-indicator-control-button next disabled";
|
||||
self.class_name = 'media-indicator-control-button next disabled';
|
||||
return;
|
||||
}
|
||||
|
||||
self.on_primary_click = () => foundPlayer.next();
|
||||
self.class_name = `media-indicator-control-button next ${foundPlayer.can_go_next !== null && foundPlayer.can_go_next ? "enabled" : "disabled"}`;
|
||||
self.on_primary_click = (): void => {
|
||||
foundPlayer.next();
|
||||
};
|
||||
self.class_name = `media-indicator-control-button next ${foundPlayer.can_go_next !== null && foundPlayer.can_go_next ? 'enabled' : 'disabled'}`;
|
||||
});
|
||||
},
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "media-indicator-control loop",
|
||||
class_name: 'media-indicator-control loop',
|
||||
children: [
|
||||
Widget.Button({
|
||||
hpack: "center",
|
||||
hpack: 'center',
|
||||
setup: (self) => {
|
||||
self.hook(media, () => {
|
||||
const foundPlayer = getPlayerInfo();
|
||||
if (foundPlayer === undefined) {
|
||||
self.tooltip_text = "Unavailable";
|
||||
self.class_name =
|
||||
"media-indicator-control-button shuffle disabled";
|
||||
self.tooltip_text = 'Unavailable';
|
||||
self.class_name = 'media-indicator-control-button shuffle disabled';
|
||||
return;
|
||||
}
|
||||
|
||||
self.tooltip_text =
|
||||
foundPlayer.loop_status !== null
|
||||
? foundPlayer.loop_status
|
||||
? "Shuffling"
|
||||
: "Not Shuffling"
|
||||
? 'Shuffling'
|
||||
: 'Not Shuffling'
|
||||
: null;
|
||||
self.on_primary_click = () => foundPlayer.loop();
|
||||
self.class_name = `media-indicator-control-button loop ${isLoopActive(foundPlayer)} ${foundPlayer.loop_status !== null ? "enabled" : "disabled"}`;
|
||||
self.on_primary_click = (): void => {
|
||||
foundPlayer.loop();
|
||||
};
|
||||
self.class_name = `media-indicator-control-button loop ${isLoopActive(foundPlayer)} ${foundPlayer.loop_status !== null ? 'enabled' : 'disabled'}`;
|
||||
});
|
||||
},
|
||||
child: Widget.Icon({
|
||||
@@ -176,7 +172,7 @@ const Controls = (getPlayerInfo: Function) => {
|
||||
const foundPlayer: MprisPlayer = getPlayerInfo();
|
||||
|
||||
if (foundPlayer === undefined) {
|
||||
self.icon = icons.mpris.loop["none"];
|
||||
self.icon = icons.mpris.loop['none'];
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -184,9 +180,8 @@ const Controls = (getPlayerInfo: Function) => {
|
||||
|
||||
if (loopStatus && isValidLoopStatus(loopStatus)) {
|
||||
self.icon = icons.mpris.loop[loopStatus];
|
||||
}
|
||||
else {
|
||||
self.icon = icons.mpris.loop["none"];
|
||||
} else {
|
||||
self.icon = icons.mpris.loop['none'];
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@@ -1,78 +1,82 @@
|
||||
const media = await Service.import("mpris");
|
||||
import { BoxWidget } from 'lib/types/widget';
|
||||
import { MprisPlayer } from 'types/service/mpris';
|
||||
|
||||
const MediaInfo = (getPlayerInfo: Function) => {
|
||||
const media = await Service.import('mpris');
|
||||
|
||||
const MediaInfo = (getPlayerInfo: () => MprisPlayer): BoxWidget => {
|
||||
return Widget.Box({
|
||||
class_name: "media-indicator-current-media-info",
|
||||
hpack: "center",
|
||||
class_name: 'media-indicator-current-media-info',
|
||||
hpack: 'center',
|
||||
hexpand: true,
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "media-indicator-current-song-name",
|
||||
hpack: "center",
|
||||
class_name: 'media-indicator-current-song-name',
|
||||
hpack: 'center',
|
||||
children: [
|
||||
Widget.Label({
|
||||
truncate: "end",
|
||||
truncate: 'end',
|
||||
max_width_chars: 31,
|
||||
wrap: true,
|
||||
class_name: "media-indicator-current-song-name-label",
|
||||
class_name: 'media-indicator-current-song-name-label',
|
||||
setup: (self) => {
|
||||
self.hook(media, () => {
|
||||
const curPlayer = getPlayerInfo();
|
||||
return (self.label =
|
||||
curPlayer !== undefined && curPlayer["track-title"].length
|
||||
? curPlayer["track-title"]
|
||||
: "No Media Currently Playing");
|
||||
curPlayer !== undefined && curPlayer['track_title'].length
|
||||
? curPlayer['track_title']
|
||||
: 'No Media Currently Playing');
|
||||
});
|
||||
},
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "media-indicator-current-song-author",
|
||||
hpack: "center",
|
||||
class_name: 'media-indicator-current-song-author',
|
||||
hpack: 'center',
|
||||
children: [
|
||||
Widget.Label({
|
||||
truncate: "end",
|
||||
truncate: 'end',
|
||||
wrap: true,
|
||||
max_width_chars: 35,
|
||||
class_name: "media-indicator-current-song-author-label",
|
||||
class_name: 'media-indicator-current-song-author-label',
|
||||
setup: (self) => {
|
||||
self.hook(media, () => {
|
||||
const curPlayer = getPlayerInfo();
|
||||
|
||||
const makeArtistList = (trackArtists: string[]) => {
|
||||
const makeArtistList = (trackArtists: string[]): string => {
|
||||
if (trackArtists.length === 1 && !trackArtists[0].length) {
|
||||
return "-----";
|
||||
return '-----';
|
||||
}
|
||||
|
||||
return trackArtists.join(", ");
|
||||
return trackArtists.join(', ');
|
||||
};
|
||||
|
||||
return (self.label =
|
||||
curPlayer !== undefined && curPlayer["track-artists"].length
|
||||
? makeArtistList(curPlayer["track-artists"])
|
||||
: "-----");
|
||||
curPlayer !== undefined && curPlayer['track_artists'].length
|
||||
? makeArtistList(curPlayer['track_artists'])
|
||||
: '-----');
|
||||
});
|
||||
},
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "media-indicator-current-song-album",
|
||||
hpack: "center",
|
||||
class_name: 'media-indicator-current-song-album',
|
||||
hpack: 'center',
|
||||
children: [
|
||||
Widget.Label({
|
||||
truncate: "end",
|
||||
truncate: 'end',
|
||||
wrap: true,
|
||||
max_width_chars: 40,
|
||||
class_name: "media-indicator-current-song-album-label",
|
||||
class_name: 'media-indicator-current-song-album-label',
|
||||
setup: (self) => {
|
||||
self.hook(media, () => {
|
||||
const curPlayer = getPlayerInfo();
|
||||
return (self.label =
|
||||
curPlayer !== undefined && curPlayer["track-album"].length
|
||||
? curPlayer["track-album"]
|
||||
: "---");
|
||||
curPlayer !== undefined && curPlayer['track_album'].length
|
||||
? curPlayer['track_album']
|
||||
: '---');
|
||||
});
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
import DropdownMenu from "../DropdownMenu.js";
|
||||
import { Media } from "./media.js";
|
||||
import Window from 'types/widgets/window.js';
|
||||
import DropdownMenu from '../DropdownMenu.js';
|
||||
import { Media } from './media.js';
|
||||
import { Attribute, Child } from 'lib/types/widget.js';
|
||||
|
||||
export default () => {
|
||||
return DropdownMenu({
|
||||
name: "mediamenu",
|
||||
transition: "crossfade",
|
||||
child: Widget.Box({
|
||||
class_name: "menu-items media",
|
||||
hpack: "fill",
|
||||
hexpand: true,
|
||||
child: Widget.Box({
|
||||
class_name: "menu-items-container media",
|
||||
hpack: "fill",
|
||||
hexpand: true,
|
||||
child: Media(),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
export default (): Window<Child, Attribute> => {
|
||||
return DropdownMenu({
|
||||
name: 'mediamenu',
|
||||
transition: 'crossfade',
|
||||
child: Widget.Box({
|
||||
class_name: 'menu-items media',
|
||||
hpack: 'fill',
|
||||
hexpand: true,
|
||||
child: Widget.Box({
|
||||
class_name: 'menu-items-container media',
|
||||
hpack: 'fill',
|
||||
hexpand: true,
|
||||
child: Media(),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
const media = await Service.import("mpris");
|
||||
import { MediaInfo } from "./components/mediainfo.js";
|
||||
import { Controls } from "./components/controls.js";
|
||||
import { Bar } from "./components/bar.js";
|
||||
import { MprisPlayer } from "types/service/mpris.js";
|
||||
import options from "options.js";
|
||||
const media = await Service.import('mpris');
|
||||
import { MediaInfo } from './components/mediainfo.js';
|
||||
import { Controls } from './components/controls.js';
|
||||
import { Bar } from './components/bar.js';
|
||||
import { MprisPlayer } from 'types/service/mpris.js';
|
||||
import options from 'options.js';
|
||||
import { BoxWidget } from 'lib/types/widget.js';
|
||||
|
||||
const { tint, color } = options.theme.bar.menus.menu.media.card;
|
||||
|
||||
const generateAlbumArt = (imageUrl: string): string => {
|
||||
const userTint = tint.value;
|
||||
const userHexColor = color.value;
|
||||
let css: string;
|
||||
|
||||
const r = parseInt(userHexColor.slice(1, 3), 16);
|
||||
const g = parseInt(userHexColor.slice(3, 5), 16);
|
||||
@@ -18,36 +18,30 @@ const generateAlbumArt = (imageUrl: string): string => {
|
||||
|
||||
const alpha = userTint / 100;
|
||||
|
||||
css = `background-image: linear-gradient(
|
||||
const css = `background-image: linear-gradient(
|
||||
rgba(${r}, ${g}, ${b}, ${alpha}),
|
||||
rgba(${r}, ${g}, ${b}, ${alpha}),
|
||||
${userHexColor} 65em
|
||||
), url("${imageUrl}");`;
|
||||
|
||||
return css;
|
||||
}
|
||||
const Media = () => {
|
||||
const curPlayer = Variable("");
|
||||
};
|
||||
const Media = (): BoxWidget => {
|
||||
const curPlayer = Variable('');
|
||||
|
||||
media.connect("changed", () => {
|
||||
media.connect('changed', () => {
|
||||
const statusOrder = {
|
||||
Playing: 1,
|
||||
Paused: 2,
|
||||
Stopped: 3,
|
||||
};
|
||||
|
||||
const isPlaying = media.players.find(
|
||||
(p) => p["play_back_status"] === "Playing",
|
||||
);
|
||||
const isPlaying = media.players.find((p) => p['play_back_status'] === 'Playing');
|
||||
|
||||
const playerStillExists = media.players.some(
|
||||
(p) => curPlayer.value === p["bus_name"],
|
||||
);
|
||||
const playerStillExists = media.players.some((p) => curPlayer.value === p['bus_name']);
|
||||
|
||||
const nextPlayerUp = media.players.sort(
|
||||
(a, b) =>
|
||||
statusOrder[a["play_back_status"]] -
|
||||
statusOrder[b["play_back_status"]],
|
||||
(a, b) => statusOrder[a['play_back_status']] - statusOrder[b['play_back_status']],
|
||||
)[0].bus_name;
|
||||
|
||||
if (isPlaying || !playerStillExists) {
|
||||
@@ -60,26 +54,22 @@ const Media = () => {
|
||||
};
|
||||
|
||||
return Widget.Box({
|
||||
class_name: "menu-section-container",
|
||||
class_name: 'menu-section-container',
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "menu-items-section",
|
||||
class_name: 'menu-items-section',
|
||||
vertical: false,
|
||||
child: Widget.Box({
|
||||
class_name: "menu-content",
|
||||
class_name: 'menu-content',
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "media-content",
|
||||
class_name: 'media-content',
|
||||
child: Widget.Box({
|
||||
class_name: "media-indicator-right-section",
|
||||
hpack: "fill",
|
||||
class_name: 'media-indicator-right-section',
|
||||
hpack: 'fill',
|
||||
hexpand: true,
|
||||
vertical: true,
|
||||
children: [
|
||||
MediaInfo(getPlayerInfo),
|
||||
Controls(getPlayerInfo),
|
||||
Bar(getPlayerInfo),
|
||||
],
|
||||
children: [MediaInfo(getPlayerInfo), Controls(getPlayerInfo), Bar(getPlayerInfo)],
|
||||
}),
|
||||
}),
|
||||
],
|
||||
@@ -91,7 +81,7 @@ const Media = () => {
|
||||
}
|
||||
});
|
||||
|
||||
Utils.merge([color.bind("value"), tint.bind("value")], () => {
|
||||
Utils.merge([color.bind('value'), tint.bind('value')], () => {
|
||||
const curPlayer = getPlayerInfo();
|
||||
if (curPlayer !== undefined) {
|
||||
self.css = generateAlbumArt(curPlayer.track_cover_url);
|
||||
|
||||
@@ -1,52 +1,54 @@
|
||||
const network = await Service.import("network");
|
||||
import { BoxWidget } from 'lib/types/widget';
|
||||
|
||||
const Ethernet = () => {
|
||||
const network = await Service.import('network');
|
||||
|
||||
const Ethernet = (): BoxWidget => {
|
||||
return Widget.Box({
|
||||
class_name: "menu-section-container ethernet",
|
||||
class_name: 'menu-section-container ethernet',
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "menu-label-container",
|
||||
hpack: "fill",
|
||||
class_name: 'menu-label-container',
|
||||
hpack: 'fill',
|
||||
child: Widget.Label({
|
||||
class_name: "menu-label",
|
||||
class_name: 'menu-label',
|
||||
hexpand: true,
|
||||
hpack: "start",
|
||||
label: "Ethernet",
|
||||
hpack: 'start',
|
||||
label: 'Ethernet',
|
||||
}),
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "menu-items-section",
|
||||
class_name: 'menu-items-section',
|
||||
vertical: true,
|
||||
child: Widget.Box({
|
||||
class_name: "menu-content",
|
||||
class_name: 'menu-content',
|
||||
vertical: true,
|
||||
setup: (self) => {
|
||||
self.hook(network, () => {
|
||||
return (self.child = Widget.Box({
|
||||
class_name: "network-element-item",
|
||||
class_name: 'network-element-item',
|
||||
child: Widget.Box({
|
||||
hpack: "start",
|
||||
hpack: 'start',
|
||||
children: [
|
||||
Widget.Icon({
|
||||
class_name: `network-icon ethernet ${network.wired.state === "activated" ? "active" : ""}`,
|
||||
class_name: `network-icon ethernet ${network.wired.state === 'activated' ? 'active' : ''}`,
|
||||
tooltip_text: network.wired.internet,
|
||||
icon: `${network.wired["icon_name"]}`,
|
||||
icon: `${network.wired['icon_name']}`,
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "connection-container",
|
||||
class_name: 'connection-container',
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: "active-connection",
|
||||
hpack: "start",
|
||||
truncate: "end",
|
||||
class_name: 'active-connection',
|
||||
hpack: 'start',
|
||||
truncate: 'end',
|
||||
wrap: true,
|
||||
label: `Ethernet Connection ${network.wired.state !== "unknown" && typeof network.wired?.speed === "number" ? `(${network.wired?.speed / 1000} Gbps)` : ""}`,
|
||||
label: `Ethernet Connection ${network.wired.state !== 'unknown' && typeof network.wired?.speed === 'number' ? `(${network.wired?.speed / 1000} Gbps)` : ''}`,
|
||||
}),
|
||||
Widget.Label({
|
||||
hpack: "start",
|
||||
class_name: "connection-status dim",
|
||||
hpack: 'start',
|
||||
class_name: 'connection-status dim',
|
||||
label:
|
||||
network.wired.internet.charAt(0).toUpperCase() +
|
||||
network.wired.internet.slice(1),
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import DropdownMenu from "../DropdownMenu.js";
|
||||
import { Ethernet } from "./ethernet/index.js";
|
||||
import { Wifi } from "./wifi/index.js";
|
||||
import Window from 'types/widgets/window.js';
|
||||
import DropdownMenu from '../DropdownMenu.js';
|
||||
import { Ethernet } from './ethernet/index.js';
|
||||
import { Wifi } from './wifi/index.js';
|
||||
import { Attribute, Child } from 'lib/types/widget.js';
|
||||
|
||||
export default () => {
|
||||
return DropdownMenu({
|
||||
name: "networkmenu",
|
||||
transition: "crossfade",
|
||||
child: Widget.Box({
|
||||
class_name: "menu-items network",
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
hexpand: true,
|
||||
class_name: "menu-items-container network",
|
||||
children: [Ethernet(), Wifi()],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
export default (): Window<Child, Attribute> => {
|
||||
return DropdownMenu({
|
||||
name: 'networkmenu',
|
||||
transition: 'crossfade',
|
||||
child: Widget.Box({
|
||||
class_name: 'menu-items network',
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
hexpand: true,
|
||||
class_name: 'menu-items-container network',
|
||||
children: [Ethernet(), Wifi()],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
const getWifiIcon = (iconName: string) => {
|
||||
const deviceIconMap = [
|
||||
["network-wireless-acquiring", ""],
|
||||
["network-wireless-connected", ""],
|
||||
["network-wireless-encrypted", ""],
|
||||
["network-wireless-hotspot", ""],
|
||||
["network-wireless-no-route", ""],
|
||||
["network-wireless-offline", ""],
|
||||
["network-wireless-signal-excellent", ""],
|
||||
["network-wireless-signal-good", ""],
|
||||
["network-wireless-signal-ok", ""],
|
||||
["network-wireless-signal-weak", ""],
|
||||
["network-wireless-signal-none", ""],
|
||||
import { WifiIcon } from 'lib/types/network';
|
||||
|
||||
const getWifiIcon = (iconName: string): WifiIcon => {
|
||||
const deviceIconMap: [string, WifiIcon][] = [
|
||||
['network-wireless-acquiring', ''],
|
||||
['network-wireless-connected', ''],
|
||||
['network-wireless-encrypted', ''],
|
||||
['network-wireless-hotspot', ''],
|
||||
['network-wireless-no-route', ''],
|
||||
['network-wireless-offline', ''],
|
||||
['network-wireless-signal-excellent', ''],
|
||||
['network-wireless-signal-good', ''],
|
||||
['network-wireless-signal-ok', ''],
|
||||
['network-wireless-signal-weak', ''],
|
||||
['network-wireless-signal-none', ''],
|
||||
];
|
||||
|
||||
const foundMatch = deviceIconMap.find((icon) =>
|
||||
RegExp(icon[0]).test(iconName.toLowerCase()),
|
||||
);
|
||||
const foundMatch = deviceIconMap.find((icon) => RegExp(icon[0]).test(iconName.toLowerCase()));
|
||||
|
||||
return foundMatch ? foundMatch[1] : "";
|
||||
return foundMatch ? foundMatch[1] : '';
|
||||
};
|
||||
|
||||
export { getWifiIcon };
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
import { Network } from "types/service/network";
|
||||
import { Variable } from "types/variable";
|
||||
import { AccessPoint } from "lib/types/network";
|
||||
const renderWapStaging = (self: any, network: Network, staging: Variable<AccessPoint>, connecting: Variable<string>) => {
|
||||
Utils.merge([network.bind("wifi"), staging.bind("value")], () => {
|
||||
import { Network } from 'types/service/network';
|
||||
import { Variable } from 'types/variable';
|
||||
import { AccessPoint } from 'lib/types/network';
|
||||
import Box from 'types/widgets/box';
|
||||
import { Attribute, Child } from 'lib/types/widget';
|
||||
|
||||
const renderWapStaging = (
|
||||
self: Box<Child, Attribute>,
|
||||
network: Network,
|
||||
staging: Variable<AccessPoint>,
|
||||
connecting: Variable<string>,
|
||||
): void => {
|
||||
Utils.merge([network.bind('wifi'), staging.bind('value')], () => {
|
||||
if (!Object.keys(staging.value).length) {
|
||||
return (self.child = Widget.Box());
|
||||
}
|
||||
|
||||
return (self.child = Widget.Box({
|
||||
class_name: "network-element-item staging",
|
||||
class_name: 'network-element-item staging',
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
hpack: "fill",
|
||||
hpack: 'fill',
|
||||
hexpand: true,
|
||||
children: [
|
||||
Widget.Icon({
|
||||
@@ -20,74 +28,70 @@ const renderWapStaging = (self: any, network: Network, staging: Variable<AccessP
|
||||
icon: `${staging.value.iconName}`,
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "connection-container",
|
||||
class_name: 'connection-container',
|
||||
hexpand: true,
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: "active-connection",
|
||||
hpack: "start",
|
||||
truncate: "end",
|
||||
class_name: 'active-connection',
|
||||
hpack: 'start',
|
||||
truncate: 'end',
|
||||
wrap: true,
|
||||
label: staging.value.ssid,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Revealer({
|
||||
hpack: "end",
|
||||
reveal_child: connecting
|
||||
.bind("value")
|
||||
.as((c) => staging.value.bssid === c),
|
||||
hpack: 'end',
|
||||
reveal_child: connecting.bind('value').as((c) => staging.value.bssid === c),
|
||||
child: Widget.Spinner({
|
||||
class_name: "spinner wap",
|
||||
class_name: 'spinner wap',
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "network-password-input-container",
|
||||
hpack: "fill",
|
||||
class_name: 'network-password-input-container',
|
||||
hpack: 'fill',
|
||||
hexpand: true,
|
||||
children: [
|
||||
Widget.Entry({
|
||||
hpack: "start",
|
||||
hpack: 'start',
|
||||
hexpand: true,
|
||||
visibility: false,
|
||||
class_name: "network-password-input",
|
||||
placeholder_text: "enter password",
|
||||
class_name: 'network-password-input',
|
||||
placeholder_text: 'enter password',
|
||||
onAccept: (selfInp) => {
|
||||
connecting.value = staging.value.bssid || "";
|
||||
connecting.value = staging.value.bssid || '';
|
||||
Utils.execAsync(
|
||||
`nmcli dev wifi connect ${staging.value.bssid} password ${selfInp.text}`,
|
||||
)
|
||||
.catch((err) => {
|
||||
connecting.value = "";
|
||||
console.error(
|
||||
`Failed to connect to wifi: ${staging.value.ssid}... ${err}`,
|
||||
);
|
||||
connecting.value = '';
|
||||
console.error(`Failed to connect to wifi: ${staging.value.ssid}... ${err}`);
|
||||
Utils.notify({
|
||||
summary: "Network",
|
||||
summary: 'Network',
|
||||
body: err,
|
||||
timeout: 5000,
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
connecting.value = "";
|
||||
connecting.value = '';
|
||||
staging.value = {} as AccessPoint;
|
||||
});
|
||||
selfInp.text = "";
|
||||
selfInp.text = '';
|
||||
},
|
||||
}),
|
||||
Widget.Button({
|
||||
hpack: "end",
|
||||
class_name: "close-network-password-input-button",
|
||||
hpack: 'end',
|
||||
class_name: 'close-network-password-input-button',
|
||||
on_primary_click: () => {
|
||||
connecting.value = "";
|
||||
connecting.value = '';
|
||||
staging.value = {} as AccessPoint;
|
||||
},
|
||||
child: Widget.Icon({
|
||||
class_name: "close-network-password-input-icon",
|
||||
icon: "window-close-symbolic",
|
||||
class_name: 'close-network-password-input-icon',
|
||||
icon: 'window-close-symbolic',
|
||||
}),
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -1,36 +1,41 @@
|
||||
import { Network } from "types/service/network.js";
|
||||
import { AccessPoint, WifiStatus } from "lib/types/network.js";
|
||||
import { Variable } from "types/variable.js";
|
||||
import { getWifiIcon } from "../utils.js";
|
||||
import { WIFI_STATUS_MAP } from "globals/network.js";
|
||||
const renderWAPs = (self: any, network: Network, staging: Variable<AccessPoint>, connecting: Variable<string>) => {
|
||||
const getIdBySsid = (ssid: string, nmcliOutput: string) => {
|
||||
const lines = nmcliOutput.trim().split("\n");
|
||||
import { Network } from 'types/service/network.js';
|
||||
import { AccessPoint, WifiStatus } from 'lib/types/network.js';
|
||||
import { Variable } from 'types/variable.js';
|
||||
import { getWifiIcon } from '../utils.js';
|
||||
import { WIFI_STATUS_MAP } from 'globals/network.js';
|
||||
import { Attribute, Child } from 'lib/types/widget.js';
|
||||
import Box from 'types/widgets/box.js';
|
||||
const renderWAPs = (
|
||||
self: Box<Child, Attribute>,
|
||||
network: Network,
|
||||
staging: Variable<AccessPoint>,
|
||||
connecting: Variable<string>,
|
||||
): void => {
|
||||
const getIdBySsid = (ssid: string, nmcliOutput: string): string | undefined => {
|
||||
const lines = nmcliOutput.trim().split('\n');
|
||||
for (const line of lines) {
|
||||
const columns = line.trim().split(/\s{2,}/);
|
||||
if (columns[0].includes(ssid)) {
|
||||
return columns[1];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const isValidWifiStatus = (status: string): status is WifiStatus => {
|
||||
return status in WIFI_STATUS_MAP;
|
||||
};
|
||||
|
||||
|
||||
const getWifiStatus = () => {
|
||||
const getWifiStatus = (): string => {
|
||||
const wifiState = network.wifi.state?.toLowerCase();
|
||||
|
||||
if (wifiState && isValidWifiStatus(wifiState)) {
|
||||
return WIFI_STATUS_MAP[wifiState];
|
||||
}
|
||||
return WIFI_STATUS_MAP["unknown"];
|
||||
}
|
||||
return WIFI_STATUS_MAP['unknown'];
|
||||
};
|
||||
|
||||
self.hook(network, () => {
|
||||
Utils.merge([staging.bind("value"), connecting.bind("value")], () => {
|
||||
Utils.merge([staging.bind('value'), connecting.bind('value')], () => {
|
||||
// NOTE: Sometimes the network service will yield a "this._device is undefined" when
|
||||
// trying to access the "access_points" property. So we must validate that
|
||||
// it's not 'undefined'
|
||||
@@ -38,12 +43,10 @@ const renderWAPs = (self: any, network: Network, staging: Variable<AccessPoint>,
|
||||
// Also this is an AGS bug that needs to be fixed
|
||||
|
||||
// TODO: Remove @ts-ignore once AGS bug is fixed
|
||||
// @ts-ignore
|
||||
let WAPs = network.wifi._device !== undefined
|
||||
? network.wifi["access_points"]
|
||||
: [];
|
||||
// @ts-expect-error to fix AGS bug
|
||||
let WAPs = network.wifi._device !== undefined ? network.wifi['access_points'] : [];
|
||||
|
||||
const dedupeWAPs = () => {
|
||||
const dedupeWAPs = (): AccessPoint[] => {
|
||||
const dedupMap: Record<string, AccessPoint> = {};
|
||||
WAPs.forEach((item: AccessPoint) => {
|
||||
if (item.ssid !== null && !Object.hasOwnProperty.call(dedupMap, item.ssid)) {
|
||||
@@ -56,7 +59,7 @@ const renderWAPs = (self: any, network: Network, staging: Variable<AccessPoint>,
|
||||
|
||||
WAPs = dedupeWAPs();
|
||||
|
||||
const isInStaging = (wap: AccessPoint) => {
|
||||
const isInStaging = (wap: AccessPoint): boolean => {
|
||||
if (Object.keys(staging.value).length === 0) {
|
||||
return false;
|
||||
}
|
||||
@@ -64,15 +67,15 @@ const renderWAPs = (self: any, network: Network, staging: Variable<AccessPoint>,
|
||||
return wap.bssid === staging.value.bssid;
|
||||
};
|
||||
|
||||
const isDisconnecting = (wap: AccessPoint) => {
|
||||
const isDisconnecting = (wap: AccessPoint): boolean => {
|
||||
if (wap.ssid === network.wifi.ssid) {
|
||||
return network.wifi.state.toLowerCase() === "deactivating";
|
||||
return network.wifi.state.toLowerCase() === 'deactivating';
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const filteredWAPs = WAPs.filter((ap: AccessPoint) => {
|
||||
return ap.ssid !== "Unknown" && !isInStaging(ap);
|
||||
return ap.ssid !== 'Unknown' && !isInStaging(ap);
|
||||
}).sort((a: AccessPoint, b: AccessPoint) => {
|
||||
if (network.wifi.ssid === a.ssid) {
|
||||
return -1;
|
||||
@@ -87,11 +90,11 @@ const renderWAPs = (self: any, network: Network, staging: Variable<AccessPoint>,
|
||||
|
||||
if (filteredWAPs.length <= 0 && Object.keys(staging.value).length === 0) {
|
||||
return (self.child = Widget.Label({
|
||||
class_name: "waps-not-found dim",
|
||||
class_name: 'waps-not-found dim',
|
||||
expand: true,
|
||||
hpack: "center",
|
||||
vpack: "center",
|
||||
label: "No Wi-Fi Networks Found",
|
||||
hpack: 'center',
|
||||
vpack: 'center',
|
||||
label: 'No Wi-Fi Networks Found',
|
||||
}));
|
||||
}
|
||||
return (self.children = filteredWAPs.map((ap: AccessPoint) => {
|
||||
@@ -103,61 +106,57 @@ const renderWAPs = (self: any, network: Network, staging: Variable<AccessPoint>,
|
||||
return;
|
||||
}
|
||||
|
||||
connecting.value = ap.bssid || "";
|
||||
connecting.value = ap.bssid || '';
|
||||
Utils.execAsync(`nmcli device wifi connect ${ap.bssid}`)
|
||||
.then(() => {
|
||||
connecting.value = "";
|
||||
connecting.value = '';
|
||||
staging.value = {} as AccessPoint;
|
||||
})
|
||||
.catch((err) => {
|
||||
if (
|
||||
err
|
||||
.toLowerCase()
|
||||
.includes("secrets were required, but not provided")
|
||||
) {
|
||||
if (err.toLowerCase().includes('secrets were required, but not provided')) {
|
||||
staging.value = ap;
|
||||
} else {
|
||||
Utils.notify({
|
||||
summary: "Network",
|
||||
summary: 'Network',
|
||||
body: err,
|
||||
timeout: 5000,
|
||||
});
|
||||
}
|
||||
connecting.value = "";
|
||||
connecting.value = '';
|
||||
});
|
||||
},
|
||||
class_name: "network-element-item",
|
||||
class_name: 'network-element-item',
|
||||
child: Widget.Box({
|
||||
hexpand: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
hpack: "start",
|
||||
hpack: 'start',
|
||||
hexpand: true,
|
||||
children: [
|
||||
Widget.Label({
|
||||
vpack: "start",
|
||||
class_name: `network-icon wifi ${ap.ssid === network.wifi.ssid ? "active" : ""} txt-icon`,
|
||||
label: getWifiIcon(`${ap["iconName"]}`),
|
||||
vpack: 'start',
|
||||
class_name: `network-icon wifi ${ap.ssid === network.wifi.ssid ? 'active' : ''} txt-icon`,
|
||||
label: getWifiIcon(`${ap['iconName']}`),
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "connection-container",
|
||||
vpack: "center",
|
||||
class_name: 'connection-container',
|
||||
vpack: 'center',
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Label({
|
||||
vpack: "center",
|
||||
class_name: "active-connection",
|
||||
hpack: "start",
|
||||
truncate: "end",
|
||||
vpack: 'center',
|
||||
class_name: 'active-connection',
|
||||
hpack: 'start',
|
||||
truncate: 'end',
|
||||
wrap: true,
|
||||
label: ap.ssid,
|
||||
}),
|
||||
Widget.Revealer({
|
||||
revealChild: ap.ssid === network.wifi.ssid,
|
||||
child: Widget.Label({
|
||||
hpack: "start",
|
||||
class_name: "connection-status dim",
|
||||
label: getWifiStatus()
|
||||
hpack: 'start',
|
||||
class_name: 'connection-status dim',
|
||||
label: getWifiStatus(),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
@@ -165,48 +164,48 @@ const renderWAPs = (self: any, network: Network, staging: Variable<AccessPoint>,
|
||||
],
|
||||
}),
|
||||
Widget.Revealer({
|
||||
hpack: "end",
|
||||
vpack: "start",
|
||||
reveal_child:
|
||||
ap.bssid === connecting.value || isDisconnecting(ap),
|
||||
hpack: 'end',
|
||||
vpack: 'start',
|
||||
reveal_child: ap.bssid === connecting.value || isDisconnecting(ap),
|
||||
child: Widget.Spinner({
|
||||
vpack: "start",
|
||||
class_name: "spinner wap",
|
||||
vpack: 'start',
|
||||
class_name: 'spinner wap',
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
Widget.Revealer({
|
||||
vpack: "start",
|
||||
vpack: 'start',
|
||||
reveal_child: ap.bssid !== connecting.value && ap.active,
|
||||
child: Widget.Button({
|
||||
tooltip_text: "Delete/Forget Network",
|
||||
class_name: "menu-icon-button network disconnect",
|
||||
tooltip_text: 'Delete/Forget Network',
|
||||
class_name: 'menu-icon-button network disconnect',
|
||||
on_primary_click: () => {
|
||||
connecting.value = ap.bssid || "";
|
||||
Utils.execAsync("nmcli connection show --active").then(() => {
|
||||
Utils.execAsync("nmcli connection show --active").then(
|
||||
(res) => {
|
||||
const connectionId = getIdBySsid(ap.ssid || "", res);
|
||||
connecting.value = ap.bssid || '';
|
||||
Utils.execAsync('nmcli connection show --active').then(() => {
|
||||
Utils.execAsync('nmcli connection show --active').then((res) => {
|
||||
const connectionId = getIdBySsid(ap.ssid || '', res);
|
||||
|
||||
Utils.execAsync(
|
||||
`nmcli connection delete ${connectionId} "${ap.ssid}"`,
|
||||
)
|
||||
.then(() => (connecting.value = ""))
|
||||
.catch((err) => {
|
||||
connecting.value = "";
|
||||
console.error(
|
||||
`Error while forgetting "${ap.ssid}": ${err}`,
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
if (connectionId === undefined) {
|
||||
console.error(
|
||||
`Error while forgetting "${ap.ssid}": Connection ID not found`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Utils.execAsync(`nmcli connection delete ${connectionId} "${ap.ssid}"`)
|
||||
.then(() => (connecting.value = ''))
|
||||
.catch((err) => {
|
||||
connecting.value = '';
|
||||
console.error(`Error while forgetting "${ap.ssid}": ${err}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
child: Widget.Label({
|
||||
class_name: "txt-icon delete-network",
|
||||
label: "",
|
||||
class_name: 'txt-icon delete-network',
|
||||
label: '',
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
|
||||
@@ -1,64 +1,63 @@
|
||||
const network = await Service.import("network");
|
||||
import { renderWAPs } from "./WirelessAPs.js";
|
||||
import { renderWapStaging } from "./APStaging.js";
|
||||
import { AccessPoint } from "lib/types/network.js";
|
||||
const network = await Service.import('network');
|
||||
import { renderWAPs } from './WirelessAPs.js';
|
||||
import { renderWapStaging } from './APStaging.js';
|
||||
import { AccessPoint } from 'lib/types/network.js';
|
||||
import { BoxWidget } from 'lib/types/widget.js';
|
||||
|
||||
const Staging = Variable({} as AccessPoint);
|
||||
const Connecting = Variable("");
|
||||
const Connecting = Variable('');
|
||||
|
||||
const searchInProgress = Variable(false);
|
||||
|
||||
const startRotation = () => {
|
||||
const startRotation = (): void => {
|
||||
searchInProgress.value = true;
|
||||
setTimeout(() => {
|
||||
searchInProgress.value = false;
|
||||
}, 5 * 1000);
|
||||
};
|
||||
|
||||
const Wifi = () => {
|
||||
const Wifi = (): BoxWidget => {
|
||||
return Widget.Box({
|
||||
class_name: "menu-section-container wifi",
|
||||
class_name: 'menu-section-container wifi',
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "menu-label-container",
|
||||
hpack: "fill",
|
||||
class_name: 'menu-label-container',
|
||||
hpack: 'fill',
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: "menu-label",
|
||||
class_name: 'menu-label',
|
||||
hexpand: true,
|
||||
hpack: "start",
|
||||
label: "Wi-Fi",
|
||||
hpack: 'start',
|
||||
label: 'Wi-Fi',
|
||||
}),
|
||||
Widget.Button({
|
||||
vpack: "center",
|
||||
hpack: "end",
|
||||
class_name: "menu-icon-button search network",
|
||||
vpack: 'center',
|
||||
hpack: 'end',
|
||||
class_name: 'menu-icon-button search network',
|
||||
on_primary_click: () => {
|
||||
startRotation();
|
||||
network.wifi.scan();
|
||||
},
|
||||
child: Widget.Icon({
|
||||
class_name: searchInProgress
|
||||
.bind("value")
|
||||
.as((v) => (v ? "spinning" : "")),
|
||||
icon: "view-refresh-symbolic",
|
||||
class_name: searchInProgress.bind('value').as((v) => (v ? 'spinning' : '')),
|
||||
icon: 'view-refresh-symbolic',
|
||||
}),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "menu-items-section",
|
||||
class_name: 'menu-items-section',
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "wap-staging",
|
||||
class_name: 'wap-staging',
|
||||
setup: (self) => {
|
||||
renderWapStaging(self, network, Staging, Connecting);
|
||||
},
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "available-waps",
|
||||
class_name: 'available-waps',
|
||||
vertical: true,
|
||||
setup: (self) => {
|
||||
renderWAPs(self, network, Staging, Connecting);
|
||||
|
||||
@@ -1,33 +1,34 @@
|
||||
import { closeNotifications } from "globals/notification";
|
||||
import { Notifications } from "types/service/notifications";
|
||||
import { closeNotifications } from 'globals/notification';
|
||||
import { BoxWidget } from 'lib/types/widget';
|
||||
import { Notifications } from 'types/service/notifications';
|
||||
|
||||
const Controls = (notifs: Notifications) => {
|
||||
const Controls = (notifs: Notifications): BoxWidget => {
|
||||
return Widget.Box({
|
||||
class_name: "notification-menu-controls",
|
||||
class_name: 'notification-menu-controls',
|
||||
expand: false,
|
||||
vertical: false,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "menu-label-container notifications",
|
||||
hpack: "start",
|
||||
vpack: "center",
|
||||
class_name: 'menu-label-container notifications',
|
||||
hpack: 'start',
|
||||
vpack: 'center',
|
||||
expand: true,
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: "menu-label notifications",
|
||||
label: "Notifications",
|
||||
class_name: 'menu-label notifications',
|
||||
label: 'Notifications',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Box({
|
||||
hpack: "end",
|
||||
vpack: "center",
|
||||
hpack: 'end',
|
||||
vpack: 'center',
|
||||
expand: false,
|
||||
children: [
|
||||
Widget.Switch({
|
||||
class_name: "menu-switch notifications",
|
||||
vpack: "center",
|
||||
active: notifs.bind("dnd").as((dnd: boolean) => !dnd),
|
||||
class_name: 'menu-switch notifications',
|
||||
vpack: 'center',
|
||||
active: notifs.bind('dnd').as((dnd: boolean) => !dnd),
|
||||
on_activate: ({ active }) => {
|
||||
notifs.dnd = !active;
|
||||
},
|
||||
@@ -35,14 +36,14 @@ const Controls = (notifs: Notifications) => {
|
||||
Widget.Box({
|
||||
children: [
|
||||
Widget.Separator({
|
||||
hpack: "center",
|
||||
hpack: 'center',
|
||||
vexpand: true,
|
||||
vertical: true,
|
||||
class_name: "menu-separator notification-controls",
|
||||
class_name: 'menu-separator notification-controls',
|
||||
}),
|
||||
Widget.Button({
|
||||
className: "clear-notifications-button",
|
||||
tooltip_text: "Clear Notifications",
|
||||
className: 'clear-notifications-button',
|
||||
tooltip_text: 'Clear Notifications',
|
||||
on_primary_click: () => {
|
||||
if (removingNotifications.value) {
|
||||
return;
|
||||
@@ -51,12 +52,12 @@ const Controls = (notifs: Notifications) => {
|
||||
closeNotifications(notifs.notifications);
|
||||
},
|
||||
child: Widget.Label({
|
||||
class_name: removingNotifications.bind("value").as((removing: boolean) => {
|
||||
class_name: removingNotifications.bind('value').as((removing: boolean) => {
|
||||
return removing
|
||||
? "clear-notifications-label txt-icon removing"
|
||||
: "clear-notifications-label txt-icon";
|
||||
? 'clear-notifications-label txt-icon removing'
|
||||
: 'clear-notifications-label txt-icon';
|
||||
}),
|
||||
label: "",
|
||||
label: '',
|
||||
}),
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -1,50 +1,45 @@
|
||||
import { Notification } from "types/service/notifications.js";
|
||||
import DropdownMenu from "../DropdownMenu.js";
|
||||
const notifs = await Service.import("notifications");
|
||||
import { Controls } from "./controls/index.js";
|
||||
import { NotificationCard } from "./notification/index.js";
|
||||
import { NotificationPager } from "./pager/index.js";
|
||||
import { Notification } from 'types/service/notifications.js';
|
||||
import DropdownMenu from '../DropdownMenu.js';
|
||||
const notifs = await Service.import('notifications');
|
||||
import { Controls } from './controls/index.js';
|
||||
import { NotificationCard } from './notification/index.js';
|
||||
import { NotificationPager } from './pager/index.js';
|
||||
|
||||
import options from "options";
|
||||
import options from 'options';
|
||||
import Window from 'types/widgets/window.js';
|
||||
import { Attribute, Child } from 'lib/types/widget.js';
|
||||
|
||||
const { displayedTotal } = options.notifications;
|
||||
|
||||
export default () => {
|
||||
export default (): Window<Child, Attribute> => {
|
||||
const curPage = Variable(1);
|
||||
|
||||
Utils.merge(
|
||||
[
|
||||
curPage.bind("value"),
|
||||
displayedTotal.bind("value"),
|
||||
notifs.bind("notifications"),
|
||||
],
|
||||
(
|
||||
currentPage: number,
|
||||
dispTotal: number,
|
||||
notifications: Notification[],
|
||||
) => {
|
||||
[curPage.bind('value'), displayedTotal.bind('value'), notifs.bind('notifications')],
|
||||
(currentPage: number, dispTotal: number, notifications: Notification[]) => {
|
||||
// If the page doesn't have enough notifications to display, go back
|
||||
// to the previous page.
|
||||
if (notifications.length <= (currentPage - 1) * dispTotal) {
|
||||
curPage.value = currentPage <= 1 ? 1 : currentPage - 1;
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
return DropdownMenu({
|
||||
name: "notificationsmenu",
|
||||
transition: "crossfade",
|
||||
name: 'notificationsmenu',
|
||||
transition: 'crossfade',
|
||||
child: Widget.Box({
|
||||
class_name: "notification-menu-content",
|
||||
css: "padding: 1px; margin: -1px;",
|
||||
class_name: 'notification-menu-content',
|
||||
css: 'padding: 1px; margin: -1px;',
|
||||
hexpand: true,
|
||||
vexpand: false,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "notification-card-container menu",
|
||||
class_name: 'notification-card-container menu',
|
||||
vertical: true,
|
||||
hexpand: false,
|
||||
vexpand: false,
|
||||
children: [Controls(notifs), NotificationCard(notifs, curPage), NotificationPager(curPage)]
|
||||
children: [Controls(notifs), NotificationCard(notifs, curPage), NotificationPager(curPage)],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
|
||||
@@ -1,35 +1,36 @@
|
||||
import { Notification, Notifications } from "types/service/notifications";
|
||||
const Actions = (notif: Notification, notifs: Notifications) => {
|
||||
import { BoxWidget } from 'lib/types/widget';
|
||||
import { Notification, Notifications } from 'types/service/notifications';
|
||||
const Actions = (notif: Notification, notifs: Notifications): BoxWidget => {
|
||||
if (notif.actions !== undefined && notif.actions.length > 0) {
|
||||
return Widget.Box({
|
||||
class_name: "notification-card-actions menu",
|
||||
class_name: 'notification-card-actions menu',
|
||||
hexpand: true,
|
||||
vpack: "end",
|
||||
vpack: 'end',
|
||||
children: notif.actions.map((action) => {
|
||||
return Widget.Button({
|
||||
hexpand: true,
|
||||
class_name: "notification-action-buttons menu",
|
||||
class_name: 'notification-action-buttons menu',
|
||||
on_primary_click: () => {
|
||||
if (action.id.includes("scriptAction:-")) {
|
||||
App.closeWindow("notificationsmenu");
|
||||
Utils.execAsync(
|
||||
`${action.id.replace("scriptAction:-", "")}`,
|
||||
).catch((err) => console.error(err));
|
||||
if (action.id.includes('scriptAction:-')) {
|
||||
App.closeWindow('notificationsmenu');
|
||||
Utils.execAsync(`${action.id.replace('scriptAction:-', '')}`).catch((err) =>
|
||||
console.error(err),
|
||||
);
|
||||
notifs.CloseNotification(notif.id);
|
||||
} else {
|
||||
App.closeWindow("notificationsmenu");
|
||||
App.closeWindow('notificationsmenu');
|
||||
notif.invoke(action.id);
|
||||
}
|
||||
},
|
||||
child: Widget.Box({
|
||||
hpack: "center",
|
||||
hpack: 'center',
|
||||
hexpand: true,
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: "notification-action-buttons-label menu",
|
||||
class_name: 'notification-action-buttons-label menu',
|
||||
hexpand: true,
|
||||
max_width_chars: 15,
|
||||
truncate: "end",
|
||||
truncate: 'end',
|
||||
wrap: true,
|
||||
label: action.label,
|
||||
}),
|
||||
@@ -41,7 +42,7 @@ const Actions = (notif: Notification, notifs: Notifications) => {
|
||||
}
|
||||
|
||||
return Widget.Box({
|
||||
class_name: "spacer",
|
||||
class_name: 'spacer',
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
import { notifHasImg } from "../../utils.js";
|
||||
import { Notification } from "types/service/notifications";
|
||||
import { BoxWidget } from 'lib/types/widget.js';
|
||||
import { notifHasImg } from '../../utils.js';
|
||||
import { Notification } from 'types/service/notifications';
|
||||
|
||||
export const Body = (notif: Notification) => {
|
||||
export const Body = (notif: Notification): BoxWidget => {
|
||||
return Widget.Box({
|
||||
vpack: "start",
|
||||
vpack: 'start',
|
||||
hexpand: true,
|
||||
class_name: "notification-card-body menu",
|
||||
class_name: 'notification-card-body menu',
|
||||
children: [
|
||||
Widget.Label({
|
||||
hexpand: true,
|
||||
use_markup: true,
|
||||
xalign: 0,
|
||||
justification: "left",
|
||||
truncate: "end",
|
||||
justification: 'left',
|
||||
truncate: 'end',
|
||||
lines: 2,
|
||||
max_width_chars: !notifHasImg(notif) ? 35 : 28,
|
||||
wrap: true,
|
||||
class_name: "notification-card-body-label menu",
|
||||
label: notif["body"],
|
||||
class_name: 'notification-card-body-label menu',
|
||||
label: notif['body'],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import { Notification, Notifications } from "types/service/notifications";
|
||||
export const CloseButton = (notif: Notification, notifs: Notifications) => {
|
||||
import { Attribute } from 'lib/types/widget';
|
||||
import { Notification, Notifications } from 'types/service/notifications';
|
||||
import Button from 'types/widgets/button';
|
||||
import Label from 'types/widgets/label';
|
||||
export const CloseButton = (notif: Notification, notifs: Notifications): Button<Label<Attribute>, Attribute> => {
|
||||
return Widget.Button({
|
||||
class_name: "close-notification-button menu",
|
||||
class_name: 'close-notification-button menu',
|
||||
on_primary_click: () => {
|
||||
notifs.CloseNotification(notif.id);
|
||||
},
|
||||
child: Widget.Label({
|
||||
class_name: "txt-icon notif-close",
|
||||
label: "",
|
||||
hpack: "center",
|
||||
class_name: 'txt-icon notif-close',
|
||||
label: '',
|
||||
hpack: 'center',
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { Notification } from "types/service/notifications.js";
|
||||
import { NotificationIcon } from "lib/types/notification.js";
|
||||
import { getNotificationIcon } from "globals/notification";
|
||||
import { Notification } from 'types/service/notifications.js';
|
||||
import { NotificationIcon } from 'lib/types/notification.js';
|
||||
import { getNotificationIcon } from 'globals/notification';
|
||||
import { BoxWidget } from 'lib/types/widget';
|
||||
|
||||
const NotificationIcon = ({ app_entry = "", app_icon = "", app_name = "" }: Partial<Notification>) => {
|
||||
const NotificationIcon = ({ app_entry = '', app_icon = '', app_name = '' }: Partial<Notification>): BoxWidget => {
|
||||
return Widget.Box({
|
||||
css: `
|
||||
min-width: 2rem;
|
||||
min-height: 2rem;
|
||||
`,
|
||||
child: Widget.Icon({
|
||||
class_name: "notification-icon menu",
|
||||
class_name: 'notification-icon menu',
|
||||
icon: getNotificationIcon(app_name, app_icon, app_entry),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -1,50 +1,51 @@
|
||||
import GLib from "gi://GLib";
|
||||
import { Notification } from "types/service/notifications";
|
||||
import { NotificationIcon } from "./icon.js";
|
||||
import { notifHasImg } from "../../utils.js";
|
||||
import options from "options.js";
|
||||
import GLib from 'gi://GLib';
|
||||
import { Notification } from 'types/service/notifications';
|
||||
import { NotificationIcon } from './icon.js';
|
||||
import { notifHasImg } from '../../utils.js';
|
||||
import options from 'options.js';
|
||||
import { BoxWidget } from 'lib/types/widget.js';
|
||||
|
||||
const { military } = options.menus.clock.time;
|
||||
|
||||
export const Header = (notif: Notification) => {
|
||||
const time = (time: number, format = "%I:%M %p") => {
|
||||
return GLib.DateTime.new_from_unix_local(time).format(military.value ? "%H:%M" : format);
|
||||
}
|
||||
export const Header = (notif: Notification): BoxWidget => {
|
||||
const time = (time: number, format = '%I:%M %p'): string => {
|
||||
return GLib.DateTime.new_from_unix_local(time).format(military.value ? '%H:%M' : format) || '--:--';
|
||||
};
|
||||
|
||||
return Widget.Box({
|
||||
vertical: false,
|
||||
hexpand: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "notification-card-header menu",
|
||||
hpack: "start",
|
||||
class_name: 'notification-card-header menu',
|
||||
hpack: 'start',
|
||||
children: [NotificationIcon(notif)],
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "notification-card-header menu",
|
||||
class_name: 'notification-card-header menu',
|
||||
hexpand: true,
|
||||
vpack: "start",
|
||||
vpack: 'start',
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: "notification-card-header-label menu",
|
||||
hpack: "start",
|
||||
class_name: 'notification-card-header-label menu',
|
||||
hpack: 'start',
|
||||
hexpand: true,
|
||||
vexpand: true,
|
||||
max_width_chars: !notifHasImg(notif) ? 34 : 22,
|
||||
truncate: "end",
|
||||
truncate: 'end',
|
||||
wrap: true,
|
||||
label: notif["summary"],
|
||||
label: notif['summary'],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "notification-card-header menu",
|
||||
hpack: "end",
|
||||
vpack: "start",
|
||||
class_name: 'notification-card-header menu',
|
||||
hpack: 'end',
|
||||
vpack: 'start',
|
||||
hexpand: true,
|
||||
child: Widget.Label({
|
||||
vexpand: true,
|
||||
class_name: "notification-time",
|
||||
class_name: 'notification-time',
|
||||
label: time(notif.time),
|
||||
}),
|
||||
}),
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
import { Notification } from "types/service/notifications";
|
||||
import { notifHasImg } from "../../utils.js";
|
||||
import { Notification } from 'types/service/notifications';
|
||||
import { notifHasImg } from '../../utils.js';
|
||||
import { BoxWidget } from 'lib/types/widget.js';
|
||||
|
||||
const Image = (notif: Notification) => {
|
||||
const Image = (notif: Notification): BoxWidget => {
|
||||
if (notifHasImg(notif)) {
|
||||
return Widget.Box({
|
||||
class_name: "notification-card-image-container menu",
|
||||
hpack: "center",
|
||||
vpack: "center",
|
||||
class_name: 'notification-card-image-container menu',
|
||||
hpack: 'center',
|
||||
vpack: 'center',
|
||||
vexpand: false,
|
||||
child: Widget.Box({
|
||||
hpack: "center",
|
||||
hpack: 'center',
|
||||
vexpand: false,
|
||||
class_name: "notification-card-image menu",
|
||||
class_name: 'notification-card-image menu',
|
||||
css: `background-image: url("${notif.image}")`,
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -1,45 +1,40 @@
|
||||
import { Notifications, Notification } from "types/service/notifications";
|
||||
import { notifHasImg } from "../utils.js";
|
||||
import { Header } from "./header/index.js";
|
||||
import { Actions } from "./actions/index.js";
|
||||
import { Image } from "./image/index.js";
|
||||
import { Placeholder } from "./placeholder/index.js";
|
||||
import { Body } from "./body/index.js";
|
||||
import { CloseButton } from "./close/index.js";
|
||||
import options from "options.js";
|
||||
import { Variable } from "types/variable.js";
|
||||
import { filterNotifications } from "lib/shared/notifications.js";
|
||||
import { Notifications, Notification } from 'types/service/notifications';
|
||||
import { notifHasImg } from '../utils.js';
|
||||
import { Header } from './header/index.js';
|
||||
import { Actions } from './actions/index.js';
|
||||
import { Image } from './image/index.js';
|
||||
import { Placeholder } from './placeholder/index.js';
|
||||
import { Body } from './body/index.js';
|
||||
import { CloseButton } from './close/index.js';
|
||||
import options from 'options.js';
|
||||
import { Variable } from 'types/variable.js';
|
||||
import { filterNotifications } from 'lib/shared/notifications.js';
|
||||
import Scrollable from 'types/widgets/scrollable.js';
|
||||
import { Attribute, Child } from 'lib/types/widget.js';
|
||||
|
||||
const { displayedTotal, ignore } = options.notifications;
|
||||
|
||||
const NotificationCard = (notifs: Notifications, curPage: Variable<number>) => {
|
||||
const NotificationCard = (notifs: Notifications, curPage: Variable<number>): Scrollable<Child, Attribute> => {
|
||||
return Widget.Scrollable({
|
||||
vscroll: "automatic",
|
||||
vscroll: 'automatic',
|
||||
child: Widget.Box({
|
||||
class_name: "menu-content-container notifications",
|
||||
hpack: "center",
|
||||
class_name: 'menu-content-container notifications',
|
||||
hpack: 'center',
|
||||
vexpand: true,
|
||||
spacing: 0,
|
||||
vertical: true,
|
||||
setup: (self) => {
|
||||
Utils.merge(
|
||||
[
|
||||
notifs.bind("notifications"),
|
||||
curPage.bind("value"),
|
||||
displayedTotal.bind("value"),
|
||||
ignore.bind("value")
|
||||
notifs.bind('notifications'),
|
||||
curPage.bind('value'),
|
||||
displayedTotal.bind('value'),
|
||||
ignore.bind('value'),
|
||||
],
|
||||
(
|
||||
notifications,
|
||||
currentPage,
|
||||
dispTotal,
|
||||
ignoredNotifs
|
||||
) => {
|
||||
(notifications, currentPage, dispTotal, ignoredNotifs) => {
|
||||
const filteredNotifications = filterNotifications(notifications, ignoredNotifs);
|
||||
|
||||
const sortedNotifications = filteredNotifications.sort(
|
||||
(a, b) => b.time - a.time,
|
||||
);
|
||||
const sortedNotifications = filteredNotifications.sort((a, b) => b.time - a.time);
|
||||
|
||||
if (filteredNotifications.length <= 0) {
|
||||
return (self.children = [Placeholder(notifs)]);
|
||||
@@ -47,37 +42,36 @@ const NotificationCard = (notifs: Notifications, curPage: Variable<number>) => {
|
||||
|
||||
const pageStart = (currentPage - 1) * dispTotal;
|
||||
const pageEnd = currentPage * dispTotal;
|
||||
return (self.children = sortedNotifications.slice(pageStart, pageEnd).map((notif: Notification) => {
|
||||
return Widget.Box({
|
||||
class_name: "notification-card-content-container",
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "notification-card menu",
|
||||
vpack: "start",
|
||||
hexpand: true,
|
||||
vexpand: false,
|
||||
children: [
|
||||
Image(notif),
|
||||
Widget.Box({
|
||||
vpack: "center",
|
||||
vertical: true,
|
||||
hexpand: true,
|
||||
class_name: `notification-card-content ${!notifHasImg(notif) ? "noimg" : " menu"}`,
|
||||
children: [
|
||||
Header(notif),
|
||||
Body(notif),
|
||||
Actions(notif, notifs),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
CloseButton(notif, notifs),
|
||||
],
|
||||
});
|
||||
}));
|
||||
});
|
||||
return (self.children = sortedNotifications
|
||||
.slice(pageStart, pageEnd)
|
||||
.map((notif: Notification) => {
|
||||
return Widget.Box({
|
||||
class_name: 'notification-card-content-container',
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: 'notification-card menu',
|
||||
vpack: 'start',
|
||||
hexpand: true,
|
||||
vexpand: false,
|
||||
children: [
|
||||
Image(notif),
|
||||
Widget.Box({
|
||||
vpack: 'center',
|
||||
vertical: true,
|
||||
hexpand: true,
|
||||
class_name: `notification-card-content ${!notifHasImg(notif) ? 'noimg' : ' menu'}`,
|
||||
children: [Header(notif), Body(notif), Actions(notif, notifs)],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
CloseButton(notif, notifs),
|
||||
],
|
||||
});
|
||||
}));
|
||||
},
|
||||
);
|
||||
},
|
||||
})
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
import { Notifications } from "types/service/notifications";
|
||||
import { BoxWidget } from 'lib/types/widget';
|
||||
import { Notifications } from 'types/service/notifications';
|
||||
|
||||
const Placeholder = (notifs: Notifications) => {
|
||||
const Placeholder = (notifs: Notifications): BoxWidget => {
|
||||
return Widget.Box({
|
||||
class_name: "notification-label-container",
|
||||
vpack: "fill",
|
||||
hpack: "center",
|
||||
class_name: 'notification-label-container',
|
||||
vpack: 'fill',
|
||||
hpack: 'center',
|
||||
expand: true,
|
||||
child: Widget.Box({
|
||||
vpack: "center",
|
||||
vpack: 'center',
|
||||
vertical: true,
|
||||
expand: true,
|
||||
children: [
|
||||
Widget.Label({
|
||||
vpack: "center",
|
||||
class_name: "placeholder-label dim bell",
|
||||
label: notifs.bind("dnd").as((dnd) => (dnd ? "" : "")),
|
||||
vpack: 'center',
|
||||
class_name: 'placeholder-label dim bell',
|
||||
label: notifs.bind('dnd').as((dnd) => (dnd ? '' : '')),
|
||||
}),
|
||||
Widget.Label({
|
||||
vpack: "start",
|
||||
class_name: "placehold-label dim message",
|
||||
vpack: 'start',
|
||||
class_name: 'placehold-label dim message',
|
||||
label: "You're all caught up :)",
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -1,91 +1,88 @@
|
||||
const notifs = await Service.import("notifications");
|
||||
const notifs = await Service.import('notifications');
|
||||
|
||||
import options from "options";
|
||||
import { Notification } from "types/service/notifications";
|
||||
import { Variable } from "types/variable";
|
||||
import { BoxWidget } from 'lib/types/widget';
|
||||
import options from 'options';
|
||||
import { Notification } from 'types/service/notifications';
|
||||
import { Variable } from 'types/variable';
|
||||
|
||||
const { displayedTotal } = options.notifications;
|
||||
const { show: showPager } = options.theme.bar.menus.menu.notifications.pager;
|
||||
|
||||
export const NotificationPager = (curPage: Variable<number>) => {
|
||||
export const NotificationPager = (curPage: Variable<number>): BoxWidget => {
|
||||
return Widget.Box({
|
||||
class_name: "notification-menu-pager",
|
||||
class_name: 'notification-menu-pager',
|
||||
hexpand: true,
|
||||
vexpand: false,
|
||||
children: Utils.merge(
|
||||
[
|
||||
curPage.bind("value"),
|
||||
displayedTotal.bind("value"),
|
||||
notifs.bind("notifications"),
|
||||
showPager.bind("value")
|
||||
curPage.bind('value'),
|
||||
displayedTotal.bind('value'),
|
||||
notifs.bind('notifications'),
|
||||
showPager.bind('value'),
|
||||
],
|
||||
(
|
||||
currentPage: number,
|
||||
dispTotal: number,
|
||||
_: Notification[],
|
||||
showPgr: boolean
|
||||
) => {
|
||||
(currentPage: number, dispTotal: number, _: Notification[], showPgr: boolean) => {
|
||||
if (showPgr === false) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
Widget.Button({
|
||||
hexpand: true,
|
||||
hpack: "start",
|
||||
class_name: `pager-button left ${currentPage <= 1 ? "disabled" : ""}`,
|
||||
hpack: 'start',
|
||||
class_name: `pager-button left ${currentPage <= 1 ? 'disabled' : ''}`,
|
||||
onPrimaryClick: () => {
|
||||
curPage.value = 1;
|
||||
},
|
||||
child: Widget.Label({
|
||||
className: "pager-button-label",
|
||||
label: ""
|
||||
className: 'pager-button-label',
|
||||
label: '',
|
||||
}),
|
||||
}),
|
||||
Widget.Button({
|
||||
hexpand: true,
|
||||
hpack: "start",
|
||||
class_name: `pager-button left ${currentPage <= 1 ? "disabled" : ""}`,
|
||||
hpack: 'start',
|
||||
class_name: `pager-button left ${currentPage <= 1 ? 'disabled' : ''}`,
|
||||
onPrimaryClick: () => {
|
||||
curPage.value = currentPage <= 1 ? 1 : currentPage - 1;
|
||||
},
|
||||
child: Widget.Label({
|
||||
className: "pager-button-label",
|
||||
label: ""
|
||||
className: 'pager-button-label',
|
||||
label: '',
|
||||
}),
|
||||
}),
|
||||
Widget.Label({
|
||||
hexpand: true,
|
||||
hpack: "center",
|
||||
class_name: "pager-label",
|
||||
label: `${currentPage} / ${Math.ceil(notifs.notifications.length / dispTotal) || 1}`
|
||||
hpack: 'center',
|
||||
class_name: 'pager-label',
|
||||
label: `${currentPage} / ${Math.ceil(notifs.notifications.length / dispTotal) || 1}`,
|
||||
}),
|
||||
Widget.Button({
|
||||
hexpand: true,
|
||||
hpack: "end",
|
||||
class_name: `pager-button right ${currentPage >= Math.ceil(notifs.notifications.length / dispTotal) ? "disabled" : ""}`,
|
||||
hpack: 'end',
|
||||
class_name: `pager-button right ${currentPage >= Math.ceil(notifs.notifications.length / dispTotal) ? 'disabled' : ''}`,
|
||||
onPrimaryClick: () => {
|
||||
const maxPage = Math.ceil(notifs.notifications.length / displayedTotal.value);
|
||||
curPage.value = currentPage >= maxPage ? currentPage : currentPage + 1;
|
||||
},
|
||||
child: Widget.Label({
|
||||
className: "pager-button-label",
|
||||
label: ""
|
||||
className: 'pager-button-label',
|
||||
label: '',
|
||||
}),
|
||||
}),
|
||||
Widget.Button({
|
||||
hexpand: true,
|
||||
hpack: "end",
|
||||
class_name: `pager-button right ${currentPage >= Math.ceil(notifs.notifications.length / dispTotal) ? "disabled" : ""}`,
|
||||
hpack: 'end',
|
||||
class_name: `pager-button right ${currentPage >= Math.ceil(notifs.notifications.length / dispTotal) ? 'disabled' : ''}`,
|
||||
onPrimaryClick: () => {
|
||||
const maxPage = Math.ceil(notifs.notifications.length / displayedTotal.value);
|
||||
curPage.value = maxPage;
|
||||
},
|
||||
child: Widget.Label({
|
||||
className: "pager-button-label",
|
||||
label: ""
|
||||
className: 'pager-button-label',
|
||||
label: '',
|
||||
}),
|
||||
}),
|
||||
]
|
||||
})
|
||||
})
|
||||
}
|
||||
];
|
||||
},
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Notification } from "types/service/notifications";
|
||||
import { Notification } from 'types/service/notifications';
|
||||
|
||||
const notifHasImg = (notif: Notification) => {
|
||||
return notif.image !== undefined && notif.image.length;
|
||||
const notifHasImg = (notif: Notification): boolean => {
|
||||
return notif.image !== undefined && notif.image.length ? true : false;
|
||||
};
|
||||
|
||||
export { notifHasImg };
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Action } from "lib/types/power";
|
||||
import options from "options";
|
||||
import { Action } from 'lib/types/power';
|
||||
import options from 'options';
|
||||
const { sleep, reboot, logout, shutdown } = options.menus.dashboard.powermenu;
|
||||
|
||||
class PowerMenu extends Service {
|
||||
@@ -8,50 +8,50 @@ class PowerMenu extends Service {
|
||||
this,
|
||||
{},
|
||||
{
|
||||
title: ["string"],
|
||||
cmd: ["string"],
|
||||
title: ['string'],
|
||||
cmd: ['string'],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#title = "";
|
||||
#cmd = "";
|
||||
#title = '';
|
||||
#cmd = '';
|
||||
|
||||
get title() {
|
||||
get title(): string {
|
||||
return this.#title;
|
||||
}
|
||||
|
||||
action(action: Action) {
|
||||
action(action: Action): void {
|
||||
[this.#cmd, this.#title] = {
|
||||
sleep: [sleep.value, "Sleep"],
|
||||
reboot: [reboot.value, "Reboot"],
|
||||
logout: [logout.value, "Log Out"],
|
||||
shutdown: [shutdown.value, "Shutdown"],
|
||||
sleep: [sleep.value, 'Sleep'],
|
||||
reboot: [reboot.value, 'Reboot'],
|
||||
logout: [logout.value, 'Log Out'],
|
||||
shutdown: [shutdown.value, 'Shutdown'],
|
||||
}[action];
|
||||
|
||||
this.notify("cmd");
|
||||
this.notify("title");
|
||||
this.emit("changed");
|
||||
App.closeWindow("powermenu");
|
||||
App.openWindow("verification");
|
||||
this.notify('cmd');
|
||||
this.notify('title');
|
||||
this.emit('changed');
|
||||
App.closeWindow('powermenu');
|
||||
App.openWindow('verification');
|
||||
}
|
||||
|
||||
customAction(action: Action, cmnd: string) {
|
||||
customAction(action: Action, cmnd: string): void {
|
||||
[this.#cmd, this.#title] = [cmnd, action];
|
||||
|
||||
this.notify("cmd");
|
||||
this.notify("title");
|
||||
this.emit("changed");
|
||||
App.closeWindow("powermenu");
|
||||
App.openWindow("verification");
|
||||
this.notify('cmd');
|
||||
this.notify('title');
|
||||
this.emit('changed');
|
||||
App.closeWindow('powermenu');
|
||||
App.openWindow('verification');
|
||||
}
|
||||
|
||||
shutdown = () => {
|
||||
this.action("shutdown");
|
||||
shutdown = (): void => {
|
||||
this.action('shutdown');
|
||||
};
|
||||
|
||||
exec = () => {
|
||||
App.closeWindow("verification");
|
||||
exec = (): void => {
|
||||
App.closeWindow('verification');
|
||||
Utils.execAsync(this.#cmd);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { Action } from "lib/types/power.js";
|
||||
import PopupWindow from "../PopupWindow.js";
|
||||
import powermenu from "./helpers/actions.js";
|
||||
import icons from "../../icons/index.js";
|
||||
import { Action } from 'lib/types/power.js';
|
||||
import PopupWindow from '../PopupWindow.js';
|
||||
import powermenu from './helpers/actions.js';
|
||||
import icons from '../../icons/index.js';
|
||||
import Window from 'types/widgets/window.js';
|
||||
import { Attribute, Child, GButton } from 'lib/types/widget.js';
|
||||
|
||||
const SysButton = (action: Action, label: string) =>
|
||||
const SysButton = (action: Action, label: string): GButton =>
|
||||
Widget.Button({
|
||||
class_name: `widget-button powermenu-button-${action}`,
|
||||
on_clicked: () => powermenu.action(action),
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
class_name: "system-button widget-box",
|
||||
class_name: 'system-button widget-box',
|
||||
children: [
|
||||
Widget.Icon({
|
||||
class_name: `system-button_icon ${action}`,
|
||||
@@ -22,17 +24,17 @@ const SysButton = (action: Action, label: string) =>
|
||||
],
|
||||
}),
|
||||
});
|
||||
export default () =>
|
||||
export default (): Window<Child, Attribute> =>
|
||||
PopupWindow({
|
||||
name: "powermenu",
|
||||
transition: "crossfade",
|
||||
name: 'powermenu',
|
||||
transition: 'crossfade',
|
||||
child: Widget.Box({
|
||||
class_name: "powermenu horizontal",
|
||||
class_name: 'powermenu horizontal',
|
||||
children: [
|
||||
SysButton("shutdown", "SHUTDOWN"),
|
||||
SysButton("logout", "LOG OUT"),
|
||||
SysButton("reboot", "REBOOT"),
|
||||
SysButton("sleep", "SLEEP"),
|
||||
SysButton('shutdown', 'SHUTDOWN'),
|
||||
SysButton('logout', 'LOG OUT'),
|
||||
SysButton('reboot', 'REBOOT'),
|
||||
SysButton('sleep', 'SLEEP'),
|
||||
],
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -1,52 +1,54 @@
|
||||
import PopupWindow from "../PopupWindow.js";
|
||||
import powermenu from "./helpers/actions.js";
|
||||
import Window from 'types/widgets/window.js';
|
||||
import PopupWindow from '../PopupWindow.js';
|
||||
import powermenu from './helpers/actions.js';
|
||||
import { Attribute, Child } from 'lib/types/widget.js';
|
||||
|
||||
export default () =>
|
||||
PopupWindow({
|
||||
name: "verification",
|
||||
transition: "crossfade",
|
||||
child: Widget.Box({
|
||||
class_name: "verification",
|
||||
child: Widget.Box({
|
||||
class_name: "verification-content",
|
||||
expand: true,
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: "text-box",
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: "title",
|
||||
label: powermenu.bind("title").as((t) => t.toUpperCase()),
|
||||
}),
|
||||
Widget.Label({
|
||||
class_name: "desc",
|
||||
label: powermenu
|
||||
.bind("title")
|
||||
.as((p) => `Are you sure you want to ${p.toLowerCase()}?`),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: "buttons horizontal",
|
||||
vexpand: true,
|
||||
vpack: "end",
|
||||
homogeneous: true,
|
||||
children: [
|
||||
Widget.Button({
|
||||
class_name: "verification-button bar-verification_yes",
|
||||
child: Widget.Label("Yes"),
|
||||
on_clicked: powermenu.exec,
|
||||
}),
|
||||
Widget.Button({
|
||||
class_name: "verification-button bar-verification_no",
|
||||
child: Widget.Label("No"),
|
||||
on_clicked: () => App.toggleWindow("verification"),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
export default (): Window<Child, Attribute> =>
|
||||
PopupWindow({
|
||||
name: 'verification',
|
||||
transition: 'crossfade',
|
||||
child: Widget.Box({
|
||||
class_name: 'verification',
|
||||
child: Widget.Box({
|
||||
class_name: 'verification-content',
|
||||
expand: true,
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Box({
|
||||
class_name: 'text-box',
|
||||
vertical: true,
|
||||
children: [
|
||||
Widget.Label({
|
||||
class_name: 'title',
|
||||
label: powermenu.bind('title').as((t) => t.toUpperCase()),
|
||||
}),
|
||||
Widget.Label({
|
||||
class_name: 'desc',
|
||||
label: powermenu
|
||||
.bind('title')
|
||||
.as((p) => `Are you sure you want to ${p.toLowerCase()}?`),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
Widget.Box({
|
||||
class_name: 'buttons horizontal',
|
||||
vexpand: true,
|
||||
vpack: 'end',
|
||||
homogeneous: true,
|
||||
children: [
|
||||
Widget.Button({
|
||||
class_name: 'verification-button bar-verification_yes',
|
||||
child: Widget.Label('Yes'),
|
||||
on_clicked: powermenu.exec,
|
||||
}),
|
||||
Widget.Button({
|
||||
class_name: 'verification-button bar-verification_no',
|
||||
child: Widget.Label('No'),
|
||||
on_clicked: () => App.toggleWindow('verification'),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -1,42 +1,44 @@
|
||||
import { PowerOptions } from "lib/types/options";
|
||||
import options from "options";
|
||||
import powermenu from "../power/helpers/actions";
|
||||
import { PowerOptions } from 'lib/types/options';
|
||||
import options from 'options';
|
||||
import powermenu from '../power/helpers/actions';
|
||||
import { GButton } from 'lib/types/widget';
|
||||
|
||||
const { confirmation, shutdown, logout, sleep, reboot, showLabel } = options.menus.power;
|
||||
|
||||
export const PowerButton = (action: PowerOptions) => {
|
||||
const handleClick = (action: PowerOptions) => {
|
||||
export const PowerButton = (action: PowerOptions): GButton => {
|
||||
const handleClick = (action: PowerOptions): void => {
|
||||
const actions = {
|
||||
shutdown: shutdown.value,
|
||||
reboot: reboot.value,
|
||||
logout: logout.value,
|
||||
sleep: sleep.value,
|
||||
};
|
||||
App.closeWindow("powerdropdownmenu");
|
||||
App.closeWindow('powerdropdownmenu');
|
||||
|
||||
if (!confirmation.value) {
|
||||
Utils.execAsync(actions[action])
|
||||
.catch((err) => console.error(`Failed to execute ${action} command. Error: ${err}`));
|
||||
Utils.execAsync(actions[action]).catch((err) =>
|
||||
console.error(`Failed to execute ${action} command. Error: ${err}`),
|
||||
);
|
||||
} else {
|
||||
powermenu.customAction(action, actions[action]);
|
||||
}
|
||||
};
|
||||
|
||||
const powerIconMap = {
|
||||
shutdown: "",
|
||||
reboot: "",
|
||||
logout: "",
|
||||
sleep: "",
|
||||
shutdown: '',
|
||||
reboot: '',
|
||||
logout: '',
|
||||
sleep: '',
|
||||
};
|
||||
|
||||
return Widget.Button({
|
||||
className: showLabel.bind("value").as(shwLbl => {
|
||||
return `power-menu-button ${action} ${!shwLbl ? "no-label" : ""}`;
|
||||
className: showLabel.bind('value').as((shwLbl) => {
|
||||
return `power-menu-button ${action} ${!shwLbl ? 'no-label' : ''}`;
|
||||
}),
|
||||
on_clicked: () => handleClick(action),
|
||||
child: Widget.Box({
|
||||
vertical: false,
|
||||
children: showLabel.bind("value").as(shwLbl => {
|
||||
children: showLabel.bind('value').as((shwLbl) => {
|
||||
if (shwLbl) {
|
||||
return [
|
||||
Widget.Label({
|
||||
@@ -44,7 +46,7 @@ export const PowerButton = (action: PowerOptions) => {
|
||||
className: `power-button-icon ${action}-icon txt-icon`,
|
||||
}),
|
||||
Widget.Label({
|
||||
hpack: "center",
|
||||
hpack: 'center',
|
||||
hexpand: true,
|
||||
label: action.charAt(0).toUpperCase() + action.slice(1),
|
||||
className: `power-button-label ${action}-label show-label`,
|
||||
@@ -58,6 +60,6 @@ export const PowerButton = (action: PowerOptions) => {
|
||||
}),
|
||||
];
|
||||
}),
|
||||
})
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,24 +1,20 @@
|
||||
import DropdownMenu from "../DropdownMenu.js";
|
||||
import { PowerButton } from "./button.js";
|
||||
import Window from 'types/widgets/window.js';
|
||||
import DropdownMenu from '../DropdownMenu.js';
|
||||
import { PowerButton } from './button.js';
|
||||
import { Attribute, Child } from 'lib/types/widget.js';
|
||||
|
||||
export default () => {
|
||||
export default (): Window<Child, Attribute> => {
|
||||
return DropdownMenu({
|
||||
name: "powerdropdownmenu",
|
||||
transition: "crossfade",
|
||||
name: 'powerdropdownmenu',
|
||||
transition: 'crossfade',
|
||||
child: Widget.Box({
|
||||
class_name: "menu-items power-dropdown",
|
||||
class_name: 'menu-items power-dropdown',
|
||||
child: Widget.Box({
|
||||
vertical: true,
|
||||
hexpand: true,
|
||||
class_name: "menu-items-container power-dropdown",
|
||||
children: [
|
||||
PowerButton('shutdown'),
|
||||
PowerButton('reboot'),
|
||||
PowerButton('logout'),
|
||||
PowerButton('sleep'),
|
||||
],
|
||||
class_name: 'menu-items-container power-dropdown',
|
||||
children: [PowerButton('shutdown'), PowerButton('reboot'), PowerButton('logout'), PowerButton('sleep')],
|
||||
}),
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user