mirror of
https://github.com/Theaninova/TheaninovOS.git
synced 2025-12-12 11:36:20 +00:00
140 lines
3.8 KiB
TypeScript
140 lines
3.8 KiB
TypeScript
import { type Binding } from "lib/utils"
|
|
import PopupWindow, { Padding } from "widget/PopupWindow"
|
|
import icons from "lib/icons"
|
|
import options from "options"
|
|
import nix from "service/nix"
|
|
import * as AppLauncher from "./AppLauncher"
|
|
import * as NixRun from "./NixRun"
|
|
import * as ShRun from "./ShRun"
|
|
|
|
const { width, margin } = options.launcher
|
|
const isnix = nix.available
|
|
|
|
function Launcher() {
|
|
const favs = AppLauncher.Favorites()
|
|
const applauncher = AppLauncher.Launcher()
|
|
const sh = ShRun.ShRun()
|
|
const shicon = ShRun.Icon()
|
|
const nix = NixRun.NixRun()
|
|
const nixload = NixRun.Spinner()
|
|
|
|
function HelpButton(cmd: string, desc: string | Binding<string>) {
|
|
return Widget.Box(
|
|
{ vertical: true },
|
|
Widget.Separator(),
|
|
Widget.Button(
|
|
{
|
|
class_name: "help",
|
|
on_clicked: () => {
|
|
entry.grab_focus()
|
|
entry.text = `:${cmd} `
|
|
entry.set_position(-1)
|
|
},
|
|
},
|
|
Widget.Box([
|
|
Widget.Label({
|
|
class_name: "name",
|
|
label: `:${cmd}`,
|
|
}),
|
|
Widget.Label({
|
|
hexpand: true,
|
|
hpack: "end",
|
|
class_name: "description",
|
|
label: desc,
|
|
}),
|
|
]),
|
|
),
|
|
)
|
|
}
|
|
|
|
const help = Widget.Revealer({
|
|
child: Widget.Box(
|
|
{ vertical: true },
|
|
HelpButton("sh", "run a binary"),
|
|
isnix ? HelpButton("nx", options.launcher.nix.pkgs.bind().as(pkg =>
|
|
`run a nix package from ${pkg}`,
|
|
)) : Widget.Box(),
|
|
),
|
|
})
|
|
|
|
const entry = Widget.Entry({
|
|
hexpand: true,
|
|
primary_icon_name: icons.ui.search,
|
|
on_accept: ({ text }) => {
|
|
if (text?.startsWith(":nx"))
|
|
nix.run(text.substring(3))
|
|
else if (text?.startsWith(":sh"))
|
|
sh.run(text.substring(3))
|
|
else
|
|
applauncher.launchFirst()
|
|
|
|
App.toggleWindow("launcher")
|
|
entry.text = ""
|
|
},
|
|
on_change: ({ text }) => {
|
|
text ||= ""
|
|
favs.reveal_child = text === ""
|
|
help.reveal_child = text.split(" ").length === 1 && text?.startsWith(":")
|
|
|
|
if (text?.startsWith(":nx"))
|
|
nix.filter(text.substring(3))
|
|
else
|
|
nix.filter("")
|
|
|
|
if (text?.startsWith(":sh"))
|
|
sh.filter(text.substring(3))
|
|
else
|
|
sh.filter("")
|
|
|
|
if (!text?.startsWith(":"))
|
|
applauncher.filter(text)
|
|
},
|
|
})
|
|
|
|
function focus() {
|
|
entry.text = "Search"
|
|
entry.set_position(-1)
|
|
entry.select_region(0, -1)
|
|
entry.grab_focus()
|
|
favs.reveal_child = true
|
|
}
|
|
|
|
const layout = Widget.Box({
|
|
css: width.bind().as(v => `min-width: ${v}pt;`),
|
|
class_name: "launcher",
|
|
vertical: true,
|
|
vpack: "start",
|
|
setup: self => self.hook(App, (_, win, visible) => {
|
|
if (win !== "launcher")
|
|
return
|
|
|
|
entry.text = ""
|
|
if (visible)
|
|
focus()
|
|
}),
|
|
children: [
|
|
Widget.Box([entry, nixload, shicon]),
|
|
favs,
|
|
help,
|
|
applauncher,
|
|
nix,
|
|
sh,
|
|
],
|
|
})
|
|
|
|
return Widget.Box(
|
|
{ vertical: true, css: "padding: 1px" },
|
|
Padding("applauncher", {
|
|
css: margin.bind().as(v => `min-height: ${v}pt;`),
|
|
vexpand: false,
|
|
}),
|
|
layout,
|
|
)
|
|
}
|
|
|
|
export default () => PopupWindow({
|
|
name: "launcher",
|
|
layout: "top",
|
|
child: Launcher(),
|
|
})
|