This commit is contained in:
Benjamin Palko 2025-06-17 14:13:35 -04:00
parent e62a7afa60
commit 6b8b5f62ea
7 changed files with 196 additions and 183 deletions

View file

@ -1 +1 @@
bun 1.2.8 bun latest

8
components/Svg.tsx Normal file
View file

@ -0,0 +1,8 @@
interface SvgProps {
color?: string;
rotation?: number;
}
export function Svg({ color, rotation }: SvgProps) {
const svgPath = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" ${rotation && 'transform="rotate(180 12 12)"'} fill="none" stroke="${color || "#cac9c9"}" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M13.73 4a2 2 0 0 0-3.46 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/></svg>`;
}

View file

@ -1,15 +1,16 @@
$crust: #181926; $primary: #1fb854;
$mantle: #1e2030; $secondary: #1eb88e;
$base: #24273a; $accent: #1fb8ab;
$neutral: #19362d;
$base100: #1b1717;
$base200: #161212;
$base300: #110d0d;
$basecontent: #cac9c9;
$surface0: #363a4f; $info: #00b5ff;
$surface1: #494d64; $success: #00a96e;
$surface2: #5b6078; $warning: #ffbe00;
$error: #ff5861;
$rosewater: #f4dbd6;
$flamingo: #dd7878;
$pink: #ea76cb;
$mauve: #8839ef;
:root { :root {
font-family: JetBrainsMono Nerd Font; font-family: JetBrainsMono Nerd Font;
@ -22,14 +23,16 @@ button {
label { label {
transition: transition:
border 0.5s, border 0.35s,
background 0.5s; background 0.35s,
background: $mantle; color 0.35s;
background: $base100;
} }
label:hover { label:hover {
background: $crust; background: $primary;
border: 3px solid $rosewater; color: $base300;
// border: 3px solid $primary;
} }
} }
@ -37,10 +40,10 @@ label {
all: initial; all: initial;
font-family: inherit; font-family: inherit;
color: $rosewater; font-size: 14px;
padding: 2px 12px; color: $basecontent;
background: $crust; padding: 0 12px;
border: 3px solid $crust; // background: $base200;
border-radius: 8px; border-radius: 8px;
} }
@ -49,53 +52,48 @@ popover > contents {
padding: 4px 12px; padding: 4px 12px;
border-radius: 8px; border-radius: 8px;
border: 4px solid $crust; border: 4px solid $base200;
background: rgba($color: $base, $alpha: 0.75); background: rgba($color: $base100, $alpha: 0.98);
} }
.no-styles { .no-styles {
all: initial; all: initial;
color: $rosewater; color: $primary;
} }
.active { .active {
label { label {
background: $crust; background: $secondary;
color: $base300;
} }
} }
.Arch { .Arch {
-gtk-icon-size: 24px; -gtk-icon-size: 22px;
padding: 4px 8px; padding: 0 6px;
border-radius: 8px;
border: 3px solid $crust;
background: $mantle;
transition:
border 0.5s,
background 0.5s;
&:hover {
background: $crust;
border: 3px solid $rosewater;
}
} }
.Pywal { .Pywal {
label { label {
font-size: 18px; font-size: 16px;
padding: 6px 16px 6px 10px; padding: 4px 16px 4px 10px;
} }
} }
.SwayNC { .SwayNC {
label { label {
font-size: 16px;
padding: 0 12px; padding: 0 12px;
} }
} }
.Tray { .Tray {
margin: 0 4px; margin: 0 4px;
arrow {
all: initial;
}
} }
.Tray > * { .Tray > * {
@ -107,12 +105,11 @@ popover > contents {
} }
.Workspaces { .Workspaces {
background: $mantle; // background: $base300;
border-radius: 8px; // border-radius: 8px;
border: 4px solid $crust;
> * { > * {
margin: 2px; margin: 0 2px;
} }
} }
@ -132,14 +129,14 @@ popover > contents {
margin: 4px; margin: 4px;
padding: 4px; padding: 4px;
border-radius: 8px; border-radius: 8px;
background: $crust; background: $base200;
transition: transition:
border 0.5s, border 0.5s,
background 0.5s; background 0.5s;
&:hover { &:hover {
color: $crust; color: $base300;
background: $rosewater; background: $primary;
} }
} }
@ -148,15 +145,15 @@ popover > contents {
} }
.today { .today {
border: 3px solid $rosewater; border: 3px solid $primary;
} }
.other-month { .other-month {
color: $surface0; color: $accent;
transition: border 0.5s; transition: border 0.5s;
&:hover { &:hover {
border: 3px solid $rosewater; border: 3px solid $secondary;
} }
} }
} }
@ -168,15 +165,19 @@ popover > contents {
window.Bar { window.Bar {
background: transparent; background: transparent;
padding: 4px;
> centerbox { > centerbox {
background: rgba($color: $base, $alpha: 0.75); border-radius: 8px;
border-bottom: 4px solid $crust;
background-color: $base300;
// background: rgba($color: $base300, $alpha: 0.75);
// border-bottom: 4px solid $base200;
> box { > box {
margin: 6px; margin: 5px 2px;
& > * { & > * {
margin: 0 4px; margin: 0 3px;
} }
} }
} }

View file

@ -13,37 +13,37 @@ import Tray from "./Tray";
import WirePlumber from "./WirePlumber"; import WirePlumber from "./WirePlumber";
export default function Bar(gdkmonitor: Gdk.Monitor) { export default function Bar(gdkmonitor: Gdk.Monitor) {
const { TOP, LEFT, RIGHT } = Astal.WindowAnchor; const { TOP, LEFT, RIGHT } = Astal.WindowAnchor;
return ( return (
<window <window
visible visible
cssClasses={["Bar"]} cssClasses={["Bar"]}
gdkmonitor={gdkmonitor} gdkmonitor={gdkmonitor}
exclusivity={Astal.Exclusivity.EXCLUSIVE} exclusivity={Astal.Exclusivity.EXCLUSIVE}
anchor={TOP | LEFT | RIGHT} anchor={TOP | LEFT | RIGHT}
application={App} application={App}
> >
<centerbox cssName="centerbox"> <centerbox cssName="centerbox">
<box halign={Gtk.Align.START}> <box halign={Gtk.Align.START}>
<OS /> <OS />
<Workspaces /> <Workspaces />
<Tray /> <Tray />
<Hyprland.Client /> <Hyprland.Client />
</box> </box>
<box halign={Gtk.Align.CENTER}> <box halign={Gtk.Align.CENTER}>
<Mpris /> <Mpris />
</box> </box>
<box halign={Gtk.Align.END}> <box halign={Gtk.Align.END}>
<Pywal /> <Pywal />
<WirePlumber /> <WirePlumber />
<Internet /> <Bluetooth />
<Bluetooth /> <Internet />
<Memory /> <Memory />
<Calendar /> <Calendar />
<SwayNC /> <SwayNC />
</box> </box>
</centerbox> </centerbox>
</window> </window>
); );
} }

View file

@ -3,46 +3,47 @@ import { bind, derive } from "astal";
import AstalBluetooth from "gi://AstalBluetooth"; import AstalBluetooth from "gi://AstalBluetooth";
const DeviceList = function ({ const DeviceList = function ({
devices, devices,
}: { }: {
devices: Binding<AstalBluetooth.Device[]>; devices: Binding<AstalBluetooth.Device[]>;
}) { }) {
return ( return (
<box vertical> <box vertical>
{devices.as((devices) => { {devices.as((devices) => {
return devices.map((device) => { return devices.map((device) => {
const name = bind(device, "name"); const name = bind(device, "name");
const connected = bind(device, "connected"); const connected = bind(device, "connected");
return ( return (
<button <button
label={bind( label={bind(
derive( derive(
[name, connected], [name, connected],
(name, connected) => `${connected ? "󰂱" : ""} ${name}`, (name, connected) => `${connected ? "󰂱" : ""} ${name}`,
), ),
)} )}
/> />
); );
}); });
})} })}
</box> </box>
); );
}; };
const Bluetooth = function () { const Bluetooth = function () {
const bluetooth = AstalBluetooth.get_default(); const bluetooth = AstalBluetooth.get_default();
const devices = bind(bluetooth, "devices"); const devices = bind(bluetooth, "devices");
const count = devices.as((devices) => devices.length);
return ( return (
<menubutton> <menubutton>
<label label={"󰂯"} /> <label label={devices.as((devices) => `󰂯 ${devices.length}`)} />
<popover> <popover>
<DeviceList devices={devices} /> <DeviceList devices={devices} />
</popover> </popover>
</menubutton> </menubutton>
); );
}; };
export default Bluetooth; export default Bluetooth;

View file

@ -1,44 +1,58 @@
import { bind } from "astal"; import { bind } from "astal";
import { Gdk } from "astal/gtk4"; import { Gdk, Gtk } from "astal/gtk4";
import Hyprland from "gi://AstalHyprland"; import Hyprland from "gi://AstalHyprland";
import Rsvg from "gi://Rsvg?version=2.0";
const Workspaces = function () { const Workspaces = function () {
const hyprland = Hyprland.get_default(); const hyprland = Hyprland.get_default();
const workspaces = bind(hyprland, "workspaces").as((workspaces) => const workspaces = bind(hyprland, "workspaces").as((workspaces) =>
workspaces workspaces
.filter((workspace) => workspace.id > 0) .filter((workspace) => workspace.id > 0)
.sort((a, b) => { .sort((a, b) => {
if (a.id > b.id) { if (a.id > b.id) {
return 1; return 1;
} else if (a.id < b.id) { } else if (a.id < b.id) {
return -1; return -1;
} }
return 0; return 0;
}), }),
); );
const focusedWorkspace = bind(hyprland, "focused_workspace"); const focusedWorkspace = bind(hyprland, "focused_workspace");
const specialWorkspaces = bind(hyprland, "workspaces").as((workspaces) => const specialWorkspaces = bind(hyprland, "workspaces").as((workspaces) =>
workspaces.filter((workspace) => workspace.id < 0), workspaces.filter((workspace) => workspace.id < 0),
); );
return ( Gtk.Image.interface_install_property;
<box cssClasses={["Workspaces"]}>
{workspaces.as((workspaces) => { const activeTrianglePath =
return workspaces.map((workspace) => ( '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#1fb854" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-triangle-icon lucide-triangle"><path d="M13.73 4a2 2 0 0 0-3.46 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/></svg>';
<button const inactiveTrianglePath =
cursor={Gdk.Cursor.new_from_name("pointer", null)} '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" transform="rotate(180 12 12)" fill="none" stroke="#cac9c9" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-triangle-icon lucide-triangle"><path d="M13.73 4a2 2 0 0 0-3.46 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/></svg>';
cssClasses={focusedWorkspace.as((focused) => [
focused.id === workspace.id ? "active" : "", const activeTrianglePixBuf =
])} Rsvg.Handle.new_from_data(activeTrianglePath).get_pixbuf();
onClicked={() => workspace.focus()} const inactiveTrianglePixBuf =
> Rsvg.Handle.new_from_data(inactiveTrianglePath).get_pixbuf();
{workspace?.name}
</button> return (
)); <box cssClasses={["Workspaces"]}>
})} {workspaces.as((workspaces) => {
</box> return workspaces.map((workspace) => (
); <button
cursor={Gdk.Cursor.new_from_name("pointer", null)}
onClicked={() => workspace.focus()}
>
{focusedWorkspace.as((focused) =>
focused.id === workspace.id
? Gtk.Image.new_from_pixbuf(activeTrianglePixBuf)
: Gtk.Image.new_from_pixbuf(inactiveTrianglePixBuf),
)}
</button>
));
})}
</box>
);
}; };
export default Workspaces; export default Workspaces;

View file

@ -1,41 +1,30 @@
import { bind } from "astal"; import { bind } from "astal";
import { Gdk } from "astal/gtk4"; import { Gdk, Gtk } from "astal/gtk4";
import AstalTray from "gi://AstalTray"; import AstalTray from "gi://AstalTray";
const TrayItemPopover = function ({ item }: { item: AstalTray.TrayItem }) { const TrayItem = function ({ item }: { item: AstalTray.TrayItem }) {
const actionGroup = bind(item, "actionGroup"); const popover = Gtk.PopoverMenu.new_from_model(item.menu_model);
popover.insert_action_group("dbusmenu", item.action_group);
return ( return (
<popover> <menubutton
<box>{actionGroup.get()}</box> cursor={Gdk.Cursor.new_from_name("pointer", null)}
</popover> icon_name={item.icon_name}
); popover={popover}
/>
);
}; };
const Tray = function () { const Tray = function () {
const tray = AstalTray.get_default(); const tray = AstalTray.get_default();
const trayItems = bind(tray, "items"); return (
<box cssClasses={["Tray"]}>
return ( {bind(tray, "items").as((items) =>
<box cssClasses={["Tray"]}> items.map((item) => TrayItem({ item })),
{trayItems.as((items) => )}
items.map((item) => { </box>
return ( );
<menubutton cursor={Gdk.Cursor.new_from_name("pointer", null)}>
<image
tooltip_text={bind(item, "tooltip_markup")}
file={bind(item, "icon_name").as(
(iconName) => iconName || "NONE",
)}
/>
<TrayItemPopover item={item} />
</menubutton>
);
}),
)}
</box>
);
}; };
export default Tray; export default Tray;