summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-01-25 18:46:48 +1100
committer2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-01-25 18:46:48 +1100
commitb7fa29274af67df9f8d4fb9165bf54946b1d53ae (patch)
tree56a9d70ea03862fda8fb1cc9c040f0cf112d551d
parentmedia: make popdown window (diff)
downloadcaelestia-shell-b7fa29274af67df9f8d4fb9165bf54946b1d53ae.tar.gz
caelestia-shell-b7fa29274af67df9f8d4fb9165bf54946b1d53ae.tar.bz2
caelestia-shell-b7fa29274af67df9f8d4fb9165bf54946b1d53ae.zip
launcher: todo subcommand
Uses todoist
-rw-r--r--config.ts3
-rw-r--r--src/modules/launcher.tsx70
-rw-r--r--src/utils/system.ts22
3 files changed, 86 insertions, 9 deletions
diff --git a/config.ts b/config.ts
index 05f9ff7..fb85d7e 100644
--- a/config.ts
+++ b/config.ts
@@ -35,6 +35,9 @@ export const launcher = {
initialClass: 0.5,
},
},
+ todo: {
+ notify: true,
+ },
};
export const notifpopups = {
diff --git a/src/modules/launcher.tsx b/src/modules/launcher.tsx
index 143f988..e3d3463 100644
--- a/src/modules/launcher.tsx
+++ b/src/modules/launcher.tsx
@@ -1,4 +1,4 @@
-import { bind, execAsync, Gio, GLib, register, timeout, Variable } from "astal";
+import { bind, execAsync, Gio, GLib, readFile, register, timeout, Variable } from "astal";
import { App, Astal, Gtk, Widget } from "astal/gtk3";
import fuzzysort from "fuzzysort";
import type AstalApps from "gi://AstalApps";
@@ -7,7 +7,7 @@ import { launcher as config } from "../../config";
import { Apps } from "../services/apps";
import MathService, { type HistoryItem } from "../services/math";
import { getAppCategoryIcon } from "../utils/icons";
-import { launch } from "../utils/system";
+import { launch, notify } from "../utils/system";
import type { Client } from "../utils/types";
import { MenuItem, setupCustomTooltip } from "../utils/widgets";
import PopupWindow from "../widgets/popupwindow";
@@ -18,7 +18,7 @@ interface Subcommand {
icon: string;
name: string;
description: string;
- command: (...args: string[]) => void;
+ command: (...args: string[]) => boolean | void;
}
const getIconFromMode = (mode: Mode) => {
@@ -204,8 +204,7 @@ const SubcommandResult = ({
label={subcommand.name}
sublabel={subcommand.description}
onClicked={() => {
- subcommand.command(...args);
- entry.set_text("");
+ if (!subcommand.command(...args)) entry.set_text("");
}}
/>
);
@@ -477,9 +476,62 @@ const Results = ({ entry, mode }: { entry: Widget.Entry; mode: Variable<Mode> })
todo: {
icon: "checklist",
name: "Todo",
- description: "Create a todo in <INSERT_TODO_APP>",
+ description: "Create a todo in Todoist",
command: (...args) => {
- // TODO: todo service or maybe use external app
+ // If no args, autocomplete cmd
+ if (args.length === 0) {
+ entry.set_text(">todo ");
+ entry.set_position(-1);
+ return true;
+ }
+
+ if (!GLib.find_program_in_path("tod")) {
+ notify({
+ summary: "Tod not installed",
+ body: "The launcher todo subcommand requires `tod`. Install it with `yay -S tod-bin`",
+ icon: "dialog-warning-symbolic",
+ urgency: "critical",
+ });
+ return;
+ }
+
+ // If tod not configured, notify and exit
+ 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",
+ });
+ return;
+ }
+
+ // Create todo, notify and close
+ execAsync(`tod t q -c ${args.join(" ")}`).catch(console.error);
+ if (config.todo.notify)
+ 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 Gio.DesktopAppInfo.new("todoist.desktop")?.launch([], null);
+ },
+ },
+ });
+ close(self);
},
},
reload: {
@@ -689,8 +741,8 @@ export default class Launcher extends PopupWindow {
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(""));
+ // Clear search on hide if not in math mode or creating a todo
+ this.connect("hide", () => mode.get() !== "math" && !entry.text.startsWith(">todo") && entry.set_text(""));
this.connect("destroy", () => showResults.drop());
}
diff --git a/src/utils/system.ts b/src/utils/system.ts
index 5d77908..1c5f011 100644
--- a/src/utils/system.ts
+++ b/src/utils/system.ts
@@ -10,6 +10,28 @@ export const launch = (app: AstalApps.Application) => {
app.frequency++;
};
+export const notify = (props: {
+ summary: string;
+ body?: string;
+ icon?: string;
+ urgency?: "low" | "normal" | "critical";
+ transient?: boolean;
+ actions?: Record<string, () => void>;
+}) =>
+ execAsync([
+ "notify-send",
+ "-a",
+ "caelestia-shell",
+ ...(props.icon ? ["-i", props.icon] : []),
+ ...(props.urgency ? ["-u", props.urgency] : []),
+ ...(props.transient ? ["-e"] : []),
+ ...Object.keys(props.actions ?? {}).flatMap((k, i) => ["-A", `${i}=${k}`]),
+ props.summary,
+ ...(props.body ? [props.body] : []),
+ ])
+ .then(action => props.actions && Object.values(props.actions)[parseInt(action, 10)]?.())
+ .catch(console.error);
+
export const osId = GLib.get_os_info("ID") ?? "unknown";
export const osIdLike = GLib.get_os_info("ID_LIKE");
export const osIcon = String.fromCodePoint(