From 02fd2e97f2c8a53bf2344e6fa8b14769cb15ee38 Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Thu, 16 Jan 2025 16:35:37 +1100 Subject: refactor: move ts to src Also move popupwindow to own file --- modules/launcher.tsx | 390 --------------------------------------------------- 1 file changed, 390 deletions(-) delete mode 100644 modules/launcher.tsx (limited to 'modules/launcher.tsx') diff --git a/modules/launcher.tsx b/modules/launcher.tsx deleted file mode 100644 index 966cdfd..0000000 --- a/modules/launcher.tsx +++ /dev/null @@ -1,390 +0,0 @@ -import { bind, execAsync, Gio, GLib, register, timeout, Variable } from "astal"; -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 { getAppCategoryIcon } from "../utils/icons"; -import { launch } from "../utils/system"; -import { PopupWindow, setupCustomTooltip } from "../utils/widgets"; - -type Mode = "apps" | "files" | "math"; - -interface Subcommand { - icon: string; - name: string; - description: string; - command: (...args: string[]) => void; -} - -const getIconFromMode = (mode: Mode) => { - switch (mode) { - case "apps": - return "apps"; - case "files": - return "folder"; - case "math": - return "calculate"; - } -}; - -const getEmptyTextFromMode = (mode: Mode) => { - switch (mode) { - case "apps": - return "No apps found"; - case "files": - return GLib.find_program_in_path("fd") === null ? "File search requires `fd`" : "No files found"; - case "math": - return "Type an expression"; - } -}; - -const close = (self: JSX.Element) => { - const toplevel = self.get_toplevel(); - if (toplevel instanceof Widget.Window) toplevel.hide(); -}; - -const launchAndClose = (self: JSX.Element, astalApp: AstalApps.Application) => { - close(self); - launch(astalApp); -}; - -const openFileAndClose = (self: JSX.Element, path: string) => { - close(self); - 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:"" || xdg-open "${path}"`, - ]).catch(console.error); -}; - -const PinnedApp = (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 = () => {config.pins.map(PinnedApp)}; - -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")) - ) - } - > - -); - -const Result = ({ - icon, - materialIcon, - label, - sublabel, - onClicked, -}: { - icon?: string; - materialIcon?: string; - label: string; - sublabel?: string; - onClicked: (self: Widget.Button) => void; -}) => ( - -); - -const SubcommandResult = ({ - entry, - subcommand, - args, -}: { - entry: Widget.Entry; - subcommand: Subcommand; - args: string[]; -}) => ( - { - subcommand.command(...args); - entry.set_text(""); - }} - /> -); - -const AppResult = ({ app }: { app: AstalApps.Application }) => ( - launchAndClose(self, app)} - /> -); - -const MathResult = ({ math, isHistory, entry }: { math: HistoryItem; isHistory?: boolean; entry: Widget.Entry }) => ( - { - if (isHistory) { - Math.get_default().select(math); - entry.set_text(math.equation); - entry.grab_focus(); - entry.set_position(-1); - } else { - execAsync(`wl-copy -- ${math.result}`).catch(console.error); - entry.set_text(""); - } - }} - /> -); - -const FileResult = ({ path }: { path: string }) => ( - openFileAndClose(self, path)} - /> -); - -const Results = ({ entry, mode }: { entry: Widget.Entry; mode: Variable }) => { - const empty = Variable(true); - - return ( - (t ? "empty" : "list"))} - > - - - { - const subcommands: Record = { - apps: { - icon: "apps", - name: "Apps", - description: "Search for apps", - command: () => mode.set("apps"), - }, - files: { - icon: "folder", - name: "Files", - description: "Search for files", - command: () => mode.set("files"), - }, - math: { - icon: "calculate", - name: "Math", - description: "Do math calculations", - command: () => mode.set("math"), - }, - todo: { - icon: "checklist", - name: "Todo", - description: "Create a todo in ", - command: (...args) => { - // TODO: todo service or maybe use external app - }, - }, - }; - const subcommandList = Object.keys(subcommands); - - const updateEmpty = () => empty.set(self.get_children().length === 0); - - const appSearch = () => { - const apps = Apps.fuzzy_query(entry.text); - if (apps.length > config.maxResults) apps.length = config.maxResults; - for (const app of apps) self.add(); - }; - - const calculate = () => { - if (entry.text) { - self.add(); - self.add(); - } - for (const item of Math.get_default().history) - self.add(); - }; - - const fileSearch = () => - execAsync(["fd", ...config.fdOpts, entry.text, HOME]) - .then(out => { - const paths = out.split("\n").filter(path => path); - if (paths.length > config.maxResults) paths.length = config.maxResults; - self.foreach(ch => ch.destroy()); - for (const path of paths) self.add(); - }) - .catch(e => { - // Ignore execAsync error - if (!(e instanceof Gio.IOErrorEnum || e instanceof GLib.SpawnError)) console.error(e); - }) - .finally(updateEmpty); - - self.hook(entry, "activate", () => { - if (mode.get() === "math") { - if (entry.text.startsWith("clear")) Math.get_default().clear(); - else Math.get_default().commit(); - } - self.get_children()[0]?.activate(); - }); - self.hook(entry, "changed", () => { - if (!entry.text && mode.get() === "apps") return; - - // Files has delay cause async so it does some stuff by itself - const ignoreFileAsync = entry.text.startsWith(">") || mode.get() !== "files"; - if (ignoreFileAsync) self.foreach(ch => ch.destroy()); - - if (entry.text.startsWith(">")) { - const args = entry.text.split(" "); - for (const { target } of fuzzysort.go(args[0].slice(1), subcommandList, { all: true })) - self.add( - - ); - } else if (mode.get() === "apps") appSearch(); - else if (mode.get() === "math") calculate(); - else if (mode.get() === "files") fileSearch(); - - if (ignoreFileAsync) updateEmpty(); - }); - }} - /> - - ); -}; - -const LauncherContent = ({ - mode, - showResults, - entry, -}: { - mode: Variable; - showResults: Variable; - entry: Widget.Entry; -}) => ( - `launcher ${m}`)}> - - - !s)} - transitionType={Gtk.RevealerTransitionType.SLIDE_DOWN} - transitionDuration={150} - > - - - - - - -); - -@register() -export default class Launcher extends PopupWindow { - readonly mode: Variable; - - constructor() { - const entry = () as Widget.Entry; - const mode = Variable("apps"); - const showResults = Variable.derive([bind(entry, "textLength"), mode], (t, m) => t > 0 || m !== "apps"); - - super({ - name: "launcher", - anchor: Astal.WindowAnchor.TOP, - keymode: Astal.Keymode.EXCLUSIVE, - 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: , - }); - - this.mode = mode; - - this.connect("show", () => (this.marginTop = AstalHyprland.get_default().focusedMonitor.height / 4)); - - // Clear search on hide if not in math mode - this.connect("hide", () => mode.get() !== "math" && entry.set_text("")); - - this.connect("destroy", () => showResults.drop()); - } - - open(mode: Mode) { - this.mode.set(mode); - this.show(); - } -} -- cgit v1.2.3-freya