Fix theme swatches and calendar nav, add collapsible card sections

Claude-Session: https://claude.ai/code/session_014ADhvRXuiTvrXSbConxUey
This commit is contained in:
2026-08-19 14:34:46 -04:00
parent c45b71b74a
commit ace9f73af6
4 changed files with 102 additions and 27 deletions
+24 -3
View File
@@ -497,13 +497,34 @@ tooltip.background label {
background-color: @accent; background-color: @accent;
} }
.widget-section-toggle {
border-top: 1px solid alpha(@foreground, 0.12);
border-left: none;
border-right: none;
border-bottom: none;
border-radius: 0;
background-color: transparent;
padding: 10px 0 2px 0;
margin-top: 4px;
}
.widget-section-toggle:hover .widget-section-title,
.widget-section-toggle:hover .widget-section-caret {
opacity: 1;
color: @accent;
}
.widget-section-title { .widget-section-title {
font-size: 10px; font-size: 10px;
font-weight: bold; font-weight: bold;
opacity: 0.45; opacity: 0.45;
border-top: 1px solid alpha(@foreground, 0.12); }
padding-top: 10px;
margin-top: 4px; .widget-section-caret {
font-size: 12px;
font-weight: bold;
opacity: 0.45;
min-width: 14px;
} }
.agent-mark { .agent-mark {
+58 -9
View File
@@ -1,6 +1,6 @@
import { Gtk } from "ags/gtk3" import { Gtk } from "ags/gtk3"
import { createPoll } from "ags/time" import { createPoll } from "ags/time"
import { For } from "ags" import { For, createState } from "ags"
import { shell } from "../lib/utils" import { shell } from "../lib/utils"
type Limit = { type Limit = {
@@ -198,8 +198,33 @@ const usage = createPoll<AgentUsage>(
(stdout) => parseUsage(stdout), (stdout) => parseUsage(stdout),
) )
function SectionTitle({ title }: { title: string }) { const [limitsExpanded, setLimitsExpanded] = createState(true)
return <label class="widget-section-title" label={title} xalign={0} halign={Gtk.Align.START} /> const [daysExpanded, setDaysExpanded] = createState(true)
const [modelsExpanded, setModelsExpanded] = createState(true)
function Section({ title, expanded, onToggle, content }: {
title: string
expanded: any
onToggle: () => void
content: any
}) {
return (
<box vertical spacing={6}>
<button
class="widget-section-toggle"
tooltipText={expanded.as((open: boolean) => (open ? `Collapse ${title}` : `Expand ${title}`))}
onClicked={onToggle}
>
<box spacing={6}>
<label class="widget-section-title" label={title} xalign={0} halign={Gtk.Align.START} hexpand />
<label class="widget-section-caret" label={expanded.as((open: boolean) => (open ? "-" : "+"))} />
</box>
</button>
<revealer revealChild={expanded} transitionDuration={150}>
{content}
</revealer>
</box>
)
} }
function LimitRow({ limit }: { limit: Limit }) { function LimitRow({ limit }: { limit: Limit }) {
@@ -289,26 +314,50 @@ export function ClaudeUsageCard() {
/> />
</box> </box>
<box vertical spacing={10} visible={usage.as((u) => u.limits.length > 0)}> <box vertical visible={usage.as((u) => u.limits.length > 0)}>
<SectionTitle title="LIMITS" /> <Section
title="LIMITS"
expanded={limitsExpanded}
onToggle={() => setLimitsExpanded(!limitsExpanded.get())}
content={
<box vertical spacing={10}>
<For each={usage.as((u) => u.limits)} id={(limit: Limit) => limit.label}> <For each={usage.as((u) => u.limits)} id={(limit: Limit) => limit.label}>
{(limit: Limit) => <LimitRow limit={limit} />} {(limit: Limit) => <LimitRow limit={limit} />}
</For> </For>
</box> </box>
}
/>
</box>
<box vertical spacing={6} visible={usage.as((u) => u.days.length > 0)}> <box vertical visible={usage.as((u) => u.days.length > 0)}>
<SectionTitle title="TOKENS BY DAY" /> <Section
title="TOKENS BY DAY"
expanded={daysExpanded}
onToggle={() => setDaysExpanded(!daysExpanded.get())}
content={
<box vertical spacing={6}>
<For each={usage.as((u) => u.days)} id={(day: DayUsage) => day.key}> <For each={usage.as((u) => u.days)} id={(day: DayUsage) => day.key}>
{(day: DayUsage) => <DayRow day={day} />} {(day: DayUsage) => <DayRow day={day} />}
</For> </For>
</box> </box>
}
/>
</box>
<box vertical spacing={4} visible={usage.as((u) => u.models.length > 0)}> <box vertical visible={usage.as((u) => u.models.length > 0)}>
<SectionTitle title="TOKENS BY MODEL" /> <Section
title="TOKENS BY MODEL"
expanded={modelsExpanded}
onToggle={() => setModelsExpanded(!modelsExpanded.get())}
content={
<box vertical spacing={4}>
<For each={usage.as((u) => u.models)} id={(model: ModelUsage) => model.key}> <For each={usage.as((u) => u.models)} id={(model: ModelUsage) => model.key}>
{(model: ModelUsage) => <ModelRow model={model} />} {(model: ModelUsage) => <ModelRow model={model} />}
</For> </For>
</box> </box>
}
/>
</box>
</box> </box>
) )
} }
+3 -3
View File
@@ -395,7 +395,7 @@ function CalendarSection() {
tooltipText="Scroll to change month" tooltipText="Scroll to change month"
/> />
<button class="cc-calendar-nav" tooltipText="Previous month" onClicked={() => shiftMonth(-1)}> <button class="cc-calendar-nav" tooltipText="Previous month" onClicked={() => shiftMonth(-1)}>
<label label={""} /> <label label={"<"} />
</button> </button>
<button <button
class="cc-calendar-nav" class="cc-calendar-nav"
@@ -403,10 +403,10 @@ function CalendarSection() {
visible={monthOffset.as((offset) => offset !== 0)} visible={monthOffset.as((offset) => offset !== 0)}
onClicked={resetMonth} onClicked={resetMonth}
> >
<label label={""} /> <label label={"Today"} />
</button> </button>
<button class="cc-calendar-nav" tooltipText="Next month" onClicked={() => shiftMonth(1)}> <button class="cc-calendar-nav" tooltipText="Next month" onClicked={() => shiftMonth(1)}>
<label label={""} /> <label label={">"} />
</button> </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} />
+8 -3
View File
@@ -11,8 +11,10 @@ const COLUMNS = 3
type Theme = { slug: string; name: string; swatches: string[] } type Theme = { slug: string; name: string; swatches: string[] }
const swatchProgram = const swatchProgram =
"{ key=$1; value=$2; gsub(/[ \\t]/, \"\", key); gsub(/[ \"\\t]/, \"\", value); " + "{ key=$1; value=$2; gsub(/[ \\t]/, \"\", key); " +
"if (value ~ /^#/) palette[key] = value } " + "if (match(value, /\"[^\"]*\"/)) value = substr(value, RSTART + 1, RLENGTH - 2); " +
"else { sub(/#.*$/, \"\", value); gsub(/[ \\t]/, \"\", value) } " +
"if (value ~ /^#[0-9A-Fa-f]+$/) palette[key] = value } " +
"END { split(\"color1 color2 color3 color4 color5 color6\", legacy, \" \"); " + "END { split(\"color1 color2 color3 color4 color5 color6\", legacy, \" \"); " +
"split(\"red green yellow blue magenta cyan\", named, \" \"); " + "split(\"red green yellow blue magenta cyan\", named, \" \"); " +
"for (i = 1; i <= 6; i++) { " + "for (i = 1; i <= 6; i++) { " +
@@ -41,7 +43,10 @@ const themes = createPoll<Theme[]>(
.filter((line) => line.length > 0) .filter((line) => line.length > 0)
.map((line) => { .map((line) => {
const [slug, name, swatchStr] = line.split("|") const [slug, name, swatchStr] = line.split("|")
return { slug, name: name ?? slug, swatches: (swatchStr ?? "").split(",").filter(Boolean) } const swatches = (swatchStr ?? "")
.split(",")
.filter((color) => /^#[0-9A-Fa-f]{3,8}$/.test(color))
return { slug, name: name ?? slug, swatches }
}), }),
) )