chore(runner): try a new approach to show results, make plugins return properties instead of the actual widget

returning the results as objects seems to be a better approach, also, the new way of showing results doesn't work for some reason(i didn't discover it yet lol)
This commit is contained in:
retrozinndev
2025-07-23 20:41:23 -03:00
parent 69b098b6bd
commit 467de2235a
9 changed files with 278 additions and 242 deletions
+3 -6
View File
@@ -1,5 +1,3 @@
import { ResultWidget, ResultWidgetProps } from "../../widget/runner/ResultWidget";
import AstalApps from "gi://AstalApps";
import { execApp, getAstalApps, lookupIcon, updateApps } from "../../scripts/apps";
import { Runner } from "../Runner";
@@ -9,13 +7,12 @@ export const PluginApps = {
// asynchronously-refresh apps list on init
init: async () => updateApps(),
handle: (text: string) => {
return getAstalApps().fuzzy_query(text).map((app: AstalApps.Application) =>
new ResultWidget({
return getAstalApps().fuzzy_query(text).map(app => ({
title: app.get_name(),
description: app.get_description(),
icon: lookupIcon(app.iconName) ? app.iconName : "application-x-executable-symbolic",
onClick: () => execApp(app)
} as ResultWidgetProps)
actionClick: () => execApp(app)
})
);
}
} as Runner.Plugin;
+33
View File
@@ -0,0 +1,33 @@
import { Gtk } from "ags/gtk4";
import { Clipboard } from "../../scripts/clipboard";
import { Runner } from "../Runner";
import { jsx } from "ags/gtk4/jsx-runtime";
export const PluginClipboard = {
prefix: '>',
prioritize: true,
handle: (search) => {
if(Clipboard.getDefault().history.length < 1)
return {
icon: "edit-paste-symbolic",
title: "Clipboard is empty",
description: "Copy something and it will be shown right here!"
};
return Clipboard.getDefault().history.filter(item =>
// not the best way to search, but it works
Runner.regExMatch(search, item.id) || Runner.regExMatch(search, item.preview)).map((item) => ({
icon: jsx(Gtk.Label, {
label: `${item.id}`,
css: "font-size: 16px; margin-right: 8px; font-weight: 600;"
}),
title: item.preview,
actionClick: () => Clipboard.getDefault().selectItem(item).catch((err: Error) => {
console.error(`Runner(Plugin/Clipboard): An error occurred while selecting clipboard item. Stderr:\n${
err.message ? `${err.message}\n` : ""}Stack: ${err.stack}`
);
})
}));
}
} as Runner.Plugin;
-28
View File
@@ -1,28 +0,0 @@
import { Gtk } from "ags/gtk4";
import { Clipboard } from "../../scripts/clipboard";
import { ResultWidget } from "../../widget/runner/ResultWidget";
import { Runner } from "../Runner";
export const PluginClipboard = {
prefix: '>',
prioritize: true,
handle: (search) => {
if(Clipboard.getDefault().history.length < 1)
return <ResultWidget icon={"edit-paste-symbolic"} title={"Clipboard is empty"}
description={"Copy something and it will be shown right here!"}
/>;
return Clipboard.getDefault().history.filter(item => // not the best way to search, but it works
Runner.regExMatch(search, item.id) || Runner.regExMatch(search, item.preview)).map((item) =>
<ResultWidget icon={<Gtk.Label label={`${item.id}`}
css={"font-size: 16px; margin-right: 8px; font-weight: 600;"} />}
title={item.preview} onClick={() => Clipboard.getDefault().selectItem(item).catch((err: Error) => {
console.error(`Runner(Plugin/Clipboard): An error occurred while selecting clipboard item. Stderr:\n${
err.message ? `${err.message}\n` : ""}Stack: ${err.stack}`
);
})
}
/>);
}
} as Runner.Plugin;
+45 -48
View File
@@ -1,56 +1,53 @@
import { createBinding, createComputed } from "ags";
import { ResultWidget, ResultWidgetProps } from "../../widget/runner/ResultWidget";
import { Runner } from "../Runner";
import AstalMpris from "gi://AstalMpris";
import { player } from "../../widget/bar/Media";
import AstalMpris from "gi://AstalMpris";
export const PluginMedia = {
prefix: ":",
handle() {
if(!player.get().available) return new ResultWidget({
icon: "folder-music-symbolic",
title: "Couldn't find any players",
handle: () => !player.get().available ? {
icon: "folder-music-symbolic",
title: "Couldn't find any players",
closeOnClick: false,
description: "No media / player found with mpris"
} : [
{
icon: createBinding(player.get(), "playbackStatus").as((status) => status === AstalMpris.PlaybackStatus.PLAYING ?
"media-playback-pause-symbolic"
: "media-playback-start-symbolic"),
closeOnClick: false,
description: "No media / player found with mpris"
} as ResultWidgetProps);
return [
new ResultWidget({
icon: createBinding(player.get(), "playbackStatus").as((status) => status === AstalMpris.PlaybackStatus.PLAYING ?
"media-playback-pause-symbolic"
: "media-playback-start-symbolic"),
closeOnClick: false,
title: createComputed([
createBinding(player.get(), "title"),
createBinding(player.get(), "artist"),
createBinding(player.get(), "playbackStatus")
], (title, artist, status) => `${ status === AstalMpris.PlaybackStatus.PLAYING ?
"Pause" : "Play"
} ${title} | ${artist}`),
onClick: () => player.get().play_pause()
} as ResultWidgetProps),
new ResultWidget({
icon: "media-skip-backward-symbolic",
closeOnClick: false,
title: createComputed([
createBinding(player.get(), "title"),
createBinding(player.get(), "artist")
], (title, artist) =>
`Go Previous ${ title ? title : player.get().busName }${ artist ? ` | ${artist}` : "" }`
),
onClick: () => player.get().canGoPrevious && player.get().previous()
} as ResultWidgetProps),
new ResultWidget({
icon: "media-skip-forward-symbolic",
closeOnClick: false,
title: createComputed([
createBinding(player.get(), "title"),
createBinding(player.get(), "artist")
], (title, artist) =>
`Go Next ${ title ? title : player.get().busName }${ artist ? ` | ${artist}` : "" }`
),
onClick: () => player.get().canGoNext && player.get().next()
} as ResultWidgetProps)
]
},
title: createComputed([
createBinding(player.get(), "title"),
createBinding(player.get(), "artist"),
createBinding(player.get(), "playbackStatus")
], (title, artist, status) => `${ status === AstalMpris.PlaybackStatus.PLAYING ?
"Pause" : "Play"
} ${title} | ${artist}`),
actionClick: () => player.get().play_pause()
},
{
icon: "media-skip-backward-symbolic",
closeOnClick: false,
title: createComputed([
createBinding(player.get(), "title"),
createBinding(player.get(), "artist")
], (title, artist) =>
`Go Previous ${ title ? title : player.get().busName }${ artist ? ` | ${artist}` : "" }`
),
actionClick: () => player.get().canGoPrevious && player.get().previous()
},
{
icon: "media-skip-forward-symbolic",
closeOnClick: false,
title: createComputed([
createBinding(player.get(), "title"),
createBinding(player.get(), "artist")
], (title, artist) =>
`Go Next ${ title ? title : player.get().busName }${ artist ? ` | ${artist}` : "" }`
),
actionClick: () => player.get().canGoNext && player.get().next()
}
]
} as Runner.Plugin;
+4 -5
View File
@@ -1,4 +1,3 @@
import { ResultWidget, ResultWidgetProps } from "../../widget/runner/ResultWidget";
import { Runner } from "../Runner";
import { Notifications } from "../../scripts/notifications";
@@ -17,7 +16,7 @@ export const PluginShell = (() => {
return {
prefix: '!',
prioritize: true,
handle: (input: string): ResultWidget => {
handle: (input) => {
let showOutputNotif: boolean = false;
if(input.startsWith('!')) {
input = input.replace('!', "");
@@ -26,8 +25,8 @@ export const PluginShell = (() => {
const command = input ? GLib.shell_parse_argv(input) : undefined;
return new ResultWidget({
onClick: () => {
return {
actionClick: () => {
if(!command || !command[0]) return;
const proc = procLauncher.spawnv([ shell, "-c", `${input}` ]);
@@ -56,7 +55,7 @@ export const PluginShell = (() => {
title: `Run ${input ? ` \`${input}\`` : `with ${shell.split('/')[shell.split('/').length-1]}`}`,
description: (input || showOutputNotif) && `${input ? `${shell}\t` : ""}${ showOutputNotif ? "(showing output on notification)" : "" }`,
icon: "utilities-terminal-symbolic"
} as ResultWidgetProps)
};
}
} as Runner.Plugin
})();
+7 -7
View File
@@ -1,6 +1,5 @@
import { Wallpaper } from "../../scripts/wallpaper";
import { Runner } from "../Runner";
import { ResultWidget, ResultWidgetProps } from "../../widget/runner/ResultWidget";
import Gio from "gi://Gio?version=2.0";
@@ -26,18 +25,19 @@ class _PluginWallpapers implements Runner.Plugin {
handle(search: string) {
if(this.#files!.length > 0)
return this.#files!.filter(file => // not the best way to search, but it works
return this.#files!.filter(file =>
// also not the best way to search, but it works
Runner.regExMatch(search, file.split('/')[file.split('/').length-1])
).map(path => new ResultWidget({
).map(path => ({
title: path.split('/')[path.split('/').length-1].replace(/\..*$/, ""),
onClick: () => Wallpaper.getDefault().setWallpaper(path)
} as ResultWidgetProps));
actionClick: () => Wallpaper.getDefault().setWallpaper(path)
}));
return new ResultWidget({
return {
title: "No wallpapers found!",
description: "Define the $WALLPAPERS variable on Hyprland or create a ~/wallpapers directory",
icon: "image-missing-symbolic"
} as ResultWidgetProps);
};
}
}
+10 -12
View File
@@ -1,7 +1,7 @@
import AstalHyprland from "gi://AstalHyprland";
import { ResultWidget, ResultWidgetProps } from "../../widget/runner/ResultWidget";
import { Runner } from "../Runner";
const searchEngines = {
duckduckgo: "https://duckduckgo.com/?q=",
google: "https://google.com/search?q=",
@@ -15,15 +15,13 @@ export const PluginWebSearch = {
name: "Web Search",
prioritize: true,
handle: (search: string): ResultWidget => {
return new ResultWidget({
icon: "system-search-symbolic",
title: search || "Type your search...",
description: `Search the Web`,
onClick: () => AstalHyprland.get_default().dispatch(
"exec",
`xdg-open \"${engine + search}\"`
)
} as ResultWidgetProps);
}
handle: (search) => ({
icon: "system-search-symbolic",
title: search || "Type your search...",
description: `Search the Web`,
actionClick: () => AstalHyprland.get_default().dispatch(
"exec",
`xdg-open \"${engine + search}\"`
)
})
} as Runner.Plugin;