diff options
author | Aylur <[email protected]> | 2024-09-25 23:22:01 +0000 |
---|---|---|
committer | Aylur <[email protected]> | 2024-09-25 23:22:01 +0000 |
commit | 65654282a98cb0590d498e4ed9c924f97646f1dc (patch) | |
tree | 9150a8c5dca5429fafdd559db6e88f8bccb23d84 /docs/guide | |
parent | fd8c3e81d8f7b235be123ce592f89e72f65a0f05 (diff) |
docs: restructure
Diffstat (limited to 'docs/guide')
25 files changed, 3166 insertions, 0 deletions
diff --git a/docs/guide/ags/cli-app.md b/docs/guide/ags/cli-app.md new file mode 100644 index 0000000..ceed56a --- /dev/null +++ b/docs/guide/ags/cli-app.md @@ -0,0 +1,131 @@ +# CLI and App + +`App` is a singleton **instance** of [Astal.Application](https://aylur.github.io/libastal/class.Application.html). + +```ts +import { App } from "astal" +``` + +## Entry point + +:::code-group + +```ts [app.ts] +App.start({ + main() { + // setup anything + // instantiate widgets + }, +}) +``` + +::: + +:::warning +You can not instantiate widgets outside of the main function. +::: + +## Instance identifier + +You can run multiple instance by defining a unique instance name. + +```ts +App.start({ + instanceName: "my-instance", // defaults to "astal" + main() {}, +}) +``` + +## Messaging from CLI + +If you want to interact with an instance from the cli, you can do so by sending a message. + +```ts +App.start({ + main() {}, + requestHandler(request: string, res: (response: any) => void) { + if (request == "say hi") { + res("hi cli") + } + res("unknown command") + }, +}) +``` + +:::code-group + +```sh [ags] +ags -m "say hi" +# hi cli +``` + +```sh [astal] +astal say hi +# hi cli +``` + +::: + +If you want to run arbitrary JavaScript from cli, you can use `App.eval`. +It will evaluate the passed string as the body of an `async` function. + +```ts +App.start({ + main() {}, + requestHandler(js: string, res) { + App.eval(js).then(res).catch(res) + }, +}) +``` + +If the string does not contain a semicolon, a single expression is assumed and returned implicity. + +```sh +ags -m "'hello'" +# hello +``` + +If the string contains a semicolon, you have to return explicitly + +```sh +ags -m "'hello';" +# undefined + +ags -m "return 'hello';" +# hello +``` + +## App without AGS + +As mentioned before AGS is only a scaffolding tool. You can setup +a dev environment and a bundler yourself. In which case you won't be using +the ags cli to run the bundled scripts. The produced script can run as the main instance +and a "client" instance. + +The first time you run your bundled script the `main` function gets executed. +While that instance is running any subsequent execution of the script will call +the `client` function. + +:::code-group + +```ts [main.ts] +App.start({ + // main instance + main(...args: Array<string>) { + print(...args) + }, + + // every subsequent calls + client(message: (msg: string) => string, ...args: Array<string>) { + const res = message("you can message the main instance") + console.log(res) + }, + + // this runs in the main instance + requestHandler(request: string, res: (response: any) => void) { + res("response from main") + }, +}) +``` + +::: diff --git a/docs/guide/ags/faq.md b/docs/guide/ags/faq.md new file mode 100644 index 0000000..6edc250 --- /dev/null +++ b/docs/guide/ags/faq.md @@ -0,0 +1,290 @@ +# Frequently asked question, common issues, tips and tricks + +## Monitor id does not match compositor + +The monitor property that windows expect is mapped by Gdk, which is not always +the same as the compositor. Instead use the `gdkmonitor` property which expects +a `Gdk.Monitor` object which you can get from compositor libraries. + +Example with Hyprland + +```tsx +import Hyprland from "gi://AstalHyprland" + +function Bar(gdkmonitor) { + return <window gdkmonitor={gdkmonitor} /> +} + +function main() { + for (const m of Hyprland.get_default().get_monitors()) { + Bar(m.gdk_monitor) + } +} + +App.start({ main }) +``` + +## Environment variables + +JavaScript is **not** an bash. + +```ts +const HOME = exec("echo $HOME") // does not work +``` + +`exec` and `execAsync` runs the passed program as is, its **not** run in a +shell environment, so the above example just passes `$HOME` as a string literal +to the `echo` program. + +:::danger Please don't do this +You could pass it to bash, but that is a horrible approach. + +```ts +const HOME = exec("bash -c 'echo $HOME'") +``` + +::: + +You can read environment variables with [GLib.getenv](https://gjs-docs.gnome.org/glib20~2.0/glib.getenv). + +```ts +import GLib from "gi://GLib" + +const HOME = GLib.getenv("HOME") +``` + +## Custom svg symbolic icons + +Put the svgs in a directory, named `<icon-name>-symbolic.svg` +and use `App.add_icons` or `icons` parameter in `App.start` + +:::code-group + +```ts [app.ts] +App.start({ + icons: `${SRC}/icons`, + main() { + Widget.Icon({ + icon: "custom-symbolic", // custom-symbolic.svg + css: "color: green;", // can be colored, like other named icons + }) + }, +}) +``` + +::: + +:::info +If there is a name clash with an icon from your current icon pack +the icon pack will take precedence +::: + +## Logging + +The `console` API in gjs uses glib logging functions. +If you just want to print some text as is to stdout +use the globally available `print` function or `printerr` for stderr. + +```ts +print("print this line to stdout") +printerr("print this line to stderr") +``` + +## Binding custom structures + +The `bind` function can take two types of objects. + +```ts +interface Subscribable<T = unknown> { + subscribe(callback: (value: T) => void): () => void + get(): T +} + +interface Connectable { + connect(signal: string, callback: (...args: any[]) => unknown): number + disconnect(id: number): void +} +``` + +`Connectable` is for mostly gobjects, while `Subscribable` is for `Variables` +and custom objects. + +For example you can compose `Variables` in using a class. + +```ts +type MyVariableValue = { + number: number + string: string +} + +class MyVariable { + number = Variable(0) + string = Variable("") + + get(): MyVariableValue { + return { + number: this.number.get(), + string: this.string.get(), + } + } + + subscribe(callback: (v: MyVariableValue) => void) { + const unsub1 = this.number.subscribe((value) => { + callback({ string: value, number: this.number.get() }) + }) + + const unsub2 = this.string.subscribe((value) => { + callback({ number: value, string: this.string.get() }) + }) + + return () => { + unsub1() + unsub2() + } + } +} +``` + +Then it can be used with `bind`. + +```tsx +function MyWidget() { + const myvar = new MyVariable() + const label = bind(myvar).as(({ string, number }) => { + return `${string} ${number}` + }) + + return <label label={label} /> +} +``` + +## Populate the global scope with frequently accessed variables + +It might be annoying to always import Gtk only for `Gtk.Align` enums. + +:::code-group + +```ts [globals.ts] +import Gtk from "gi://Gtk" + +declare global { + const START: number + const CENTER: number + const END: number + const FILL: number +} + +Object.assign(globalThis, { + START: Gtk.Align.START, + CENTER: Gtk.Align.CENTER, + END: Gtk.Align.END, + FILL: Gtk.Align.FILL, +}) +``` + +::: + +:::code-group + +```tsx [Bar.tsx] +export default function Bar() { + return <window> + <box halign={START} /> + </window> +} +``` + +::: + +:::code-group + +```ts [app.ts] +import "./globals" +import Bar from "./Bar" + +App.start({ + main: Bar +}) +``` + +::: + +:::info +It is considered bad practice to populate the global scope, but its your code, not a public library. +::: + +## Auto create Window for each Monitor + +To have Window widgets appear on a monitor when its plugged in, listen to `App.monitor_added`. + +:::code-group + +```tsx [Bar.tsx] +export default function Bar(gdkmonitor: Gdk.Monitor) { + return <window gdkmonitor={gdkmonitor} /> +} +``` + +::: + +:::code-group + +```ts [app.ts] +import { Gdk, Gtk } from "astal" +import Bar from "./Bar" + +function main() { + const bars = new Map<Gdk.Monitor, Gtk.Widget>() + + // initialize + for (const gdkmonitor of App.get_monitors()) { + bars.set(gdkmonitor, Bar(gdkmonitor)) + } + + App.connect("monitor-added", (_, gdkmonitor) => { + bars.set(gdkmonitor, Bar(gdkmonitor)) + }) + + App.connect("monitor-removed", (_, gdkmonitor) => { + bars.get(gdkmonitor)?.destroy() + bars.delete(gdkmonitor) + }) +} + +App.start({ main }) +``` + +::: + +## Error: Can't convert non-null pointer to JS value + +These happen when accessing list type properties. Gjs fails to correctly bind +`List` and other array like types of Vala as a property. + +```ts +import Notifd from "gi://AstalNotifd" +const notifd = Notifd.get_default() + +notifd.notifications // ❌ // [!code error] + +notifd.get_notifications() // ✅ +``` + +## How to create regular floating windows + +Use `Gtk.Window` with [Widget.astalify](/guide/ags/widget#how-to-use-non-builtin-gtk-widgets). + +By default `Gtk.Window` is destroyed on close. To prevent this add a handler for `delete-event`. + +```tsx {4-7} +const RegularWindow = Widget.astalify(Gtk.Window) + +return <RegularWindow + onDeleteEvent={(self) => { + self.hide() + return true + }} +> + {child} +</RegularWindow> +``` diff --git a/docs/guide/ags/first-widgets.md b/docs/guide/ags/first-widgets.md new file mode 100644 index 0000000..2c64732 --- /dev/null +++ b/docs/guide/ags/first-widgets.md @@ -0,0 +1,400 @@ +# First Widgets + +AGS is the predecessor of Astal, which was written purely in TypeScript and so only supported +JavaScript/TypeScript. Now it serves as a scaffolding tool for Astal projects in TypeScript. +While what made AGS what it is, is now part of the Astal project, for simplicity we will +refer to the Astal TypeScript lib as AGS. + +:::tip +If you are not familiar with the JavaScript syntax [MDN](https://developer.mozilla.org/en-US/) +and [javascript.info](https://javascript.info/) have great references. +::: + +## Getting Started + +Start by initializing a project + +```sh +ags --init +``` + +then run `ags` in the terminal + +```sh +ags +``` + +Done! You have now a custom written bar using Gtk. + +:::tip +AGS will transpile every `.ts`, `.jsx` and `.tsx` files into regular javascript then +it will bundle everything into a single javascript file which then GJS can execute. +The bundler used is [esbuild](https://esbuild.github.io/). +::: + +## Root of every shell component: Window + +Astal apps are composed of widgets. A widget is a piece of UI that has its own logic and style. +A widget can be as small as a button or an entire bar. +The top level widget is always a [Window](https://aylur.github.io/libastal/class.Window.html) which will hold all widgets. + +::: code-group + +```tsx [widget/Bar.tsx] +function Bar(monitor = 0) { + return <window className="Bar" monitor={monitor}> + <box>Content of the widget</box> + </window> +} +``` + +::: + +::: code-group + +```ts [app.ts] +import Bar from "./widget/Bar" + +App.start({ + main() { + Bar(0) + Bar(1) // instantiate for each monitor + }, +}) +``` + +::: + +## Creating and nesting widgets + +Widgets are JavaScript functions which return Gtk widgets, +either by using JSX or using a widget constructor. + +:::code-group + +```tsx [MyButton.tsx] +function MyButton(): JSX.Element { + return <button onClicked="echo hello"> + Clicke Me! + </button> +} +``` + +```ts [MyButton.ts] +import { Widget } from "astal" + +function MyButton(): Widget.Button { + return Widget.Button({ + onClicked: "echo hello", + label: "Click Me!", + }) +} +``` + +::: + +:::info +The only difference between the two is the return type. +Using markup the return type is always `Gtk.Widget` (globally available as `JSX.Element`), +while using constructors the return type is the type of the widget. +It is rare to need the actual return type, so most if not all of the time, you can use markup. +::: + +Now that you have declared `MyButton`, you can nest it into another component. + +```tsx +function MyBar() { + return <window> + <box> + Click The button + <MyButton /> + </box> + </window> +} +``` + +Notice that widgets you defined start with a capital letter `<MyButton />`. +Lowercase tags are builtin widgets, while capital letter is for custom widgets. + +## Displaying Data + +JSX lets you put markup into JavaScript. +Curly braces let you “escape back” into JavaScript so that you can embed some variable +from your code and display it. + +```tsx +function MyWidget() { + const label = "hello" + + return <button>{label}</button> +} +``` + +You can also pass JavaScript to markup attributes + +```tsx +function MyWidget() { + const label = "hello" + + return <button label={label} /> +} +``` + +## Conditional Rendering + +You can use the same techniques as you use when writing regular JavaScript code. +For example, you can use an if statement to conditionally include JSX: + +```tsx +function MyWidget() { + let content + + if (condition) { + content = <True /> + } else { + content = <False /> + } + + return <box>{content}</box> +} +``` + +You can also inline a [conditional `?`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_operator) (ternary) expression. + +```tsx +function MyWidget() { + return <box>{condition ? <True /> : <False />}</box> +} +``` + +When you don’t need the `else` branch, you can also use a shorter [logical && syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND#short-circuit_evaluation): + +```tsx +function MyWidget() { + return <box>{condition && <True />}</box> +} +``` + +:::info +As you can guess from the above snippet, [falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) values are not rendered. +::: + +## Rendering lists + +You can use [`for` loops](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for) or [array `map()` function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map). + +```tsx +function MyWidget() { + const labels = [ + "label1" + "label2" + "label3" + ] + + return <box> + {labels.map(label => ( + <label label={label} /> + ))} + </box> +} +``` + +## Widget signal handlers + +You can respond to events by declaring event handler functions inside your widget: + +```tsx +function MyButton() { + function onClicked(self: Widget.Button, ...args) { + console.log(self, "was clicked") + } + + return <button onClicked={onClicked} /> +} +``` + +The handler can also be a string, which will get executed in a subprocess asynchronously. + +```tsx +function MyButton() { + return <button onClicked="echo hello" /> +} +``` + +:::info +Attributes prefixed with `on` will connect to a `signal` of the widget. +Their types are not generated, but written by hand, which means not all of them are typed. +Refer to the Gtk and Astal docs to have a full list of them. +::: + +## How properties are passed + +Using JSX, a custom widget will always have a single object as its parameter. + +```ts +type Props = { + myprop: string + child?: JSX.Element // when only one child is passed + children?: Array<JSX.Element> // when multiple children are passed +} + +function MyWidget({ myprop, child, children }: Props) { + // +} +``` + +```tsx +// child prop of MyWidget is the box +return <MyWidget myprop="hello"> + <box /> +</MyWidget> +``` + +```tsx +// children prop of MyWidget is [box, box, box] +return <MyWidget myprop="hello"> + <box /> + <box /> + <box /> +</MyWidget> +``` + +## State management + +The state of widgets are handled with Bindings. A `Binding` lets you +connect the state of one [GObject](https://docs.gtk.org/gobject/class.Object.html) to another, in our case it is used to +rerender part of a widget based on the state of a `GObject`. +A `GObject` can be a [Variable](./variable) or it can be from a [Library](../libraries/references). + +We use the `bind` function to create a `Binding` object from a `Variable` or +a regular GObject and one of its properties. + +Here is an example of a Counter widget that uses a `Variable` as its state: + +```tsx +import { Variable, bind } from "astal" + +function Counter() { + const count = Variable(0) + + function increment() { + count.set(count.get() + 1) + } + + return <box> + <label label={bind(count).as(num => num.toString())} /> + <button onClicked={increment}> + Click to increment + <button> + </box> +} +``` + +:::info +Bindings have an `.as()` method which lets you transform the assigned value. +In the case of a Label, its label property expects a string, so it needs to be +turned to a string first. +::: + +:::tip +`Variables` have a shorthand for `bind(variable).as(transform)` + +```tsx +const v = Variable(0) +const transform = (v) => v.toString() + +return <box> + {/* these two are equivalent */} + <label label={bind(v).as(transform)} /> + <label label={v(transform)} /> +</box> +``` + +::: + +Here is an example of a battery percent label that binds the `percentage` +property of the Battery object from the [Battery Library](/guide/libraries/battery): + +```tsx +import Battery from "gi://AstalBattery" +import { bind } from "astal" + +function BatteryPercentage() { + const bat = Battery.get_default() + + return <label label={bind(bat, "percentage").as((p) => p * 100 + " %")} /> +} +``` + +## Dynamic children + +You can also use a `Binding` for `child` and `children` properties. + +```tsx +const child = Variable(<box />) + +return <box>{child}</box> +``` + +```tsx +const num = Variable(3) +const range = (n) => [...Array(n).keys()] + +return <box> + {num(n => range(n).map(i => ( + <button> + {i.toString()} + <button/> + )))} +<box> +``` + +:::warning +Only bind children of the `box` or the `stack` widget. Gtk does not cleanup widgets by default, +they have to be explicitly destroyed. The box widget is a special container that +will implicitly call `.destroy()` on its removed child widgets. +You can disable this behavior by setting the `noImplicityDestroy` property. +::: + +:::info +The above example destroys and recreates every widget in the list everytime +the value of the `Variable` changes. There might be cases where you would +want to handle child creation yourself, because you don't want to lose the +inner state of widgets that does not need to be recreated. +::: + +When there is at least one `Binding` passed as a child, the `children` +parameter will always be a flattened `Binding<Array<JSX.Element>>` + +```tsx +function MyContainer({ children }) { + // children is a Binding over an Array of widgets +} + +return <MyContainer> + <box /> + {num(n => range(n).map(i => ( + <button> + {i.toString()} + <button/> + )))} + [ + [ + <button /> + ] + <button /> + ] +</MyContainer> +``` + +:::info +You can pass the followings as children: + +- widgets +- deeply nested arrays of widgets +- bindings of widgets, +- bindings of deeply nested arrays of widgets + +[falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) values are not rendered and anything not from this list +will be coerced into a string and rendered as a label +::: diff --git a/docs/guide/ags/installation.md b/docs/guide/ags/installation.md new file mode 100644 index 0000000..0adcf68 --- /dev/null +++ b/docs/guide/ags/installation.md @@ -0,0 +1,65 @@ +# Installation + +## Nix + +maintainer: [@Aylur](https://github.com/Aylur) + +Read more about it on the [nix page](../getting-started/nix#ags) + +You can try without installing. + +<!--TODO: remove v2 after merge--> +```sh +nix run github:aylur/ags/v2 -- --help +``` + +## Bulding AGS from source + +1. [Install Astal](../getting-started/installation.md) if you have not already + +2. Install the following dependencies + +:::code-group + +```sh [<i class="devicon-archlinux-plain"></i> Arch] +sudo pacman -Syu go npm gjs +``` + +```sh [<i class="devicon-fedora-plain"></i> Fedora] +sudo dnf install golang npm gjs +``` + +```sh [<i class="devicon-ubuntu-plain"></i> Ubuntu] +sudo apt install golang-go npm gjs +``` + +::: + +3. Clone the repo and Install + +<!--TODO: remove v2 after merge--> +```sh +git clone https://github.com/aylur/ags.git +cd ags/src +git checkout v2 # https://github.com/Aylur/ags/pull/504 +go install +``` + +:::info +If you have installed Astal **not** in `/usr` make sure to set its path. + +```sh +go install -ldflags "-X main.astalGjs=$(pkg-config --variable prefix astal-0.1)/share/astal/gjs" +``` + +::: + +:::tip +`go install` installs the `ags` binary to `$GOPATH/bin` so make sure its in your `$PATH`. +You can move it to another directory if you like. For example + +```sh +mv $GOPATH/bin/ags ~/.local/bin/ags +``` + +::: diff --git a/docs/guide/ags/theming.md b/docs/guide/ags/theming.md new file mode 100644 index 0000000..ea83e35 --- /dev/null +++ b/docs/guide/ags/theming.md @@ -0,0 +1,169 @@ +# Theming + +Since the widget toolkit is **GTK3** theming is done with **CSS**. + +- [CSS tutorial](https://www.w3schools.com/css/) +- [GTK CSS Overview wiki](https://docs.gtk.org/gtk3/css-overview.html) +- [GTK CSS Properties Overview wiki](https://docs.gtk.org/gtk3/css-properties.html) + +:::warning GTK is not the web +While most features are implemented in GTK, +you can't assume anything that works on the web will work with GTK. +Refer to the [GTK docs](https://docs.gtk.org/gtk3/css-overview.html) +to see what is available. +::: + +So far every widget you made used your default GTK3 theme. +To make them more custom, you can apply stylesheets to them. + +## From file at startup + +You can pass a path to a file or css as a string in `App.start` + +:::code-group + +```ts [app.ts] +const inlineCss = ` +window { + background-color: transparent; +} +` + +App.start({ + css: "./style.css", + css: `${SRC}/style.css'`, + css: inlineCss, +}) +``` + +::: + +:::info +The global `SRC` will point to the directory `app.ts` is in. +AGS will set the current working directory to `--config`, so `./style.css` also works. +::: + +## Css Property on Widgets + +```ts +Widget.Label({ + css: "color: blue; padding: 1em;", + label: "hello", +}) +``` + +:::info +The `css` property of a widget will not cascade to its children. +::: + +## Apply Stylesheets at Runtime + +You can apply additional styles at runtime. + +```ts +App.apply_css("/path/to/file.css") +``` + +```ts +App.apply_css(` +window { + background-color: transparent; +} +`) +``` + +```ts +App.reset_css() // reset if need +``` + +:::warning +`App.apply_css` will apply on top of other stylesheets applied before. +You can reset stylesheets with `App.resetCss` +::: + +## Inspector + +If you are not sure about the widget hierarchy or any CSS selector, +you can use the [GTK inspector](https://wiki.gnome.org/Projects/GTK/Inspector) + +```sh +# to bring up the inspector run +ags --inspector +``` + +## Using SCSS + +Gtk's CSS only supports a subset of what the web offers. +Most notably nested selectors are unsupported by Gtk, but this can be +workaround by using preprocessors like [SCSS](https://sass-lang.com/). + +:::code-group + +```sh [Arch] +sudo pacman -Syu dart-sass +``` + +```sh [Fedora] +npm install -g sass # not packaged on Fedora +``` + +```sh [Alpine] +sudo apk add dart-sass +``` + +```sh [Ubuntu] +npm install -g sass # not packaged on Ubuntu +``` + +```sh [openSUSE] +sudo zypper install dart-sass +``` + +::: + +Importing `scss` files will simply return transpiled css. + +:::code-group + +```ts [app.ts] +import style from "./style.scss" + +App.start({ + css: style, + main() {}, +}) +``` + +::: + +:::tip +If you for example want to set scss varibles from JS, +You can inline import, compose, and transpile yourself. + +```ts [app.ts] +import style1 from "inline:./style1.scss" +import style2 from "inline:./style2.scss" + +const tmpscss = "/tmp/style.scss" +const target = "/tmp/style.css" + +writeFile(tmpscss, ` + $var1: red; + $var1: blue; + ${style1} + ${style1} +`) + +exec(`sass ${tmpscss} ${target}`) + +App.start({ + css: target, +}) + +``` + +::: + +:::info +If you want other preprocessors support builtin open an Issue. +::: diff --git a/docs/guide/ags/utilities.md b/docs/guide/ags/utilities.md new file mode 100644 index 0000000..42589d3 --- /dev/null +++ b/docs/guide/ags/utilities.md @@ -0,0 +1,174 @@ +# Utilities + +## File functions + +Import them from `astal` or `astal/file` + +```ts +import { + readFile, + readFileAsync, + writeFile, + writeFileAsync, + monitorFile, +} from "astal" +``` + +### Reading files + +```ts +function readFile(path: string): string +function readFileAsync(path: string): Promise<string> +``` + +### Writing files + +```ts +function writeFile(path: string, content: string): void +function writeFileAsync(path: string, content: string): Promise<void> +``` + +### Monitoring files + +```ts +function monitorFile( + path: string, + callback: (file: string, event: Gio.FileMonitorEvent) => void, +): Gio.FileMonitor +``` + +## Timeouts and Intervals + +Import them from `astal` or `astal/time` + +```ts +import { interval, timeout, idle } from "astal" +``` + +You can use javascript native `setTimeout` or `setInterval` +they return a [GLib.Source](https://docs.gtk.org/glib/struct.Source.html) instance. +Alternatively you can use these functions provided by Astal, +which return an [Astal.Time](https://aylur.github.io/libastal/class.Time.html) instance. + +`Astal.Time` has a `now` signal and a `cancelled` signal. + +### Interval + +Will immediately execute the function and every `interval` millisecond. + +```ts +function interval(interval: number, callback?: () => void): Astal.Time +``` + +### Timeout + +Will execute the `callback` after `timeout` millisecond. + +```ts +function timeout(timeout: number, callback?: () => void): Astal.Time +``` + +### Idle + +Executes `callback` whenever there are no higher priority events pending. + +```ts +function idle(callback?: () => void): Astal.Time +``` + +Example: + +```ts +const timer = interval(1000, () => { + console.log("optional callback") +}) + +timer.connect("now", () => { + console.log("tick") +}) + +timer.connect("cancelled", () => { + console.log("cancelled") +}) + +timer.cancel() +``` + +## Process functions + +Import them from `astal` or `astal/proc` + +```ts +import { subprocess, exec, execAsync } from "astal" +``` + +### Subprocess + +You can start a subprocess and run callback functions whenever it outputs to +stdout or stderr. [Astal.Process](https://aylur.github.io/libastal/class.Process.html) has a `stdout` and `stderr` signal. + +```ts +function subprocess(args: { + cmd: string | string[] + out?: (stdout: string) => void + err?: (stderr: string) => void +}): Astal.Process + +function subprocess( + cmd: string | string[], + onOut?: (stdout: string) => void, + onErr?: (stderr: string) => void, +): Astal.Process +``` + +Example: + +```ts +const proc = subprocess( + "some-command", + (out) => console.log(out), // optional + (err) => console.error(out), // optional +) + +// or with signals +const proc = subprocess("some-command") +proc.connect("stdout", (out) => console.log(out)) +proc.connect("stderr", (err) => console.error(err)) +``` + +### Executing external commands and scripts + +```ts +function exec(cmd: string | string[]): string +function execAsync(cmd: string | string[]): Promise<string> +``` + +Example: + +```ts +try { + const out = exec("/path/to/script") + console.log(out) +} catch (err) { + console.error(err) +} + +execAsync(["bash", "-c", "/path/to/script.sh"]) + .then((out) => console.log(out)) + .catch((err) => console.error(err)) +``` + +:::warning +`subprocess`, `exec`, and `execAsync` executes the passed executable as is. +They are **not** executed in a shell environment, +they do **not** expand env variables like `$HOME`, +and they do **not** handle logical operators like `&&` and `||`. + +If you want bash, run them with bash. + +```ts +exec(["bash", "-c", "command $VAR && command"]) +exec("bash -c 'command $VAR' && command") +``` + +::: diff --git a/docs/guide/ags/variable.md b/docs/guide/ags/variable.md new file mode 100644 index 0000000..96e8d38 --- /dev/null +++ b/docs/guide/ags/variable.md @@ -0,0 +1,141 @@ +# Variable + +```js +import { Variable } from "astal" +``` + +Variable is just a simple `GObject` that holds a value. +And has shortcuts for hooking up subprocesses. + +:::info +The `Variable` object imported from the `"astal"` package is **not** [Astal.Variable](https://aylur.github.io/libastal/class.Variable.html). +::: + +## Variable as state + +```typescript +const myvar = Variable<string>("initial-value") + +// whenever its value changes, callback will be executed +myvar.subscribe((value: string) => { + console.log(value) +}) + +// settings its value +myvar.set("new value") + +// getting its value +const value = myvar.get() + +// binding them to widgets +Widget.Label({ + label: bind(myvar).as((value) => `transformed ${value}`), + label: myvar((value) => `transformed ${value}`), // shorthand for the above +}) +``` + +:::warning +Make sure to make the transform functions pure. The `.get()` function can be called +anytime by `astal` especially when `deriving`, so make sure there are no sideeffects. +::: + +## Composing variables + +Using `Variable.derive` we can compose both Variables and Bindings. + +```typescript +const v1: Variable<number> = Variable(2) +const v2: Variable<number> = Variable(3) + +// first argument is a list of dependencies +// second argument is a transform function, +// where the parameters are the values of the dependencies in the order they were passed +const v3: Variable<number> = Variable.derive([v1, v2], (v1, v2) => { + return v1 * v2 +}) + +const b1: Binding<string> = bind(obj, "prop") +const b2: Binding<string> = bind(obj, "prop") + +const b3: Variable<string> = Variable.derive([b1, b2], (b1, b2) => { + return `${b1}-${b2}` +}) +``` + +## Subprocess shorthands + +Using `.poll` and `.watch` we can start subprocess and capture their +output in `Variables`. They can poll and watch at the same time, but they +can only poll/watch one subprocess. + +:::warning +The command parameter is passed to [execAsync](/guide/ags/utilities#executing-external-commands-and-scripts) +which means they are **not** executed in a shell environment, +they do **not** expand env variables like `$HOME`, +and they do **not** handle logical operators like `&&` and `||`. + +If you want bash, run them with bash. + +```js +Variable("").poll(1000, ["bash", "-c", "command $VAR && command"]) +``` + +::: + +```typescript +const myVar = Variable<number>(0) + .poll(1000, "command", (out: string, prev: number) => parseInt(out)) + .poll(1000, ["bash", "-c", "command"], (out, prev) => parseInt(out)) + .poll(1000, (prev) => prev + 1) +``` + +```typescript +const myVar = Variable<number>(0) + .watch("command", (out: string, prev: number) => parseInt(out)) + .watch(["bash", "-c", "command"], (out, prev) => parseInt(out)) +``` + +You can temporarily stop them and restart them whenever. + +```js +myvar.stopWatch() // this kills the subprocess +myvar.stopPoll() + +myvar.startListen() // launches the subprocess again +myvar.startPoll() + +console.log(myvar.isListening()) +console.log(myvar.isPolling()) +``` + +## Gobject connection shorthands + +Using `.observe` you can connect gobject signals and capture their value. + +```typescript +const myvar = Variable("") + .observe(obj1, "signal", () => "") + .observe(obj2, "signal", () => "") +``` + +## Dispose if no longer needed + +This will stop the interval and force exit the subprocess and disconnect gobjects. + +```js +myVar.drop() +``` + +:::warning +Don't forget to drop them when they are defined inside widgets +with either `.poll`, `.watch` or `.observe` + +```tsx +function MyWidget() { + const myvar = Variable().poll() + + return <box onDestroy={() => myvar.drop()} /> +} +``` + +::: diff --git a/docs/guide/ags/widget.md b/docs/guide/ags/widget.md new file mode 100644 index 0000000..1fe755f --- /dev/null +++ b/docs/guide/ags/widget.md @@ -0,0 +1,227 @@ +# Widget + +## AGS widget properties + +These are properties that Astal.js additionally adds to Gtk.Widgets + +- className: `string` - List of class CSS selectors separated by white space. +- css: `string` - Inline CSS. e.g `label { color: white; }`. If no selector is specified `*` will be assumed. e.g `color: white;` will be inferred as `* { color: white; }`. +- cursor: `string` - Cursor style when hovering over widgets that have hover states, e.g it won't work on labels. [list of valid values](https://docs.gtk.org/gdk3/ctor.Cursor.new_from_name.html). +- clickThrough: `boolean` - Lets click events through. + +To have a full list of available properties, reference the documentation of the widget. + +- [Astal widgets](https://aylur.github.io/libastal/index.html#classes) +- [Gtk widgets](https://docs.gtk.org/gtk3/#classes) + +## AGS widget methods + +Additional methods that Astal.js adds to Gtk.Widget instances + +### setup + +`setup` is a convenience prop to not have predefine widgets before returning them + +without `setup` + +```tsx +function MyWidget() { + const button = Widget.Button() + // setup button + return button +} +``` + +using `setup` + +```tsx +function MyWidget() { + function setup(button: Widget.Button) { + // setup button + } + + return <buttons setup={setup} /> +} +``` + +### hook + +Shorthand for connection and disconnecting to gobjects. + +without `hook` + +```tsx +function MyWidget() { + const id = gobject.connect("signal", callback) + + return <box + onDestroy={() => { + gobject.disconnect(id) + }} + /> +} +``` + +with `hook` + +```tsx +function MyWidget() { + return <box + setup={(self) => { + self.hook(gobject, "signal", callback) + }} + /> +} +``` + +### toggleClassName + +Toggle classNames based on a condition + +```tsx +function MyWidget() { + return <box + setup={(self) => { + self.toggleClassName("classname", someCondition) + }} + /> +} +``` + +## How to use non builtin Gtk widgets + +Using `Widget.astalify` you can setup widget constructors to behave like builtin widgets. +The `astalify` function will apply the following: + +- set `visible` to true by default (Gtk3 widgets are invisible by default) +- make gobject properties accept and consume `Binding` objects +- add properties and methods listed above +- proxify the constructor so the `new` keyword is not needed +- sets up signal handlers that are passed as props prefixed with `on` + +```tsx +import { Widget, Gtk } from "astal" + +// define its props, constructor and type +export type ColorButtonProps = Widget.ConstructProps< + Gtk.ColorButton, + Gtk.ColorButton.ConstructorProps, + { onColorSet: [] } +> +export const ColorButton = Widget.astalify< + typeof Gtk.ColorButton, + ColorButtonProps, + "ColorButton" +>(Gtk.ColorButton) +export type ColorButton = ReturnType<typeof ColorButton> + +function MyWidget() { + function setup(button: ColorButton) {} + + return <ColorButton + setup={setup} + useAlpha + rgba={new Gdk.RGBA({ + red: 1, + green: 0, + blue: 0, + alpha: 0.5, + })} + onColorSet={(self) => { + console.log(self.rgba) + }} + /> +} +``` + +:::info +Signal properties have to be annotated manually for TypeScript. +You can reference [Gtk3](https://gjs-docs.gnome.org/gtk30~3.0/) +and [Astal](https://aylur.github.io/libastal/index.html#classes) for available signals. +::: + +:::tip + +As stated before children are passed as either `child` or `children` property, +when passing a container widget with `Widget.astalify` these rules still apply. +While subclasses of `Gtk.Bin` *can* take a `child` property in gjs, you might notice +a warning that it is deprecated. You can workaround this with a simple wrapper function. + +```tsx +const GtkFrame = Widget.astalify< + typeof Gtk.Frame, + FrameProps, + "Frame" +>(Gtk.Frame) + +export function Frame({ child, ...props }: FrameProps) { + const frame = GtkFrame(props) + frame.add(child) // use the widget's child adding function + return frame +} +``` + +::: + +## TypeScript + +Type of widgets are available through `Widget`. +Here is an example Widget that takes in and handles a possibly `Binding` prop. + +```tsx +import { Binding, Variable, Widget } from "astal" + +export interface ToggleButtonProps extends Widget.ButtonProps { + onToggled?: (self: Widget.Button, on: boolean) => void + state?: Binding<boolean> | boolean + child?: JSX.Element +} + +export default function ToggleButton(btnprops: ToggleButtonProps) { + const { state = false, onToggled, setup, child, ...props } = btnprops + const innerState = Variable(state instanceof Binding ? state.get() : state) + + return <button + {...props} + setup={self => { + setup?.(self) + + self.toggleClassName("active", innerState.get()) + self.hook(innerState, () => self.toggleClassName("active", innerState.get())) + + if (state instanceof Binding) { + self.hook(state, () => innerState.set(state.get())) + } + }} + onClicked={self => { + onToggled?.(self, !innerState.get()) + }} + > + {child} + </button> +} +``` + +## Builtin Widgets + +You can check the [source code](https://github.com/aylur/astal/blob/main/core/gjs/src/widgets.ts) to have a full list of builtin widgets. + +These widgets are available by default in JSX. + +- box: [Astal.Box](https://aylur.github.io/libastal/class.Box.html) +- button: [Astal.Button](https://aylur.github.io/libastal/class.Button.html) +- centerbox: [Astal.CenterBox](https://aylur.github.io/libastal/class.CenterBox.html) +- circularprogress: [Astal.CircularProgress](https://aylur.github.io/libastal/class.CircularProgress.html) +- drawingarea: [Gtk.DrawingArea](https://docs.gtk.org/gtk3/class.DrawingArea.html) +- entry: [Gtk.Entry](https://docs.gtk.org/gtk3/class.Entry.html) +- eventbox: [Astal.EventBox](https://aylur.github.io/libastal/class.EventBox.html) +- icon: [Astal.Icon](https://aylur.github.io/libastal/class.Icon.html) +- label: [Astal.Label](https://aylur.github.io/libastal/class.Label.html) +- levelbar: [Astal.LevelBar](https://aylur.github.io/libastal/class.LevelBar.html) +- overlay: [Astal.Overlay](https://aylur.github.io/libastal/class.Overlay.html) +- revealer: [Gtk.Revealer](https://docs.gtk.org/gtk3/class.Revealer.html) +- scrollable: [Astal.Scrollable](https://aylur.github.io/libastal/class.Scrollable.html) +- slider: [Astal.Slider](https://aylur.github.io/libastal/class.Slider.html) +- stack: [Astal.Stack](https://aylur.github.io/libastal/class.Stack.html) +- switch: [Gtk.Switch](https://docs.gtk.org/gtk3/class.Switch.html) +- window: [Astal.Window](https://aylur.github.io/libastal/class.Window.html) diff --git a/docs/guide/getting-started/installation.md b/docs/guide/getting-started/installation.md new file mode 100644 index 0000000..ef5c6e8 --- /dev/null +++ b/docs/guide/getting-started/installation.md @@ -0,0 +1,69 @@ +# Installation + +## Nix + +maintainer: [@Aylur](https://github.com/Aylur) + +Read more about it on the [nix page](./nix#astal) + +## Arch + +maintainer: [@kotontrion](https://github.com/kotontrion) + +:::code-group + +```sh [Core Library] +yay -S libastal-git +``` + +```sh [Every Library] +yay -S libastal-meta +``` + +::: + +## Bulding libastal from source + +1. Clone the repo + +```sh +git clone https://github.com/aylur/astal.git +cd astal/core +``` + +2. Install the following dependencies + +:::code-group + +```sh [<i class="devicon-archlinux-plain"></i> Arch] +sudo pacman -Syu meson vala gtk3 gtk-layer-shell gobject-introspection +``` + +```sh [<i class="devicon-fedora-plain"></i> Fedora] +sudo dnf install meson gcc valac gtk3-devel gtk-layer-shell-devel gobject-introspection-devel +``` + +```sh [<i class="devicon-ubuntu-plain"></i> Ubuntu] +sudo apt install meson valac libgtk3-dev libgtk-layer-shell-dev gobject-introspection +``` + +::: + +3. Build and install with `meson` + +```sh +meson setup build +meson install -C build +``` + +:::tip +Most distros recommend manual installs in `/usr/local`, +which is what `meson` defaults to. If you want to install to `/usr` +instead which most package managers do, set the `prefix` option: + +```sh +meson setup --prefix /usr build +meson install -C build +``` + +::: diff --git a/docs/guide/getting-started/introduction.md b/docs/guide/getting-started/introduction.md new file mode 100644 index 0000000..92d2ff1 --- /dev/null +++ b/docs/guide/getting-started/introduction.md @@ -0,0 +1,23 @@ +# Introduction + +## What is Astal? + +Astal (_meaning "desk"_) is a bundle of libraries built using [GLib](https://docs.gtk.org/glib/) in Vala and C. +The core library [libastal](https://aylur.github.io/libastal) has some Gtk widgets that come packaged, +the most important one is the [Window](https://aylur.github.io/libastal/class.Window.html) which is the main toplevel component using [gtk-layer-shell](https://github.com/wmww/gtk-layer-shell). +This is what allows us to use Gtk as shell components on Wayland. +libastal also comes with some utility functions such as running external processes, +reading, writing and monitoring files. + +## Why Astal? + +What makes Astal convenient to use is not the core library, as it could easily be replaced +by the standard library of any of your favorite language that has bindings to Gtk, it is the +accompanying libraries (_formerly known as "services" in AGS_) + +Have you ever wanted to write a custom bar, custom notification popups +or an applauncher, but gave up because writing a workspace widget, +implementing the notification daemon or handling a search filter was too much of a hassle? + +Astal libraries have you [covered](/guide/libraries/references), you don't have to worry about these, +you just define the layout, style it with CSS and that's it. diff --git a/docs/guide/getting-started/nix.md b/docs/guide/getting-started/nix.md new file mode 100644 index 0000000..2b04bdc --- /dev/null +++ b/docs/guide/getting-started/nix.md @@ -0,0 +1,154 @@ +# Nix + +## Astal + +Using Astal on Nix will require you to package your project. + +:::code-group + +```nix [<i class="devicon-lua-plain"></i> Lua] +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + astal = { + url = "github:aylur/astal"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { self, nixpkgs, astal }: let + system = "x86_64-linux"; + pkgs = nixpkgs.legacyPackages.${system}; + in { + packages.${system}.default = astal.lib.mkLuaPacakge { + inherit pkgs; + src = ./path/to/project; # should contain init.lua + + # add extra glib packages or binaries + extraPackages = [ + astal.packages.${system}.battery + pkgs.dart-sass + ]; + }; + }; +} +``` + +```nix [<i class="devicon-python-plain"></i> Python] +# Not documented yet +``` + +```nix [<i class="devicon-vala-plain"></i> Vala] +# keep in mind that this is just the nix derivation +# and you still have to use some build tool like meson +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + astal.url = "github:aylur/astal"; + }; + + outputs = { self, nixpkgs, astal }: let + system = "x86_64-linux"; + pkgs = nixpkgs.legacyPackages.${system}; + in { + packages.${system} = { + default = pkgs.stdenv.mkDerivation { + name = "my-shell"; + src = ./.; + + nativeBuildInputs = with pkgs; [ + meson + ninja + pkg-config + vala + gobject-introspection + ]; + + # add extra packages + buildInputs = [ + astal.packages.${system}.astal + ]; + }; + }; + }; +} +``` + +::: + +## AGS + +The recommended way to use AGS on NixOS is through the home-manager module. + +Example content of a `flake.nix` file that contains your `homeConfigurations`. + +<!--TODO: remove v2 after merge--> + +:::code-group + +```nix [<i class="devicon-nixos-plain"></i> flake.nix] +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + home-manager = { + url = "github:nix-community/home-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + # add ags https://github.com/Aylur/ags/pull/504 + ags.url = "github:aylur/ags/v2"; + }; + + outputs = { home-manager, nixpkgs, ... }@inputs: + let + system = "x86_64-linux"; + in + { + homeConfigurations."${username}" = home-manager.lib.homeManagerConfiguration { + pkgs = import nixpkgs { inherit system; }; + + # pass inputs as specialArgs + extraSpecialArgs = { inherit inputs; }; + + # import your home.nix + modules = [ ./home-manager/home.nix ]; + }; + }; +} +``` + +::: + +Example content of `home.nix` file + +:::code-group + +```nix [<i class="devicon-nixos-plain"></i> home.nix] +{ inputs, pkgs, ... }: +{ + # add the home manager module + imports = [ inputs.ags.homeManagerModules.default ]; + + programs.ags = { + enable = true; + configDir = ../ags; + + # additional packages to add to gjs's runtime + extraPackages = with pkgs; [ + inputs.ags.packages.${pkgs.system}.battery + fzf + ]; + }; +} +``` + +::: + +AGS by default only includes the core `libastal` library. +If you want to include any other [library](../libraries/references) you have to add them to `extraPackages`. +You can also add binaries which will be added to `$PATH`. + +:::warning +The `configDir` option symlinks the given path to `~/.config/ags`. +If you already have your source code there leave it as `null`. +::: diff --git a/docs/guide/getting-started/supported-languages.md b/docs/guide/getting-started/supported-languages.md new file mode 100644 index 0000000..634bad0 --- /dev/null +++ b/docs/guide/getting-started/supported-languages.md @@ -0,0 +1,65 @@ +# Supported Languages + +## JavaScript + +The main intended usage of Astal is in TypeScript with [AGS](/guide/ags/first-widgets). +It supports JSX and has a state management solution similar to web frameworks. +Only a minimal knowledge of JavaScript's syntax is needed to get started. + +:::info +The runtime is [GJS](https://gitlab.gnome.org/GNOME/gjs) and **not** nodejs +::: + +Examples: + +- [Simple Bar](https://github.com/Aylur/astal/tree/main/examples/js/simple-bar) + + +## Lua + +Similar to how there is a [TypeScript](https://github.com/Aylur/astal/tree/main/core/gjs) lib for GJS, there is also an accompanying library for [Lua](https://github.com/Aylur/astal/tree/main/core/lua). +<!--TODO: open issue and link performance issue--> +Unfortunately, I have encountered very heavy [performance issues](https://github.com/aylur/astal) with [lgi](https://github.com/lgi-devs/lgi), +and so currently I don't recommend using Lua for full desktop shells, but only for static +components that don't render child nodes dynamically, bars and panels for example. + +Examples: + +- [Simple Bar](https://github.com/Aylur/astal/tree/main/examples/lua/simple-bar) + + +## Python + +There was a WIP [library for python](https://github.com/aylur/astal/tree/feat/python), to make it behave similar to the above two +but I don't plan on finishing it, because I'm not a fan of python. +If you are interested in picking it up, feel free to open a PR. +Nonetheless you can still use python the OOP way [pygobject](https://pygobject.gnome.org/tutorials/gobject/subclassing.html) intended it. + +Examples: + +- [Starter Bar](https://github.com/Aylur/astal/tree/main/examples/py/starter-bar) + +## Vala + +Vala is a language that simply put uses C# syntax and compiles to C. +It is the language most of Astal is written in. I would still recommend +using TypeScript or Lua over Vala as they don't need a build step. + +Examples: + +- [Simple Bar](https://github.com/Aylur/astal/tree/main/examples/vala/simple-bar) + + +## C + +I don't recommend using C as it requires quite a lot of boilerplate, both for +build step and code. + +Examples: + +- TODO + +## Other languages + +There a few more that supports gobject-introspection, most notably Haskell, Rust and C++. +If you are interested and feel like contributing, PRs are welcome for bindings, and examples. diff --git a/docs/guide/libraries/apps.md b/docs/guide/libraries/apps.md new file mode 100644 index 0000000..c53daf0 --- /dev/null +++ b/docs/guide/libraries/apps.md @@ -0,0 +1,113 @@ +# Apps + +Library and CLI tool for querying and launching +applications that have a corresponding `.desktop` file. + +## Installation + +1. install dependencies + +:::code-group + +```sh [<i class="devicon-archlinux-plain"></i> Arch] +sudo pacman -Syu meson vala json-glib gobject-introspection +``` + +```sh [<i class="devicon-fedora-plain"></i> Fedora] +sudo dnf install meson gcc valac json-glib-devel gobject-introspection-devel +``` + +```sh [<i class="devicon-ubuntu-plain"></i> Ubuntu] +sudo apt install meson valac libjson-glib-dev gobject-introspection +``` + +::: + +2. clone repo + +```sh +git clone https://github.com/aylur/astal.git +cd astal/lib/apps +``` + +3. install + +```sh +meson setup build +meson install -C build +``` + +:::tip +Most distros recommend manual installs in `/usr/local`, +which is what `meson` defaults to. If you want to install to `/usr` +instead which most package managers do, set the `prefix` option: + +```sh +meson setup --prefix /usr build +``` + +::: + +## Usage + +You can browse the [Apps reference](https://aylur.github.io/libastal/apps). + +### CLI + +```sh +astal-apps --help +``` + +### Library + +:::code-group + +```js [<i class="devicon-javascript-plain"></i> JavaScript] +import Apps from "gi://AstalApps" + +const apps = new Apps.Apps({ + includeEntry: true, + includeExecutable: true, +}) + +for (const app of apps.fuzzy_query("spotify")) { + print(app.name) +} +``` + +```py [<i class="devicon-python-plain"></i> Python] +from gi.repository import AstalApps as Apps + +apps = Apps.Apps( + include_entry=True, + include_executable=True, +) + +for app in apps.fuzzy_query("obsidian"): + print(app.get_name()) + +``` + +```lua [<i class="devicon-lua-plain"></i> Lua] +local Apps = require("lgi").require("AstalApps") + +local apps = Apps.Apps({ + include_entry = true, + include_executable = true, +}) + +for _, app in ipairs(apps:fuzzy_query("lutris")) do + print(app.name) +end +``` + +```vala [<i class="devicon-vala-plain"></i> Vala] +// Not yet documented, contributions are appreciated +``` + +::: + +:::info +The fuzzy query uses [Levenshtein distance](https://en.wikipedia.org/wiki/Levenshtein_distance). I am not a mathematician, but if you know how to reimplement +the logic of [fzf](https://github.com/junegunn/fzf) to make it better feel free to open PRs. +::: diff --git a/docs/guide/libraries/auth.md b/docs/guide/libraries/auth.md new file mode 100644 index 0000000..1f07a17 --- /dev/null +++ b/docs/guide/libraries/auth.md @@ -0,0 +1,118 @@ +# Auth + +Library and CLI tool for authentication using [pam](https://github.com/linux-pam/linux-pam). + +## Installation + +1. install dependencies + +:::code-group + +```sh [<i class="devicon-archlinux-plain"></i> Arch] +sudo pacman -Syu meson pam gobject-introspection +``` + +```sh [<i class="devicon-fedora-plain"></i> Fedora] +sudo dnf install meson pam-devel gobject-introspection-devel +``` + +```sh [<i class="devicon-ubuntu-plain"></i> Ubuntu] +# Not yet documented +``` + +::: + +::: warning On NixOS you have to add `astal-auth` to `security.pam`. +::: code-group + +```nix [configuration.nix] +{ + security.pam.services.astal-auth = {} +} +``` + +::: + +2. clone repo + +```sh +git clone https://github.com/aylur/astal.git +cd astal/lib/auth +``` + +3. install + +```sh +meson setup build +meson install -C build +``` + +:::tip +Most distros recommend manual installs in `/usr/local`, +which is what `meson` defaults to. If you want to install to `/usr` +instead which most package managers do, set the `prefix` option: + +```sh +meson setup --prefix /usr build +``` + +::: + +## Usage + +You can browse the [Auth reference](https://aylur.github.io/libastal/auth). + +### CLI + +```sh +astal-auth --password my-password +``` + +### Library + +:::code-group + +```js [<i class="devicon-javascript-plain"></i> JavaScript] +import Auth from "gi://AstalAuth" + +Auth.Pam.authenticate("password", (_, task) => { + try { + AstalAuth.Pam.authenticate_finish(task) + print("authentication sucessful") + } catch (error) { + print(error) + } +}) +``` + +```py [<i class="devicon-python-plain"></i> Python] +from gi.repository import AstalAuth as Auth + +def callback(_, task) -> None: + try: + Auth.Pam.authenticate_finish(task) + print("success") + except Exception as e: + print(e) + +Auth.Pam.authenticate("password", callback) +``` + +```lua [<i class="devicon-lua-plain"></i> Lua] +local Auth = require("lgi").require("AstalAuth") + +Auth.Pam.authenticate("password", function(_, task) + local status, err = Auth.Pam.authenticate_finish(task) + if err ~= nil then + print(err) + else + print("success") + end +end) +``` + +```vala [<i class="devicon-vala-plain"></i> Vala] +// Not yet documented +``` + +::: diff --git a/docs/guide/libraries/battery.md b/docs/guide/libraries/battery.md new file mode 100644 index 0000000..b42d747 --- /dev/null +++ b/docs/guide/libraries/battery.md @@ -0,0 +1,97 @@ +# Battery + +Library and CLI tool for monitoring [upowerd](https://upower.freedesktop.org/) devices. + +## Installation + +1. install dependencies + +:::code-group + +```sh [<i class="devicon-archlinux-plain"></i> Arch] +sudo pacman -Syu meson vala json-glib gobject-introspection +``` + +```sh [<i class="devicon-fedora-plain"></i> Fedora] +sudo dnf install meson gcc valac json-glib-devel gobject-introspection-devel +``` + +```sh [<i class="devicon-ubuntu-plain"></i> Ubuntu] +sudo apt install meson valac libjson-glib-dev gobject-introspection +``` + +::: + +::: info +Although UPower is not a direct build dependency, +it should be self-explanatory that the daemon is required to be available at runtime. +::: + +2. clone repo + +```sh +git clone https://github.com/aylur/astal.git +cd astal/lib/battery +``` + +3. install + +```sh +meson setup build +meson install -C build +``` + +:::tip +Most distros recommend manual installs in `/usr/local`, +which is what `meson` defaults to. If you want to install to `/usr` +instead which most package managers do, set the `prefix` option: + +```sh +meson setup --prefix /usr build +``` + +::: + +## Usage + +You can browse the [Battery reference](https://aylur.github.io/libastal/battery). + +### CLI + +```sh +astal-battery --help +``` + +### Library + +:::code-group + +```js [<i class="devicon-javascript-plain"></i> JavaScript] +import Battery from "gi://AstalBattery" + +const battery = Battery.get_default() + +print(battery.percentage) +``` + +```py [<i class="devicon-python-plain"></i> Python] +from gi.repository import AstalBattery as Battery + +battery = Battery.get_default() + +print(battery.get_percentage()) +``` + +```lua [<i class="devicon-lua-plain"></i> Lua] +local Battery = require("lgi").require("AstalBattery") + +local battery = Battery.get_default() + +print(battery.percentage) +``` + +```vala [<i class="devicon-vala-plain"></i> Vala] +// Not yet documented +``` + +::: diff --git a/docs/guide/libraries/bluetooth.md b/docs/guide/libraries/bluetooth.md new file mode 100644 index 0000000..04d9db2 --- /dev/null +++ b/docs/guide/libraries/bluetooth.md @@ -0,0 +1,104 @@ +# Bluetooth + +Library for monitoring [bluez](https://www.bluez.org/) over dbus. + +## Installation + +1. install dependencies + +:::code-group + +```sh [<i class="devicon-archlinux-plain"></i> Arch] +sudo pacman -Syu meson vala gobject-introspection +``` + +```sh [<i class="devicon-fedora-plain"></i> Fedora] +sudo dnf install meson gcc valac gobject-introspection-devel +``` + +```sh [<i class="devicon-ubuntu-plain"></i> Ubuntu] +sudo apt install meson valac gobject-introspection +``` + +::: + +::: info +Although bluez is not a direct build dependency, +it should be self-explanatory that the daemon is required to be available at runtime. +::: + +2. clone repo + +```sh +git clone https://github.com/aylur/astal.git +cd astal/lib/bluetooth +``` + +3. install + +```sh +meson setup build +meson install -C build +``` + +:::tip +Most distros recommend manual installs in `/usr/local`, +which is what `meson` defaults to. If you want to install to `/usr` +instead which most package managers do, set the `prefix` option: + +```sh +meson setup --prefix /usr build +``` + +::: + +## Usage + +You can browse the [Bluetooth reference](https://aylur.github.io/libastal/bluetooth). + +### CLI + +There is no CLI for this library, use the one provided by bluez. + +```sh +bluetoothctl --help +``` + +### Library + +:::code-group + +```js [<i class="devicon-javascript-plain"></i> JavaScript] +import Bluetooth from "gi://AstalBluetooth" + +const bluetooth = Bluetooth.get_default() + +for (const device of bluetooth.get_devices()) { + print(device.name) +} +``` + +```py [<i class="devicon-python-plain"></i> Python] +from gi.repository import AstalBluetooth as Bluetooth + +bluetooth = Bluetooth.get_default() + +for device in bluetooth.get_devices(): + print(device.get_name()) +``` + +```lua [<i class="devicon-lua-plain"></i> Lua] +local Bluetooth = require("lgi").require("AstalBluetooth") + +local bluetooth = Bluetooth.get_default() + +for _, d in ipairs(bluetooth.devices) do + print(d.name) +end +``` + +```vala [<i class="devicon-vala-plain"></i> Vala] +// Not yet documented +``` + +::: diff --git a/docs/guide/libraries/hyprland.md b/docs/guide/libraries/hyprland.md new file mode 100644 index 0000000..faf9e50 --- /dev/null +++ b/docs/guide/libraries/hyprland.md @@ -0,0 +1,97 @@ +# Hyprland + +Library and CLI tool for monitoring the [Hyprland socket](https://wiki.hyprland.org/IPC/). + +## Installation + +1. install dependencies + +:::code-group + +```sh [<i class="devicon-archlinux-plain"></i> Arch] +sudo pacman -Syu meson vala json-glib gobject-introspection +``` + +```sh [<i class="devicon-fedora-plain"></i> Fedora] +sudo dnf install meson gcc valac json-glib-devel gobject-introspection-devel +``` + +```sh [<i class="devicon-ubuntu-plain"></i> Ubuntu] +sudo apt install meson valac libjson-glib-dev gobject-introspection +``` + +::: + +2. clone repo + +```sh +git clone https://github.com/aylur/astal.git +cd astal/lib/hyprland +``` + +3. install + +```sh +meson setup build +meson install -C build +``` + +:::tip +Most distros recommend manual installs in `/usr/local`, +which is what `meson` defaults to. If you want to install to `/usr` +instead which most package managers do, set the `prefix` option: + +```sh +meson setup --prefix /usr build +``` + +::: + +## Usage + +You can browse the [Hyprland reference](https://aylur.github.io/libastal/hyprland). + +### CLI + +```sh +astal-hyprland # starts monitoring +``` + +### Library + +:::code-group + +```js [<i class="devicon-javascript-plain"></i> JavaScript] +import Hyprland from "gi://AstalHyprland" + +const hyprland = Hyprland.get_default() + +for (const client of hyprland.get_clients()) { + print(client.title) +} +``` + +```py [<i class="devicon-python-plain"></i> Python] +from gi.repository import AstalHyprland as Hyprland + +hyprland = Hyprland.get_default() + +for client in hyprland.get_clients(): + print(client.get_title()) +``` + +```lua [<i class="devicon-lua-plain"></i> Lua] +local Hyprland = require("lgi").require("AstalHyprland") + +local hyprland = Hyprland.get_default() + +for _, c in ipairs(hyprland.clients) do + print(c.title) +end +``` + +```vala [<i class="devicon-vala-plain"></i> Vala] +// Not yet documented +``` + +::: diff --git a/docs/guide/libraries/mpris.md b/docs/guide/libraries/mpris.md new file mode 100644 index 0000000..dfe7956 --- /dev/null +++ b/docs/guide/libraries/mpris.md @@ -0,0 +1,100 @@ +# Mpris + +Library and CLI tool for interacting and monitoring media players +exposing an mpris interface through dbus. + +An alternative for [playerctl](https://github.com/altdesktop/playerctl) that better integrates +with astal. + +## Installation + +1. install dependencies + +:::code-group + +```sh [<i class="devicon-archlinux-plain"></i> Arch] +sudo pacman -Syu meson vala json-glib gobject-introspection +``` + +```sh [<i class="devicon-fedora-plain"></i> Fedora] +sudo dnf install meson gcc valac json-glib-devel gobject-introspection-devel +``` + +```sh [<i class="devicon-ubuntu-plain"></i> Ubuntu] +sudo apt install meson valac libjson-glib-dev gobject-introspection +``` + +::: + +2. clone repo + +```sh +git clone https://github.com/aylur/astal.git +cd astal/lib/mpris +``` + +3. install + +```sh +meson setup build +meson install -C build +``` + +:::tip +Most distros recommend manual installs in `/usr/local`, +which is what `meson` defaults to. If you want to install to `/usr` +instead which most package managers do, set the `prefix` option: + +```sh +meson setup --prefix /usr build +``` + +::: + +## Usage + +You can browse the [Mpris reference](https://aylur.github.io/libastal/mpris). + +### CLI + +```sh +astal-mpris --help +``` + +### Library + +:::code-group + +```js [<i class="devicon-javascript-plain"></i> JavaScript] +import Mpris from "gi://AstalMpris" + +const spotify = Mpris.Player.new("spotify") + +if (spotify.available) + print(spotify.title) +``` + +```py [<i class="devicon-python-plain"></i> Python] +from gi.repository import AstalMpris as Mpris + +spotify = Mpris.Player.new("spotify") + +if spotify.get_available(): + print(spotify.get_title()) +``` + +```lua [<i class="devicon-lua-plain"></i> Lua] +local Mpris = require("lgi").require("AstalMpris") + +local spotify = Mpris.Player.new("spotify") + +if spotify.available then + print(spotify.title) +end +``` + +```vala [<i class="devicon-vala-plain"></i> Vala] +// Not yet documented +``` + +::: diff --git a/docs/guide/libraries/network.md b/docs/guide/libraries/network.md new file mode 100644 index 0000000..afeb5d2 --- /dev/null +++ b/docs/guide/libraries/network.md @@ -0,0 +1,94 @@ +# Network + +Wrapper library over [networkmanager](https://networkmanager.dev/) to better integrate with Astal. + +## Installation + +1. install dependencies + +:::code-group + +```sh [<i class="devicon-archlinux-plain"></i> Arch] +sudo pacman -Syu meson vala libnm gobject-introspection +``` + +```sh [<i class="devicon-fedora-plain"></i> Fedora] +sudo dnf install meson gcc valac NetworkManager-libnm-devel gobject-introspection-devel +``` + +```sh [<i class="devicon-ubuntu-plain"></i> Ubuntu] +sudo apt install meson valac libnm-dev gobject-introspection +``` + +::: + +2. clone repo + +```sh +git clone https://github.com/aylur/astal.git +cd astal/lib/network +``` + +3. install + +```sh +meson setup build +meson install -C build +``` + +:::tip +Most distros recommend manual installs in `/usr/local`, +which is what `meson` defaults to. If you want to install to `/usr` +instead which most package managers do, set the `prefix` option: + +```sh +meson setup --prefix /usr build +``` + +::: + +## Usage + +You can browse the [Network reference](https://aylur.github.io/libastal/network). + +### CLI + +There is no CLI for this library, use the one provided by networkmanager. + +```sh +nmcli --help +``` + +### Library + +:::code-group + +```js [<i class="devicon-javascript-plain"></i> JavaScript] +import Network from "gi://AstalNetwork" + +const network = Network.get_default() + +print(network.wifi.ssid) +``` + +```py [<i class="devicon-python-plain"></i> Python] +from gi.repository import AstalNetwork as Network + +network = Network.get_default() + +print(network.get_wifi().get_ssid()) +``` + +```lua [<i class="devicon-lua-plain"></i> Lua] +local Network = require("lgi").require("AstalNetwork") + +local network = Network.get_default() + +print(network.wifi.ssid) +``` + +```vala [<i class="devicon-vala-plain"></i> Vala] +// Not yet documented +``` + +::: diff --git a/docs/guide/libraries/notifd.md b/docs/guide/libraries/notifd.md new file mode 100644 index 0000000..7e02149 --- /dev/null +++ b/docs/guide/libraries/notifd.md @@ -0,0 +1,106 @@ +# Notifd + +A [notification daemon](https://specifications.freedesktop.org/notification-spec/latest/) implementation as a library and CLI tool. + +## How it works + +The first instantiation of the [Notifd](https://aylur.github.io/libastal/notifd/class.Notifd.html) class will become the daemon and every subsequent instantiation will queue up to act as the daemon and will act as a client in the meantime. This means this library can be used throughout multiple processes. + +## Installation + +1. install dependencies + +:::code-group + +```sh [<i class="devicon-archlinux-plain"></i> Arch] +sudo pacman -Syu meson vala gdk-pixbuf2 json-glib gobject-introspection +``` + +```sh [<i class="devicon-fedora-plain"></i> Fedora] +sudo dnf install meson gcc valac gdk-pixbuf2-devel json-glib-devel gobject-introspection-devel +``` + +```sh [<i class="devicon-ubuntu-plain"></i> Ubuntu] +sudo apt install meson valac libgdk-pixbuf-2.0-dev libjson-glib-dev gobject-introspection +``` + +::: + +2. clone repo + +```sh +git clone https://github.com/aylur/astal.git +cd astal/lib/notifd +``` + +3. install + +```sh +meson setup build +meson install -C build +``` + +:::tip +Most distros recommend manual installs in `/usr/local`, +which is what `meson` defaults to. If you want to install to `/usr` +instead which most package managers do, set the `prefix` option: + +```sh +meson setup --prefix /usr build +``` + +::: + +## Usage + +You can browse the [Notifd reference](https://aylur.github.io/libastal/notifd). + +### CLI + +```sh +astal-notifd --help +``` + +### Library + +:::code-group + +```js [<i class="devicon-javascript-plain"></i> JavaScript] +import Notifd from "gi://AstalNotifd" + +const notifd = Notifd.get_default() + +notifd.connect("notified", (_, id) => { + const n = notifd.get_notification(id) + print(n.summary, n.body) +}) +``` + +```py [<i class="devicon-python-plain"></i> Python] +from gi.repository import AstalNotifd as Notifd + +notifd = Notifd.get_default() + +def on_notified(_, id): + n = notifd.get_notification(id) + print(n.get_body(), n.get_body()) + +notifd.connect("notified", on_notified) +``` + +```lua [<i class="devicon-lua-plain"></i> Lua] +local Notifd = require("lgi").require("AstalNotifd") + +local notifd = Notifd.get_default() + +notifd.on_notified = function(_, id) + local n = notifd.get_notification(id) + print(n.body, n.summary) +end +``` + +```vala [<i class="devicon-vala-plain"></i> Vala] +// Not yet documented +``` + +::: diff --git a/docs/guide/libraries/powerprofiles.md b/docs/guide/libraries/powerprofiles.md new file mode 100644 index 0000000..8571c29 --- /dev/null +++ b/docs/guide/libraries/powerprofiles.md @@ -0,0 +1,97 @@ +# Power Profiles + +Library and CLI tool for monitoring [upowerd](https://upower.freedesktop.org/) powerprofiles. + +## Installation + +1. install dependencies + +:::code-group + +```sh [<i class="devicon-archlinux-plain"></i> Arch] +sudo pacman -Syu meson vala json-glib gobject-introspection +``` + +```sh [<i class="devicon-fedora-plain"></i> Fedora] +sudo dnf install meson gcc valac json-glib-devel gobject-introspection-devel +``` + +```sh [<i class="devicon-ubuntu-plain"></i> Ubuntu] +sudo apt install meson valac libjson-glib-dev gobject-introspection +``` + +::: + +::: info +Although UPower is not a direct build dependency, +it should be self-explanatory that the daemon is required to be available at runtime. +::: + +2. clone repo + +```sh +git clone https://github.com/aylur/astal.git +cd astal/lib/powerprofiles +``` + +3. install + +```sh +meson setup build +meson install -C build +``` + +:::tip +Most distros recommend manual installs in `/usr/local`, +which is what `meson` defaults to. If you want to install to `/usr` +instead which most package managers do, set the `prefix` option: + +```sh +meson setup --prefix /usr build +``` + +::: + +## Usage + +You can browse the [PowerProfiles reference](https://aylur.github.io/libastal/powerprofiles). + +### CLI + +```sh +astal-power-profiles --help +``` + +### Library + +:::code-group + +```js [<i class="devicon-javascript-plain"></i> JavaScript] +import PowerProfiles from "gi://AstalPowerProfiles" + +const powerprofiles = PowerProfiles.get_default() + +print(powerprofiles.activeProfile) +``` + +```py [<i class="devicon-python-plain"></i> Python] +from gi.repository import AstalPowerProfiles as PowerProfiles + +powerprofiles = PowerProfiles.get_default() + +print(powerprofiles.get_active_profile()) +``` + +```lua [<i class="devicon-lua-plain"></i> Lua] +local PowerProfiles = require("lgi").require("AstalPowerProfiles") + +local powerprofiles = PowerProfiles.get_default() + +print(powerprofiles.active_profile) +``` + +```vala [<i class="devicon-vala-plain"></i> Vala] +// Not yet documented +``` + +::: diff --git a/docs/guide/libraries/references.md b/docs/guide/libraries/references.md new file mode 100644 index 0000000..8f2bd02 --- /dev/null +++ b/docs/guide/libraries/references.md @@ -0,0 +1,44 @@ +# References + +The references of the libraries are annotated for the C language. +Reading their documentation will vary depending on the language they are used in. + +<!--TODO: list some examples on how to read docs,--> +<!--for example the difference between C enums and gjs enums--> + +## Additional references + +### GJS + +- [gjs-docs.gnome.org](https://gjs-docs.gnome.org/): Library references annotated for GJS +- [gjs.guide](https://gjs.guide/): GJS and GObject guide + +### Python + +- [pgi-docs](https://lazka.github.io/pgi-docs/): Library references annotated for Python +- [pygobject.gnome.org](https://pygobject.gnome.org/): PyGObject reference and guide + +### Lua + +- [lua-lgi docs](https://github.com/lgi-devs/lgi/tree/master/docs): GObject bindings guide for Lua + +### Vala + +- [vala.dev](https://vala.dev/): Guide for the Vala language +- [valadoc.org](https://valadoc.org/): Library references annotated for Vala + +## Astal Libraries + +- [Astal](https://aylur.github.io/libastal): libastal the core library, which has the widgets and utilites +- [Apps](https://aylur.github.io/libastal/apps): Library and cli tool for querying applications +- [Auth](https://aylur.github.io/libastal/auth): Authentication library using PAM +- [Battery](https://aylur.github.io/libastal/battery): DBus proxy library for upower daemon +- [Bluetooth](https://aylur.github.io/libastal/bluetooth): Library to control bluez over dbus +- [Hyprland](https://aylur.github.io/libastal/hyprland): Library and cli tool for Hyprland IPC socket +- [Mpris](https://aylur.github.io/libastal/mpris): Library and cli tool for controlling media players +- [Network](https://aylur.github.io/libastal/network): NetworkManager wrapper library +- [Notifd](https://aylur.github.io/libastal/notifd): A notification daemon library and cli tool +- [PowerProfiles](https://aylur.github.io/libastal/powerprofiles): Library and cli to control upowerd powerprofiles +- [River](https://aylur.github.io/libastal/river): Library and cli tool for getting status information of the river wayland compositor +- [Tray](https://aylur.github.io/libastal/tray): A systemtray library and cli tool +- [WirePlumber](https://aylur.github.io/libastal/wireplumber): A library for audio control using wireplumber diff --git a/docs/guide/libraries/river.md b/docs/guide/libraries/river.md new file mode 100644 index 0000000..4818d0b --- /dev/null +++ b/docs/guide/libraries/river.md @@ -0,0 +1,97 @@ +# River + +Library and CLI tool for monitoring the [River Wayland Compositor](https://isaacfreund.com/software/river/). + +## Installation + +1. install dependencies + +:::code-group + +```sh [<i class="devicon-archlinux-plain"></i> Arch] +sudo pacman -Syu meson json-glib gobject-introspection +``` + +```sh [<i class="devicon-fedora-plain"></i> Fedora] +sudo dnf install meson gcc json-glib-devel gobject-introspection-devel +``` + +```sh [<i class="devicon-ubuntu-plain"></i> Ubuntu] +sudo apt install meson libjson-glib-dev gobject-introspection +``` + +::: + +2. clone repo + +```sh +git clone https://github.com/aylur/astal.git +cd astal/lib/river +``` + +3. install + +```sh +meson setup build +meson install -C build +``` + +:::tip +Most distros recommend manual installs in `/usr/local`, +which is what `meson` defaults to. If you want to install to `/usr` +instead which most package managers do, set the `prefix` option: + +```sh +meson setup --prefix /usr build +``` + +::: + +## Usage + +You can browse the [River reference](https://aylur.github.io/libastal/river). + +### CLI + +```sh +astal-river --help +``` + +### Library + +:::code-group + +```js [<i class="devicon-javascript-plain"></i> JavaScript] +import River from "gi://AstalRiver" + +const river = River.get_default() + +for (const output of river.get_outputs()) { + print(output.name) +} +``` + +```py [<i class="devicon-python-plain"></i> Python] +from gi.repository import AstalRiver as River + +river = River.get_default() + +for output in river.get_outputs(): + print(output.get_name()) +``` + +```lua [<i class="devicon-lua-plain"></i> Lua] +local River = require("lgi").require("AstalRiver") + +local river = River.River.get_default() + +for _, o in ipairs(river.outputs) do + print(o.name) +end +``` + +```vala [<i class="devicon-vala-plain"></i> Vala] +// Not yet documented +``` + +::: diff --git a/docs/guide/libraries/tray.md b/docs/guide/libraries/tray.md new file mode 100644 index 0000000..c8d093b --- /dev/null +++ b/docs/guide/libraries/tray.md @@ -0,0 +1,97 @@ +# Tray + +Library for managing the systemtray by implementing the [StatusNotifierItem](https://www.freedesktop.org/wiki/Specifications/StatusNotifierItem/) protocol. + +## Installation + +1. install dependencies + +:::code-group + +```sh [<i class="devicon-archlinux-plain"></i> Arch] +sudo pacman -Syu meson gtk3 gobject-introspection libdbusmenu-gtk3 +``` + +```sh [<i class="devicon-fedora-plain"></i> Fedora] +sudo dnf install meson gcc gtk3-devel libdbusmenu-gtk3 gobject-introspection-devel +``` + +```sh [<i class="devicon-ubuntu-plain"></i> Ubuntu] +sudo apt install meson libgtk-3-dev libdbusmenu-gtk3-dev gobject-introspection +``` + +::: + +2. clone repo + +```sh +git clone https://github.com/aylur/astal.git +cd astal/lib/tray +``` + +3. install + +```sh +meson setup build +meson install -C build +``` + +:::tip +Most distros recommend manual installs in `/usr/local`, +which is what `meson` defaults to. If you want to install to `/usr` +instead which most package managers do, set the `prefix` option: + +```sh +meson setup --prefix /usr build +``` + +::: + +## Usage + +You can browse the [Tray reference](https://aylur.github.io/libastal/tray). + +### CLI + +```sh +astal-tray --help +``` + +### Library + +:::code-group + +```js [<i class="devicon-javascript-plain"></i> JavaScript] +import Tray from "gi://AstalTray" + +const tray = Tray.get_default() + +for (const item of tray.get_items()) { + print(item.title) +} +``` + +```py [<i class="devicon-python-plain"></i> Python] +from gi.repository import AstalTray as Tray + +tray = Tray.get_default() + +for item in tray.get_items(): + print(item.title) +``` + +```lua [<i class="devicon-lua-plain"></i> Lua] +local Tray = require("lgi").require("AstalTray") + +local tray = Tray.get_default() + +for _, i in ipairs(tray.items) do + print(i.title) +end +``` + +```vala [<i class="devicon-vala-plain"></i> Vala] +// Not yet documented +``` + +::: diff --git a/docs/guide/libraries/wireplumber.md b/docs/guide/libraries/wireplumber.md new file mode 100644 index 0000000..5f1daab --- /dev/null +++ b/docs/guide/libraries/wireplumber.md @@ -0,0 +1,94 @@ +# Wire Plumber + +Wrapper library over [wireplumber](https://pipewire.pages.freedesktop.org/wireplumber/) to better integrate with Astal. + +## Installation + +1. install dependencies + +:::code-group + +```sh [<i class="devicon-archlinux-plain"></i> Arch] +sudo pacman -Syu meson vala wireplumber gobject-introspection +``` + +```sh [<i class="devicon-fedora-plain"></i> Fedora] +sudo dnf install meson gcc valac wireplumber-devel gobject-introspection-devel +``` + +```sh [<i class="devicon-ubuntu-plain"></i> Ubuntu] +# Not yet documented +``` + +::: + +2. clone repo + +```sh +git clone https://github.com/aylur/astal.git +cd astal/lib/wireplumber +``` + +3. install + +```sh +meson setup build +meson install -C build +``` + +:::tip +Most distros recommend manual installs in `/usr/local`, +which is what `meson` defaults to. If you want to install to `/usr` +instead which most package managers do, set the `prefix` option: + +```sh +meson setup --prefix /usr build +``` + +::: + +## Usage + +You can browse the [Wireplumber reference](https://aylur.github.io/libastal/wireplumber). + +### CLI + +There is no CLI for this library, use the one provided by wireplumber. + +```sh +wpctl --help +``` + +### Library + +:::code-group + +```js [<i class="devicon-javascript-plain"></i> JavaScript] +import Wp from "gi://AstalWp" + +const audio = Wp.get_default().audio + +print(audio.default_speaker.volume) +``` + +```py [<i class="devicon-python-plain"></i> Python] +from gi.repository import AstalWp as Wp + +audio = Wp.get_default().get_audio() + +print(audio.get_default_speaker().get_volume()) +``` + +```lua [<i class="devicon-lua-plain"></i> Lua] +local Wp = require("lgi").require("AstalWp") + +local audio = Wp.get_default().audio + +print(audio.default_speaker.volume) +``` + +```vala [<i class="devicon-vala-plain"></i> Vala] +// Not yet documented +``` + +::: |