replace the old dotfiles with the sakura line rice

The bare $HOME repo is gone. This is now the rice: install.sh, rice-*
scripts in bin/, configs in home/ linked into $HOME, theme templates, st
and the particle wallpaper. Clone to ~/rice and run install.sh.
This commit is contained in:
Sarthak 2026-09-14 16:12:09 +05:30
commit 7f41cf7a82
104 changed files with 8077 additions and 0 deletions

217
home/.config/eww/eww.scss Normal file
View file

@ -0,0 +1,217 @@
// Sakura Line - eww design system.
// The same tokens as polybar, rofi and dunst: the theme's alpha, 1px border,
// 12px radius, rows with an 8px radius and a 17% accent tint on hover.
// colors.scss is a link to ~/.local/state/rice/theme/eww-colors.scss.
@import "colors";
$tint: rgba($accent, 0.17);
* {
all: unset;
font-family: $font, "Symbols Nerd Font";
}
window, .closer { background-color: transparent; }
tooltip {
background-color: rgba($bg, 0.95);
border: $border-width solid $border;
border-radius: $radius-small;
padding: 6px 10px;
color: $fg;
font-size: 11px;
}
// ------------------------------------------------------------------ panel
.panel {
background-color: rgba($bg, $alpha);
border: $border-width solid $border;
border-radius: $radius;
padding: 4px 8px 10px 8px;
color: $fg;
font-size: 13px;
}
.head {
padding: 10px 10px 12px 10px;
margin-bottom: 6px;
border-bottom: $border-width solid $border;
}
.title { font-weight: bold; font-size: 14px; }
.head-action {
color: $dim;
font-size: 15px;
padding: 2px 7px;
margin-left: 2px;
border-radius: $radius-small;
}
.head-action:hover { color: $fg; background-color: $tint; }
.head-action.active { color: $accent; }
.section {
color: $dim;
font-size: 11px;
font-weight: 600;
padding: 10px 10px 4px 10px;
}
.sep {
min-height: 1px;
margin: 6px 4px;
background-color: $border;
}
.empty { color: $dim; padding: 8px 10px; }
// -------------------------------------------------------------------- rows
.row {
padding: 8px 10px;
border-radius: $radius-small;
}
.row:hover { background-color: $tint; }
.row.active .row-title, .row.active .glyph { color: $accent; }
.row.dimmed .row-title, .row.dimmed .glyph { color: $dim; }
.glyph { font-size: 15px; min-width: 24px; margin-right: 10px; }
.row-title { color: $fg; }
.row-sub { color: $dim; font-size: 11px; margin-top: 1px; }
.row-value { color: $dim; margin-left: 10px; }
.chev { color: $dim; margin-left: 8px; font-size: 15px; }
.accent { color: $accent; }
.urgent { color: $urgent; }
.good { color: $green; }
.warn { color: $yellow; }
.dim { color: $dim; }
// ---------------------------------------------------------------- controls
.slider-row:hover { background-color: transparent; }
.slider-row .glyph { padding: 2px 0; border-radius: $radius-small; }
.slider-row .glyph:hover { color: $accent; }
.slider-value { min-width: 48px; }
scale trough {
min-height: 6px;
min-width: 140px;
margin: 0 4px;
border-radius: 3px;
background-color: $selection;
}
scale highlight {
border-radius: 3px;
background-color: $accent;
}
scale slider {
min-width: 12px;
min-height: 12px;
margin: -3px;
border-radius: 6px;
background-color: $fg;
}
.switch {
min-width: 30px;
min-height: 16px;
padding: 2px;
border-radius: 10px;
background-color: $selection;
}
.switch.on { background-color: rgba($accent, 0.55); }
.knob {
min-width: 12px;
min-height: 12px;
border-radius: 6px;
background-color: $dim;
}
.switch.on .knob { background-color: $fg; }
.pill {
padding: 4px 10px;
margin: 0 3px;
border-radius: $radius-small;
color: $dim;
}
.pills { padding: 2px 6px 6px 6px; }
.pill:hover { color: $fg; background-color: $tint; }
.pill.active { color: $accent; background-color: $tint; }
.field {
padding: 8px 10px;
border-radius: $radius-small;
background-color: rgba($surface, 0.8);
color: $fg;
caret-color: $accent;
}
progressbar trough {
min-height: 4px;
border-radius: 2px;
background-color: $selection;
}
progressbar progress {
min-height: 4px;
border-radius: 2px;
background-color: $accent;
}
// ---------------------------------------------------------------- calendar
.cal-title { font-weight: bold; }
.cal-grid { margin: 4px 4px 2px 4px; }
.cal-dow, .cal-wk { color: $dim; font-size: 10px; font-weight: bold; min-height: 22px; }
.cal-wk { min-width: 26px; }
.cal-day {
min-width: 34px;
min-height: 30px;
border-radius: $radius-small;
color: $fg;
}
.cal-day:hover { background-color: $tint; }
.cal-day.other { color: $muted; }
.cal-day.weekend { color: $fg-alt; }
.cal-day.selected { background-color: $tint; color: $accent; }
.cal-day.today { background-color: $accent; color: $bg; font-weight: bold; }
.cal-dot { color: $magenta; font-size: 8px; min-height: 8px; }
.cal-day.today .cal-dot { color: $bg; }
.event-time { color: $accent; min-width: 52px; font-size: 12px; }
.event-bar { min-width: 3px; border-radius: 2px; margin-right: 10px; background-color: $magenta; }
.event-bar.reminder { background-color: $yellow; }
// ------------------------------------------------------------------- media
.art {
min-width: 72px;
min-height: 72px;
border-radius: $radius-small;
background-size: cover;
background-position: center;
background-color: $surface;
}
.media-title { font-weight: bold; }
.media-artist { color: $dim; font-size: 12px; }
.media-btn {
font-size: 18px;
padding: 4px 12px;
border-radius: $radius-small;
}
.media-btn:hover { background-color: $tint; color: $accent; }
.media-time { color: $dim; font-size: 10px; }
// ----------------------------------------------------------- notifications
// The event box takes the hover tint; GTK ignores padding on event boxes,
// so the box inside it carries the padding.
.notif-hit { border-radius: $radius-small; }
.notif-hit:hover { background-color: $tint; }
.notif { padding: 8px 10px; }
.notif.critical .notif-summary { color: $urgent; }
.notif-app { color: $dim; font-size: 10px; font-weight: bold; }
.notif-summary { font-weight: bold; }
.notif-body { color: $fg-alt; font-size: 12px; }
.notif-ago { color: $dim; font-size: 10px; }
.notif-close {
color: $dim;
padding: 0 6px;
border-radius: $radius-small;
font-size: 13px;
}
.notif-close:hover { color: $urgent; background-color: rgba($urgent, 0.15); }

91
home/.config/eww/eww.yuck Normal file
View file

@ -0,0 +1,91 @@
;; Sakura Line - eww popups.
;; rice-panel opens one window at a time; every panel is built from the
;; widgets below so they all look and behave like the rofi menus.
;; ------------------------------------------------------------------ state
(defvar open_panel "") ; set by rice-panel; list polls only run while their panel is open
(defvar cal_offset 0) ; months away from the current one
(defvar cal_day "") ; selected day as YYYY-MM-DD, "" = today
(defvar cal_input "") ; text typed into the calendar's add box
(deflisten notifs :initial "{}" "rice-notify watch")
(deflisten audio :initial "{}" "rice-audio watch")
(deflisten media :initial "{}" "rice-media watch")
(defpoll net :interval "4s" :initial "{}" :run-while {open_panel == "network" || open_panel == "hub"} "rice-net list")
(defpoll bt :interval "4s" :initial "{}" :run-while {open_panel == "bluetooth" || open_panel == "hub"} "rice-bt list")
(defpoll display :interval "10s" :initial "{}" :run-while {open_panel == "display" || open_panel == "hub"} "rice-display info")
(defpoll power :interval "3s" :initial "{}" :run-while {open_panel == "power" || open_panel == "hub"} "rice-power info")
;; rice-cal reads cal_offset / cal_day itself and pushes fresh data on every click.
(defpoll month :interval "60s" :initial "{}" :run-while {open_panel == "calendar"} "rice-cal month")
(defpoll agenda :interval "30s" :initial "{}" :run-while {open_panel == "calendar"} "rice-cal agenda")
(defpoll idle :interval "3s" :initial "{}" :run-while {open_panel == "hub"} "rice-idle status")
;; ---------------------------------------------------------------- windows
;; A transparent layer under the open panel: clicking anywhere else closes it.
(defwindow closer
:monitor 0
:geometry (geometry :x "0" :y "0" :width "100%" :height "100%")
:stacking "fg"
:wm-ignore true
(eventbox :class "closer" :onclick "rice-panel close" :onrightclick "rice-panel close"))
;; -------------------------------------------------------- shared widgets
;; Header: title on the left, optional action buttons on the right.
(defwidget head [title]
(box :class "head" :space-evenly false
(label :class "title" :text title :hexpand true :xalign 0)
(children)))
(defwidget head-action [glyph onclick ?tip ?active]
(button :class "head-action ${active == true ? 'active' : ''}" :onclick onclick :tooltip {tip ?: ""} glyph))
(defwidget section [text]
(label :class "section" :xalign 0 :text text))
(defwidget sep []
(box :class "sep"))
;; One list row: glyph, title and optional subtitle, value on the right.
(defwidget row [glyph title ?sub ?value ?onclick ?active ?chev ?dim]
(button :class "row ${active == true ? 'active' : ''} ${dim == true ? 'dimmed' : ''}" :onclick {onclick ?: ""}
(box :space-evenly false
(label :class "glyph" :text glyph)
(box :orientation "v" :hexpand true :space-evenly false :valign "center"
(label :class "row-title" :xalign 0 :limit-width 30 :text title)
(label :class "row-sub" :xalign 0 :limit-width 38 :visible {(sub ?: "") != ""} :text {sub ?: ""}))
(label :class "row-value" :visible {(value ?: "") != ""} :text {value ?: ""})
(label :class "chev" :visible {chev == true} :text ""))))
;; A row with a pill switch.
(defwidget switch-row [glyph title on onclick ?sub]
(button :class "row" :onclick onclick
(box :space-evenly false
(label :class "glyph ${on == true ? 'accent' : ''}" :text glyph)
(box :orientation "v" :hexpand true :space-evenly false :valign "center"
(label :class "row-title" :xalign 0 :text title)
(label :class "row-sub" :xalign 0 :visible {(sub ?: "") != ""} :text {sub ?: ""}))
(box :class "switch ${on == true ? 'on' : ''}" :valign "center" :space-evenly false
(box :class "knob" :hexpand true :halign {on == true ? "end" : "start"})))))
;; A slider row: clicking the glyph runs onglyph (mute), dragging runs onchange.
(defwidget slider-row [glyph value onchange ?onglyph ?muted]
(box :class "row slider-row" :space-evenly false
(button :class "glyph ${muted == true ? 'urgent' : ''}" :onclick {onglyph ?: ""} glyph)
(scale :class "slider" :hexpand true :min 0 :max 101 :value {value ?: 0} :onchange onchange)
(label :class "row-value slider-value" :xalign 1 :text {muted == true ? "muted" : "${value ?: 0}%"})))
;; An empty-state line inside a list.
(defwidget empty [text]
(label :class "empty" :xalign 0 :text text))
;; ----------------------------------------------------------------- panels
(include "panels/hub.yuck")
(include "panels/audio.yuck")
(include "panels/network.yuck")
(include "panels/bluetooth.yuck")
(include "panels/display.yuck")
(include "panels/power.yuck")
(include "panels/media.yuck")
(include "panels/calendar.yuck")
(include "panels/notifications.yuck")

View file

@ -0,0 +1,3 @@
# sxhkd config that rice-panel runs only while a panel is open.
Escape
rice-panel close

View file

@ -0,0 +1,45 @@
;; Audio: output and mic levels, device pickers, per-app mixer.
(defwindow audio
:monitor 0
:geometry (geometry :x "-10px" :y "47px" :width "380px" :anchor "top right")
:stacking "fg"
:wm-ignore true
:resizable false
(box :class "panel" :orientation "v" :space-evenly false
(head :title "Audio"
(head-action :glyph "󰒓" :tip "Advanced mixer" :onclick "rice-panel close; setsid -f pavucontrol"))
(slider-row :glyph {audio?.sink?.muted == true ? "󰝟" : "󰕾"}
:value {audio?.sink?.vol ?: 0}
:muted {audio?.sink?.muted ?: false}
:onglyph "rice-volume mute"
:onchange "rice-volume set {}")
(slider-row :glyph {audio?.source?.muted == true ? "󰍭" : "󰍬"}
:value {audio?.source?.vol ?: 0}
:muted {audio?.source?.muted ?: false}
:onglyph "rice-audio mic-mute"
:onchange "rice-audio mic {}")
;; eww appends a loop's items after their siblings, so every loop gets its own box.
(section :text "Output")
(box :orientation "v" :space-evenly false
(for s in {audio?.sinks ?: []}
(row :glyph "󰓃" :title {s.desc} :active {s.default} :onclick "rice-audio sink ${s.index}")))
(section :text "Input")
(box :orientation "v" :space-evenly false
(for s in {audio?.sources ?: []}
(row :glyph "󰍬" :title {s.desc} :active {s.default} :onclick "rice-audio source ${s.index}")))
(box :orientation "v" :space-evenly false :visible {arraylength(audio?.apps ?: []) > 0}
(section :text "Apps")
(box :orientation "v" :space-evenly false
(for a in {audio?.apps ?: []}
(box :class "row slider-row" :space-evenly false
(button :class "glyph ${a.muted ? 'urgent' : ''}" :tooltip {a.name}
:onclick "rice-audio app-mute ${a.id}"
{a.muted ? "󰝟" : "󰎆"})
(label :class "row-title" :xalign 0 :width 90 :limit-width 11 :text {a.name})
(scale :class "slider" :hexpand true :min 0 :max 101 :value {a.vol}
:onchange "rice-audio app-volume ${a.id} {}")
(label :class "row-value slider-value" :xalign 1 :text "${a.vol}%")))))))

View file

@ -0,0 +1,32 @@
;; Bluetooth: power, paired devices with battery, nearby devices to pair.
(defwindow bluetooth
:monitor 0
:geometry (geometry :x "-10px" :y "47px" :width "380px" :anchor "top right")
:stacking "fg"
:wm-ignore true
:resizable false
(box :class "panel" :orientation "v" :space-evenly false
(head :title "Bluetooth"
(head-action :glyph "󰑓" :tip "Look for new devices" :active {bt?.discovering == true}
:onclick "rice-bt scan"))
(switch-row :glyph "󰂯" :title "Bluetooth" :on {bt?.powered == true}
:onclick "rice-bt power toggle"
:sub {bt?.powered != true ? "off" : (bt?.connected > 0 ? "${bt.connected} connected" : "on")})
(box :orientation "v" :space-evenly false :visible {bt?.powered == true}
(section :text "My devices")
(box :orientation "v" :space-evenly false
(for d in {bt?.paired ?: []}
(row :glyph {d.glyph} :title {d.name} :sub {d.sub} :value {d.battery}
:active {d.connected} :onclick "rice-bt toggle ${d.mac}")))
(box :visible {arraylength(bt?.paired ?: []) == 0}
(empty :text "No paired devices"))
(section :text {bt?.discovering == true ? "Nearby · scanning" : "Nearby"})
(box :orientation "v" :space-evenly false
(for d in {bt?.nearby ?: []}
(row :glyph {d.glyph} :title {d.name} :sub {d.sub} :dim true
:onclick "rice-bt toggle ${d.mac}")))
(box :visible {arraylength(bt?.nearby ?: []) == 0}
(empty :text {bt?.discovering == true ? "Looking…" : "Press 󰑓 to look for devices"})))))

View file

@ -0,0 +1,47 @@
;; Calendar: month grid with ISO weeks, the selected day's events and reminders.
(defwindow calendar
:monitor 0
:geometry (geometry :x "0" :y "47px" :width "360px" :anchor "top center")
:stacking "fg"
:wm-ignore true
:resizable false
(box :class "panel" :orientation "v" :space-evenly false
(head :title {month?.title ?: "Calendar"}
(head-action :glyph "󰅁" :tip "Previous month" :onclick "rice-cal shift -1")
(head-action :glyph "󰃭" :tip "Today" :onclick "rice-cal today")
(head-action :glyph "󰅂" :tip "Next month" :onclick "rice-cal shift 1"))
(box :class "cal-grid" :orientation "v" :space-evenly false
(box :space-evenly false
(label :class "cal-wk" :text "Wk")
(for d in {month?.dow ?: []}
(label :class "cal-dow" :hexpand true :text d)))
(for w in {month?.weeks ?: []}
(box :space-evenly false
(label :class "cal-wk" :text {w.wk})
(for d in {w.days}
(button :hexpand true :onclick "rice-cal select ${d.date}"
:class "cal-day ${d.other ? 'other' : ''} ${d.weekend ? 'weekend' : ''} ${d.selected ? 'selected' : ''} ${d.today ? 'today' : ''}"
(box :orientation "v" :space-evenly false :valign "center"
(label :text {d.d})
(label :class "cal-dot" :text {d.events > 0 ? "●" : " "})))))))
(sep)
(box :space-evenly false
(label :class "section" :hexpand true :xalign 0 :text {agenda?.heading ?: "Today"})
(head-action :glyph "󰃅" :tip "Add event" :onclick "rice-cal add")
(head-action :glyph "󰀠" :tip "Add reminder" :onclick "rice-panel close; rice-reminder new"))
(box :orientation "v" :space-evenly false
(for ev in {agenda?.items ?: []}
(box :class "row" :space-evenly false
(box :class "event-bar ${ev.kind == 'reminder' ? 'reminder' : ''}")
(label :class "event-time" :xalign 0 :text {ev.time})
(box :orientation "v" :hexpand true :space-evenly false :valign "center"
(label :class "row-title" :xalign 0 :limit-width 28 :text {ev.title})
(label :class "row-sub" :xalign 0 :limit-width 36 :visible {ev.sub != ""} :text {ev.sub}))
(button :class "notif-close" :valign "center" :visible {ev.kind == "reminder"}
:tooltip "Cancel reminder" :onclick "rice-reminder cancel ${ev.id}" "󰅖"))))
(box :orientation "v" :space-evenly false :visible {arraylength(agenda?.items ?: []) == 0}
(empty :text {(agenda?.status ?: "") != "" ? agenda.status : "Nothing planned"}))))

View file

@ -0,0 +1,30 @@
;; Display: brightness over DDC, resolution, refresh rate, bar, style shortcuts.
(defwindow display
:monitor 0
:geometry (geometry :x "-10px" :y "47px" :width "380px" :anchor "top right")
:stacking "fg"
:wm-ignore true
:resizable false
(box :class "panel" :orientation "v" :space-evenly false
(head :title "Display"
(head-action :glyph "󰸉" :tip "Wallpaper" :onclick "rice-panel close; rice-menu wallpaper")
(head-action :glyph "󰏘" :tip "Theme" :onclick "rice-panel close; rice-menu theme"))
(box :orientation "v" :space-evenly false :visible {display?.ddc == true}
(slider-row :glyph "󰃠" :value {display?.brightness ?: 0}
:onchange "rice-display brightness {}"))
(box :orientation "v" :space-evenly false :visible {display?.ddc != true}
(row :glyph "󰃠" :title "Brightness" :sub {display?.ddcHint ?: ""} :dim true))
(section :text {display?.output ?: "Output"})
(box :orientation "v" :space-evenly false
(for m in {display?.modes ?: []}
(row :glyph "󰍹" :title {m.mode} :active {m.current} :onclick "rice-display mode ${m.id}")))
(section :text "Refresh rate")
(box :class "pills" :space-evenly true
(for r in {display?.rates ?: []}
(button :class "pill ${r.current ? 'active' : ''}" :onclick "rice-display rate ${r.id}" {r.label})))
(sep)
(switch-row :glyph "󰍜" :title "Bar" :on {display?.bar == true} :onclick "rice-bar toggle")))

View file

@ -0,0 +1,50 @@
;; Control centre (super + x): levels, a line per subsystem, quick switches, now playing.
(defwindow hub
:monitor 0
:geometry (geometry :x "-10px" :y "47px" :width "400px" :anchor "top right")
:stacking "fg"
:wm-ignore true
:resizable false
(box :class "panel" :orientation "v" :space-evenly false
(head :title "Control Centre"
(head-action :glyph "󰍜" :tip "Rice menu" :onclick "rice-panel close; rice-menu main")
(head-action :glyph "󰐥" :tip "Power menu" :onclick "rice-panel close; rice-menu power"))
(slider-row :glyph {audio?.sink?.muted == true ? "󰝟" : "󰕾"}
:value {audio?.sink?.vol ?: 0} :muted {audio?.sink?.muted ?: false}
:onglyph "rice-volume mute" :onchange "rice-volume set {}")
(slider-row :glyph {audio?.source?.muted == true ? "󰍭" : "󰍬"}
:value {audio?.source?.vol ?: 0} :muted {audio?.source?.muted ?: false}
:onglyph "rice-audio mic-mute" :onchange "rice-audio mic {}")
(box :orientation "v" :space-evenly false :visible {display?.ddc == true}
(slider-row :glyph "󰃠" :value {display?.brightness ?: 0} :onchange "rice-display brightness {}"))
(sep)
(row :glyph {net?.active?.type == "ethernet" ? "󰈀" : (net?.active?.type == "wifi" ? "󰖩" : "󰖪")}
:title "Network" :value {net?.active?.name ?: "offline"} :chev true :onclick "rice-panel network")
(row :glyph "󰂯" :title "Bluetooth" :chev true :onclick "rice-panel bluetooth"
:value {bt?.powered != true ? "off" : (bt?.connected > 0 ? "${bt.connected} connected" : "on")})
(row :glyph "󰓃" :title "Sound" :value {audio?.sink?.name ?: ""} :chev true :onclick "rice-panel audio")
(row :glyph "󰍹" :title "Display" :chev true :onclick "rice-panel display"
:value "${display?.mode ?: ''} · ${round(display?.rate ?: 0, 0)} Hz")
(row :glyph "󰍛" :title "System" :chev true :onclick "rice-panel power"
:value "CPU ${power?.cpu?.load ?: 0}% · Memory ${power?.memory?.percent ?: 0}%")
(row :glyph "󰃭" :title "Calendar" :value {formattime(EWW_TIME, "%a %-d %b")} :chev true :onclick "rice-panel calendar")
(sep)
(switch-row :glyph "󰂛" :title "Do not disturb" :on {notifs?.dnd == true} :onclick "rice-notify dnd toggle"
:sub {(notifs?.count ?: 0) > 0 ? "${notifs.count} in the notification centre" : ""})
(switch-row :glyph "󰅶" :title "Stay awake" :on {idle?.awake == true} :onclick "rice-idle toggle"
:sub {idle?.awake == true ? "screensaver and lock paused" : ""})
(box :orientation "v" :space-evenly false :visible {media?.status == "Playing" || media?.status == "Paused"}
(sep)
(box :class "row" :space-evenly false
(label :class "glyph accent" :text "󰎆")
(eventbox :hexpand true :onclick "rice-panel media"
(box :orientation "v" :space-evenly false :valign "center"
(label :class "row-title" :xalign 0 :limit-width 22 :text {media?.title ?: ""})
(label :class "row-sub" :xalign 0 :limit-width 26 :text {media?.artist ?: ""})))
(button :class "media-btn" :onclick "playerctl previous" "󰒮")
(button :class "media-btn accent" :onclick "playerctl play-pause" {media?.status == "Playing" ? "󰏤" : "󰐊"})
(button :class "media-btn" :onclick "playerctl next" "󰒭")))))

View file

@ -0,0 +1,39 @@
;; Media: what is playing, a seek bar, and transport controls.
(defpoll media_pos :interval "1s" :initial "{}" :run-while {open_panel == "media"} "rice-media position")
(defwindow media
:monitor 0
:geometry (geometry :x "0" :y "47px" :width "380px" :anchor "top center")
:stacking "fg"
:wm-ignore true
:resizable false
(box :class "panel" :orientation "v" :space-evenly false
(head :title "Media"
(label :class "dim" :text {media?.player ?: ""}))
(box :orientation "v" :space-evenly false
:visible {media?.status != "Playing" && media?.status != "Paused"}
(empty :text "Nothing is playing"))
(box :orientation "v" :space-evenly false
:visible {media?.status == "Playing" || media?.status == "Paused"}
(box :class "row" :space-evenly false
(box :class "art" :visible {(media?.art ?: "") != ""}
:style "background-image: url('${media?.art ?: ''}');")
(box :orientation "v" :space-evenly false :valign "center" :hexpand true
(label :class "media-title" :xalign 0 :limit-width 26 :text {media?.title ?: ""})
(label :class "media-artist" :xalign 0 :limit-width 32 :text {media?.artist ?: ""})
(label :class "media-artist" :xalign 0 :limit-width 32 :visible {(media?.album ?: "") != ""}
:text {media?.album ?: ""})))
(box :class "row slider-row" :space-evenly false :visible {(media?.length ?: 0) > 0}
(label :class "media-time" :width 36 :xalign 0 :text {media_pos?.text ?: "0:00"})
(scale :class "slider" :hexpand true :min 0 :max {media?.length ?: 1}
:value {media_pos?.pos ?: 0} :onchange "rice-media seek {}")
(label :class "media-time" :width 36 :xalign 1 :text {media?.lengthText ?: ""}))
(box :class "row" :space-evenly true
(button :class "media-btn" :tooltip "Previous" :onclick "playerctl previous" "󰒮")
(button :class "media-btn accent" :tooltip "Play / pause" :onclick "playerctl play-pause"
{media?.status == "Playing" ? "󰏤" : "󰐊"})
(button :class "media-btn" :tooltip "Next" :onclick "playerctl next" "󰒭")))))

View file

@ -0,0 +1,37 @@
;; Network: Wi-Fi radio, the active connection, nearby networks and VPNs.
(defwindow network
:monitor 0
:geometry (geometry :x "-10px" :y "47px" :width "380px" :anchor "top right")
:stacking "fg"
:wm-ignore true
:resizable false
(box :class "panel" :orientation "v" :space-evenly false
(head :title "Network"
(head-action :glyph "󰑓" :tip "Rescan" :onclick "rice-net rescan")
(head-action :glyph "󰐲" :tip "Share Wi-Fi as a QR code" :onclick "rice-panel close; rice-net share")
(head-action :glyph "󰒓" :tip "Connection editor" :onclick "rice-panel close; setsid -f nm-connection-editor"))
(box :orientation "v" :space-evenly false :visible {net?.active?.type == "ethernet"}
(row :glyph "󰈀" :title "Ethernet" :sub {net?.active?.name ?: ""} :active true))
;; Machines without a Wi-Fi adapter (like a wired desktop) skip this part.
(box :orientation "v" :space-evenly false :visible {net?.hasWifi == true}
(switch-row :glyph "󰖩" :title "Wi-Fi" :on {net?.radio == true}
:onclick "rice-net radio toggle"
:sub {net?.active?.type == "wifi" ? net.active.name : (net?.radio == true ? "not connected" : "off")}))
(box :orientation "v" :space-evenly false :visible {net?.hasWifi == true && net?.radio == true}
(section :text "Networks")
(box :orientation "v" :space-evenly false
(for w in {net?.wifi ?: []}
(row :glyph {w.glyph} :title {w.ssid} :sub {w.sub} :active {w.active}
:onclick {w.active ? "rice-net disconnect" : "rice-net connect ${w.id}"})))
(box :visible {arraylength(net?.wifi ?: []) == 0}
(empty :text "No networks found yet")))
(box :orientation "v" :space-evenly false :visible {arraylength(net?.vpn ?: []) > 0}
(section :text "VPN")
(box :orientation "v" :space-evenly false
(for v in {net?.vpn ?: []}
(row :glyph {v.active ? "󰖂" : "󰕥"} :title {v.name} :sub {v.active ? "connected" : ""}
:active {v.active} :onclick "rice-net vpn ${v.id}"))))))

View file

@ -0,0 +1,37 @@
;; Notification centre: recent notifications, dismiss one or all, do-not-disturb.
(defwindow notifications
:monitor 0
:geometry (geometry :x "-10px" :y "47px" :width "380px" :anchor "top right")
:stacking "fg"
:wm-ignore true
:resizable false
(box :class "panel" :orientation "v" :space-evenly false
(head :title "Notifications"
(head-action :glyph {notifs?.dnd == true ? "󰂛" : "󰂚"} :active {notifs?.dnd == true}
:tip "Do not disturb" :onclick "rice-notify dnd toggle")
(head-action :glyph "󰎟" :tip "Clear all" :onclick "rice-notify clear"))
(box :orientation "v" :space-evenly false :visible {notifs?.dnd == true && (notifs?.waiting ?: 0) > 0}
(row :glyph "󰂛" :title "Held back" :sub "They appear when do-not-disturb is off"
:value "${notifs?.waiting ?: 0}" :dim true))
(box :orientation "v" :space-evenly false :visible {(notifs?.count ?: 0) == 0}
(empty :text "You're all caught up"))
(box :orientation "v" :space-evenly false
(for n in {notifs?.items ?: []}
(eventbox :class "notif-hit"
:onclick "rice-panel close; rice-notify open ${n.id}"
(box :class "notif ${n.urgency == 'CRITICAL' ? 'critical' : ''}" :space-evenly false
(box :orientation "v" :hexpand true :space-evenly false
(box :space-evenly false
(label :class "notif-app" :xalign 0 :hexpand true :limit-width 30 :text {n.app})
(label :class "notif-ago" :text {n.ago}))
(label :class "notif-summary" :xalign 0 :limit-width 36 :text {n.summary})
(label :class "notif-body" :xalign 0 :wrap true :limit-width 140
:visible {n.body != ""} :text {n.body}))
(button :class "notif-close" :valign "start" :tooltip "Dismiss"
:onclick "rice-notify remove ${n.id}" "󰅖")))))
(box :orientation "v" :space-evenly false :visible {(notifs?.count ?: 0) > 8}
(row :glyph "󰇘" :title "${notifs?.count - 8} older" :sub "Clear all to start fresh" :dim true))))

View file

@ -0,0 +1,31 @@
;; Power: what the machine is doing, the power profile, and session actions.
(defwindow power
:monitor 0
:geometry (geometry :x "-10px" :y "47px" :width "380px" :anchor "top right")
:stacking "fg"
:wm-ignore true
:resizable false
(box :class "panel" :orientation "v" :space-evenly false
(head :title "Power"
(head-action :glyph "󰍛" :tip "Activity monitor" :onclick "rice-panel close; setsid -f st -c rice-float -e btop"))
(row :glyph "󰍛" :title "CPU"
:value "${power?.cpu?.load ?: 0}%${power?.cpu?.temp != 'null' && power?.cpu?.temp != '' ? ' ' + power.cpu.temp + '°' : ''}")
(row :glyph "󰢮" :title "GPU" :sub {power?.gpu?.vram ?: ""}
:value "${power?.gpu?.load ?: 0}%${power?.gpu?.temp != 'null' && power?.gpu?.temp != '' ? ' ' + power.gpu.temp + '°' : ''}")
(row :glyph "󰘚" :title "Memory" :value "${power?.memory?.used ?: '-'} / ${power?.memory?.total ?: '-'}")
(row :glyph "󰔟" :title "Uptime" :value {power?.uptime ?: ""})
(box :orientation "v" :space-evenly false :visible {arraylength(power?.profiles ?: []) > 0}
(section :text "Power profile")
(box :class "row" :space-evenly true
(for p in {power?.profiles ?: []}
(button :class "pill ${p.active ? 'active' : ''}" :onclick "rice-power profile ${p.name}" {p.label}))))
(sep)
(box :space-evenly true :class "row"
(button :class "media-btn" :tooltip "Lock" :onclick "rice-power lock" "󰌾")
(button :class "media-btn" :tooltip "Suspend" :onclick "rice-power suspend" "󰒲")
(button :class "media-btn" :tooltip "Log out" :onclick "rice-power logout" "󰍃")
(button :class "media-btn" :tooltip "Reboot" :onclick "rice-power reboot" "󰜉")
(button :class "media-btn urgent" :tooltip "Shut down" :onclick "rice-power shutdown" "󰐥"))))