hyprpanel

This commit is contained in:
wt committed 2024-10-23 21:12:15 +07:00
1 parent 10080a83ee
commit 5151ac24da
322 files changed
+38276

No files matched your search

@@ -0,0 +1,75 @@
const audio = await Service.import('audio');
import { getIcon } from '../utils.js';
import Box from 'types/widgets/box.js';
import { Attribute, Child } from 'lib/types/widget.js';
const renderActiveInput = (): Box<Child, Attribute>[] => {
return [
Widget.Box({
class_name: 'menu-slider-container input',
children: [
Widget.Button({
vexpand: false,
vpack: 'end',
setup: (self) => {
const updateClass = (): void => {
const mic = audio.microphone;
const className = `menu-active-button input ${mic.is_muted ? 'muted' : ''}`;
self.class_name = className;
};
self.hook(audio.microphone, updateClass, 'notify::is-muted');
},
on_primary_click: () => (audio.microphone.is_muted = !audio.microphone.is_muted),
child: Widget.Icon({
class_name: 'menu-active-icon input',
setup: (self) => {
const updateIcon = (): void => {
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'];
} else {
self.icon = getIcon(100, true)['mic'];
}
};
self.hook(audio.microphone, updateIcon, 'notify::volume');
self.hook(audio.microphone, updateIcon, 'notify::is-muted');
},
}),
}),
Widget.Box({
vertical: true,
children: [
Widget.Label({
class_name: 'menu-active input',
hpack: 'start',
truncate: 'end',
wrap: true,
label: audio.bind('microphone').as((v) => {
return 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',
draw_value: false,
hexpand: true,
min: 0,
max: 1,
onChange: ({ value }) => (audio.microphone.volume = value),
}),
],
}),
Widget.Label({
class_name: 'menu-active-percentage input',
vpack: 'end',
label: audio.microphone.bind('volume').as((v) => `${Math.round(v * 100)}%`),
}),
],
}),
];
};
export { renderActiveInput };
@@ -0,0 +1,68 @@
const audio = await Service.import('audio');
import { getIcon } from '../utils.js';
import Box from 'types/widgets/box.js';
import { Attribute, Child } from 'lib/types/widget.js';
const renderActivePlayback = (): Box<Child, Attribute>[] => {
return [
Widget.Box({
class_name: 'menu-slider-container playback',
children: [
Widget.Button({
vexpand: false,
vpack: 'end',
setup: (self) => {
const updateClass = (): void => {
const spkr = audio.speaker;
const className = `menu-active-button playback ${spkr.is_muted ? 'muted' : ''}`;
self.class_name = className;
};
self.hook(audio.speaker, updateClass, 'notify::is-muted');
},
on_primary_click: () => (audio.speaker.is_muted = !audio.speaker.is_muted),
child: Widget.Icon({
class_name: 'menu-active-icon playback',
setup: (self) => {
const updateIcon = (): void => {
const isSpeakerMuted = audio.speaker.is_muted !== null ? audio.speaker.is_muted : true;
self.icon = getIcon(audio.speaker.volume, isSpeakerMuted)['spkr'];
};
self.hook(audio.speaker, updateIcon, 'notify::volume');
self.hook(audio.speaker, updateIcon, 'notify::is-muted');
},
}),
}),
Widget.Box({
vertical: true,
children: [
Widget.Label({
class_name: 'menu-active playback',
hpack: 'start',
truncate: 'end',
expand: true,
wrap: true,
label: audio.bind('speaker').as((v) => v.description || ''),
}),
Widget.Slider({
value: audio['speaker'].bind('volume'),
class_name: 'menu-active-slider menu-slider playback',
draw_value: false,
hexpand: true,
min: 0,
max: 1,
onChange: ({ value }) => (audio.speaker.volume = value),
}),
],
}),
Widget.Label({
vpack: 'end',
class_name: 'menu-active-percentage playback',
label: audio.speaker.bind('volume').as((v) => `${Math.round(v * 100)}%`),
}),
],
}),
];
};
export { renderActivePlayback };
@@ -0,0 +1,41 @@
import { renderActiveInput } from './SelectedInput.js';
import { renderActivePlayback } from './SelectedPlayback.js';
import Box from 'types/widgets/box.js';
import { Attribute, Child } from 'lib/types/widget.js';
const activeDevices = (): Box<Child, Attribute> => {
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',
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 };
@@ -0,0 +1,66 @@
const audio = await Service.import('audio');
import { InputDevices } from 'lib/types/audio';
import { Stream } from 'types/service/audio';
const renderInputDevices = (inputDevices: Stream[]): InputDevices => {
if (inputDevices.length === 0) {
return [
Widget.Button({
class_name: `menu-unfound-button input`,
child: Widget.Box({
children: [
Widget.Box({
hpack: 'start',
children: [
Widget.Label({
class_name: 'menu-button-name input',
label: 'No input devices found...',
}),
],
}),
],
}),
}),
];
}
return inputDevices.map((device) => {
return Widget.Button({
on_primary_click: () => (audio.microphone = device),
class_name: `menu-button audio input ${device}`,
child: Widget.Box({
children: [
Widget.Box({
hpack: 'start',
children: [
Widget.Label({
wrap: true,
class_name: audio.microphone
.bind('description')
.as((v) =>
device.description === v
? 'menu-button-icon active input txt-icon'
: 'menu-button-icon input txt-icon',
),
label: '',
}),
Widget.Label({
truncate: 'end',
wrap: true,
class_name: audio.microphone
.bind('description')
.as((v) =>
device.description === v
? 'menu-button-name active input'
: 'menu-button-name input',
),
label: device.description,
}),
],
}),
],
}),
});
});
};
export { renderInputDevices };
@@ -0,0 +1,60 @@
const audio = await Service.import('audio');
import { PlaybackDevices } from 'lib/types/audio';
import { Stream } from 'types/service/audio';
const renderPlaybacks = (playbackDevices: Stream[]): PlaybackDevices => {
return playbackDevices.map((device) => {
if (device.description === 'Dummy Output') {
return Widget.Box({
class_name: 'menu-unfound-button playback',
child: Widget.Box({
children: [
Widget.Label({
class_name: 'menu-button-name playback',
label: 'No playback devices found...',
}),
],
}),
});
}
return Widget.Button({
class_name: `menu-button audio playback ${device}`,
on_primary_click: () => (audio.speaker = device),
child: Widget.Box({
children: [
Widget.Box({
hpack: 'start',
children: [
Widget.Label({
truncate: 'end',
wrap: true,
class_name: audio.speaker
.bind('description')
.as((v) =>
device.description === v
? 'menu-button-icon active playback txt-icon'
: 'menu-button-icon playback txt-icon',
),
label: '',
}),
Widget.Label({
truncate: 'end',
wrap: true,
class_name: audio.speaker
.bind('description')
.as((v) =>
device.description === v
? 'menu-button-name active playback'
: 'menu-button-name playback',
),
label: device.description,
}),
],
}),
],
}),
});
});
};
export { renderPlaybacks };
@@ -0,0 +1,72 @@
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 = (): BoxWidget => {
return Widget.Box({
vertical: true,
children: [
Widget.Box({
class_name: 'menu-section-container playback',
vertical: true,
children: [
Widget.Box({
class_name: 'menu-label-container playback',
hpack: 'fill',
child: Widget.Label({
class_name: 'menu-label audio playback',
hexpand: true,
hpack: 'start',
label: 'Playback Devices',
}),
}),
Widget.Box({
class_name: 'menu-items-section playback',
vertical: true,
children: [
Widget.Box({
class_name: 'menu-container playback',
vertical: true,
children: [
Widget.Box({
vertical: true,
children: audio.bind('speakers').as((v) => renderPlaybacks(v)),
}),
],
}),
],
}),
Widget.Box({
class_name: 'menu-label-container input',
hpack: 'fill',
child: Widget.Label({
class_name: 'menu-label audio input',
hexpand: true,
hpack: 'start',
label: 'Input Devices',
}),
}),
Widget.Box({
class_name: 'menu-items-section input',
vertical: true,
children: [
Widget.Box({
class_name: 'menu-container input',
vertical: true,
children: [
Widget.Box({
vertical: true,
children: audio.bind('microphones').as((v) => renderInputDevices(v)),
}),
],
}),
],
}),
],
}),
],
});
};
export { availableDevices };
@@ -0,0 +1,25 @@
import Window from 'types/widgets/window.js';
import DropdownMenu from '../shared/dropdown/index.js';
import { activeDevices } from './active/index.js';
import { availableDevices } from './available/index.js';
import { Attribute, Child } from 'lib/types/widget.js';
import options from 'options.js';
export default (): Window<Child, Attribute> => {
return DropdownMenu({
name: 'audiomenu',
transition: options.menus.transition.bind('value'),
child: Widget.Box({
class_name: 'menu-items audio',
hpack: 'fill',
hexpand: true,
child: Widget.Box({
vertical: true,
hpack: 'fill',
hexpand: true,
class_name: 'menu-items-container audio',
children: [activeDevices(), availableDevices()],
}),
}),
});
};
@@ -0,0 +1,29 @@
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',
} 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',
};
type IconVolumes = keyof typeof speakerIcons;
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;
return {
spkr: speakerIcons[icon],
mic: inputIcons[icon],
};
};
export { getIcon };