summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2025-04-13 19:42:47 +0200
committerLinnea Gräf <nea@nea.moe>2025-04-13 19:42:47 +0200
commit0d3b6a3f698853cbbf7d0ebf866eca8bccd196fb (patch)
treee756da2ef87a13a269da71fac1cabd966423c2e9
downloadags-master.tar.gz
ags-master.tar.bz2
ags-master.zip
ags initHEADmaster
-rw-r--r--.gitignore2
-rw-r--r--app.ts12
-rw-r--r--env.d.ts21
-rw-r--r--package.json6
-rw-r--r--style.scss44
-rw-r--r--tsconfig.json14
-rw-r--r--widget/Bar.tsx93
7 files changed, 192 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..298eb4d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+node_modules/
+@girs/
diff --git a/app.ts b/app.ts
new file mode 100644
index 0000000..2e70ccf
--- /dev/null
+++ b/app.ts
@@ -0,0 +1,12 @@
+import { App } from "astal/gtk3"
+import style from "./style.scss"
+import Bar from "./widget/Bar"
+
+App.start({
+ css: style,
+ main() {
+ const monitors = App.get_monitors()
+ monitors.sort(it => it.geometry.x)
+ Bar(monitors[0])
+ },
+})
diff --git a/env.d.ts b/env.d.ts
new file mode 100644
index 0000000..467c0a4
--- /dev/null
+++ b/env.d.ts
@@ -0,0 +1,21 @@
+declare const SRC: string
+
+declare module "inline:*" {
+ const content: string
+ export default content
+}
+
+declare module "*.scss" {
+ const content: string
+ export default content
+}
+
+declare module "*.blp" {
+ const content: string
+ export default content
+}
+
+declare module "*.css" {
+ const content: string
+ export default content
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..44226f2
--- /dev/null
+++ b/package.json
@@ -0,0 +1,6 @@
+{
+ "name": "astal-shell",
+ "dependencies": {
+ "astal": "/usr/share/astal/gjs"
+ }
+}
diff --git a/style.scss b/style.scss
new file mode 100644
index 0000000..6c070fb
--- /dev/null
+++ b/style.scss
@@ -0,0 +1,44 @@
+// https://gitlab.gnome.org/GNOME/gtk/-/blob/gtk-3-24/gtk/theme/Adwaita/_colors-public.scss
+$fg-color: #{"@theme_fg_color"};
+$bg-color: #{"@theme_bg_color"};
+
+window {
+ font-size: x-large;
+}
+.linky-button {
+ all: unset;
+ background: transparent;
+ border: 0;
+ color: inherit;
+}
+
+.linky-button:hover {
+ /* This does not work. Why? */
+ text-decoration-line: underline;
+ color: green;
+}
+.output-list {
+ background: #306c;
+ min-width: 20em;
+ padding: 1em;
+ padding-right: 1em;
+ color: #afa;
+ border-right: 1px solid #afa;
+ font-family: monospace;
+}
+
+.monitor {
+ font-weight: bold;
+}
+
+.workspace {
+ margin-left: 1em;
+}
+.active-ws {
+ color: greenyellow;
+}
+
+.window {
+ margin-left: 2em;
+}
+
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..9471e35
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "compilerOptions": {
+ "experimentalDecorators": true,
+ "strict": true,
+ "target": "ES2022",
+ "module": "ES2022",
+ "moduleResolution": "Bundler",
+ // "checkJs": true,
+ // "allowJs": true,
+ "jsx": "react-jsx",
+ "jsxImportSource": "astal/gtk3",
+ }
+}
diff --git a/widget/Bar.tsx b/widget/Bar.tsx
new file mode 100644
index 0000000..ccbd496
--- /dev/null
+++ b/widget/Bar.tsx
@@ -0,0 +1,93 @@
+import { App, Astal, Gtk, Gdk } from "astal/gtk3"
+import { exec, monitorFile, Variable } from "astal"
+import { Button, Label } from "astal/gtk3/widget";
+import Pango from "gi://Pango?version=1.0";
+
+const time = Variable("").poll(1000, "date")
+
+type Id = number
+interface SwayNodeCommon {
+ id: Id,
+ orientation: 'horizontal' | 'vertical' | 'none',
+ name: string,
+ focus: Id[],
+ rect: Rect,
+ deco_rect: Rect,
+ window_rect: Rect,
+ geometry: Rect,
+}
+type SwayNode = SwayOutput | SwayRoot | SwayCon | SwayWorkspace;
+type Rect = { x: number, y: number, width: number, height: number }
+interface SwayWorkspace extends SwayNodeCommon {
+ type: 'workspace',
+ nodes: SwayCon[]
+}
+interface SwayCon extends SwayNodeCommon {
+ type: 'con',
+ window_properties?: {
+ class: string,
+ instance: string,
+ title: string,
+ window_role: string,
+ window_type: string,
+ },
+}
+interface SwayOutput extends SwayNodeCommon {
+ type: 'output',
+ nodes: SwayWorkspace[],
+ focused: boolean,
+}
+interface SwayRoot extends SwayNodeCommon {
+ type: 'root',
+ nodes: SwayOutput[]
+}
+const swayTree = Variable({} as SwayRoot | { type: undefined }).poll(1000, "swaymsg -t get_tree",
+ text => JSON.parse(text) as SwayRoot
+)
+function omit<T extends object, K extends keyof T>(obj: T, ...ks: K[]): Omit<T, K> {
+ return Object.fromEntries(Object.entries(obj).filter(it => !ks.includes(it[0] as K))) as any
+}
+function Monitor(props: { monitor: SwayOutput }) {
+ return <>
+ <Label halign={Gtk.Align.START} className={"monitor"}>
+ {`${props.monitor.name} (${props.monitor.nodes.length})`}
+ </Label>
+ {props.monitor.nodes.map(ws =>
+ <Workspace ws={ws} focused={props.monitor.focus.includes(ws.id)}></Workspace>
+ )}
+ </>
+}
+function Workspace(props: { ws: SwayWorkspace, focused: boolean }) {
+ return <>
+ <Button
+ halign={Gtk.Align.START}
+ onClickRelease={() => exec(['swaymsg', `workspace ${JSON.stringify(props.ws.name)}`])}
+ className={"workspace linky-button " + (props.focused ? 'active-ws ' : '')} relief={Gtk.ReliefStyle.NONE}>
+ {`Workspace ${props.ws.name}`}
+ </Button>
+ {props.ws.nodes.map(window =>
+ <Label
+ ellipsize={Pango.EllipsizeMode.MIDDLE}
+ maxWidthChars={30}
+ halign={Gtk.Align.START}
+ className={"window"}>{window.window_properties?.title ?? window.name}</Label>
+ )}
+ </>
+}
+export default function Bar(gdkmonitor: Gdk.Monitor) {
+ const { TOP, LEFT, RIGHT, BOTTOM } = Astal.WindowAnchor
+ return <window
+ className="Bar"
+ gdkmonitor={gdkmonitor}
+ exclusivity={Astal.Exclusivity.EXCLUSIVE}
+ anchor={TOP | LEFT | BOTTOM}
+ application={App}>
+ <box vertical className={"output-list"}>
+ {swayTree(root => !root.type ? null
+ : root.nodes
+ .map(monitor => monitor.name.startsWith("_") ? null : <Monitor monitor={monitor}></Monitor>)
+ .filter(it => it)
+ )}
+ </box>
+ </window>
+}