better config + notifpopups max popups

Use a config file
This commit is contained in:
2 * r + 2 * t 2025-01-14 19:53:49 +11:00
parent ecf4916687
commit c2e03775db
4 changed files with 44 additions and 45 deletions

21
config.ts Normal file
View file

@ -0,0 +1,21 @@
export const bar = {
wsPerGroup: 10,
dateTimeFormat: "%d/%m/%y %R",
};
export const launcher = {
maxResults: 15,
fdOpts: ["-a", "-t", "f"],
pins: [
["firefox", "waterfox", "google-chrome", "chromium", "brave-browser", "vivaldi-stable", "vivaldi-snapshot"],
["foot", "alacritty", "kitty", "wezterm"],
["thunar", "nemo", "nautilus"],
["codium", "code", "clion", "intellij-idea-ultimate-edition"],
["spotify-adblock", "spotify", "audacious", "elisa"],
],
};
export const notifpopups = {
maxPopups: -1,
expire: false,
};

View file

@ -4,6 +4,7 @@ import { App, Astal, astalify, Gdk, Gtk, type ConstructProps } from "astal/gtk3"
import AstalHyprland from "gi://AstalHyprland";
import AstalNotifd from "gi://AstalNotifd";
import AstalTray from "gi://AstalTray";
import { bar as config } from "../config";
import Players from "../services/players";
import { getAppCategoryIcon } from "../utils/icons";
import { ellipsize } from "../utils/strings";
@ -12,8 +13,6 @@ import { setupCustomTooltip } from "../utils/widgets";
const hyprland = AstalHyprland.get_default();
const wsPerGroup = 10;
const hookFocusedClientProp = (
self: any, // Ugh why is there no base Widget type
prop: keyof AstalHyprland.Client,
@ -99,7 +98,7 @@ const MediaPlaying = () => {
};
const Workspace = ({ idx }: { idx: number }) => {
let wsId = Math.floor((hyprland.focusedWorkspace.id - 1) / wsPerGroup) * wsPerGroup + idx;
let wsId = Math.floor((hyprland.focusedWorkspace.id - 1) / config.wsPerGroup) * config.wsPerGroup + idx;
return (
<button
halign={Gtk.Align.CENTER}
@ -115,7 +114,7 @@ const Workspace = ({ idx }: { idx: number }) => {
};
self.hook(hyprland, "notify::focused-workspace", () => {
wsId = Math.floor((hyprland.focusedWorkspace.id - 1) / wsPerGroup) * wsPerGroup + idx;
wsId = Math.floor((hyprland.focusedWorkspace.id - 1) / config.wsPerGroup) * config.wsPerGroup + idx;
update();
});
self.hook(hyprland, "client-added", update);
@ -138,7 +137,7 @@ const Workspaces = () => (
}}
>
<box className="module workspaces">
{Array.from({ length: wsPerGroup }).map((_, idx) => (
{Array.from({ length: config.wsPerGroup }).map((_, idx) => (
<Workspace idx={idx + 1} /> // Start from 1
))}
</box>
@ -237,7 +236,8 @@ const DateTime = () => (
<label
setup={self => {
const pollVar = Variable(null).poll(5000, () => {
self.label = GLib.DateTime.new_now_local().format("%d/%m/%y %R") ?? new Date().toLocaleString();
self.label =
GLib.DateTime.new_now_local().format(config.dateTimeFormat) ?? new Date().toLocaleString();
return null;
});
self.connect("destroy", () => pollVar.drop());

View file

@ -3,6 +3,7 @@ import { Astal, Gtk, Widget } from "astal/gtk3";
import fuzzysort from "fuzzysort";
import type AstalApps from "gi://AstalApps";
import AstalHyprland from "gi://AstalHyprland";
import { launcher as config } from "../config";
import { Apps } from "../services/apps";
import Math, { type HistoryItem } from "../services/math";
import { HOME } from "../utils/constants";
@ -19,23 +20,6 @@ interface Subcommand {
command: (...args: string[]) => void;
}
const maxSearchResults = 15;
const fdOptions = ["-a", "-t", "f", "-E", ".git"];
const browser = [
"firefox",
"waterfox",
"google-chrome",
"chromium",
"brave-browser",
"vivaldi-stable",
"vivaldi-snapshot",
];
const terminal = ["foot", "alacritty", "kitty", "wezterm"];
const files = ["thunar", "nemo", "nautilus"];
const ide = ["codium", "code", "clion", "intellij-idea-ultimate-edition"];
const music = ["spotify-adblock", "spotify", "audacious", "elisa"];
const getIconFromMode = (mode: Mode) => {
switch (mode) {
case "apps":
@ -77,7 +61,7 @@ const openFileAndClose = (self: JSX.Element, path: string) => {
]).catch(console.error);
};
const PinnedApp = ({ names }: { names: string[] }) => {
const PinnedApp = (names: string[]) => {
let app: Gio.DesktopAppInfo | null = null;
let astalApp: AstalApps.Application | undefined;
for (const name of names) {
@ -103,15 +87,7 @@ const PinnedApp = ({ names }: { names: string[] }) => {
) : null;
};
const PinnedApps = () => (
<box homogeneous>
<PinnedApp names={browser} />
<PinnedApp names={terminal} />
<PinnedApp names={files} />
<PinnedApp names={ide} />
<PinnedApp names={music} />
</box>
);
const PinnedApps = () => <box homogeneous>{config.pins.map(PinnedApp)}</box>;
const SearchEntry = ({ entry }: { entry: Widget.Entry }) => (
<stack
@ -275,7 +251,7 @@ const Results = ({ entry, mode }: { entry: Widget.Entry; mode: Variable<Mode> })
const appSearch = () => {
const apps = Apps.fuzzy_query(entry.text);
if (apps.length > maxSearchResults) apps.length = maxSearchResults;
if (apps.length > config.maxResults) apps.length = config.maxResults;
for (const app of apps) self.add(<AppResult app={app} />);
};
@ -289,10 +265,10 @@ const Results = ({ entry, mode }: { entry: Widget.Entry; mode: Variable<Mode> })
};
const fileSearch = () =>
execAsync(["fd", ...fdOptions, entry.text, HOME])
execAsync(["fd", ...config.fdOpts, entry.text, HOME])
.then(out => {
const paths = out.split("\n").filter(path => path);
if (paths.length > maxSearchResults) paths.length = maxSearchResults;
if (paths.length > config.maxResults) paths.length = config.maxResults;
self.foreach(ch => ch.destroy());
for (const path of paths) self.add(<FileResult path={path} />);
})

View file

@ -1,6 +1,7 @@
import { GLib, register, timeout } from "astal";
import { Astal, Gtk, Widget } from "astal/gtk3";
import AstalNotifd from "gi://AstalNotifd";
import { notifpopups as config } from "../config";
import { desktopEntrySubs } from "../utils/icons";
import { setupChildClickthrough } from "../utils/widgets";
@ -104,8 +105,8 @@ class NotifPopup extends Widget.Box {
this.css = `transition: 300ms cubic-bezier(0.05, 0.9, 0.1, 1.1); margin-left: 0; margin-right: 0;`;
});
// Close popup after timeout if transient
if (notification.transient)
// Close popup after timeout if transient or expire enabled in config
if (config.expire || notification.transient)
timeout(
notification.expireTimeout > 0
? notification.expireTimeout
@ -145,8 +146,11 @@ export default () => (
const map = new Map<number, NotifPopup>();
self.hook(notifd, "notified", (self, id) => {
const notification = notifd.get_notification(id);
const popup = (<NotifPopup notification={notification} />) as NotifPopup;
popup.connect("destroy", () => map.delete(notification.id));
map.set(notification.id, popup);
self.add(
<eventbox
// Dismiss on middle click
@ -157,14 +161,12 @@ export default () => (
{popup}
</eventbox>
);
// Limit number of popups
if (config.maxPopups > 0 && self.children.length > config.maxPopups)
map.values().next().value?.destroyWithAnims();
});
self.hook(notifd, "resolved", (_, id) => {
const popup = map.get(id);
if (popup) {
popup.destroyWithAnims();
map.delete(id);
}
});
self.hook(notifd, "resolved", (_, id) => map.get(id)?.destroyWithAnims());
// Change input region to child region so can click through empty space
setupChildClickthrough(self);