summaryrefslogtreecommitdiff
path: root/docs/src
diff options
context:
space:
mode:
authorAylur <[email protected]>2024-09-01 15:23:08 +0200
committerAylur <[email protected]>2024-09-01 15:23:08 +0200
commit6c4753b5e14a50c617e326d97d270ff51156a183 (patch)
tree560ffd167d6b4f1da04e545990666c447c15e35b /docs/src
parent27c25b0e6070f291b33107a0bbfe5086b6d3f119 (diff)
initial docs
Diffstat (limited to 'docs/src')
-rw-r--r--docs/src/assets/front-image.pngbin0 -> 138489 bytes
-rw-r--r--docs/src/components/Showcase.astro37
-rw-r--r--docs/src/content/config.ts6
-rw-r--r--docs/src/content/docs/ags/cli-app.md129
-rw-r--r--docs/src/content/docs/ags/faq.md80
-rw-r--r--docs/src/content/docs/ags/first-widgets.mdx429
-rw-r--r--docs/src/content/docs/ags/libraries.md8
-rw-r--r--docs/src/content/docs/ags/theming.md113
-rw-r--r--docs/src/content/docs/ags/utilities.md8
-rw-r--r--docs/src/content/docs/ags/variable.md8
-rw-r--r--docs/src/content/docs/ags/widget.md124
-rw-r--r--docs/src/content/docs/getting-started/installation.mdx92
-rw-r--r--docs/src/content/docs/getting-started/introduction.md28
-rw-r--r--docs/src/content/docs/getting-started/languages.md64
-rw-r--r--docs/src/content/docs/getting-started/nix.md8
-rw-r--r--docs/src/content/docs/index.mdx67
-rw-r--r--docs/src/content/docs/libraries/overview.md8
-rw-r--r--docs/src/content/showcases.ts13
-rw-r--r--docs/src/env.d.ts2
-rw-r--r--docs/src/style.css72
20 files changed, 1296 insertions, 0 deletions
diff --git a/docs/src/assets/front-image.png b/docs/src/assets/front-image.png
new file mode 100644
index 0000000..0fbfcca
--- /dev/null
+++ b/docs/src/assets/front-image.png
Binary files differ
diff --git a/docs/src/components/Showcase.astro b/docs/src/components/Showcase.astro
new file mode 100644
index 0000000..7eea178
--- /dev/null
+++ b/docs/src/components/Showcase.astro
@@ -0,0 +1,37 @@
+---
+interface Props {
+ src: string
+ author: string
+ url: string
+}
+
+const { src, author, url } = Astro.props
+---
+
+<figure>
+ <a href={url}>
+ <img src={src} alt={author}>
+ </a>
+ <figcaption>
+ <span>Author: {author}</span>
+ </figcaption>
+</figure>
+
+<style>
+img {
+ border-radius: calc(var(--card-radius) - 8px);
+}
+
+figure {
+ padding: .8rem;
+ padding-bottom: 0;
+ border-radius: var(--card-radius);
+ background-color: color-mix(in srgb, var(--sl-color-black), var(--sl-color-white) 2%);
+ border: 1px solid var(--sl-color-gray-6);
+}
+
+figcaption {
+ text-align: center;
+ margin: 0;
+}
+</style>
diff --git a/docs/src/content/config.ts b/docs/src/content/config.ts
new file mode 100644
index 0000000..45f60b0
--- /dev/null
+++ b/docs/src/content/config.ts
@@ -0,0 +1,6 @@
+import { defineCollection } from 'astro:content';
+import { docsSchema } from '@astrojs/starlight/schema';
+
+export const collections = {
+ docs: defineCollection({ schema: docsSchema() }),
+};
diff --git a/docs/src/content/docs/ags/cli-app.md b/docs/src/content/docs/ags/cli-app.md
new file mode 100644
index 0000000..6b9d4c3
--- /dev/null
+++ b/docs/src/content/docs/ags/cli-app.md
@@ -0,0 +1,129 @@
+---
+title: CLI and App
+description: Reference of the singleton App instance
+sidebar:
+ order: 3
+---
+
+`App` is a singleton **instance** of [Astal.Application](/libastal/class.Application.html).
+
+```tsx
+import { App } from "astal"
+```
+
+## Entry point
+
+```tsx
+// app.ts
+App.start({
+ main() {
+ // setup anything
+ // instantiate widgets
+ },
+})
+```
+
+:::caution
+You can not instantiate widgets outside of the main function.
+:::
+
+## Instance identifier
+
+You can run multiple instance by defining a unique instance name.
+
+```tsx
+// app.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.ts
+App.start({
+ main() {},
+ requestHandler(request: string, res: (response: any) => void) {
+ if (request == "say hi") {
+ res("hi cli")
+ }
+ res("unknown command")
+ },
+})
+```
+
+```bash
+# ags cli
+$ ags -m "say hi"
+hi cli
+
+# astal cli
+$ 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.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.
+
+```bash
+$ ags -m "'hello'"
+hello
+```
+
+If the string contains a semicolon, you have to return explicitly
+
+```bash
+$ 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.
+
+```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/src/content/docs/ags/faq.md b/docs/src/content/docs/ags/faq.md
new file mode 100644
index 0000000..eca31c7
--- /dev/null
+++ b/docs/src/content/docs/ags/faq.md
@@ -0,0 +1,80 @@
+---
+title: FAQ
+description: 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
+
+```js
+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.
+
+```tsx
+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.
+
+:::caution[Please don't do this]
+You could pass it to bash, but that is a horrible approach.
+
+```tsx
+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).
+
+```tsx
+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`
+
+```js
+// 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
+ })
+ },
+})
+```
+
+:::note
+If there is a name clash with an icon from your current icon pack
+the icon pack will take precedence
+:::
diff --git a/docs/src/content/docs/ags/first-widgets.mdx b/docs/src/content/docs/ags/first-widgets.mdx
new file mode 100644
index 0000000..3feb498
--- /dev/null
+++ b/docs/src/content/docs/ags/first-widgets.mdx
@@ -0,0 +1,429 @@
+---
+title: First Widgets
+description: Getting started with creating widgets
+sidebar:
+ order: 0
+---
+
+import { FileTree } from "@astrojs/starlight/components"
+import { Tabs, TabItem } from "@astrojs/starlight/components"
+
+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.
+
+## JavaScript
+
+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
+
+```bash
+ags --init
+```
+
+then run `ags` in the terminal
+
+```bash
+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/).
+:::
+
+The generated project structure is the following:
+
+<FileTree>
+
+- @girs/ generated types with [ts-for-gir](https://github.com/gjsify/ts-for-gir)
+- widget
+ - Bar.tsx
+- app.ts entry point of our app
+- style.css
+- env.d.ts informs the LSP about the environment
+- tsconfig.json configuration of the LSP
+
+</FileTree>
+
+## 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]() which will hold all widgets.
+
+```tsx
+// widget/Bar.tsx
+function Bar(monitor = 0) {
+ return (
+ <window className="Bar" monitor={monitor}>
+ <box>Content of the widget</box>
+ </window>
+ )
+}
+```
+
+```tsx
+// 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.
+
+<Tabs>
+ <TabItem label="markup">
+
+```tsx
+function MyButton(): JSX.Element {
+ return <button onClicked="echo hello">Clicke Me!</button>
+}
+```
+
+ </TabItem>
+ <TabItem label="constructor functions">
+
+```tsx
+import { Widget } from "astal"
+
+function MyButton(): Widget.Button {
+ return Widget.Button({
+ onClicked: "echo hello",
+ label: "Click Me!",
+ })
+}
+```
+
+ </TabItem>
+</Tabs>
+
+:::note
+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 to the user.
+
+```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>
+}
+```
+
+:::note
+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" />
+}
+```
+
+:::note
+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]() 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]() or it can be from a [Library]().
+
+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>
+}
+```
+
+:::note
+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()
+
+// these two are equivalent
+return (
+ <box>
+ <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]():
+
+```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>
+```
+
+:::caution
+Only bind children of the `box` 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.
+:::
+
+:::note
+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>
+```
+
+:::note
+You can pass the followings as children:
+
+- widgets
+- deeply nested arrays of widgets
+- bindings of widgets,
+- bindings of deeply nested arrays of widgets
+
+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/src/content/docs/ags/libraries.md b/docs/src/content/docs/ags/libraries.md
new file mode 100644
index 0000000..e594de8
--- /dev/null
+++ b/docs/src/content/docs/ags/libraries.md
@@ -0,0 +1,8 @@
+---
+title: Libraries
+description: How to use Astal libraries
+sidebar:
+ order: 7
+---
+
+## TODO:
diff --git a/docs/src/content/docs/ags/theming.md b/docs/src/content/docs/ags/theming.md
new file mode 100644
index 0000000..5c930ea
--- /dev/null
+++ b/docs/src/content/docs/ags/theming.md
@@ -0,0 +1,113 @@
+---
+title: Theming
+description: GTK3 CSS theming
+sidebar:
+ order: 1
+---
+
+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)
+
+:::caution[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`
+
+```tsx
+const inlineCss = `
+window {
+ background-color: transparent;
+}
+`
+
+// app.ts
+App.start({
+ css: "/home/username/.config/ags/style.css",
+ css: `${SRC}/style.css'`,
+ css: inlineCss,
+})
+```
+
+:::note
+The global `SRC` will point to the directory `app.ts` is in.
+You can pass a relative path, but its resolution will be relative to the current working directory.
+:::
+
+## Css Property on Widgets
+
+```tsx
+Widget.Label({
+ css: "color: blue; padding: 1em;",
+ label: "hello",
+})
+```
+
+:::note
+The `css` property of a widget will not cascade to its children.
+:::
+
+## Apply Stylesheets at Runtime
+
+You can apply additional styles at runtime.
+
+```tsx
+App.apply_css("/path/to/file.css")
+```
+
+```tsx
+App.apply_css(`
+window {
+ background-color: transparent;
+}
+`)
+```
+
+```tsx
+App.reset_css() // reset if need
+```
+
+:::caution
+`App.apply_css` will apply over 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)
+
+```bash
+# to bring up the inspector run
+ags --inspector
+```
+
+## Using pre-processors like SCSS
+
+```tsx
+// app.ts
+// main scss file
+const scss = `${SRC}/style.scss`
+
+// target css file
+const css = `/tmp/my-style.css`
+
+// make sure sassc is installed on your system
+exec(`sassc ${scss} ${css}`)
+
+App.config({
+ css,
+ main() {},
+})
+```
diff --git a/docs/src/content/docs/ags/utilities.md b/docs/src/content/docs/ags/utilities.md
new file mode 100644
index 0000000..7c03f50
--- /dev/null
+++ b/docs/src/content/docs/ags/utilities.md
@@ -0,0 +1,8 @@
+---
+title: Utilities
+description: Reference of bultin utility functions
+sidebar:
+ order: 5
+---
+
+## TODO:
diff --git a/docs/src/content/docs/ags/variable.md b/docs/src/content/docs/ags/variable.md
new file mode 100644
index 0000000..e172b7d
--- /dev/null
+++ b/docs/src/content/docs/ags/variable.md
@@ -0,0 +1,8 @@
+---
+title: Variable
+description: Reference of the builtin Variable type
+sidebar:
+ order: 6
+---
+
+## TODO:
diff --git a/docs/src/content/docs/ags/widget.md b/docs/src/content/docs/ags/widget.md
new file mode 100644
index 0000000..905c2a0
--- /dev/null
+++ b/docs/src/content/docs/ags/widget.md
@@ -0,0 +1,124 @@
+---
+title: Widget
+description: Reference of the builtin and gtk widgets
+sidebar:
+ order: 4
+---
+
+## 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](/libastal/#classes)
+- [Gtk widgets](https://docs.gtk.org/gtk3/#classes)
+
+You can check the [source code](https://github.com/aylur/astal/blob/main/gjs/src/widgets.ts) to have a full list of builtin widgets.
+
+## AGS widget methods
+
+Additional methods that Astal.js add 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
+
+```tsx
+import { astalify, Gtk } from "astal"
+
+// define its props, constructor and type
+export type ColorButtonProps = Widget.ConstructProps<Gtk.ColorButton, Gtk.ColorButton.ConstructorProps>
+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,
+ })}
+ />
+}
+```
diff --git a/docs/src/content/docs/getting-started/installation.mdx b/docs/src/content/docs/getting-started/installation.mdx
new file mode 100644
index 0000000..c1f4060
--- /dev/null
+++ b/docs/src/content/docs/getting-started/installation.mdx
@@ -0,0 +1,92 @@
+---
+title: Installation
+description: How to install Astal
+sidebar:
+ order: 1
+---
+
+import { Tabs, TabItem } from "@astrojs/starlight/components"
+
+## Nix
+
+maintainer: [@Aylur](https://github.com/Aylur)
+
+Read more about it on the [nix page](../nix)
+
+## Arch
+
+maintainer: [@kotontrion](https://github.com/kotontrion)
+
+<Tabs>
+ <TabItem label="Core Library">
+
+```bash
+yay -S libastal-git
+```
+
+ </TabItem>
+ <TabItem label="Every Library">
+
+```bash
+yay -S libastal-meta
+```
+
+ </TabItem>
+ <TabItem label="AGS">
+
+```bash
+yay -S aylurs-gtk-shell-git
+```
+
+ </TabItem>
+</Tabs>
+
+## From Source
+
+```bash
+git clone https://github.com/Aylur/Astal.git
+cd Astal
+```
+
+<Tabs>
+ <TabItem label="Fedora">
+
+```bash
+sudo dnf install meson
+
+meson setup --prefix /usr build
+meson install -C build
+```
+
+ </TabItem>
+ <TabItem label="Arch">
+
+```bash
+sudo pacman -Syu meson
+
+arch-meson build
+meson install -C build
+```
+
+ </TabItem>
+ <TabItem label="Alpine">
+
+```bash
+sudo apk add meson
+
+meson setup --prefix /usr build
+meson install -C build
+```
+
+ </TabItem>
+ <TabItem label="Ubuntu">
+
+```bash
+sudo apt install meson
+
+meson setup --prefix /usr build
+meson install -C build
+```
+
+ </TabItem>
+</Tabs>
diff --git a/docs/src/content/docs/getting-started/introduction.md b/docs/src/content/docs/getting-started/introduction.md
new file mode 100644
index 0000000..cb46b29
--- /dev/null
+++ b/docs/src/content/docs/getting-started/introduction.md
@@ -0,0 +1,28 @@
+---
+title: Introduction
+description: What is Astal?
+sidebar:
+ order: 0
+---
+
+## 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](/libastal) has some Gtk widgets that come packaged,
+the most important one is the [Window](/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](/astal/libraries/overview), 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/src/content/docs/getting-started/languages.md b/docs/src/content/docs/getting-started/languages.md
new file mode 100644
index 0000000..08dda01
--- /dev/null
+++ b/docs/src/content/docs/getting-started/languages.md
@@ -0,0 +1,64 @@
+---
+title: Supported Languages
+description: Choosing a language to get started
+banner:
+ content: 🚧 Examples are in the works. 🚧
+---
+
+## JavaScript
+
+The main intended usage of Astal is in TypeScript with [AGS](/astal/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.
+
+:::tip
+The runtime is [GJS](https://gitlab.gnome.org/GNOME/gjs) and **not** nodejs
+:::
+
+Examples:
+
+- TODO
+
+## Lua
+
+Similar to how there is a [TypeScript]() lib for GJS, there is also an accompanying library for [Lua]().
+Unfortunately, I have encountered very heavy [performance issues]() 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:
+
+- TODO
+
+## Python
+
+There is a WIP [library for 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]() intended it.
+
+Examples:
+
+- TODO
+
+## 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.
+
+Examples:
+
+- TODO
+
+## C
+
+I don't recommend using C as it requires quite a lot of boilerplate.
+
+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/src/content/docs/getting-started/nix.md b/docs/src/content/docs/getting-started/nix.md
new file mode 100644
index 0000000..8a1df7a
--- /dev/null
+++ b/docs/src/content/docs/getting-started/nix.md
@@ -0,0 +1,8 @@
+---
+title: Nix
+description: Usage with the Nix package manager
+sidebar:
+ hidden: true
+---
+
+## Nix
diff --git a/docs/src/content/docs/index.mdx b/docs/src/content/docs/index.mdx
new file mode 100644
index 0000000..17cb867
--- /dev/null
+++ b/docs/src/content/docs/index.mdx
@@ -0,0 +1,67 @@
+---
+title: Build Your Own Desktop Shell
+description: The best way to make beautiful and functional widgets on wayland
+template: splash
+banner:
+ content: |
+ 🚧 The library and this documentation is still under development. 🚧
+hero:
+ tagline: The best way to make <i>beautiful</i> <b>and</b> <i>functional</i> wayland widgets!
+ image:
+ file: ../../assets/front-image.png
+ actions:
+ - text: Get Started
+ link: /astal/getting-started/introduction/
+ icon: right-arrow
+ variant: primary
+ - text: Reference
+ link: /astal/reference/references
+ icon: open-book
+ variant: secondary
+ - text: View on GitHub
+ link: https://github.com/Aylur/Astal
+ icon: github
+ variant: minimal
+---
+
+import { Card, CardGrid } from "@astrojs/starlight/components";
+import { showcases } from "../showcases.ts";
+import Showcase from "../../components/Showcase.astro";
+
+<CardGrid>
+ <Card title="Use your preferred language" icon="seti:json">
+ The main focus of Astal is TypeScript using JSX. But you can use the
+ libraries in any language that supports [Gobject
+ Introspection](https://en.wikipedia.org/wiki/List_of_language_bindings_for_GTK).
+ </Card>
+ <Card title="No bash scripts needed" icon="pencil">
+ Includes modules to work with Network, Bluetooth, Battery, Audio and more.
+ See the list of included [Libraries](./libraries/overview).
+ </Card>
+ <Card title="Use any Gtk widget" icon="puzzle">
+ With Astal you work with Gtk directly. You are not limited to only a set of
+ them. See the list of [Widgets](./libraries/widgets) that come bundled.
+ </Card>
+ <Card title="Examples" icon="open-book">
+ Have a look at some simple and not simple [examples](./config/examples). In
+ [TypeScript](https://github.com/Aylur/Astal/tree/main/examples/ts),
+ [Lua](https://github.com/Aylur/Astal/tree/main/examples/lua),
+ [Python](https://github.com/Aylur/Astal/tree/main/examples/python).
+ </Card>
+</CardGrid>
+
+## Showcases
+
+<div class="showcases">
+ {showcases.map((showcase) =>
+ Array.isArray(showcase) ? (
+ <CardGrid>
+ {showcase.map((elem) => (
+ <Showcase {...elem} />
+ ))}
+ </CardGrid>
+ ) : (
+ <Showcase {...showcase} />
+ )
+ )}
+</div>
diff --git a/docs/src/content/docs/libraries/overview.md b/docs/src/content/docs/libraries/overview.md
new file mode 100644
index 0000000..e39d746
--- /dev/null
+++ b/docs/src/content/docs/libraries/overview.md
@@ -0,0 +1,8 @@
+---
+title: Libraries
+description: Overview of incuded Astal Libraries
+sidebar:
+ order: 0
+---
+
+## Libs
diff --git a/docs/src/content/showcases.ts b/docs/src/content/showcases.ts
new file mode 100644
index 0000000..0082c93
--- /dev/null
+++ b/docs/src/content/showcases.ts
@@ -0,0 +1,13 @@
+type Showcase = {
+ author: string
+ src: string
+ url: string
+}
+
+type Grid<T> =
+ | [T, T]
+ | [T, T, T, T]
+
+export const showcases: Array<Showcase | Grid<Showcase>> = [
+ { author: "Aylur", src: "/astal/showcase/aylur1.png", url: "https://github.com/Aylur/dotfiles" },
+]
diff --git a/docs/src/env.d.ts b/docs/src/env.d.ts
new file mode 100644
index 0000000..acef35f
--- /dev/null
+++ b/docs/src/env.d.ts
@@ -0,0 +1,2 @@
+/// <reference path="../.astro/types.d.ts" />
+/// <reference types="astro/client" />
diff --git a/docs/src/style.css b/docs/src/style.css
new file mode 100644
index 0000000..b6a0047
--- /dev/null
+++ b/docs/src/style.css
@@ -0,0 +1,72 @@
+/* Dark mode colors. */
+:root {
+ --sl-color-accent-low: #10243e;
+ --sl-color-accent: #3584e4;
+ --sl-color-accent-high: #99c1f1;
+ --sl-color-white: #ffffff;
+ --sl-color-gray-1: #eeeeee;
+ --sl-color-gray-2: #c2c2c2;
+ --sl-color-gray-3: #8b8b8b;
+ --sl-color-gray-4: #585858;
+ --sl-color-gray-5: #383838;
+ --sl-color-gray-6: #272727;
+ --sl-color-black: #171718;
+}
+
+/* Light mode colors. */
+:root[data-theme='light'] {
+ --sl-color-accent-low: #c5d9f2;
+ --sl-color-accent: #156ac7;
+ --sl-color-accent-high: #11325b;
+ --sl-color-white: #181818;
+ --sl-color-gray-1: #272727;
+ --sl-color-gray-2: #383838;
+ --sl-color-gray-3: #585858;
+ --sl-color-gray-4: #8b8b8b;
+ --sl-color-gray-5: #c2c2c2;
+ --sl-color-gray-6: #eeeeee;
+ --sl-color-gray-7: #f6f6f6;
+ --sl-color-black: #ffffff;
+}
+
+:root {
+ --card-radius: 7px;
+}
+
+article.card {
+ border-radius: var(--card-radius);
+ background-color: color-mix(in srgb, var(--sl-color-black), var(--sl-color-white) 2%);
+ border-color: var(--sl-color-gray-6);
+}
+
+/* landing page gradient */
+:root {
+ --overlay-gradient: color-mix(in srgb, var(--sl-color-accent), transparent 85%);
+}
+
+:root[data-theme='light'] {
+ --overlay-gradient: color-mix(in srgb, var(--sl-color-accent), transparent 55%);
+}
+
+[data-has-hero] .page {
+ background:
+ linear-gradient(215deg, var(--overlay-gradient), transparent 40%),
+ radial-gradient(var(--overlay-gradient), transparent 40%) no-repeat -60vw -40vh / 105vw 200vh,
+ radial-gradient(var(--overlay-gradient), transparent 65%) no-repeat 50% calc(100% + 20rem) / 60rem 30rem;
+}
+
+[data-has-hero] header {
+ border-bottom: 1px solid transparent;
+ background-color: transparent;
+ -webkit-backdrop-filter: blur(16px);
+ backdrop-filter: blur(16px);
+}
+
+[data-has-hero] .hero>img {
+ filter: drop-shadow(0 0 3rem var(--overlay-gradient));
+}
+
+div.showcases {
+ display: flex;
+ flex-direction: column;
+}