From ec117cea004ec01ebdaa1037f150412eab988086 Mon Sep 17 00:00:00 2001 From: tux Date: Sat, 31 May 2025 14:00:56 +0530 Subject: [PATCH] feat: initial commit --- .gitignore | 2 + LICENSE | 22 ++++++++ app.ts | 10 ++++ assets/avatar.png | Bin 0 -> 2143 bytes env.d.ts | 21 +++++++ flake.lock | 91 ++++++++++++++++++++++++++++++ flake.nix | 60 ++++++++++++++++++++ package-lock.json | 35 ++++++++++++ package.json | 7 +++ style.scss | 84 +++++++++++++++++++++++++++ tsconfig.json | 14 +++++ widget/app-launcher/index.tsx | 103 ++++++++++++++++++++++++++++++++++ widget/bar/battery.tsx | 15 +++++ widget/bar/index.tsx | 45 +++++++++++++++ widget/bar/launcher.tsx | 22 ++++++++ widget/bar/workspace.tsx | 56 ++++++++++++++++++ widget/common/index.ts | 1 + widget/common/picture.tsx | 11 ++++ windows.ts | 4 ++ 19 files changed, 603 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 app.ts create mode 100644 assets/avatar.png create mode 100644 env.d.ts create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 style.scss create mode 100644 tsconfig.json create mode 100644 widget/app-launcher/index.tsx create mode 100644 widget/bar/battery.tsx create mode 100644 widget/bar/index.tsx create mode 100644 widget/bar/launcher.tsx create mode 100644 widget/bar/workspace.tsx create mode 100644 widget/common/index.ts create mode 100644 widget/common/picture.tsx create mode 100644 windows.ts 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/LICENSE b/LICENSE new file mode 100644 index 0000000..d46f767 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2025 tux + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/app.ts b/app.ts new file mode 100644 index 0000000..0ff002c --- /dev/null +++ b/app.ts @@ -0,0 +1,10 @@ +import { App } from "astal/gtk4"; +import style from "./style.scss"; +import windows from "./windows"; + +App.start({ + css: style, + main() { + windows.map((win) => App.get_monitors().map(win)); + }, +}); diff --git a/assets/avatar.png b/assets/avatar.png new file mode 100644 index 0000000000000000000000000000000000000000..2a5052884cb2e788c6288aa19066012d5d66f3f8 GIT binary patch literal 2143 zcmeAS@N?(olHy`uVBq!ia0y~yU}ykg4rT@hhA$5${$yZaXbS68|1>q!?d|Noef=666Z7cd!wVPAdwRIvx^*)>Ep6}KJ;6bN8#iot z|Nh;ysZ)*}J62eb|Ki1q#fuj9baz))RJ1fV*Vol@e%4&Tz`!6_666=m@Q>$nyQ5S^ z$g}xc*Eb(ZJ9jyOJD!1oz0A|aF{I+w+o;aG)m8#~`8d75CGgysarVuG|Nr-;dt92) zvL$Ej7nQz{m0qjQRM-^Pq`j&CFQyZ*A>m*XE4SF6^&2OdzslSjr~N8T{r}#tXMb&& z&ajSAt}m`3;le?*F7C4l`{TFaP^iX=>y<_bOv( z(y>&A1#jm*tu#N-Hr-KfNrmlVX09E4o6lcXxc)4;`L5l@d)IHs9Gm|z?Q`fMso(3D zuW!sT=ZP)(`Nl=(tZ36*v54G#+qvE5O3CeB#INUYd!{x^wojDVojcx1_9c}(m*!l0 zCG7b0R^Bomjw3=QTJ5domSO6QUYSB=RSb7CY=b#FWKxd!-JHZC@Ko-lUQ2>KyZDLp zt`>b=ng3@49+nRTO{^mu)!gF1x7k;RA7G5ZRE}(-;Ao;ZH zh1bO=H{D7X+_OQSU18DJWgjmqbuL#w_{wALHK$VBXD3U)M#TJD*sIfg*s1JQO)jIp zUB80}J732_em~)IhpuSP>7O|iI)vsjKRC14Q$Xd)=>`D}?TdS-e5`-)@-+XFP8ETz zr~4T0E-6fUwQHq)!nOTY=R=;Znp1zwe$8zTGhI%zTiov>8Z&-zZgzFCJGyJ;)m#ys zgCb^exyqjwxg2`2?a{W%y=!MED5YjeU%nYJ^^^Idy^ppF%-B_ae}#LZjdx0r;;H9) zx;4G`%o^^!aaPv(D_(l&`va3Y=7O6y|6VHNShS~5SgKCoWy7>%+@A8sxHeyIPh(kh zh&ldQVS#+)4a+<&Ch=VUPa9*lT-?)tmcwi2bl1QqDy}!?_+3~vr`=n@EGpA_lk?5F zn?HOuSTXVGy4?H6LN6#BG*+|fJF0xy%PsTrRmSD^Ch`3~?c&oPhJ`i6d}z45(dd;x z*YDEB0=G7PcH?PuSkTcY5xyemLbl9d<4>KEx8m~ znzai{JO1o@tiWD!E1z@c!B{2#hI_ddyVTxa$#}(RbVMxEdIF=+m2$p=*E3r`MoSnS zYVk6-u^{r`s}H=)Th>M|Nn%{hksrib^dx7-v;#GMUlk;;sLrd(Wq7{Gz^_1=-=Mei z_3?Ac@*k(BoHaX;KK;i0o+q{ajS4LqyhrvP-@%;P95I8#|*zM6>6I>@lv`% z`?@c>zY8bqnwmaq$^>iUMlXfMq4``3!uUTK#;1C&oy_3-_ElE+#mD_Orw6R8ntZXJ zu_4TBclQ3c;)1hz=H>MjDqK@t`+e!BSAUK&*?iRs zv-4h*=k2agbhB>rYyFOGrj^%B&nUxZ%S0w|#n~c`M_d_2mj7S; zX10mUHM#ZIf}U6kPWx6}Xm`@I*N*S!1;w*joA2daU*5aMYvT&rj~2fV=pQ@xCY(Ld zA%o@D4v__AMKX6~?E9Bi=^1G;e@QyDCdAG@(olXTV_&S#4r}X&LUzw5FIEb;d9S5f z`KO`Uv!b*)%p$(KEg!cQ1T(Vqdo^_U%s79i-Nm>2g!LaozJ^0wYWL=ya$i2v`uF0j z36Y{Rs&cp8yk7fn1;-4YpBKMKONSPpSfTRd8!y|ImFH)rd|hL$Xu|fHVO@3dY5xP2 zmyXV@Vv+RL)$3vFYW#gyZHrg_A@8nVcSBO|@8f*B<9}vD@psGFGd@ge%4Ytp$hqL7 zul=_qyN|7_VR7!QBh%C5}GVoF2BDxnM?r zPxlUumI>aMwD^V2pR|zCPyBdQT~B<{nZp)culyHxoVLo{AG-YG0WaH(8=jvgwV0Xb zwAalsVe_8f@!@ilDxdhI=%sZpZaY|>{dB8*Yw)&oU#1!BubecvaPIco!u6g)@mle= z?qTbv>VMl2dL_Q=14.17" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..74960ce --- /dev/null +++ b/package.json @@ -0,0 +1,7 @@ +{ + "name": "astal-shell", + "dependencies": { + "astal": "/nix/store/8cl58ip2nasg6rdyj59cwg2f0qbixs28-astal-gjs/share/astal/gjs", + "typescript": "^5.7.3" + } +} diff --git a/style.scss b/style.scss new file mode 100644 index 0000000..4906cc5 --- /dev/null +++ b/style.scss @@ -0,0 +1,84 @@ +@use "sass:math"; +@use "sass:list"; + +$accent: #ebdbb2; +$fg-color: #ebdbb2; +$bg-color: #282828; +$inactive-color: #807966; + +window.Bar { + background: $bg-color; + color: $fg-color; + margin: 10px; + border-radius: 10px; + padding: 10px; + + .launcher { + button { + background-color: $inactive-color; + border-radius: 10px; + transition: all 0.15s ease-out; + padding: 0px; + margin: 0px; + + &:hover { + background-color: $bg-color; + } + } + } + + .workspace-container { + background-color: $bg-color; + color: $fg-color; + border-radius: 10px; + + .workspace-button { + padding: 0px; + border: 0px; + margin: 2px; + background-color: $inactive-color; + min-height: 1em; + min-width: 1rem; + border-radius: calc(list.nth(8px, 1) * 1.5); + transition: min-width 0.15s ease-out; + + &:hover { + background-color: $fg-color; + &.occupied { + background-color: $fg-color; + } + } + + &.active { + min-width: 2rem; + min-height: 1rem; + background-color: $accent; + } + + &.occupied { + background-color: $accent; + &.active { + background-color: $accent; + } + } + } + } +} + +window.app-launcher { + background: $bg-color; + border-radius: 10px; + padding: 10px; + + .app-list { + background: $bg-color; + } + + .app-button { + border-radius: 10px; + } + + .name { + color: $fg-color; + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..a92bc43 --- /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/gtk4", + } +} diff --git a/widget/app-launcher/index.tsx b/widget/app-launcher/index.tsx new file mode 100644 index 0000000..cba7df1 --- /dev/null +++ b/widget/app-launcher/index.tsx @@ -0,0 +1,103 @@ +import { Variable, Gio } from "astal"; +import { App, Astal, Gdk, Gtk, hook } from "astal/gtk4"; +import AstalApps from "gi://AstalApps"; +import { Picture } from "../common"; + +export const WINDOW_NAME = "app-launcher"; +const apps = new AstalApps.Apps(); +const text = Variable(""); + +const hide = () => { + App.get_window(WINDOW_NAME)?.set_visible(false); +}; + +const AppButton = ({ app }: { app: AstalApps.Application }) => { + return ( + + ); +}; + +const AppList = () => { + const appList = text((text) => apps.fuzzy_query(text)); + + return ( + + + {appList.as((list) => list.map((app) => ))} + + + ); +}; + +const AppSearch = () => { + const onEnter = () => { + apps.fuzzy_query(text.get())?.[0].launch(); + hide(); + }; + return ( + + + + + text.set(self.text)} + onActivate={onEnter} + setup={(self) => { + hook(self, App, "window-toggled", (_, win) => { + const winName = win.name; + const visible = win.visible; + + if (winName == WINDOW_NAME && visible) { + text.set(""); + self.set_text(""); + self.grab_focus(); + } + }); + }} + /> + + ); +}; + +export const AppLauncher = (gdkmonitor: Gdk.Monitor) => { + return ( + { + if (keyval === Gdk.KEY_Escape) { + App.toggle_window(WINDOW_NAME); + } + }} + > + + + + + + ); +}; diff --git a/widget/bar/battery.tsx b/widget/bar/battery.tsx new file mode 100644 index 0000000..dcab464 --- /dev/null +++ b/widget/bar/battery.tsx @@ -0,0 +1,15 @@ +import { bind } from "astal"; +import AstalBattery from "gi://AstalBattery"; + +export const Battery = () => { + const battery = AstalBattery.get_default(); + + return ( + + + + ); +}; diff --git a/widget/bar/index.tsx b/widget/bar/index.tsx new file mode 100644 index 0000000..914d8ea --- /dev/null +++ b/widget/bar/index.tsx @@ -0,0 +1,45 @@ +import { App, Astal, Gdk } from "astal/gtk4"; +import { FocusedClient, WorkspaceButton } from "./workspace"; +import { Battery } from "./battery"; +import { Launcher } from "./launcher"; + +export const WINDOW_NAME = "bar"; + +export const Bar = (gdkmonitor: Gdk.Monitor) => { + const { TOP, LEFT, RIGHT } = Astal.WindowAnchor; + + return ( + + + +
+ + + + ); +}; + +const Start = () => { + return ( + + + + + ); +}; + +const Center = () => { + return ; +}; + +const End = () => { + return ; +}; diff --git a/widget/bar/launcher.tsx b/widget/bar/launcher.tsx new file mode 100644 index 0000000..441a034 --- /dev/null +++ b/widget/bar/launcher.tsx @@ -0,0 +1,22 @@ +import { App, Gtk } from "astal/gtk4"; +import { Gio } from "astal"; +import { Picture } from "../common"; +import { WINDOW_NAME } from "../app-launcher"; + +export const Launcher = () => { + return ( + + + + ); +}; diff --git a/widget/bar/workspace.tsx b/widget/bar/workspace.tsx new file mode 100644 index 0000000..5130f02 --- /dev/null +++ b/widget/bar/workspace.tsx @@ -0,0 +1,56 @@ +import { Variable, bind } from "astal"; +import { Gtk } from "astal/gtk4"; +import { ButtonProps } from "astal/gtk4/widget"; +import AstalHyprland from "gi://AstalHyprland"; + +type WsButtonProps = ButtonProps & { + ws: AstalHyprland.Workspace; +}; + +const Workspace = ({ ws, ...props }: WsButtonProps) => { + const hyprland = AstalHyprland.get_default(); + const classNames = Variable.derive( + [bind(hyprland, "focusedWorkspace"), bind(hyprland, "clients")], + (fws, _) => { + const classes = ["workspace-button"]; + + const active = fws.id == ws.id; + active && classes.push("active"); + + const occupied = hyprland.get_workspace(ws.id)?.get_clients().length > 0; + occupied && classes.push("occupied"); + return classes; + }, + ); + + return ( +