import { bind, Gio, timeout, Variable } from "astal";
import { Astal, Gtk, Widget } from "astal/gtk3";
import type AstalApps from "gi://AstalApps";
import AstalHyprland from "gi://AstalHyprland";
import { Apps } from "../services/apps";
import { getAppCategoryIcon } from "../utils/icons";
import { launch } from "../utils/system";
import { PopupWindow, setupCustomTooltip, TransitionType } from "../utils/widgets";
const maxSearchResults = 15;
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 launchAndClose = (self: JSX.Element, astalApp: AstalApps.Application) => {
const toplevel = self.get_toplevel();
if (toplevel instanceof Widget.Window) toplevel.hide();
launch(astalApp);
};
const PinnedApp = ({ names }: { names: string[] }) => {
let app: Gio.DesktopAppInfo | null = null;
let astalApp: AstalApps.Application | undefined;
for (const name of names) {
app = Gio.DesktopAppInfo.new(`${name}.desktop`);
if (app) {
astalApp = Apps.get_list().find(a => a.entry === `${name}.desktop`);
if (app.get_icon() && astalApp) break;
else app = null; // Set app to null if no icon or matching AstalApps#Application
}
}
if (!app) console.error(`Launcher - Unable to find app for "${names.join(", ")}"`);
return app ? (
) : null;
};
const PinnedApps = () => (
);
const SearchEntry = ({ entry }: { entry: Widget.Entry }) => (
self.hook(entry, "notify::text-length", () =>
// Timeout to avoid flickering when replacing entire text (cause it'll set len to 0 then back to > 0)
timeout(1, () => (self.shown = entry.textLength > 0 ? "entry" : "placeholder"))
)
}
>
{entry}
);
const Result = ({ app }: { app: AstalApps.Application }) => (
);
const Results = ({ entry }: { entry: Widget.Entry }) => {
const empty = Variable(true);
return (
(t ? "empty" : "list"))}
>
{
let apps: AstalApps.Application[] = [];
self.hook(entry, "activate", () => {
if (entry.text && apps[0]) launchAndClose(self, apps[0]);
});
self.hook(entry, "changed", () => {
if (!entry.text) return;
self.foreach(ch => ch.destroy());
apps = Apps.fuzzy_query(entry.text);
empty.set(apps.length === 0);
if (apps.length > maxSearchResults) apps.length = maxSearchResults;
for (const app of apps) self.add();
});
}}
/>
);
};
const Launcher = ({ entry }: { entry: Widget.Entry }) => (
`margin-top: ${m.height / 4}px;`)}
>
t === 0)}
transitionType={Gtk.RevealerTransitionType.SLIDE_DOWN}
transitionDuration={150}
>
t > 0)}
transitionType={Gtk.RevealerTransitionType.SLIDE_UP}
transitionDuration={150}
>
);
export default () => {
const entry = () as Widget.Entry;
return (
{
const keyval = event.get_keyval()[1];
// Focus entry on typing
if (!entry.isFocus && keyval >= 32 && keyval <= 126) {
entry.text += String.fromCharCode(keyval);
entry.grab_focus();
entry.set_position(-1);
// Consume event, if not consumed it will duplicate character in entry
return true;
}
}}
// Clear entry text on hide
setup={self => self.connect("hide", () => entry.set_text(""))}
transitionType={TransitionType.SLIDE_DOWN}
halign={Gtk.Align.CENTER}
valign={Gtk.Align.START}
>
);
};