added
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.6 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 246 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 MiB |
@@ -12,6 +12,7 @@
|
||||
../packages/matugen/default.nix
|
||||
../packages/swww/default.nix
|
||||
../packages/quickshell/default.nix
|
||||
../packages/ags/default.nix
|
||||
]
|
||||
++ lib.optionals (myConfig.linux.gaming == true) [
|
||||
../packages/mangohud/default.nix
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
node_modules/
|
||||
@girs/
|
||||
@@ -0,0 +1,17 @@
|
||||
import { App } from "astal/gtk3"
|
||||
import style from "./style.scss"
|
||||
import Bar from "./widget/Bar"
|
||||
|
||||
App.start({
|
||||
css: style,
|
||||
instanceName: "js",
|
||||
requestHandler(request, res) {
|
||||
print(request)
|
||||
res("ok")
|
||||
},
|
||||
main: () => {
|
||||
const monitors = App.get_monitors()
|
||||
const primary = monitors.find(m => m.primary) || monitors[0]
|
||||
return Bar(primary)
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,3 @@
|
||||
$background: #131318;
|
||||
$foreground: #e4e1e9;
|
||||
$primary: #bec2ff;
|
||||
Vendored
+21
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "astal-shell",
|
||||
"dependencies": {
|
||||
"astal": "/home/biscuit/.local/share/ags"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
@use "sass:color";
|
||||
@use "./colors" as *;
|
||||
|
||||
// default
|
||||
// $bg: #212223;
|
||||
// $fg: #f1f1f1;
|
||||
// $accent: #378DF7;
|
||||
// $radius: 7px;
|
||||
|
||||
// Kanagawa Theme
|
||||
// $bg: #1F1F28;
|
||||
// $fg: #DCD7BA;
|
||||
// $accent: #C0A36E;
|
||||
// $radius: 7px;
|
||||
|
||||
// mstcl
|
||||
// $bg: #121212;
|
||||
// $fg: #f1f1f1;
|
||||
// $accent: #C0A36E;
|
||||
// $radius: 7px;
|
||||
|
||||
$bg: $background;
|
||||
$fg: $foreground;
|
||||
$accent: $primary;
|
||||
$radius: 7px;
|
||||
|
||||
window.Bar {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
background-color: $bg;
|
||||
color: $fg;
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
font-family: "JetBrainsMono Nerd Font";
|
||||
|
||||
label {
|
||||
margin: 0 8px;
|
||||
}
|
||||
|
||||
.Workspaces {
|
||||
button {
|
||||
all: unset;
|
||||
background-color: transparent;
|
||||
|
||||
&:hover label {
|
||||
background-color: color.adjust($fg, $alpha: -0.84);
|
||||
border-color: color.adjust($accent, $alpha: -0.8);
|
||||
}
|
||||
|
||||
&:active label {
|
||||
background-color: color.adjust($fg, $alpha: -0.8)
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
transition: 200ms;
|
||||
padding: 0 8px;
|
||||
margin: 2px;
|
||||
border-radius: $radius;
|
||||
border: 1pt solid transparent;
|
||||
}
|
||||
|
||||
.focused label {
|
||||
color: $accent;
|
||||
border-color: $accent;
|
||||
}
|
||||
}
|
||||
|
||||
.SysTray {
|
||||
margin-right: 8px;
|
||||
|
||||
button {
|
||||
padding: 0 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.Time {
|
||||
.TimeHM {
|
||||
font-weight: bold;
|
||||
color: $accent;
|
||||
}
|
||||
|
||||
.TimeDate {
|
||||
// color: color.adjust($fg, $lightness: -10%);
|
||||
color: $fg;
|
||||
opacity: 0.85;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.FocusedClient {
|
||||
color: color.adjust($fg, $lightness: -30%);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.Media .Cover {
|
||||
min-height: 1.2em;
|
||||
min-width: 1.2em;
|
||||
border-radius: $radius;
|
||||
background-position: center;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.Battery label {
|
||||
padding-left: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.AudioSlider {
|
||||
* {
|
||||
all: unset;
|
||||
}
|
||||
|
||||
icon {
|
||||
margin-right: .6em;
|
||||
}
|
||||
|
||||
& {
|
||||
margin: 0 1em;
|
||||
}
|
||||
|
||||
trough {
|
||||
background-color: color.adjust($fg, $alpha: -0.8);
|
||||
border-radius: $radius;
|
||||
}
|
||||
|
||||
highlight {
|
||||
background-color: $accent;
|
||||
min-height: .8em;
|
||||
border-radius: $radius;
|
||||
}
|
||||
|
||||
slider {
|
||||
background-color: $fg;
|
||||
border-radius: $radius;
|
||||
min-height: 1em;
|
||||
min-width: 1em;
|
||||
margin: -.2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
import { App } from "astal/gtk3"
|
||||
import { Variable, GLib, bind } from "astal"
|
||||
import { Astal, Gtk, Gdk } from "astal/gtk3"
|
||||
import Hyprland from "gi://AstalHyprland"
|
||||
import Mpris from "gi://AstalMpris"
|
||||
import Battery from "gi://AstalBattery"
|
||||
import Wp from "gi://AstalWp"
|
||||
import Network from "gi://AstalNetwork"
|
||||
import Tray from "gi://AstalTray"
|
||||
|
||||
function SysTray() {
|
||||
const tray = Tray.get_default()
|
||||
|
||||
return <box className="SysTray">
|
||||
{bind(tray, "items").as(items => items.map(item => (
|
||||
<menubutton
|
||||
tooltipMarkup={bind(item, "tooltipMarkup")}
|
||||
usePopover={false}
|
||||
actionGroup={bind(item, "actionGroup").as(ag => ["dbusmenu", ag])}
|
||||
menuModel={bind(item, "menuModel")}>
|
||||
<icon gicon={bind(item, "gicon")} />
|
||||
</menubutton>
|
||||
)))}
|
||||
</box>
|
||||
}
|
||||
|
||||
function Wifi() {
|
||||
const network = Network.get_default()
|
||||
const wifi = bind(network, "wifi")
|
||||
|
||||
return <box visible={wifi.as(Boolean)}>
|
||||
{wifi.as(wifi => wifi && (
|
||||
<icon
|
||||
tooltipText={bind(wifi, "ssid").as(String)}
|
||||
className="Wifi"
|
||||
icon={bind(wifi, "iconName")}
|
||||
/>
|
||||
))}
|
||||
</box>
|
||||
|
||||
}
|
||||
|
||||
function AudioSlider() {
|
||||
const speaker = Wp.get_default()?.audio.defaultSpeaker!
|
||||
|
||||
return <box className="AudioSlider" css="min-width: 140px">
|
||||
<icon icon={bind(speaker, "volumeIcon")} />
|
||||
<slider
|
||||
hexpand
|
||||
onDragged={({ value }) => speaker.volume = value}
|
||||
value={bind(speaker, "volume")}
|
||||
/>
|
||||
</box>
|
||||
}
|
||||
|
||||
function BatteryLevel() {
|
||||
const bat = Battery.get_default()
|
||||
|
||||
return <box className="Battery"
|
||||
visible={bind(bat, "isPresent")}>
|
||||
<icon icon={bind(bat, "batteryIconName")} />
|
||||
<label label={bind(bat, "percentage").as(p =>
|
||||
`${Math.floor(p * 100)} %`
|
||||
)} />
|
||||
</box>
|
||||
}
|
||||
|
||||
function Media() {
|
||||
const mpris = Mpris.get_default()
|
||||
|
||||
return <box className="Media">
|
||||
{bind(mpris, "players").as(ps => ps[0] ? (
|
||||
<box>
|
||||
<box
|
||||
className="Cover"
|
||||
valign={Gtk.Align.CENTER}
|
||||
css={bind(ps[0], "coverArt").as(cover =>
|
||||
`background-image: url('${cover}');`
|
||||
)}
|
||||
/>
|
||||
<label
|
||||
label={bind(ps[0], "metadata").as(() =>
|
||||
`${ps[0].title} - ${ps[0].artist}`
|
||||
)}
|
||||
/>
|
||||
</box>
|
||||
) : (
|
||||
<label label="Biscuit" />
|
||||
))}
|
||||
</box>
|
||||
}
|
||||
|
||||
|
||||
function Workspaces() {
|
||||
const hypr = Hyprland.get_default();
|
||||
|
||||
return (
|
||||
<box className="Workspaces">
|
||||
{bind(hypr, "focusedWorkspace").as((fw) => {
|
||||
if (!fw) return null;
|
||||
|
||||
// Determine the current chunk of 5 visible workspace buttons
|
||||
const currentChunkStart = Math.floor((fw.id - 1) / 5) * 5 + 1;
|
||||
const visibleIds = Array.from({ length: 5 }, (_, i) => currentChunkStart + i);
|
||||
|
||||
return visibleIds.map((id) => {
|
||||
// Try to get the real workspace, fall back to a dummy one if it doesn't exist
|
||||
const ws =
|
||||
hypr.workspaces.find((w) => w.id === id) ??
|
||||
Hyprland.Workspace.dummy(id, null);
|
||||
|
||||
return (
|
||||
<button
|
||||
className={fw.id === id ? "focused" : ""}
|
||||
onClick={() => ws.focus()}
|
||||
>
|
||||
{id}
|
||||
</button>
|
||||
);
|
||||
});
|
||||
})}
|
||||
</box>
|
||||
);
|
||||
}
|
||||
|
||||
function FocusedClient() {
|
||||
const hypr = Hyprland.get_default();
|
||||
const focused = bind(hypr, "focusedClient");
|
||||
|
||||
return (
|
||||
<box className="FocusedClient" visible={focused.as(Boolean)}>
|
||||
{focused.as(client => {
|
||||
if (!client) return null;
|
||||
|
||||
return (
|
||||
<label
|
||||
label={bind(client, "title").as(title => {
|
||||
return title.length > 40
|
||||
? title.slice(0, 37) + "..."
|
||||
: title;
|
||||
})}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</box>
|
||||
);
|
||||
}
|
||||
function Time({ format = "%H:%M|%a %b %d" }) {
|
||||
const time = Variable<string>("").poll(1000, () =>
|
||||
GLib.DateTime.new_now_local().format(format)!
|
||||
);
|
||||
|
||||
return bind(time).as(str => {
|
||||
const [hm, date] = str.split("|");
|
||||
|
||||
return (
|
||||
<box className="Time">
|
||||
<label className="TimeHM" label={hm} />
|
||||
<label className="TimeDate" label={date} />
|
||||
</box>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export default function Bar(monitor: Gdk.Monitor) {
|
||||
const { TOP, LEFT, RIGHT } = Astal.WindowAnchor
|
||||
|
||||
return <window
|
||||
className="Bar"
|
||||
gdkmonitor={monitor}
|
||||
exclusivity={Astal.Exclusivity.EXCLUSIVE}
|
||||
anchor={TOP | LEFT | RIGHT}>
|
||||
<centerbox>
|
||||
<box hexpand halign={Gtk.Align.START}>
|
||||
<Workspaces />
|
||||
<FocusedClient />
|
||||
</box>
|
||||
<box>
|
||||
<Time />
|
||||
</box>
|
||||
<box hexpand halign={Gtk.Align.END} >
|
||||
<SysTray />
|
||||
<Wifi />
|
||||
<AudioSlider />
|
||||
<BatteryLevel />
|
||||
</box>
|
||||
</centerbox>
|
||||
</window>
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
inputs,
|
||||
pkgs,
|
||||
system,
|
||||
...
|
||||
}: {
|
||||
imports = [inputs.ags.homeManagerModules.default];
|
||||
|
||||
programs.ags = {
|
||||
enable = true;
|
||||
configDir = ./biscuit;
|
||||
|
||||
extraPackages = let
|
||||
agsPkgs = inputs.ags.packages.${system};
|
||||
in
|
||||
with pkgs; [
|
||||
agsPkgs.battery
|
||||
agsPkgs.hyprland
|
||||
agsPkgs.mpris
|
||||
agsPkgs.wireplumber
|
||||
agsPkgs.notifd
|
||||
agsPkgs.apps
|
||||
agsPkgs.network
|
||||
agsPkgs.tray
|
||||
fzf
|
||||
];
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user