summaryrefslogtreecommitdiff
path: root/src/modules/launcher
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/launcher')
-rw-r--r--src/modules/launcher/actions.tsx522
-rw-r--r--src/modules/launcher/index.tsx144
-rw-r--r--src/modules/launcher/modes.tsx225
-rw-r--r--src/modules/launcher/util.tsx19
4 files changed, 0 insertions, 910 deletions
diff --git a/src/modules/launcher/actions.tsx b/src/modules/launcher/actions.tsx
deleted file mode 100644
index 40d37b5..0000000
--- a/src/modules/launcher/actions.tsx
+++ /dev/null
@@ -1,522 +0,0 @@
-import { Apps } from "@/services/apps";
-import Palette from "@/services/palette";
-import Schemes, { type Colours } from "@/services/schemes";
-import Wallpapers, { type ICategory, type IWallpaper } from "@/services/wallpapers";
-import { basename } from "@/utils/strings";
-import { notify } from "@/utils/system";
-import { setupCustomTooltip, type FlowBox } from "@/utils/widgets";
-import { bind, execAsync, GLib, readFile, register, type Variable } from "astal";
-import { Gtk, Widget } from "astal/gtk3";
-import { launcher as config } from "config";
-import { setConfig } from "config/funcs";
-import fuzzysort from "fuzzysort";
-import AstalHyprland from "gi://AstalHyprland";
-import { close, ContentBox, type LauncherContent, type Mode } from "./util";
-
-interface IAction {
- icon: string;
- name: string;
- description: string;
- action: (...args: string[]) => void;
- available?: () => boolean;
-}
-
-interface ActionMap {
- [k: string]: IAction;
-}
-
-const variantActions = {
- vibrant: {
- icon: "sentiment_very_dissatisfied",
- name: "Vibrant",
- description: "A high chroma palette. The primary palette's chroma is at maximum.",
- },
- tonalspot: {
- icon: "android",
- name: "Tonal Spot",
- description: "Default for Material theme colours. A pastel palette with a low chroma.",
- },
- expressive: {
- icon: "compare_arrows",
- name: "Expressive",
- description:
- "A medium chroma palette. The primary palette's hue is different from the seed colour, for variety.",
- },
- fidelity: {
- icon: "compare",
- name: "Fidelity",
- description: "Matches the seed colour, even if the seed colour is very bright (high chroma).",
- },
- content: {
- icon: "sentiment_calm",
- name: "Content",
- description: "Almost identical to fidelity.",
- },
- fruitsalad: {
- icon: "nutrition",
- name: "Fruit Salad",
- description: "A playful theme - the seed colour's hue does not appear in the theme.",
- },
- rainbow: {
- icon: "looks",
- name: "Rainbow",
- description: "A playful theme - the seed colour's hue does not appear in the theme.",
- },
- neutral: {
- icon: "contrast",
- name: "Neutral",
- description: "Close to grayscale, a hint of chroma.",
- },
- monochrome: {
- icon: "filter_b_and_w",
- name: "Monochrome",
- description: "All colours are grayscale, no chroma.",
- },
-};
-
-const transparencyActions = {
- off: {
- icon: "blur_off",
- name: "Off",
- description: "Completely opaque",
- },
- low: {
- icon: "blur_circular",
- name: "Low",
- description: "Less transparent",
- },
- normal: {
- icon: "blur_linear",
- name: "Normal",
- description: "Somewhat transparent",
- },
- high: {
- icon: "blur_on",
- name: "High",
- description: "Extremely transparent",
- },
-};
-
-const autocomplete = (entry: Widget.Entry, action: string) => {
- entry.set_text(`${config.actionPrefix.get()}${action} `);
- entry.set_position(-1);
-};
-
-const actions = (mode: Variable<Mode>, entry: Widget.Entry): ActionMap => ({
- apps: {
- icon: "apps",
- name: "Apps",
- description: "Search for apps",
- action: () => {
- mode.set("apps");
- entry.set_text("");
- },
- },
- files: {
- icon: "folder",
- name: "Files",
- description: "Search for files",
- action: () => {
- mode.set("files");
- entry.set_text("");
- },
- },
- math: {
- icon: "calculate",
- name: "Math",
- description: "Do math calculations",
- action: () => {
- mode.set("math");
- entry.set_text("");
- },
- },
- light: {
- icon: "light_mode",
- name: "Light",
- description: "Change scheme to light mode",
- action: () => {
- Palette.get_default().switchMode("light");
- close();
- },
- available: () => Palette.get_default().hasMode("light"),
- },
- dark: {
- icon: "dark_mode",
- name: "Dark",
- description: "Change scheme to dark mode",
- action: () => {
- Palette.get_default().switchMode("dark");
- close();
- },
- available: () => Palette.get_default().hasMode("dark"),
- },
- scheme: {
- icon: "palette",
- name: "Scheme",
- description: "Change the current colour scheme",
- action: () => autocomplete(entry, "scheme"),
- },
- variant: {
- icon: "colors",
- name: "Variant",
- description: "Change the current scheme variant",
- action: () => autocomplete(entry, "variant"),
- available: () => Palette.get_default().scheme === "dynamic",
- },
- wallpaper: {
- icon: "image",
- name: "Wallpaper",
- description: "Change the current wallpaper",
- action: () => autocomplete(entry, "wallpaper"),
- },
- transparency: {
- icon: "opacity",
- name: "Transparency",
- description: "Change shell transparency",
- action: () => autocomplete(entry, "transparency"),
- },
- todo: {
- icon: "checklist",
- name: "Todo",
- description: "Create a todo in Todoist",
- action: (...args) => {
- // If no args, autocomplete cmd
- if (args.length === 0) return autocomplete(entry, "todo");
-
- // If tod not configured, notify
- let token = null;
- try {
- token = JSON.parse(readFile(GLib.get_user_config_dir() + "/tod.cfg")).token;
- } catch {} // Ignore
- if (!token) {
- notify({
- summary: "Tod not configured",
- body: "You need to configure tod first. Run any tod command to do this.",
- icon: "dialog-warning-symbolic",
- urgency: "critical",
- });
- } else {
- // Create todo and notify if configured
- execAsync(`tod t q -c ${args.join(" ")}`).catch(console.error);
- if (config.todo.notify.get())
- notify({
- summary: "Todo created",
- body: `Created todo with content: ${args.join(" ")}`,
- icon: "view-list-bullet-symbolic",
- urgency: "low",
- transient: true,
- actions: {
- "Copy content": () => execAsync(`wl-copy -- ${args.join(" ")}`).catch(console.error),
- View: () => {
- const client = AstalHyprland.get_default().clients.find(c => c.class === "Todoist");
- if (client) client.focus();
- else execAsync("app2unit -- todoist").catch(console.error);
- },
- },
- });
- }
-
- close();
- },
- available: () => !!GLib.find_program_in_path("tod"),
- },
- reload: {
- icon: "refresh",
- name: "Reload",
- description: "Reload app list",
- action: () => {
- Apps.reload();
- entry.set_text("");
- },
- },
- lock: {
- icon: "lock",
- name: "Lock",
- description: "Lock the current session",
- action: () => {
- execAsync("loginctl lock-session").catch(console.error);
- close();
- },
- },
- logout: {
- icon: "logout",
- name: "Logout",
- description: "End the current session",
- action: () => {
- execAsync("uwsm stop").catch(console.error);
- close();
- },
- },
- sleep: {
- icon: "bedtime",
- name: "Sleep",
- description: "Suspend then hibernate",
- action: () => {
- execAsync("systemctl suspend-then-hibernate").catch(console.error);
- close();
- },
- },
- reboot: {
- icon: "cached",
- name: "Reboot",
- description: "Restart the machine",
- action: () => {
- execAsync("systemctl reboot").catch(console.error);
- close();
- },
- },
- hibernate: {
- icon: "downloading",
- name: "Hibernate",
- description: "Suspend to RAM",
- action: () => {
- execAsync("systemctl hibernate").catch(console.error);
- close();
- },
- },
- shutdown: {
- icon: "power_settings_new",
- name: "Shutdown",
- description: "Suspend to disk",
- action: () => {
- execAsync("systemctl poweroff").catch(console.error);
- close();
- },
- },
-});
-
-const Action = ({ args, icon, name, description, action }: IAction & { args: string[] }) => (
- <Gtk.FlowBoxChild visible canFocus={false}>
- <button
- className="result"
- cursor="pointer"
- onClicked={() => action(...args)}
- setup={self => setupCustomTooltip(self, description)}
- >
- <box>
- <label className="icon" label={icon} />
- <box vertical className="has-sublabel">
- <label truncate xalign={0} label={name} />
- <label truncate xalign={0} label={description} className="sublabel" />
- </box>
- </box>
- </button>
- </Gtk.FlowBoxChild>
-);
-
-const Swatch = ({ colour }: { colour: string }) => <box className="swatch" css={"background-color: " + colour + ";"} />;
-
-const Scheme = ({ scheme, name, colours }: { scheme?: string; name: string; colours?: Colours }) => {
- const palette = colours![Palette.get_default().mode] ?? colours!.light ?? colours!.dark!;
- return (
- <Gtk.FlowBoxChild visible canFocus={false}>
- <button
- className="result"
- cursor="pointer"
- onClicked={() => {
- execAsync(`caelestia scheme ${scheme ?? ""} ${name}`).catch(console.error);
- close();
- }}
- >
- <box>
- <box valign={Gtk.Align.CENTER}>
- <box className="swatch big left" css={"background-color: " + palette.base + ";"} />
- <box className="swatch big right" css={"background-color: " + palette.primary + ";"} />
- </box>
- <box vertical className="has-sublabel">
- <label truncate xalign={0} label={scheme ? `${scheme} (${name})` : name} />
- <box className="swatches">
- <Swatch colour={palette.rosewater} />
- <Swatch colour={palette.flamingo} />
- <Swatch colour={palette.pink} />
- <Swatch colour={palette.mauve} />
- <Swatch colour={palette.red} />
- <Swatch colour={palette.maroon} />
- <Swatch colour={palette.peach} />
- <Swatch colour={palette.yellow} />
- <Swatch colour={palette.green} />
- <Swatch colour={palette.teal} />
- <Swatch colour={palette.sky} />
- <Swatch colour={palette.sapphire} />
- <Swatch colour={palette.blue} />
- <Swatch colour={palette.lavender} />
- </box>
- </box>
- </box>
- </button>
- </Gtk.FlowBoxChild>
- );
-};
-
-const Variant = ({ name }: { name: keyof typeof variantActions }) => (
- <Action
- {...variantActions[name]}
- args={[]}
- action={() => {
- execAsync(`caelestia variant ${name}`).catch(console.error);
- close();
- }}
- />
-);
-
-const Wallpaper = ({ path, thumbnails }: IWallpaper) => (
- <Gtk.FlowBoxChild visible canFocus={false}>
- <button
- className="result wallpaper-container"
- cursor="pointer"
- onClicked={() => {
- execAsync(`caelestia wallpaper -f ${path}`).catch(console.error);
- close();
- }}
- setup={self => setupCustomTooltip(self, path.replace(HOME, "~"))}
- >
- <box
- vertical={config.wallpaper.style.get() !== "compact"}
- className={`wallpaper ${config.wallpaper.style.get()}`}
- >
- <box
- className="thumbnail"
- css={bind(config.wallpaper.style).as(
- s => "background-image: url('" + thumbnails[s as keyof typeof thumbnails] + "');"
- )}
- />
- <label truncate label={basename(path)} />
- </box>
- </button>
- </Gtk.FlowBoxChild>
-);
-
-const CategoryThumbnail = ({ style, wallpapers }: { style: string; wallpapers: IWallpaper[] }) => (
- <box className="thumbnail">
- {wallpapers.slice(0, 3).map(w => (
- <box hexpand css={"background-image: url('" + w.thumbnails[style as keyof typeof w.thumbnails] + "');"} />
- ))}
- </box>
-);
-
-const Category = ({ path, wallpapers }: ICategory) => (
- <Gtk.FlowBoxChild visible canFocus={false}>
- <button
- className="result wallpaper-container"
- cursor="pointer"
- onClicked={() => {
- execAsync(`caelestia wallpaper -d ${path}`).catch(console.error);
- close();
- }}
- setup={self => setupCustomTooltip(self, path.replace(HOME, "~"))}
- >
- <box
- vertical={config.wallpaper.style.get() !== "compact"}
- className={`wallpaper ${config.wallpaper.style.get()}`}
- >
- {bind(config.wallpaper.style).as(s =>
- s === "compact" ? (
- <box
- className="thumbnail"
- css={"background-image: url('" + wallpapers[0].thumbnails.compact + "');"}
- />
- ) : (
- <CategoryThumbnail style={s} wallpapers={wallpapers} />
- )
- )}
- <label truncate label={basename(path)} />
- </box>
- </button>
- </Gtk.FlowBoxChild>
-);
-
-const Transparency = ({ amount }: { amount: keyof typeof transparencyActions }) => (
- <Action
- {...transparencyActions[amount]}
- args={[]}
- action={() => {
- setConfig("style.transparency", amount).catch(console.error);
- close();
- }}
- />
-);
-
-@register()
-export default class Actions extends Widget.Box implements LauncherContent {
- #map: ActionMap;
- #list: string[];
-
- #content: FlowBox;
-
- constructor(mode: Variable<Mode>, entry: Widget.Entry) {
- super({ name: "actions", className: "actions" });
-
- this.#map = actions(mode, entry);
- this.#list = Object.keys(this.#map);
-
- this.#content = (<ContentBox />) as FlowBox;
-
- this.add(
- <scrollable expand hscroll={Gtk.PolicyType.NEVER}>
- {this.#content}
- </scrollable>
- );
- }
-
- updateContent(search: string): void {
- this.#content.foreach(c => c.destroy());
- const args = search.split(" ");
- const action = args[0].slice(1).toLowerCase();
-
- if (action === "scheme") {
- const scheme = args[1] ?? "";
- const schemes = Object.values(Schemes.get_default().map)
- .flatMap(s => (s.colours ? s.name : Object.values(s.flavours!).map(f => `${f.scheme}-${f.name}`)))
- .filter(s => s !== undefined)
- .sort();
- for (const { target } of fuzzysort.go(scheme, schemes, { all: true })) {
- if (Schemes.get_default().map.hasOwnProperty(target))
- this.#content.add(<Scheme {...Schemes.get_default().map[target]} />);
- else {
- const [scheme, flavour] = target.split("-");
- this.#content.add(<Scheme {...Schemes.get_default().map[scheme].flavours![flavour]} />);
- }
- }
- } else if (action === "variant") {
- const list = Object.keys(variantActions);
-
- for (const { target } of fuzzysort.go(args[1], list, { all: true }))
- this.#content.add(<Variant name={target as keyof typeof variantActions} />);
- } else if (action === "wallpaper") {
- if (args[1]?.toLowerCase() === "random") {
- const list = Wallpapers.get_default().categories;
- for (const { obj } of fuzzysort.go(args[2] ?? "", list, { all: true, key: "path" }))
- this.#content.add(<Category {...obj} />);
- } else {
- const list = Wallpapers.get_default().list;
- let limit = undefined;
- if ((args[1] || !config.wallpaper.showAllEmpty.get()) && config.wallpaper.maxResults.get() > 0)
- limit = config.wallpaper.maxResults.get();
-
- for (const { obj } of fuzzysort.go(args[1] ?? "", list, { all: true, key: "path", limit }))
- this.#content.add(<Wallpaper {...obj} />);
- }
- } else if (action === "transparency") {
- const list = Object.keys(transparencyActions);
-
- for (const { target } of fuzzysort.go(args[1], list, { all: true }))
- this.#content.add(<Transparency amount={target as keyof typeof transparencyActions} />);
- } else {
- const list = this.#list.filter(
- a => this.#map[a].available?.() ?? !config.disabledActions.get().includes(a)
- );
- for (const { target } of fuzzysort.go(action, list, { all: true }))
- this.#content.add(<Action {...this.#map[target]} args={args.slice(1)} />);
- }
- }
-
- handleActivate(search: string): void {
- const args = search.split(" ");
- const action = args[0].slice(1).toLowerCase();
-
- if (action === "scheme" && args[1]?.toLowerCase() === "random") {
- execAsync(`caelestia scheme`).catch(console.error);
- close();
- } else this.#content.get_child_at_index(0)?.get_child()?.activate();
- }
-}
diff --git a/src/modules/launcher/index.tsx b/src/modules/launcher/index.tsx
deleted file mode 100644
index b75ecce..0000000
--- a/src/modules/launcher/index.tsx
+++ /dev/null
@@ -1,144 +0,0 @@
-import PopupWindow from "@/widgets/popupwindow";
-import { bind, register, Variable } from "astal";
-import { Astal, Gtk, Widget } from "astal/gtk3";
-import { launcher as config } from "config";
-import Actions from "./actions";
-import Modes from "./modes";
-import type { Mode } from "./util";
-
-const getModeIcon = (mode: Mode) => {
- if (mode === "apps") return "apps";
- if (mode === "files") return "folder";
- if (mode === "math") return "calculate";
- return "search";
-};
-
-const getPrettyMode = (mode: Mode) => {
- if (mode === "apps") return "Apps";
- if (mode === "files") return "Files";
- if (mode === "math") return "Math";
- return mode;
-};
-
-const isAction = (text: string, action: string = "") => text.startsWith(config.actionPrefix.get() + action);
-
-const SearchBar = ({ mode, entry }: { mode: Variable<Mode>; entry: Widget.Entry }) => (
- <box className="search-bar">
- <box className="mode">
- <label className="icon" label={bind(mode).as(getModeIcon)} />
- <label label={bind(mode).as(getPrettyMode)} />
- </box>
- {entry}
- </box>
-);
-
-const ModeSwitcher = ({ mode, modes }: { mode: Variable<Mode>; modes: Mode[] }) => (
- <box homogeneous hexpand className="mode-switcher">
- {modes.map(m => (
- <button
- className={bind(mode).as(c => `mode ${c === m ? "selected" : ""}`)}
- cursor="pointer"
- onClicked={() => mode.set(m)}
- >
- <box halign={Gtk.Align.CENTER}>
- <label className="icon" label={getModeIcon(m)} />
- <label label={getPrettyMode(m)} />
- </box>
- </button>
- ))}
- </box>
-);
-
-@register()
-export default class Launcher extends PopupWindow {
- readonly mode: Variable<Mode>;
-
- constructor() {
- const entry = (
- <entry
- hexpand
- className="entry"
- placeholderText={bind(config.actionPrefix).as(p => `Type "${p}" for subcommands`)}
- />
- ) as Widget.Entry;
- const mode = Variable<Mode>("apps");
- const content = Modes();
- const actions = new Actions(mode, entry);
- const className = Variable.derive([mode, config.style], (m, s) => `launcher ${m} ${s}`);
-
- super({
- name: "launcher",
- anchor:
- Astal.WindowAnchor.TOP | Astal.WindowAnchor.LEFT | Astal.WindowAnchor.BOTTOM | Astal.WindowAnchor.RIGHT,
- keymode: Astal.Keymode.EXCLUSIVE,
- exclusivity: Astal.Exclusivity.IGNORE,
- borderWidth: 0,
- onKeyPressEvent(_, event) {
- 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;
- }
- },
- child: (
- <box
- vertical
- halign={Gtk.Align.CENTER}
- valign={Gtk.Align.CENTER}
- className={bind(className)}
- onDestroy={() => className.drop()}
- >
- <SearchBar mode={mode} entry={entry} />
- <stack
- expand
- transitionType={Gtk.StackTransitionType.CROSSFADE}
- transitionDuration={100}
- shown={bind(entry, "text").as(t => (isAction(t) ? "actions" : "content"))}
- >
- <stack
- name="content"
- transitionType={Gtk.StackTransitionType.SLIDE_LEFT_RIGHT}
- transitionDuration={200}
- shown={bind(mode)}
- >
- {Object.values(content)}
- </stack>
- {actions}
- </stack>
- <ModeSwitcher mode={mode} modes={Object.keys(content) as Mode[]} />
- </box>
- ),
- });
-
- this.mode = mode;
-
- content[mode.get()].updateContent(entry.get_text());
- this.hook(mode, (_, v: Mode) => {
- entry.set_text("");
- content[v].updateContent(entry.get_text());
- });
- this.hook(entry, "changed", () =>
- (isAction(entry.get_text()) ? actions : content[mode.get()]).updateContent(entry.get_text())
- );
- this.hook(entry, "activate", () => {
- (isAction(entry.get_text()) ? actions : content[mode.get()]).handleActivate(entry.get_text());
- if (mode.get() === "math" && !isAction(entry.get_text())) entry.set_text(""); // Cause math mode doesn't auto clear
- });
-
- // Clear search on hide if not in math mode or creating a todo
- this.connect("hide", () => {
- if ((mode.get() !== "math" || isAction(entry.get_text())) && !isAction(entry.get_text(), "todo"))
- entry.set_text("");
- });
- }
-
- open(mode: Mode) {
- this.mode.set(mode);
- this.show();
- }
-}
diff --git a/src/modules/launcher/modes.tsx b/src/modules/launcher/modes.tsx
deleted file mode 100644
index e278779..0000000
--- a/src/modules/launcher/modes.tsx
+++ /dev/null
@@ -1,225 +0,0 @@
-import { Apps as AppsService } from "@/services/apps";
-import MathService, { type HistoryItem } from "@/services/math";
-import { getAppCategoryIcon } from "@/utils/icons";
-import { launch } from "@/utils/system";
-import { type FlowBox, setupCustomTooltip } from "@/utils/widgets";
-import { bind, execAsync, Gio, register, Variable } from "astal";
-import { Astal, Gtk, Widget } from "astal/gtk3";
-import { launcher as config } from "config";
-import type AstalApps from "gi://AstalApps";
-import { close, ContentBox, type LauncherContent, limitLength } from "./util";
-
-const AppResult = ({ app }: { app: AstalApps.Application }) => (
- <Gtk.FlowBoxChild visible canFocus={false}>
- <button
- className="result"
- cursor="pointer"
- onClicked={() => {
- launch(app);
- close();
- }}
- setup={self => setupCustomTooltip(self, app.description ? `${app.name}: ${app.description}` : app.name)}
- >
- <box>
- {app.iconName && Astal.Icon.lookup_icon(app.iconName) ? (
- <icon className="icon" icon={app.iconName} />
- ) : (
- <label className="icon" label={getAppCategoryIcon(app)} />
- )}
- <label truncate label={app.name} />
- </box>
- </button>
- </Gtk.FlowBoxChild>
-);
-
-const FileResult = ({ path }: { path: string }) => (
- <Gtk.FlowBoxChild visible canFocus={false}>
- <button
- className="result"
- cursor="pointer"
- onClicked={() => {
- execAsync([
- "bash",
- "-c",
- `dbus-send --session --dest=org.freedesktop.FileManager1 --type=method_call /org/freedesktop/FileManager1 org.freedesktop.FileManager1.ShowItems array:string:'file://${path}' string:'' || app2unit -O '${path}'`,
- ]).catch(console.error);
- close();
- }}
- >
- <box setup={self => setupCustomTooltip(self, path.replace(HOME, "~"))}>
- <icon
- className="icon"
- gicon={
- Gio.File.new_for_path(path)
- .query_info(Gio.FILE_ATTRIBUTE_STANDARD_ICON, Gio.FileQueryInfoFlags.NONE, null)
- .get_icon()!
- }
- />
- <label
- truncate
- label={
- path.replace(HOME, "~").length > config.files.shortenThreshold.get()
- ? path
- .replace(HOME, "~")
- .split("/")
- .map((n, i, arr) => (i === 0 || i === arr.length - 1 ? n : n.slice(0, 1)))
- .join("/")
- : path.replace(HOME, "~")
- }
- />
- </box>
- </button>
- </Gtk.FlowBoxChild>
-);
-
-const MathResult = ({ icon, equation, result }: HistoryItem) => (
- <Gtk.FlowBoxChild visible canFocus={false}>
- <button
- className="result"
- cursor="pointer"
- onClicked={() => {
- execAsync(["wl-copy", "--", result]).catch(console.error);
- close();
- }}
- setup={self => setupCustomTooltip(self, `${equation} -> ${result}`)}
- >
- <box>
- <label className="icon" label={icon} />
- <box vertical className="has-sublabel">
- <label truncate xalign={0} label={equation} />
- <label truncate xalign={0} label={result} className="sublabel" />
- </box>
- </box>
- </button>
- </Gtk.FlowBoxChild>
-);
-
-@register()
-class Apps extends Widget.Box implements LauncherContent {
- #content: FlowBox;
-
- constructor() {
- super({ name: "apps", className: "apps" });
-
- this.#content = (<ContentBox />) as FlowBox;
-
- this.add(
- <scrollable expand hscroll={Gtk.PolicyType.NEVER}>
- {this.#content}
- </scrollable>
- );
- }
-
- updateContent(search: string): void {
- this.#content.foreach(c => c.destroy());
- for (const app of limitLength(AppsService.fuzzy_query(search), config.apps))
- this.#content.add(<AppResult app={app} />);
- }
-
- handleActivate(): void {
- this.#content.get_child_at_index(0)?.get_child()?.grab_focus();
- this.#content.get_child_at_index(0)?.get_child()?.activate();
- }
-}
-
-@register()
-class Files extends Widget.Box implements LauncherContent {
- #content: FlowBox;
-
- constructor() {
- super({ name: "files", className: "files" });
-
- this.#content = (<ContentBox />) as FlowBox;
-
- this.add(
- <scrollable expand hscroll={Gtk.PolicyType.NEVER}>
- {this.#content}
- </scrollable>
- );
- }
-
- updateContent(search: string): void {
- execAsync(["fd", ...config.files.fdOpts.get(), search, HOME])
- .then(out => {
- this.#content.foreach(c => c.destroy());
- const paths = out.split("\n").filter(path => path);
- for (const path of limitLength(paths, config.files)) this.#content.add(<FileResult path={path} />);
- })
- .catch(() => {}); // Ignore errors
- }
-
- handleActivate(): void {
- this.#content.get_child_at_index(0)?.get_child()?.grab_focus();
- this.#content.get_child_at_index(0)?.get_child()?.activate();
- }
-}
-
-@register()
-class Math extends Widget.Box implements LauncherContent {
- #showResult: Variable<boolean>;
- #result: Variable<HistoryItem>;
- #content: FlowBox;
-
- constructor() {
- super({ name: "math", className: "math", vertical: true });
-
- this.#showResult = Variable(false);
- this.#result = Variable({ equation: "", result: "", icon: "" });
- this.#content = (<ContentBox />) as FlowBox;
-
- this.add(
- <revealer
- transitionType={Gtk.RevealerTransitionType.SLIDE_DOWN}
- transitionDuration={150}
- revealChild={bind(this.#showResult)}
- >
- <box vertical className="preview">
- <box className="result">
- <label className="icon" label={bind(this.#result).as(r => r.icon)} />
- <box vertical>
- <label xalign={0} label="Result" />
- <label
- truncate
- xalign={0}
- className="sublabel"
- label={bind(this.#result).as(r => r.result)}
- />
- </box>
- </box>
- <box visible={bind(config.style).as(s => s === "lines")} className="separator" />
- </box>
- </revealer>
- );
- this.add(
- <scrollable expand hscroll={Gtk.PolicyType.NEVER}>
- {this.#content}
- </scrollable>
- );
- }
-
- updateContent(search: string): void {
- this.#showResult.set(search.length > 0);
- this.#result.set(MathService.get_default().evaluate(search));
-
- this.#content.foreach(c => c.destroy());
- for (const item of limitLength(MathService.get_default().history, config.math))
- this.#content.add(<MathResult {...item} />);
- }
-
- handleActivate(search: string): void {
- if (!search) return;
- MathService.get_default().commit();
- const res = this.#result.get();
- // Copy and close if not assignment, help or error
- if (!["equal", "help", "error"].includes(res.icon)) {
- execAsync(["wl-copy", "--", res.result]).catch(console.error);
- close();
- }
- }
-}
-
-export default () => ({
- apps: new Apps(),
- files: new Files(),
- math: new Math(),
-});
diff --git a/src/modules/launcher/util.tsx b/src/modules/launcher/util.tsx
deleted file mode 100644
index 8288588..0000000
--- a/src/modules/launcher/util.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import { FlowBox } from "@/utils/widgets";
-import type { Variable } from "astal";
-import { App, Gtk } from "astal/gtk3";
-
-export type Mode = "apps" | "files" | "math";
-
-export interface LauncherContent {
- updateContent(search: string): void;
- handleActivate(search: string): void;
-}
-
-export const close = () => App.get_window("launcher")?.hide();
-
-export const limitLength = <T,>(arr: T[], cfg: { maxResults: Variable<number> }) =>
- cfg.maxResults.get() > 0 && arr.length > cfg.maxResults.get() ? arr.slice(0, cfg.maxResults.get()) : arr;
-
-export const ContentBox = () => (
- <FlowBox homogeneous valign={Gtk.Align.START} minChildrenPerLine={2} maxChildrenPerLine={2} />
-);