Migration Guide
From v2
Subclassing
astalify
has been removed, jsx
function and JSX expressions handle everything. It is possible to use Gtk widgets directly without any prior setup.
const Calendar = astalify(Gtk.Calendar)
const _ = <Calendar />
const _ = <Gtk.Calendar />
If you still prefer to use regular JS functions instead of JSX, you can do
import { CCProps } from "ags/gtk4"
type BoxProps = CCProps<Gtk.Box, Gtk.Box.ConstructorProps>
const Box = (props: BoxProps) => jsx(Gtk.Box, props)
Box({
orientation: state,
children: [Box()],
})
GObject decorators
They were updated to the stage 3 proposal. You can read more about them on the Gnim documentation.
@register()
class MyObj extends GObject.Object {
@property(String) declare myProp: string
@property(String) myProp = ""
@property(String)
@getter(String)
get myProp() {
return ""
}
@property(String)
@setter(String)
set myProp(v: string) {
//
}
}
Syntax changes
setup
->$
className
->class
<button class="my-button" $={(self) => print("ref", self)} />
Variable
Variable
has been removed in favor of Accessor
and createState
. You can read more about them on the Gnim documentation.
Dynamic rendering
Dynamic children rendering is done with <With>
and <For>
components. children
prop can no longer take a Binding
.
const value: Binding<object>
const list: Binding<Array<object>>
return (
<box>
{value.as((value) => (
<></>
))}
<With value={value}>
{(value) => <></>}
</With>
{list.as(list => list.map(item => (
<></>
)))}
<For each={list}>
{(item) => <></>}
</For>
</box>
)
From v1
Ags was rewritten from scratch and unfortunately everything changed drastically, you will have to rewrite your projects from the ground up.
There were so many changes I'm unable to list everything, but these are some highlights.
Entry Point
Instead of a fixed ~/.config/ags/config.js
entry you can name the main file anything and specify it as an arg to ags run </path/to/entry>
.
If you wish to stick to having the source code in ~/.config/ags
then name the entry file app.js
, app.ts
, app.jsx
or app.tsx
which ags run
will use by default.
The entry point in code changed from App.config
to app.start
App.config({
windows: [
// window instances
],
})
import app from "astal/gtk4/app"
app.start({
main() {
// any initialization code
},
})
Instantiating widgets
It is no longer recommended to create top level instances because scripts can run in client mode and it is recommended to only execute code in either main
or client
blocks.
const win = Widget.Window()
App.config({
windows: [win],
})
app.main({
main() {
new Widget.Window()
},
})
Templating
AGS now supports and recommends the usage of JSX.
const _ = Widget.Box({
vertical: true,
children: [Widget.Label("hello")],
})
const _ = (
<box vertical>
<label label="hello" />
</box>
)
Reactivity
Variable
has been removed in favor of signals.
const label = Variable("hello")
Label({
label: label.bind().as((hello) => `${hello} world`),
})
import { createState } from "ags"
const [label, setLabel] = createState("hello")
return <label label={label((hello) => `${hello} world`)} />
Hooks
Widgets are no longer subclassed, added methods have been removed.
Widget.Button({
setup: (self) => {
self.on("signal-name", handler)
self.hook(obj, handler, "changed")
},
})
import { onCleanup } from "ags"
function MyWidget() {
const id = obj.connect("signal-name", callback)
onCleanup(() => {
obj.disconnect(id)
})
return (
<button onClicked={handler} />
)
}
NOTE
.keybind
and .poll
hooks have been removed. Polling should be done using createPoll
. Keybinds should be done using the intended Gtk APIs.
Widgets
JSX
handles everything, it is no longer needed to subclass widgets.
import Gtk from "gi://Gtk"
const calendar = <Gtk.Calendar />
Globals
App
, Service
, Utils
, Widget
, Variable
are no longer globally available
import app from "ags/gtk4/app"
import * as fileUtils from "ags/file"
import * as procUtils from "ags/process"
import * as timeUtils from "ags/time"
import { createBinding, createState } from "ags"
Services
These are no longer called Service
. There is no longer a distinction between a Service
and GObject.Object
and there are no longer builtin Services.
These are now simply external libraries that will have to be installed next to AGS. They are now implemented in Vala or C which makes it possible to also use them outside of AGS.
They work very similarly however.
// importing
const battery = await Service.import("battery")
import Battery from "gi://AstalBattery"
const battery = Battery.get_default()
// binding
const b = battery.bind("percentage")
import { createBinding } from "ags"
const b = createBinding(battery, "percentage")
Creating custom "Services" now simply means creating a GObject.Object
subclass.
class MyService extends Service {
static {
Service.register(
this,
{
"my-signal": ["float"],
},
{
"my-value": ["float", "rw"],
},
)
}
get my_value(): number
set my_value(v: number)
}
import GObject, { register, signal, property } from "ags/gobject"
@register()
class MyService extends GObject.Object {
@property(Number) myValue = 0
@signal(Number)
mySignal(n: number): void {}
}
Utils
File, Process and Time utility functions are available from their own module
jsUtils.exec("command") Utils.readFile("file") Utils.timeout(1000, callback) Utils.fetch("url") import { exec } from "ags/process" import { readFile } from "ags/file" import { timeout } from "ags/time" import { fetch } from "ags/fetch"
Icon lookup is has no alternative. Use
Gtk.IconTheme
.Authenticating have been moved to AstalAuth
Sending notifications will be available in AstalNotifd. Until then see #26.
CLI
To make windows toggleable through CLI you will have to now pass the app
instance to Window
instances instead of passing an array of windows to App.config
.
App.config({
windows: [Widget.Window({ name: "window-name" })],
})
app.start({
main() {
return <window name="window-name" application={app}></window>
},
})
ags --run-js
have been removed in favor of requests.
globalThis.myfunction = () => {
print("hello")
}
app.start({
requestHandler(request: string, res: (response: any) => void) {
if (request == "myfunction") {
res("hello")
}
res("unknown command")
},
})
ags -r "myfunction()"
ags request myfunction
Instance name is now defined in code instead of cli of first launch
app.start({
instanceName: "name",
})
ags -i name
ags -t window-name -i name
ags run
ags toggle window-name -i name