Move bar indicators into AGS, add calendar navigation and tooltips
Claude-Session: https://claude.ai/code/session_014ADhvRXuiTvrXSbConxUey
This commit is contained in:
+19
-27
@@ -587,32 +587,9 @@
|
|||||||
color: @foreground;
|
color: @foreground;
|
||||||
}
|
}
|
||||||
|
|
||||||
.agent-model-bar {
|
.agent-model-row {
|
||||||
min-height: 22px;
|
min-height: 22px;
|
||||||
}
|
padding: 3px 8px;
|
||||||
|
|
||||||
.agent-model-bar trough {
|
|
||||||
min-height: 22px;
|
|
||||||
border-radius: 0;
|
|
||||||
background-color: alpha(@foreground, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.agent-model-bar block {
|
|
||||||
min-height: 22px;
|
|
||||||
min-width: 0;
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.agent-model-bar block.filled {
|
|
||||||
background-color: alpha(@foreground, 0.18);
|
|
||||||
}
|
|
||||||
|
|
||||||
.agent-model-bar block.empty {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.agent-model-text {
|
|
||||||
padding: 0 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.agent-model-label {
|
.agent-model-label {
|
||||||
@@ -713,11 +690,25 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.cc-calendar-month {
|
.cc-calendar-month {
|
||||||
font-size: 13px;
|
font-size: 15px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: @accent;
|
color: @accent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cc-calendar-nav {
|
||||||
|
color: @foreground;
|
||||||
|
font-size: 13px;
|
||||||
|
min-width: 24px;
|
||||||
|
padding: 2px 4px;
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cc-calendar-nav:hover {
|
||||||
|
color: @accent;
|
||||||
|
background-color: alpha(@foreground, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
.cc-calendar-time {
|
.cc-calendar-time {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: @foreground;
|
color: @foreground;
|
||||||
@@ -725,6 +716,7 @@
|
|||||||
|
|
||||||
.cc-calendar-grid {
|
.cc-calendar-grid {
|
||||||
font-family: "JetBrainsMono Nerd Font", monospace;
|
font-family: "JetBrainsMono Nerd Font", monospace;
|
||||||
font-size: 13px;
|
font-size: 17px;
|
||||||
color: @foreground;
|
color: @foreground;
|
||||||
|
padding: 4px 2px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -236,18 +236,14 @@ function DayRow({ day }: { day: DayUsage }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function ModelRow({ model }: { model: ModelUsage }) {
|
function ModelRow({ model }: { model: ModelUsage }) {
|
||||||
|
const fillPercent = Math.round(model.fraction * 100)
|
||||||
|
const fill = `linear-gradient(to right, alpha(@foreground, 0.18) ${fillPercent}%, alpha(@foreground, 0.06) ${fillPercent}%)`
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<overlay
|
<box class="agent-model-row" spacing={8} tooltipText={model.detail} css={`background-image: ${fill};`}>
|
||||||
tooltipText={model.detail}
|
|
||||||
overlays={[
|
|
||||||
<box class="agent-model-text" spacing={8}>
|
|
||||||
<label class="agent-model-label" label={model.label} xalign={0} halign={Gtk.Align.START} hexpand />
|
<label class="agent-model-label" label={model.label} xalign={0} halign={Gtk.Align.START} hexpand />
|
||||||
<label class="agent-model-value" label={model.tokens} xalign={1} halign={Gtk.Align.END} />
|
<label class="agent-model-value" label={model.tokens} xalign={1} halign={Gtk.Align.END} />
|
||||||
</box>,
|
</box>
|
||||||
]}
|
|
||||||
>
|
|
||||||
<levelbar class="agent-model-bar" value={model.fraction} />
|
|
||||||
</overlay>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+114
-13
@@ -1,5 +1,5 @@
|
|||||||
import app from "ags/gtk3/app"
|
import app from "ags/gtk3/app"
|
||||||
import { Astal, Gtk } from "ags/gtk3"
|
import { Astal, Gdk, Gtk } from "ags/gtk3"
|
||||||
import { createState } from "ags"
|
import { createState } from "ags"
|
||||||
import { createPoll, interval } from "ags/time"
|
import { createPoll, interval } from "ags/time"
|
||||||
import { sh, shell } from "../lib/utils"
|
import { sh, shell } from "../lib/utils"
|
||||||
@@ -52,6 +52,21 @@ const [nightLight, setNightLight] = pollBool(
|
|||||||
2000,
|
2000,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const [stayAwake, setStayAwake] = pollBool(
|
||||||
|
"omarchy-toggle-idle status 2>/dev/null | grep -q '\"enabled\":true' && echo on || echo off",
|
||||||
|
2000,
|
||||||
|
)
|
||||||
|
|
||||||
|
const [dictating] = pollBool(
|
||||||
|
"omarchy-voxtype-status 2>/dev/null | grep -q recording && echo on || echo off",
|
||||||
|
2000,
|
||||||
|
)
|
||||||
|
|
||||||
|
const [recording] = pollBool(
|
||||||
|
"pgrep -f '^gpu-screen-recorder' >/dev/null && echo on || echo off",
|
||||||
|
2000,
|
||||||
|
)
|
||||||
|
|
||||||
const clock = createPoll("", 1000, shell("date '+%H:%M'"), (stdout) => stdout.trim())
|
const clock = createPoll("", 1000, shell("date '+%H:%M'"), (stdout) => stdout.trim())
|
||||||
const today = createPoll("", 10000, shell("date '+%A, %B %-d'"), (stdout) => stdout.trim())
|
const today = createPoll("", 10000, shell("date '+%A, %B %-d'"), (stdout) => stdout.trim())
|
||||||
|
|
||||||
@@ -109,16 +124,19 @@ function Tile({
|
|||||||
icon,
|
icon,
|
||||||
label,
|
label,
|
||||||
active,
|
active,
|
||||||
|
tooltip,
|
||||||
onClicked,
|
onClicked,
|
||||||
}: {
|
}: {
|
||||||
icon: string
|
icon: string
|
||||||
label: string
|
label: string
|
||||||
active?: any
|
active?: any
|
||||||
|
tooltip?: any
|
||||||
onClicked: () => void
|
onClicked: () => void
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
class={active ? active.as((on: boolean) => (on ? "qs-tile active" : "qs-tile")) : "qs-tile"}
|
class={active ? active.as((on: boolean) => (on ? "qs-tile active" : "qs-tile")) : "qs-tile"}
|
||||||
|
tooltipText={tooltip ?? label}
|
||||||
onClicked={onClicked}
|
onClicked={onClicked}
|
||||||
hexpand
|
hexpand
|
||||||
>
|
>
|
||||||
@@ -172,9 +190,37 @@ function Toggles() {
|
|||||||
sh("omarchy-toggle-nightlight")
|
sh("omarchy-toggle-nightlight")
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Tile icon={""} label="Record" onClicked={() => { closePanel(); sh("omarchy-capture-screenrecording") }} />
|
<Tile
|
||||||
|
icon={""}
|
||||||
|
label="Record"
|
||||||
|
active={recording}
|
||||||
|
tooltip={recording.as((on: boolean) => (on ? "Stop screen recording" : "Start a screen recording"))}
|
||||||
|
onClicked={() => {
|
||||||
|
closePanel()
|
||||||
|
sh(recording.get()
|
||||||
|
? "omarchy-capture-screenrecording --stop-recording"
|
||||||
|
: "omarchy-menu toggle trigger.capture.screenrecord")
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</box>
|
</box>
|
||||||
<box class="qs-tiles" spacing={8} homogeneous>
|
<box class="qs-tiles" spacing={8} homogeneous>
|
||||||
|
<Tile
|
||||||
|
icon={""}
|
||||||
|
label="Stay Awake"
|
||||||
|
active={stayAwake}
|
||||||
|
tooltip={stayAwake.as((on: boolean) => (on ? "Allow idle lock and screensaver" : "Keep the screen awake"))}
|
||||||
|
onClicked={() => {
|
||||||
|
setStayAwake(!stayAwake.get())
|
||||||
|
sh("omarchy-toggle-idle toggle")
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Tile
|
||||||
|
icon={""}
|
||||||
|
label="Dictate"
|
||||||
|
active={dictating}
|
||||||
|
tooltip={dictating.as((on: boolean) => (on ? "Stop dictation" : "Start voxtype dictation"))}
|
||||||
|
onClicked={() => sh("voxtype record toggle")}
|
||||||
|
/>
|
||||||
<Tile icon={""} label="Pick Color" onClicked={() => { closePanel(); sh("hyprpicker -a") }} />
|
<Tile icon={""} label="Pick Color" onClicked={() => { closePanel(); sh("hyprpicker -a") }} />
|
||||||
<Tile
|
<Tile
|
||||||
icon={""}
|
icon={""}
|
||||||
@@ -204,6 +250,7 @@ function VolumeSlider() {
|
|||||||
</button>
|
</button>
|
||||||
<slider
|
<slider
|
||||||
class="qs-slider"
|
class="qs-slider"
|
||||||
|
tooltipText={volume.as((level: number) => `Volume ${Math.round(level)}%`)}
|
||||||
hexpand
|
hexpand
|
||||||
min={0}
|
min={0}
|
||||||
max={100}
|
max={100}
|
||||||
@@ -227,6 +274,7 @@ function BrightnessSlider() {
|
|||||||
<label class="qs-slider-icon" label={""} />
|
<label class="qs-slider-icon" label={""} />
|
||||||
<slider
|
<slider
|
||||||
class="qs-slider"
|
class="qs-slider"
|
||||||
|
tooltipText={brightness.as((level: number) => `Brightness ${Math.round(level)}%`)}
|
||||||
hexpand
|
hexpand
|
||||||
min={1}
|
min={1}
|
||||||
max={100}
|
max={100}
|
||||||
@@ -292,13 +340,13 @@ function MediaPlayer() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<box class="cc-media-controls" spacing={14} halign={Gtk.Align.CENTER}>
|
<box class="cc-media-controls" spacing={14} halign={Gtk.Align.CENTER}>
|
||||||
<button onClicked={() => sh("playerctl previous")}>
|
<button tooltipText="Previous track" onClicked={() => sh("playerctl previous")}>
|
||||||
<label label={""} />
|
<label label={""} />
|
||||||
</button>
|
</button>
|
||||||
<button onClicked={() => sh("playerctl play-pause")}>
|
<button tooltipText="Play / pause" onClicked={() => sh("playerctl play-pause")}>
|
||||||
<label label={media.as((m) => (m.status === "Playing" ? "" : ""))} />
|
<label label={media.as((m) => (m.status === "Playing" ? "" : ""))} />
|
||||||
</button>
|
</button>
|
||||||
<button onClicked={() => sh("playerctl next")}>
|
<button tooltipText="Next track" onClicked={() => sh("playerctl next")}>
|
||||||
<label label={""} />
|
<label label={""} />
|
||||||
</button>
|
</button>
|
||||||
</box>
|
</box>
|
||||||
@@ -307,22 +355,75 @@ function MediaPlayer() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const monthName = createPoll("", 60000, shell("date '+%B %Y'"), (stdout) => stdout.trim())
|
const [monthOffset, setMonthOffset] = createState(0)
|
||||||
const monthGrid = createPoll(
|
const [monthName, setMonthName] = createState("")
|
||||||
"",
|
const [monthGrid, setMonthGrid] = createState("")
|
||||||
60000,
|
|
||||||
shell("cal | sed '1d'"),
|
const loadMonth = (offset: number) => {
|
||||||
(stdout) => stdout.replace(/\s+$/, ""),
|
const firstOfMonth = `$(date +%Y-%m-01) ${offset} months`
|
||||||
)
|
const command =
|
||||||
|
`date -d "${firstOfMonth}" '+%B %Y'; ` +
|
||||||
|
`cal $(date -d "${firstOfMonth}" '+%m %Y') | sed '1d'`
|
||||||
|
|
||||||
|
sh(command).then((stdout) => {
|
||||||
|
const lines = String(stdout).replace(/\s+$/, "").split("\n")
|
||||||
|
setMonthName(lines[0]?.trim() ?? "")
|
||||||
|
setMonthGrid(lines.slice(1).join("\n"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const shiftMonth = (delta: number) => {
|
||||||
|
const next = monthOffset.get() + delta
|
||||||
|
setMonthOffset(next)
|
||||||
|
loadMonth(next)
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetMonth = () => {
|
||||||
|
setMonthOffset(0)
|
||||||
|
loadMonth(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
loadMonth(0)
|
||||||
|
interval(3600000, () => { if (monthOffset.get() === 0) loadMonth(0) })
|
||||||
|
|
||||||
function CalendarSection() {
|
function CalendarSection() {
|
||||||
return (
|
return (
|
||||||
|
<eventbox
|
||||||
|
onScroll={(_self: any, event: any) => {
|
||||||
|
const direction = event?.direction
|
||||||
|
const deltaY = Number(event?.delta_y ?? 0)
|
||||||
|
if (direction === Gdk.ScrollDirection.UP || deltaY < 0) shiftMonth(-1)
|
||||||
|
else if (direction === Gdk.ScrollDirection.DOWN || deltaY > 0) shiftMonth(1)
|
||||||
|
}}
|
||||||
|
>
|
||||||
<box class="cc-calendar" vertical spacing={6}>
|
<box class="cc-calendar" vertical spacing={6}>
|
||||||
<box class="cc-calendar-head" spacing={8}>
|
<box class="cc-calendar-head" spacing={8}>
|
||||||
<label class="cc-calendar-month" label={monthName} xalign={0} hexpand halign={Gtk.Align.START} />
|
<label
|
||||||
|
class="cc-calendar-month"
|
||||||
|
label={monthName}
|
||||||
|
xalign={0}
|
||||||
|
hexpand
|
||||||
|
halign={Gtk.Align.START}
|
||||||
|
tooltipText="Scroll to change month"
|
||||||
|
/>
|
||||||
|
<button class="cc-calendar-nav" tooltipText="Previous month" onClicked={() => shiftMonth(-1)}>
|
||||||
|
<label label={""} />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="cc-calendar-nav"
|
||||||
|
tooltipText="Back to this month"
|
||||||
|
visible={monthOffset.as((offset) => offset !== 0)}
|
||||||
|
onClicked={resetMonth}
|
||||||
|
>
|
||||||
|
<label label={""} />
|
||||||
|
</button>
|
||||||
|
<button class="cc-calendar-nav" tooltipText="Next month" onClicked={() => shiftMonth(1)}>
|
||||||
|
<label label={""} />
|
||||||
|
</button>
|
||||||
</box>
|
</box>
|
||||||
<label class="cc-calendar-grid" label={monthGrid} halign={Gtk.Align.CENTER} />
|
<label class="cc-calendar-grid" label={monthGrid} halign={Gtk.Align.CENTER} />
|
||||||
</box>
|
</box>
|
||||||
|
</eventbox>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,12 @@ function Metric({
|
|||||||
detail: any
|
detail: any
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<box class="sys-metric" vertical spacing={4}>
|
<box
|
||||||
|
class="sys-metric"
|
||||||
|
vertical
|
||||||
|
spacing={4}
|
||||||
|
tooltipText={detail.as((value: string) => `${name}: ${value}`)}
|
||||||
|
>
|
||||||
<box spacing={8}>
|
<box spacing={8}>
|
||||||
<label class="sys-metric-icon" label={icon} />
|
<label class="sys-metric-icon" label={icon} />
|
||||||
<label class="sys-metric-name" label={name} xalign={0} halign={Gtk.Align.START} hexpand />
|
<label class="sys-metric-name" label={name} xalign={0} halign={Gtk.Align.START} hexpand />
|
||||||
|
|||||||
+9
-6
@@ -14,10 +14,10 @@ Ported from the old `waybar/config.jsonc`, section for section:
|
|||||||
| `hyprland/workspaces` | `blob.workspaces` (cloned plugin) |
|
| `hyprland/workspaces` | `blob.workspaces` (cloned plugin) |
|
||||||
| `clock` | `blob.clock` (custom module) |
|
| `clock` | `blob.clock` (custom module) |
|
||||||
| `custom/update` | `omarchy.system-update` |
|
| `custom/update` | `omarchy.system-update` |
|
||||||
| `custom/voxtype` | `omarchy.indicators` -> `Dictation` |
|
| `custom/voxtype` | AGS quick settings -> Dictate tile |
|
||||||
| `custom/screenrecording-indicator` | `omarchy.indicators` -> `ScreenRecording` |
|
| `custom/screenrecording-indicator` | AGS quick settings -> Record tile |
|
||||||
| `custom/idle-indicator` | `omarchy.indicators` -> `StayAwake` |
|
| `custom/idle-indicator` | AGS quick settings -> Stay Awake tile |
|
||||||
| `custom/notification-silencing-indicator` | `omarchy.indicators` -> `Dnd` |
|
| `custom/notification-silencing-indicator` | AGS quick settings -> Silence tile |
|
||||||
| `group/tray-expander` | `omarchy.tray` |
|
| `group/tray-expander` | `omarchy.tray` |
|
||||||
| `bluetooth` | `omarchy.bluetooth` |
|
| `bluetooth` | `omarchy.bluetooth` |
|
||||||
| `network` | `omarchy.network` |
|
| `network` | `omarchy.network` |
|
||||||
@@ -28,8 +28,11 @@ Ported from the old `waybar/config.jsonc`, section for section:
|
|||||||
| (new in Omarchy 4) | `omarchy.agents` - AI agent usage |
|
| (new in Omarchy 4) | `omarchy.agents` - AI agent usage |
|
||||||
| (new in Omarchy 4) | `omarchy.monitor` - brightness and display controls |
|
| (new in Omarchy 4) | `omarchy.monitor` - brightness and display controls |
|
||||||
|
|
||||||
Drop the `items` list from `omarchy.indicators` to show all six indicators
|
The bar carries no `omarchy.indicators` widget: those four toggles live in the
|
||||||
(adds `NightLight` and `Reminder`).
|
AGS quick settings panel instead, so the bar keeps only the clock and the
|
||||||
|
update indicator in its center. Add `{ "id": "omarchy.indicators" }` back to
|
||||||
|
`center` to restore them (omit `items` for all six, which adds `NightLight`
|
||||||
|
and `Reminder`).
|
||||||
|
|
||||||
## Custom modules
|
## Custom modules
|
||||||
|
|
||||||
|
|||||||
@@ -29,15 +29,6 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "omarchy.system-update"
|
"id": "omarchy.system-update"
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "omarchy.indicators",
|
|
||||||
"items": [
|
|
||||||
"Dictation",
|
|
||||||
"ScreenRecording",
|
|
||||||
"StayAwake",
|
|
||||||
"Dnd"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"right": [
|
"right": [
|
||||||
|
|||||||
Reference in New Issue
Block a user