launcher modes + player controls IPC

This commit is contained in:
2 * r + 2 * t 2025-01-14 15:41:28 +11:00
parent 824ebda7c8
commit a313624734
6 changed files with 735 additions and 52 deletions

View file

@ -4,6 +4,7 @@ import AstalHyprland from "gi://AstalHyprland";
import Bar from "./modules/bar";
import Launcher from "./modules/launcher";
import NotifPopups from "./modules/notifpopups";
import Players from "./services/players";
import { PopupWindow } from "./utils/widgets";
const loadStyleAsync = async () => {
@ -29,6 +30,10 @@ App.start({
let log = true;
if (request === "reload css") loadStyleAsync().catch(console.error);
else if (request === "media play pause") Players.get_default().lastPlayer?.play_pause();
else if (request === "media next") Players.get_default().lastPlayer?.next();
else if (request === "media previous") Players.get_default().lastPlayer?.previous();
else if (request === "media stop") Players.get_default().lastPlayer?.stop();
else if (request.startsWith("toggle")) {
const window = App.get_window(request.slice(7));
if (window instanceof PopupWindow) window.toggle();

View file

@ -1,12 +1,23 @@
import { bind, Gio, timeout, Variable } from "astal";
import { bind, execAsync, Gio, 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 Mexp from "math-expression-evaluator";
import { Apps } from "../services/apps";
import { getAppCategoryIcon } from "../utils/icons";
import { launch } from "../utils/system";
import { PopupWindow, setupCustomTooltip, TransitionType } from "../utils/widgets";
type Mode = "apps" | "files" | "math";
interface Subcommand {
icon: string;
name: string;
description: string;
command: (...args: string[]) => void;
}
const maxSearchResults = 15;
const browser = [
@ -23,6 +34,17 @@ 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":
return "apps";
case "files":
return "folder";
case "math":
return "calculate";
}
};
const launchAndClose = (self: JSX.Element, astalApp: AstalApps.Application) => {
const toplevel = self.get_toplevel();
if (toplevel instanceof Widget.Window) toplevel.hide();
@ -45,7 +67,7 @@ const PinnedApp = ({ names }: { names: string[] }) => {
return app ? (
<button
className="app"
className="pinned-app result"
cursor="pointer"
onClicked={self => launchAndClose(self, astalApp!)}
setup={self => setupCustomTooltip(self, app.get_display_name())}
@ -56,7 +78,7 @@ const PinnedApp = ({ names }: { names: string[] }) => {
};
const PinnedApps = () => (
<box homogeneous className="pinned">
<box homogeneous>
<PinnedApp names={browser} />
<PinnedApp names={terminal} />
<PinnedApp names={files} />
@ -82,21 +104,72 @@ const SearchEntry = ({ entry }: { entry: Widget.Entry }) => (
</stack>
);
const Result = ({ app }: { app: AstalApps.Application }) => (
<button className="app" cursor="pointer" onClicked={self => launchAndClose(self, app)}>
// TODO: description field
const Result = ({
icon,
materialIcon,
label,
sublabel,
onClicked,
}: {
icon?: string;
materialIcon?: string;
label: string;
sublabel?: string;
onClicked: (self: Widget.Button) => void;
}) => (
<button className="result" cursor="pointer" onClicked={onClicked}>
<box>
{Astal.Icon.lookup_icon(app.iconName) ? (
<icon className="icon" icon={app.iconName} />
{icon && Astal.Icon.lookup_icon(icon) ? (
<icon className="icon" icon={icon} />
) : (
<label className="icon" label={getAppCategoryIcon(app)} />
<label className="icon" label={materialIcon} />
)}
{sublabel ? (
<box vertical valign={Gtk.Align.CENTER} className="has-sublabel">
<label xalign={0} label={label} />
<label hexpand truncate maxWidthChars={1} className="sublabel" xalign={0} label={sublabel} />
</box>
) : (
<label xalign={0} label={label} />
)}
<label xalign={0} label={app.name} />
</box>
</button>
);
const Results = ({ entry }: { entry: Widget.Entry }) => {
const SubcommandResult = ({
entry,
subcommand,
args,
}: {
entry: Widget.Entry;
subcommand: Subcommand;
args: string[];
}) => (
<Result
materialIcon={subcommand.icon}
label={subcommand.name}
sublabel={subcommand.description}
onClicked={() => {
subcommand.command(...args);
entry.set_text("");
}}
/>
);
const AppResult = ({ app }: { app: AstalApps.Application }) => (
<Result
icon={app.iconName}
materialIcon={getAppCategoryIcon(app)}
label={app.name}
sublabel={app.description}
onClicked={self => launchAndClose(self, app)}
/>
);
const Results = ({ entry, mode }: { entry: Widget.Entry; mode: Variable<Mode> }) => {
const empty = Variable(true);
return (
<stack
className="results"
@ -112,17 +185,82 @@ const Results = ({ entry }: { entry: Widget.Entry }) => {
vertical
name="list"
setup={self => {
let apps: AstalApps.Application[] = [];
self.hook(entry, "activate", () => {
if (entry.text && apps[0]) launchAndClose(self, apps[0]);
});
const subcommands: Record<string, Subcommand> = {
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"),
},
calc: {
icon: "calculate",
name: "Calculator",
description: "A calculator...",
command: () => mode.set("math"),
},
todo: {
icon: "checklist",
name: "Todo",
description: "Create a todo in <INSERT_TODO_APP>",
command: (...args) => {
// TODO: todo service or maybe use external app
},
},
};
const subcommandList = Object.keys(subcommands);
const mexp = new Mexp();
const appSearch = () => {
const 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(<AppResult app={app} />);
};
const calculate = () => {
// TODO: allow defs, history
let math = null;
try {
math = mexp.eval(entry.text);
} catch (e) {
// Ignore
}
if (math !== null)
self.add(
<Result
materialIcon="calculate"
label={entry.text}
sublabel={String(math)}
onClicked={() => execAsync(`wl-copy -- ${math}`).catch(console.error)}
/>
);
};
self.hook(entry, "activate", () => entry.text && self.get_children()[0].activate());
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(<Result app={app} />);
if (entry.text.startsWith(">")) {
const args = entry.text.split(" ");
for (const { target } of fuzzysort.go(args[0].slice(1), subcommandList, { all: true }))
self.add(
<SubcommandResult
entry={entry}
subcommand={subcommands[target]}
args={args.slice(1)}
/>
);
} else if (mode.get() === "apps") appSearch();
else if (mode.get() === "math") calculate();
empty.set(self.get_children().length === 0);
});
}}
/>
@ -130,16 +268,16 @@ const Results = ({ entry }: { entry: Widget.Entry }) => {
);
};
const Launcher = ({ entry }: { entry: Widget.Entry }) => (
const LauncherContent = ({ mode, entry }: { mode: Variable<Mode>; entry: Widget.Entry }) => (
<box
vertical
className="launcher"
className={bind(mode).as(m => `launcher ${m}`)}
css={bind(AstalHyprland.get_default(), "focusedMonitor").as(m => `margin-top: ${m.height / 4}px;`)}
>
<box className="search-bar">
<label className="icon" label="search" />
<SearchEntry entry={entry} />
<label className="icon" label="apps" />
<label className="icon" label={bind(mode).as(getIconFromMode)} />
</box>
<revealer
revealChild={bind(entry, "textLength").as(t => t === 0)}
@ -153,20 +291,24 @@ const Launcher = ({ entry }: { entry: Widget.Entry }) => (
transitionType={Gtk.RevealerTransitionType.SLIDE_UP}
transitionDuration={150}
>
<Results entry={entry} />
<Results entry={entry} mode={mode} />
</revealer>
</box>
);
export default () => {
const entry = (<entry name="entry" />) as Widget.Entry;
@register()
export default class Launcher extends PopupWindow {
readonly mode: Variable<Mode>;
return (
<PopupWindow
name="launcher"
keymode={Astal.Keymode.EXCLUSIVE}
exclusivity={Astal.Exclusivity.IGNORE}
onKeyPressEvent={(_, event) => {
constructor() {
const entry = (<entry name="entry" />) as Widget.Entry;
const mode = Variable<Mode>("apps");
super({
name: "launcher",
keymode: Astal.Keymode.EXCLUSIVE,
exclusivity: Astal.Exclusivity.IGNORE,
onKeyPressEvent(_, event) {
const keyval = event.get_keyval()[1];
// Focus entry on typing
if (!entry.isFocus && keyval >= 32 && keyval <= 126) {
@ -177,14 +319,20 @@ export default () => {
// 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}
>
<Launcher entry={entry} />
</PopupWindow>
);
};
},
transitionType: TransitionType.SLIDE_DOWN,
halign: Gtk.Align.CENTER,
valign: Gtk.Align.START,
child: <LauncherContent mode={mode} entry={entry} />,
});
this.mode = mode;
this.connect("hide", () => entry.set_text(""));
}
open(mode: Mode) {
this.mode.set(mode);
this.show();
}
}

494
package-lock.json generated Normal file
View file

@ -0,0 +1,494 @@
{
"name": "caelestia",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"fuzzysort": "^3.1.0",
"math-expression-evaluator": "^2.0.6"
},
"devDependencies": {
"esbuild": "0.24.2"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz",
"integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz",
"integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz",
"integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz",
"integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz",
"integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz",
"integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz",
"integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz",
"integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz",
"integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz",
"integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz",
"integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz",
"integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz",
"integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz",
"integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz",
"integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz",
"integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz",
"integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz",
"integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz",
"integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz",
"integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz",
"integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz",
"integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz",
"integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz",
"integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz",
"integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/esbuild": {
"version": "0.24.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz",
"integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.24.2",
"@esbuild/android-arm": "0.24.2",
"@esbuild/android-arm64": "0.24.2",
"@esbuild/android-x64": "0.24.2",
"@esbuild/darwin-arm64": "0.24.2",
"@esbuild/darwin-x64": "0.24.2",
"@esbuild/freebsd-arm64": "0.24.2",
"@esbuild/freebsd-x64": "0.24.2",
"@esbuild/linux-arm": "0.24.2",
"@esbuild/linux-arm64": "0.24.2",
"@esbuild/linux-ia32": "0.24.2",
"@esbuild/linux-loong64": "0.24.2",
"@esbuild/linux-mips64el": "0.24.2",
"@esbuild/linux-ppc64": "0.24.2",
"@esbuild/linux-riscv64": "0.24.2",
"@esbuild/linux-s390x": "0.24.2",
"@esbuild/linux-x64": "0.24.2",
"@esbuild/netbsd-arm64": "0.24.2",
"@esbuild/netbsd-x64": "0.24.2",
"@esbuild/openbsd-arm64": "0.24.2",
"@esbuild/openbsd-x64": "0.24.2",
"@esbuild/sunos-x64": "0.24.2",
"@esbuild/win32-arm64": "0.24.2",
"@esbuild/win32-ia32": "0.24.2",
"@esbuild/win32-x64": "0.24.2"
}
},
"node_modules/fuzzysort": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/fuzzysort/-/fuzzysort-3.1.0.tgz",
"integrity": "sha512-sR9BNCjBg6LNgwvxlBd0sBABvQitkLzoVY9MYYROQVX/FvfJ4Mai9LsGhDgd8qYdds0bY77VzYd5iuB+v5rwQQ==",
"license": "MIT"
},
"node_modules/math-expression-evaluator": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-2.0.6.tgz",
"integrity": "sha512-DRung1qNcKbgkhFeQ0fBPUFB6voRUMY7KyRyp1TRQ2v95Rp2egC823xLRooM1mDx1rmbkY7ym6ZWmpaE/VimOA==",
"license": "MIT"
}
}
}

9
package.json Normal file
View file

@ -0,0 +1,9 @@
{
"dependencies": {
"fuzzysort": "^3.1.0",
"math-expression-evaluator": "^2.0.6"
},
"devDependencies": {
"esbuild": "0.24.2"
}
}

11
run.fish Executable file
View file

@ -0,0 +1,11 @@
#!/bin/fish
cd (dirname (status filename)) || exit 1
set -q XDG_RUNTIME_DIR && set bundle_dir $XDG_RUNTIME_DIR || set bundle_dir /tmp
./node_modules/.bin/esbuild app.tsx --bundle --outfile=$bundle_dir/caelestia.js \
--external:console --external:system --external:cairo --external:gettext --external:'file://*' --external:'gi://*' --external:'resource://*' \
--define:SRC=\"(pwd)\" --format=esm --platform=neutral --main-fields=module,main
gjs -m $bundle_dir/caelestia.js

View file

@ -29,7 +29,6 @@
.icon {
font-size: lib.s(18);
color: scheme.$sapphire;
}
.placeholder {
@ -37,7 +36,7 @@
}
}
.app {
.result {
@include lib.element-decel;
padding: lib.s(5) lib.s(10);
@ -52,35 +51,52 @@
}
}
.pinned .app {
.pinned-app {
@include lib.rounded(5);
font-size: lib.s(64);
}
.results {
.icon {
font-size: lib.s(32);
}
.empty {
color: scheme.$subtext0;
@include lib.spacing;
.icon {
font-size: lib.s(32);
}
}
.app {
.result {
@include lib.rounded(10);
font-size: lib.s(18);
& > * {
@include lib.spacing;
@include lib.spacing(8);
}
.icon {
font-size: lib.s(32);
.has-sublabel {
font-size: lib.s(16);
.sublabel {
color: scheme.$subtext0;
font-size: lib.s(14);
}
}
}
}
&.apps label.icon {
color: scheme.$sapphire;
}
&.files label.icon {
color: scheme.$peach;
}
&.math label.icon {
color: scheme.$green;
}
}