Author SHA1 Message Date
nvrl 6addb504e6 some qs fixes 2026-10-09 04:17:36 +02:00
nvrl 32d5683fad feat(quickshell): group the notification log by sender
The log lists one entry per app, ordered by each app's newest
notification. An app with several shows a header with its name, a
count and a clear mark for that app alone, then its newest entry and a
'+ N more' line; clicking either unfolds the rest. Entries under a
header drop their own sender line and keep the urgency dot.
2026-10-04 23:45:57 +02:00
nvrl ec76f8dec3 chore(hypr): drop screenshot_window.sh, replaced by shot.sh 2026-10-04 23:40:22 +02:00
nvrl 299291eaf6 chore(quickshell): drop the unused halftone, noise and stripe tiles
Only dots.png and scanline.png are referenced (Theme.texDots and
Theme.texScanline).
2026-10-04 23:40:11 +02:00
nvrl add12ccc76 refactor(quickshell): drop the Persona names from the primitives
P5Panel, P5Button, P5Slider and P5Tooltip become Panel, ActionButton,
LevelSlider and Tooltip. ActionButton and LevelSlider avoid clashing
with QtQuick.Controls, which the ask panel imports.
2026-10-04 23:39:51 +02:00
nvrl 7b7cd3e575 style: one sans and one mono, and no stretched text
GTK, Qt and Hyprland's own text move to the shell's pair: Noto Sans for
interface text and JetBrainsMono Nerd Font for figures and code. They
had been Adwaita Sans, Sans Serif and FiraCode.

The tray menu now slides and fades in without scaling, and a pressed
button sinks one pixel instead of shrinking, so no label is ever
stretched mid-animation.
2026-10-04 23:38:46 +02:00
nvrl 31b57f4f8c feat: screenshots answer with a thumbnail notification
All four Print binds go through hypr/scripts/shot.sh. Every capture
lands on the clipboard and posts a notification showing the picture,
with Open, Save (for clipboard captures, which wait in the runtime dir)
and Edit when satty is installed. Cancelling the region picker does
nothing.

Notification cards now load images passed as a path hint: Quickshell
routes those through the icon provider, which returned a placeholder.
2026-10-04 23:36:56 +02:00
nvrl 373584f34e feat(quickshell): quick switches under the dashboard
A row of pill switches under the dashboard's columns: Wi-Fi,
Bluetooth, do not disturb and stay awake, plus night light through
hyprsunset when it is installed. On is the light slab with ink type;
off is a hairline.
2026-10-04 23:34:27 +02:00
nvrl f7551006b3 feat(quickshell): privacy indicator at the head of the rail
A lit icon tops the rail while another program records from a
microphone, uses a camera, or shares the screen. It is read from
PipeWire: capture streams for the mic, links out of video sources for
the camera and screencasts. The tooltip names the recording apps.
2026-10-04 23:31:39 +02:00
nvrl dc2a8fc883 feat(quickshell): scratchpad ring in the bar, keyboard layout in the OSD
The Super+S scratchpad gets a ring after the workspace pips while it
holds windows, filled while it is open; clicking it toggles it.
Switching keyboard layout flashes the OSD with the short name (US,
Dvorak) and the full keymap underneath.
2026-10-04 23:29:42 +02:00
nvrl b69f4bcf0d feat(quickshell): Alt+Tab overview of every monitor and workspace
Each monitor is a row of workspace cards at its own aspect ratio, with
live captures of the windows where they sit, including windows on
hidden workspaces. Alt+Tab opens it with the previous window picked,
further Tabs walk the focus history, and releasing Alt switches.
Super+W opens it without the hold. Hover, click, Enter and the
workspace digits work too.

Group cycling moves from Alt+Tab to Super+Tab and Super+Shift+Tab.
2026-10-04 23:27:48 +02:00
nvrl e9735d1129 feat(quickshell): "Take your time." glows and flickers
A new GlowText component lights a line from inside: a soft two-layer
halo that breathes slowly, and every 4-14 seconds a short, uneven
stutter like a tube catching. Only opacity and the halo move, so the
glyphs stay on the pixel grid. The lock screen and the power menu use
it; the power menu's stops while it is closed.
2026-10-04 23:12:50 +02:00
nvrl c36a74e861 feat(quickshell): idle chain, drifting wallpaper, and a few fixes
- Idle is handled in the shell instead of hypridle: the screen dims
  slowly at 4 minutes, locks at 5, powers down at 6, and suspends at 15
  on battery. Idle inhibitors hold every stage off; Super+Shift+I does
  the same by hand and puts a cup on the rail.
- The shell draws the wallpaper (backdrop/), so hyprpaper is gone. The
  picture is drawn a little larger than the screen and drifts with the
  workspace a beat after the windows, and dims a shade while windows are
  open. The path lives in quickshell/wallpaper.
- The lock screen and power menu say "Take your time." instead of the
  NieR slogan.
- hyprpaper is no longer launched twice, the media module keeps the
  player that played last instead of an empty browser tab, and grouped
  windows show thin untitled tabs with their place in the group (2/3)
  in the bar title.
- qt6ct uses Darkly; the package list gains adw-gtk-theme and darkly-bin.
2026-10-04 23:10:19 +02:00
nvrl 864420281b chore: drop waybar, hyprlock and the catppuccin leftovers
None of these are referenced any more: the shell replaced waybar and
hyprlock, and btop, zathura and the rest now use the YoRHa themes. The
hyprpaper unit is disabled because the shell draws the wallpaper.
2026-10-04 23:10:08 +02:00
nvrlandClaude Opus 5.5 b86e584b02 fix(quickshell): keep text sharp under zoom and on the lock screen
Native-rendered glyphs are rasterised at rest size, so anything moved
with `scale` stretched a bitmap and came out blurred and stepped.

- Icons render as curves, so their pulse and bump stay smooth.
- The lock face is laid out at uiScale instead of being drawn at
  1080p size and scaled by up to 1.3, and it sits on whole pixels. Its
  native labels now land on the pixel grid.
- The OSD rises into place instead of scaling in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 21:58:00 +02:00
nvrlandClaude Opus 5.5 f1d16e5175 theme: zathura takes the YoRHa palette
A local zathura/yorha replaces the catppuccin include. Pages are
recoloured onto the hull with bone text while images keep their
colours, the index and completion select with the light slab, and
search hits use a bone wash with the current hit in sand.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 21:48:28 +02:00
nvrlandClaude Opus 5.5 485ee5c0ca theme: GTK and Qt take the YoRHa palette
GTK moves from Catppuccin Mocha to adw-gtk3-dark, recoloured through the
libadwaita named colours in gtk-3.0/gtk.css and as CSS variables in
gtk-4.0/gtk.css, so Nautilus and other libadwaita apps follow too. The
gtk-4.0 symlinks into the Catppuccin theme are gone, since libadwaita
would load its gtk-dark.css over the overrides.

Qt gets qt6ct/colors/yorha.conf: the same palette in QPalette roles,
with the light slab as the highlight and ink as its text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 21:17:52 +02:00
nvrlandClaude Opus 5.5 9af24ccf87 theme: workspaces drift behind the hull instead of over the bar
The hull moves to the top layer, with its transparent opening and empty input
region, so windows sliding out of the opening during a workspace switch pass
behind the frame like things behind a porthole rim rather than drawing over
the bar. A layer rule keeps the bar and the rail above it on that layer.

The animation tree is redone around that: workspaces drift 18% vertically
and fade on a slow, jolt-free "drift" curve; the SUPER+S scratchpad slides in
whole from under the bar, the same gesture as the dashboard and popouts;
windows and layers keep their calm resolve and fade.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 21:13:11 +02:00
nvrlandClaude Opus 5.5 63a910bf14 theme: calm YoRHa lock screen and power menu; nvim and btop follow
The lock screen and the power menu lose the last Persona motion: no split
display type, no grid sweeping in behind a dark column, no tilted slabs, no
skewed password blocks or hard shake. Both are now one small group of type in
the dark — the porthole mark, a line of light display type, a status line, and
either a rounded password field that fills with dots or a NieR menu whose
selected row is a pill of light (rust for the session-ending three). The lock
keeps the wallpaper almost whole; a wrong password nudges the field once and
turns it to rust. P5Backdrop and the grid texture had no users left and go.

Neovim moves to a local yorha colorscheme (nvim submodule) and btop gets a
yorha theme with hairline boxes and grey-to-light gradients.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 21:09:07 +02:00
nvrlandClaude Opus 5.5 a8c19961e2 theme: soften the YoRHa shapes; SUPER+D opens the dashboard
The square markers, block meters and hard-cornered fills read as boxy
next to the porthole. The desktop is now soft throughout: the porthole
is the shape everything is built around and every smaller surface echoes
it.

- Theme gains radiusS / radiusM / radiusL. radiusL is frameRadius - padS,
  so anything inset padS inside one of the frame's curves is concentric
  with it.
- P5Panel and Skew draw rounded rectangles (Geom.roundedRect) whenever
  they neither lean nor are chopped, which is always now; the polygon
  path is kept for shapes brought back on purpose.
- Markers are dots; workspace pips, toggles, transport and media chips,
  notification actions and today's date are pills or discs; the slider
  handle is round. Meters are one continuous rounded line that glides to
  its level instead of a row of segments.
- Album art is rounded with ClippingRectangle in the bar, the media
  popout and the dashboard.
- Free-standing panels (launcher, ask) drop the corner brackets and take
  the frame radius.
- Fix: a hovered notification's wash was a full-bleed square, so in the
  drawer's bottom corner it ran past the drawer's curve. It is inset by
  padS/2 and rounded by radiusL, which keeps it inside the curve.

Hyprland: the discord special workspace and its window rule are gone,
SUPER+S stays the only scratchpad, and SUPER+D now opens the dashboard
(it was on SUPER+I).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 20:56:43 +02:00
nvrlandClaude Opus 5.5 d6de04eaaf feat(quickshell): notifications as a NieR message log in a corner drawer
Notifications were still Persona: separate floating cards with an urgency
slash down the leading edge, action chips that filled with the accent on
hover, and a 60px shove in from the right.

NotifCard is now laid out like an entry in NieR's message log: a square
urgency marker and the sender's name running into a hairline, the summary
and body set in from the marker, and hairline action buttons with their
own small marker that lift on hover instead of inverting. It draws no
panel; rest is bare and hover lifts the row onto the surface tone.
Critical entries take the red into the marker and the sender line only.

The toast stack is one hull drawer docked into the opening's top-right
corner — off the bar through one fillet, onto the rail through the other,
the same construction as a corner-docked popout. It grows with its
entries and slides back behind the frame when the last one goes. Each
entry's countdown is a hairline along its foot, and new entries fade in
place. The history popout lists the same entries flush.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 20:48:30 +02:00
nvrlandClaude Opus 5.5 ae6539218e theme: strip the remaining Persona punch from Hyprland and the popouts
Hyprland:
- Windows round at 8px, concentric with the porthole's 18px corners at
  the 10px gap, instead of square corners inside a rounded opening.
- Group tabs are solid hull slabs; the titles used to float over the
  wallpaper. The current tab is lifted a step with its title in the light.
- Motion rewritten to glide and settle: no overshoot curve, gentler
  popin, workspaces slide-fade a short way instead of sweeping the whole
  screen, and layers only fade so they never stack on quickshell's own
  slides.

Quickshell:
- Album art in the bar and the media popout is square in a hairline frame,
  not masked to a leaning chamfered shape; the accent slab under the
  popout art is gone, and a track change dips the title instead of
  tilting the art.
- The bar's equaliser is a five-hairline signal meter that rests as dots.
- Segment meters (system, battery, OSD) are 4px rows instead of 10-12px
  blocks; the toast fuse is a hairline; the clock's accent slab is a
  neutral divider.
- No hover pops: rail icons, tray icons, media transport buttons and
  P5Button no longer scale up or lunge sideways; the icon bump is reduced.

Terminal: ANSI bright black drops to #464d44. TUIs use it as a panel fill,
and at the outline grey it read as a flat light slab.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 20:45:24 +02:00
nvrlandClaude Opus 5.5 a27dd6f93b fix(quickshell): rail popouts no longer land short of the rail
Popouts anchored to their trigger item, and Quickshell maps an anchor
rect given in item coordinates through the item's transform. Rail icons
scale to 1.08 on hover, so a panel placed ~360px left of the icon landed
up to ~29px further left still, leaving a strip of desktop between it and
the rail — by however far the hover scale had got when the dwell timer
fired, which is why it only happened sometimes.

Anchor to the trigger window's content item instead, which never
transforms, and place in window coordinates from the trigger's centre,
which a scale about the centre leaves where it is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 20:38:06 +02:00
nvrlandClaude Opus 5.5 196abc2311 feat(quickshell): popouts grow out of the frame
Hover panels used to float a gap away from their trigger as free-standing
cards. They are now part of the hull: flush against the frame edge they
open from, painted in the hull colour, joined to it through concave
fillets, with the porthole rim carried round their open side, and they
slide out from behind the frame instead of fading and scaling in.

Popout places itself rather than relying on the compositor's slide
adjustment. A panel is centred on its trigger and, if that lands it near
one of the opening's corners, docked into the corner — attached to the
bar and the rail at once — so it never leaves a thin strip of opening
between itself and the rail. The calendar, network and audio panels dock
top-right; notifications dock bottom-right.

PopoutSurface leaves fill, border and corner ticks to the hull when it is
inside a Popout, and keeps them in the launcher and the ask panel. Its
attached fill opacity is 0 rather than a transparent colour, because
P5Panel re-applies fillOpacity to the colour and turned "transparent"
into 97% black.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 20:34:17 +02:00
nvrlandClaude Opus 5.5 ecc7d2884c feat(quickshell): dashboard drawer that drops from the bar
A drawer lowered out of the underside of the bar, painted in the hull
colour and joined to it by two concave fillets so it reads as more of the
hull rather than as a floating card — caelestia's dashboard idea, in the
YoRHa language. Four columns divided by hairlines: operator and clock, the
month with today as an inverted square, the system meters, and the active
player with a hairline timeline and square transport buttons.

Opened by clicking the bar's clock (which used to pin the calendar), by
SUPER+I, or by `qs ipc call shell dashboard`. Escape or moving focus away
closes it through a HyprlandFocusGrab; Actions.dashboardClosedAt keeps the
clock click that cleared the grab from reopening it. The clock's calendar
hover is suppressed while the drawer is down, since it shows the month.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 20:19:30 +02:00
nvrlandClaude Opus 5.5 18091a7109 theme: YoRHa dark — NieR:Automata interface on the space-1 wallpaper
Replaces the Persona x Mocha rice. NieR's menu language is kept whole —
plain rectangles, hairline rules, letter-spaced upright capitals, a square
marker on the current row, corner ticks round panels, and selection shown
by inverting the row — but the colours come from ~/.wallpapers/space-1.jpg
rather than from the game's beige: the grey-green void as the ground, the
suit's sage grey as text, the planet's white as the one accent, and the
hull's red indicator lights as the alarm.

The Persona geometry is switched off rather than ripped out: Theme.skew
and Theme.cut are 0, so P5Panel, Skew and Geom collapse to rectangles.
Overshoot easing, italics and black weights are gone shell-wide.

New, borrowed from caelestia's screen border: bar/Frame.qml draws the bar,
the rail and a thin left/bottom band as one hull on the bottom layer, with
the desktop as a rounded opening cut out of it. Frame also owns all four
exclusive zones, so the bar and rail are placed from the screen edge.

Hyprland moves to 1px borders lit from the planet white into grey, a soft
wide shadow and gaps measured from the opening. Alacritty, fish and dunst
get matching YoRHa palettes. Neovim, GTK/Qt and btop are still Mocha.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 20:10:53 +02:00
nvrl 5be22d86a9 updated autostart 2026-10-04 19:31:19 +02:00
nvrl 1f7f5aef8a fixed some quickshell stuff + hypr 2026-09-28 23:27:00 +02:00
nvrl 32b43bdbb3 quickshell fixes 2026-09-24 13:35:20 +02:00
nvrlandClaude Opus 5 d2975650fa theme: move the alarm colour from pink to mauve
Mocha's pink is a pastel. Filled at slab size against the near-black
Persona surfaces it went chalky rather than sharp, which is what made
hovered destructive buttons read as a washed-out red. Mauve is saturated
enough to bite against that ground and still cannot be confused with the
blue accent.

Applies across quickshell, Hyprland, dunst, fish and nvim. The GTK
overrides are dropped entirely — stock Catppuccin Mocha Blue already
paints those surfaces, and a hand-written destructive colour there was
one more thing to keep in step for no gain.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 02:12:53 +02:00
nvrlandClaude Opus 5 e221445a51 theme: Catppuccin Mocha blue/pink variant of the Persona rice
Keeps the Takemi geometry — the lean, the chamfer, the slabs — and swaps
the palette wholesale. Mocha carries the ground; blue is the accent for
anything live (focus, selection, borders, hover) and pink is the alarm
for anything destructive or failed, so one hue no longer has to mean
both 'active' and 'wrong'.

Destructive controls rest as a pink keyline and fill solid only when
hovered or selected: Mocha's pink is a pastel, and a resting slab of it
reads as washed-out red rather than as a warning. The load ramp climbs
text -> blue -> pink for the same reason, instead of fading straight
through a muddy mid-tone.

Surfaces covered: quickshell, Hyprland borders, alacritty, fish, dunst,
and nvim (via the catppuccin plugin, submodule branch catppuccin-mocha).
GTK 3/4, Qt and xsettingsd move to catppuccin-mocha-blue and are now
tracked, since nothing else makes non-shell apps match the rice; the
previous Takemi copies are in theme-repair-backups/.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 02:11:02 +02:00
nvrlandClaude Opus 5 3fb89c4370 fix(qs): show power menu only on the focused screen
A PowerMenu window exists per screen and every one of them lit up on
open. Latch the focused monitor in Actions at open time — same shape as
launcherScreen — and gate each window's open state on it, so exactly one
copy draws and takes keyboard focus.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 01:53:56 +02:00
nvrlandClaude Opus 5 1cf5a4b68d chore: add enqs-headroom user service, set nvim as text/plain handler
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 01:52:50 +02:00
nvrl 5f3a44f82d fixed launcher nav + headroom completion 2026-09-17 12:21:09 +02:00
nvrl 51dc5e80c4 feat(menu) added launcher, bt menu, clipboard 2026-09-16 15:53:19 +02:00
nvrl 35048ae283 feat(quickshell): replace fuzzel with an in-process launcher
fuzzel could only answer "which app", so launching Brave at a URL meant
picking the app, waiting for it, then typing the address somewhere else.
The launcher now takes a verb and an object in one line: type
`gitlab.enqs.de` and every app that can open it sorts to the top, default
browser first, ready for Enter — or arrow/hjkl down to Brave's New
Incognito Window, which is offered as a peer of Brave itself rather than
hidden behind a submenu.

services/Apps.qml is the brain and holds no UI: it flattens desktop
entries and their actions into one index, classifies the typed argument
(url / mail / path), ranks by fuzzy match plus handler affinity plus
frecency, and launches. Launching through `uwsm app` keeps each app in its
own scope; entries whose Exec declares no field code (Brave's
`--incognito` action, among others) are launched by tokenising Exec per
the Desktop Entry spec and appending the argument, never via `sh -c`.

Four prefixes reuse the same window, so there is one picker and one set of
keys: `>` runs a command, `=` evaluates arithmetic, `:` is clipboard
history (cliphist, which had been a second fuzzel invocation), and a
dmenu mode is driven over IPC.

quickshell/scripts/takemi-dmenu is the bridge for that last one — a
`fuzzel --dmenu` drop-in for scripts that want a picker, symlinked into
~/.local/bin. fluxo's bt menu now goes through it.

Living in the Quickshell process means the window is on screen on the
frame the bind fires, with no process to spawn.

Removes fuzzel entirely: its config, its theme submodule, the package,
and the last consumers (the bar sigil, fluxo's menu_command). Also drops
hypr/scripts/powermenu.sh, which was fuzzel-based and bound to nothing.
2026-09-16 15:46:51 +02:00
nvrl 9e73c1338f fixed some bugs 2026-08-15 02:11:03 +02:00
nvrl 9cf510e93b fix(qs,fluxo) performance fix for repaints 2026-08-12 20:18:56 +02:00
nvrl 86e90b0213 chore(hypr) migrated to lua confs 2026-08-12 18:05:43 +02:00
nvrl a868f50ab7 improvements 2026-08-11 17:35:26 +02:00
nvrl 39f925cb58 updated design 2026-08-11 16:34:09 +02:00
nvrl 3e02c3f36a fixed tray + media popouts 2026-08-11 11:29:04 +02:00
nvrl ccb54f01d9 added tray menu + fixed antialising 2026-08-11 11:08:44 +02:00
nvrl 58e0ba2f99 fixed tray 2026-08-11 09:40:55 +02:00
nvrl 08146fe16f final theme 2026-08-11 02:05:46 +02:00
nvrl 105cf6b3a8 updated theme 2026-08-11 01:53:26 +02:00
nvrl 0a439d951e updated style 2026-08-11 00:22:59 +02:00
nvrl 47ff5233bd added quickshell 2026-08-10 23:57:26 +02:00
196 changed files with 16657 additions and 2591 deletions

No files matched your search

+22 -1
View File
@@ -8,15 +8,17 @@
!hypr/ !hypr/
!hyprpanel/ !hyprpanel/
!nvim/ !nvim/
!quickshell/
!waybar/ !waybar/
!zathura/ !zathura/
!mimeapps.list !mimeapps.list
!fuzzel/
!fish/ !fish/
!packages !packages
!brave-flags.conf !brave-flags.conf
!chromium-flags.conf !chromium-flags.conf
!README.md !README.md
!plans/
!DESIGN-IS-2026-08-11/
!rice.png !rice.png
!rice-2.png !rice-2.png
!rice-3.png !rice-3.png
@@ -26,3 +28,22 @@
fish/fish_variables fish/fish_variables
waybar/fluxo-rs waybar/fluxo-rs
!fluxo/ !fluxo/
# Desktop theming. These live outside the per-app configs above, but they are
# what makes an app that is neither GTK-aware nor shell-aware match the rice,
# so the branch carries them.
!gtk-3.0/
gtk-3.0/*
!gtk-3.0/settings.ini
!gtk-3.0/gtk.css
!gtk-4.0/
gtk-4.0/*
!gtk-4.0/settings.ini
!gtk-4.0/gtk.css
!xsettingsd/
!qt6ct/
qt6ct/*
!qt6ct/qt6ct.conf
!qt6ct/colors/
qt6ct/colors/*
!qt6ct/colors/yorha.conf
-12
View File
@@ -1,27 +1,15 @@
[submodule "waybar/catppuccin-waybar"]
path = waybar/catppuccin-waybar
url = https://github.com/catppuccin/waybar.git
[submodule "hypr/catppuccin-hyprland"] [submodule "hypr/catppuccin-hyprland"]
path = hypr/catppuccin-hyprland path = hypr/catppuccin-hyprland
url = https://github.com/catppuccin/hyprland.git url = https://github.com/catppuccin/hyprland.git
[submodule "hypr/catppuccin-hyprlock"] [submodule "hypr/catppuccin-hyprlock"]
path = hypr/catppuccin-hyprlock path = hypr/catppuccin-hyprlock
url = https://github.com/catppuccin/hyprlock.git url = https://github.com/catppuccin/hyprlock.git
[submodule "zathura/catppuccin-zathura"]
path = zathura/catppuccin-zathura
url = https://github.com/catppuccin/zathura.git
[submodule "nvim/pack/nvim/start/mini.nvim"] [submodule "nvim/pack/nvim/start/mini.nvim"]
path = nvim/pack/nvim/start/mini.nvim path = nvim/pack/nvim/start/mini.nvim
url = https://github.com/echasnovski/mini.nvim url = https://github.com/echasnovski/mini.nvim
[submodule "nvim/pack/nvim/start/nvim-lspconfig"] [submodule "nvim/pack/nvim/start/nvim-lspconfig"]
path = nvim/pack/nvim/start/nvim-lspconfig path = nvim/pack/nvim/start/nvim-lspconfig
url = https://github.com/neovim/nvim-lspconfig url = https://github.com/neovim/nvim-lspconfig
[submodule "btop/catppuccin-btop"]
path = btop/catppuccin-btop
url = https://github.com/catppuccin/btop.git
[submodule "fuzzel/catppuccin-fuzzel"]
path = fuzzel/catppuccin-fuzzel
url = https://github.com/catppuccin/fuzzel.git
[submodule "nvim"] [submodule "nvim"]
path = nvim path = nvim
url = https://git.narl.io/nvrl/nvim-config.git url = https://git.narl.io/nvrl/nvim-config.git
+36
View File
@@ -0,0 +1,36 @@
# Takemi shell design audit — scope
## Audited artifact
- Running Persona 5-inspired desktop shell shown in the two supplied 1920×1080 screenshots.
- Quickshell implementation under `quickshell/`, including the top bar, right rail,
pop-outs, tray menus, power overlay, notifications/OSD, and lock screen.
- Shared design tokens and primitives in `quickshell/config/` and
`quickshell/components/`.
## User and task
- Primary user: the workstation owner using the shell throughout the day.
- Primary task: identify system state and reach common desktop controls quickly,
without the theme obscuring application content.
## Constraints
- Preserve the Persona 5 / Tae Takemi identity.
- Preserve the crimson, black, and white palette and screen-print character.
- Stack remains Quickshell, Hyprland, and Fluxo.
- The result must work at desktop scale, with keyboard and pointer input, and
remain legible during long sessions.
## References
- Persona 5 interface language: asymmetric editorial composition, hard ink,
decisive silhouettes, halftone texture, and rapid card-like motion.
- Current implementation and supplied screenshots are the concrete baseline;
fidelity to the game is subordinate to daily desktop usefulness.
## Out of scope
- Application-level themes outside the shell.
- Replacing the wallpaper or character artwork.
- Changing the desktop stack.
+114
View File
@@ -0,0 +1,114 @@
# Evidence
## 1. Innovative
- The shell applies one custom QML geometry and print system across panels,
backdrops, motion, and textures (`quickshell/config/Theme.qml:118-153`,
`quickshell/components/P5Panel.qml:7-56`).
- This refreshes standard desktop bars/popovers, but the interaction model is
still conventional bar + rail + popup + modal.
## 2. Useful
- The permanent bar exposes workspaces, active window, media, resources, and
time in one slab (`quickshell/bar/Bar.qml:67-122`).
- Power and lock are keyboard-operable; bar, rail, tray, and most pop-out
actions remain pointer-only (`quickshell/overlays/PowerMenu.qml:242-261`,
`quickshell/lock/LockFace.qml:290-311`,
`quickshell/bar/RailIcon.qml:104-124`).
- The primary surfaces contain at least 41 interactive targets; the live total
grows with workspaces, devices, streams, notifications, and tray entries.
## 3. Aesthetic
- A clear system exists: spacing `[6,12,20,32]`, type
`[9,11,12,15,20,34,92]`, 12 literal palette colors, one nominal 14° angle,
and shared 10px cuts (`quickshell/config/Theme.qml:19-49,92-148`).
- Runtime type exceptions include 8, 30, 42, and 156.4px; local geometry also
introduces several non-token cuts and leans.
- White on crimson is 3.68:1 and fails normal-text AA where selected destructive
actions or unread badges use that pairing
(`quickshell/components/P5Button.qml:32-73`,
`quickshell/bar/NotifButton.qml:38-58`).
- Screenshot #1: permanent chrome is visually continuous and disciplined, but
competes with an already high-contrast Persona wallpaper/frame.
- Screenshot #2: the left headline/right action-stack composition is strong;
the repeated stripe field and still-legible desktop behind it dilute focus.
## 4. Understandable
- The power screen labels all actions and prints its keyboard map
(`quickshell/overlays/PowerMenu.qml:31-38,216-261`).
- Rail icons expose different left/right/middle/scroll behaviors without visible
instruction (`quickshell/bar/Rail.qml:76-132`).
- Several state labels behave as inverse actions: “Wi-Fi On,” Bluetooth “On,”
“Scanning,” and “Silenced” toggle those states off.
- “WHO GOES THERE,” “WRONG,” “HELD,” “PROCS,” “VRAM,” and unlabeled load values
favor voice or jargon over immediate comprehension.
## 5. Unobtrusive
- Normal chrome consumes only a 40px top bar and 48px right rail
(`quickshell/config/Theme.qml:109-116`).
- The full-screen modes add stripes, a wedge, oversized split text, attribution,
six slabs, and a hint simultaneously
(`quickshell/overlays/PowerMenu.qml:50-228`).
- Twelve action identities are repeated between permanent chrome and detail
panels; repetition improves reach but adds competing affordances.
## 6. Honest
- No marketing inflation or dark patterns were found. Power labels map directly
to their system action (`quickshell/overlays/PowerMenu.qml:31-38,230-239`).
- “n HELD” counts all history rather than suppressed items
(`quickshell/popouts/NotifPopout.qml:27`,
`quickshell/services/Notifs.qml:36-44`).
- Muted OSDs display 0% even when stored volume remains nonzero
(`quickshell/overlays/Osd.qml:51-64,172-185`).
- A visually disabled slider can still emit changes
(`quickshell/components/P5Slider.qml:9-12,48-84`).
## 7. Long-lasting
- The system is based on reusable primitives rather than per-screen decoration.
- Literal `P5` branding, the quote attribution, and the esports-like continuous
frame tie the result closely to one entertainment reference and visual era
(`quickshell/bar/Sigil.qml:31`,
`quickshell/overlays/PowerMenu.qml:88-118`).
## 8. Thorough
- Empty, loading, failure, focus, and disabled states exist.
- A rendered success state is absent; generic buttons lack a disabled state;
focus treatment is concentrated in power and lock; explicit accessible names
and roles total zero (`quickshell/components/P5Button.qml:6-107`).
- The lock’s disabled state is clear, but `P5Slider` can look disabled while
remaining interactive.
## 9. Environmentally friendly
- The shell has 0 JavaScript bytes, about 259KB of QML, 1.4KB of local texture
assets, no declared HTTP request, and a measured load proxy below two seconds.
- Seven Fluxo reads recur in polling batches; playing media creates 3–6 idle
animation loops (`quickshell/services/Sys.qml:78-102`,
`quickshell/bar/MediaPill.qml:125-153`).
- No reduced-motion branch exists (`quickshell/config/Theme.qml:139-148`).
## 10. As little design as possible
- The one-slab bar avoids a row of redundant cards
(`quickshell/bar/Bar.qml:8-14,40-63`).
- The audited tree reaches seven declarative levels, contains twelve repeated
action identities, and includes removable atmospheric elements in modal and
lock compositions.
- The permanent surface, transient surface, and cinematic surface currently
share too much of the same visual intensity instead of forming a hierarchy.
## Known gaps
- Dynamic application strings, device counts, and menu depth cannot be fixed
from static source.
- Exact compositor frame time, GPU cost, and live accessibility-tree output
were not available.
- Screenshot evidence covers the normal desktop and power overlay; lock-screen
judgments also use its implementation source.
+43
View File
@@ -0,0 +1,43 @@
# Rams scorecard
1. **Good design is innovative — 2/3**
Evidence: one custom print/geometry system refreshes conventional shell surfaces (`01-evidence.md#1-innovative`).
Justification: the expression is distinctive, but it does not introduce a new desktop interaction pattern.
2. **Good design makes a product useful — 2/3**
Evidence: core state and actions are direct, while most shell controls require a pointer (`01-evidence.md#2-useful`).
Justification: the primary tasks work quickly, but keyboard and discoverability gaps add friction.
3. **Good design is aesthetic — 2/3**
Evidence: the token system is strong, with several type/geometry exceptions and a 3.68:1 text pairing (`01-evidence.md#3-aesthetic`).
Justification: the visual system is unmistakable, but contrast and intensity inconsistencies prevent a fully resolved score.
4. **Good design makes a product understandable — 1/3**
Evidence: multiple rail gestures are undisclosed and several state labels read like actions (`01-evidence.md#4-understandable`).
Justification: more than three controls require prior knowledge or experimentation.
5. **Good design is unobtrusive — 1/3**
Evidence: compact permanent chrome expands into visually competing full-screen decoration (`01-evidence.md#5-unobtrusive`).
Justification: the theme sometimes becomes the figure when the user’s task should remain the figure.
6. **Good design is honest — 2/3**
Evidence: no dark patterns exist, but held-count, muted-volume, and disabled-slider presentation diverge from behavior (`01-evidence.md#6-honest`).
Justification: the mismatches are minor rather than deceptive, but prevent a one-to-one mapping everywhere.
7. **Good design is long-lasting — 2/3**
Evidence: reusable primitives coexist with literal franchise and gaming-HUD markers (`01-evidence.md#7-long-lasting`).
Justification: the craft will last, while the most literal references may date the surface.
8. **Good design is thorough — 1/3**
Evidence: success, generic disabled, broad focus, and accessibility metadata are missing or rough (`01-evidence.md#8-thorough`).
Justification: three or more important state families remain incomplete.
9. **Good design is environmentally friendly — 1/3**
Evidence: source weight is small, but recurring polling and idle motion have no reduced-motion gate (`01-evidence.md#9-environmentally-friendly`).
Justification: the runtime is lean yet motion is always on when its conditions are met.
10. **Good design is as little design as possible — 1/3**
Evidence: restrained single slabs coexist with repeated affordances and multi-layer modal atmosphere (`01-evidence.md#10-as-little-design-as-possible`).
Justification: at least three decorative or duplicated elements could be removed without breaking a task.
**Total: 15/30**
+30
View File
@@ -0,0 +1,30 @@
# Verdict — REDESIGN
The strict Rams verdict is **REDESIGN (15/30)**: preserve the excellent brand
tokens and working shell primitives, but redesign the hierarchy so permanent,
transient, and cinematic surfaces no longer compete at the same intensity.
## Highest-leverage moves
1. **Principles #4 and #8 — make behavior legible:** give every icon one primary
click action, add concise Persona-styled hover labels, expose secondary actions
inside its pop-out, and add real focus/disabled/accessibility states. Evidence:
`01-evidence.md#4-understandable` and `#8-thorough`.
2. **Principles #5 and #10 — introduce three intensity tiers:** quiet permanent
chrome, expressive transient panels, theatrical full-screen scenes; reserve
stripes, large split type, and strong halftone for the latter two. Evidence:
`01-evidence.md#5-unobtrusive` and `#10-as-little-design-as-possible`.
3. **Principles #3 and #6 — assign semantics by treatment, not red alone:** use
white fill for selection, a red rule/slash for active state, solid red only
for destructive/error, and pulse only for urgency; fix white-on-red small text.
Evidence: `01-evidence.md#3-aesthetic` and `#6-honest`.
4. **Principles #3 and #7 — move the chaos to macro-composition:** keep clean
module geometry, but vary panel silhouette, title placement, and scale by
context instead of repeating the same framed HUD grammar everywhere. Evidence:
`01-evidence.md#3-aesthetic` and `#7-long-lasting`.
5. **Principles #2 and #9 — add a calm mode:** stop idle equalizer/pulse/marquee
motion when reduced motion is requested and reduce polling when a detail panel
is closed. Evidence: `01-evidence.md#2-useful` and `#9-environmentally-friendly`.
This is a hierarchy redesign, not an instruction to discard the palette,
textures, panel engine, services, or current functionality.
+41
View File
@@ -0,0 +1,41 @@
# Planning handoff
```text
/make-plan Redesign the Takemi Persona 5 desktop shell. Current design failed audit at 15/30 with critical gaps in principles #4 understandable, #5 unobtrusive, #8 thorough, #9 environmentally friendly, and #10 as little design as possible.
Verdict paragraph:
> The strict Rams verdict is REDESIGN (15/30): preserve the excellent brand tokens and working shell primitives, but redesign the hierarchy so permanent, transient, and cinematic surfaces no longer compete at the same intensity.
Why redesign and not refine: the total is below the 20-point refine threshold because the same high-intensity visual and interaction grammar spans permanent chrome, transient controls, and full-screen scenes.
Preserve from current design:
- Crimson/black/white palette, type scale foundation, and motion timings in quickshell/config/Theme.qml:19-148.
- Single-slab bar/rail architecture in quickshell/bar/Bar.qml:8-63 and quickshell/bar/Rail.qml:9-64.
- Reusable P5Panel geometry/texture engine in quickshell/components/P5Panel.qml:7-248.
- Existing services and feature coverage; this is an information/design hierarchy change, not a functionality cut.
Discard:
- One-intensity-fits-all use of stripes, halftone, split type, slashes, and repeated framed geometry. Evidence: DESIGN-IS-2026-08-11/01-evidence.md#5-unobtrusive. Caused failure on principles #5 and #10.
- Undisclosed multi-button/multi-gesture behavior on icon-only permanent controls. Evidence: DESIGN-IS-2026-08-11/01-evidence.md#4-understandable. Caused failure on principles #4 and #8.
Top moves:
1. Principles #4 and #8 — make behavior legible: give every icon one primary click action, add concise Persona-styled hover labels, expose secondary actions inside its pop-out, and add real focus/disabled/accessibility states. Evidence: DESIGN-IS-2026-08-11/01-evidence.md#4-understandable and #8-thorough.
2. Principles #5 and #10 — introduce three intensity tiers: quiet permanent chrome, expressive transient panels, theatrical full-screen scenes; reserve stripes, large split type, and strong halftone for the latter two. Evidence: DESIGN-IS-2026-08-11/01-evidence.md#5-unobtrusive and #10-as-little-design-as-possible.
3. Principles #3 and #6 — assign semantics by treatment, not red alone: use white fill for selection, a red rule/slash for active state, solid red only for destructive/error, and pulse only for urgency; fix white-on-red small text. Evidence: DESIGN-IS-2026-08-11/01-evidence.md#3-aesthetic and #6-honest.
4. Principles #3 and #7 — move the chaos to macro-composition: keep clean module geometry, but vary panel silhouette, title placement, and scale by context instead of repeating the same framed HUD grammar everywhere. Evidence: DESIGN-IS-2026-08-11/01-evidence.md#3-aesthetic and #7-long-lasting.
5. Principles #2 and #9 — add a calm mode: stop idle equalizer/pulse/marquee motion when reduced motion is requested and reduce polling when a detail panel is closed. Evidence: DESIGN-IS-2026-08-11/01-evidence.md#2-useful and #9-environmentally-friendly.
Redesign priorities:
1. Principle #4 — every visible control communicates its primary action without memorized mouse-button lore.
2. Principles #5/#10 — visual intensity increases deliberately from chrome to pop-out to modal.
3. Principles #3/#6 — color, fill, outline, pattern, and motion each have one stable semantic job.
Deliverables:
- New information architecture, not derived mechanically from the old layout.
- New labeled primary flow compared side-by-side with the current shell.
- Empty/loading/error/success/focus/disabled state matrix.
- Migration path that preserves existing service behavior and keybinds.
- Cutover criteria based on discoverability, contrast, reduced motion, and visual hierarchy.
Guard against porting the old structure under new styling, running both hierarchies indefinitely, following generic gaming-HUD trends, or discarding the preserved brand tokens and primitives.
```
+9 -5
View File
@@ -12,7 +12,10 @@ WINIT_X11_SCALE_FACTOR = "1.0"
position = "None" position = "None"
dynamic_padding = true dynamic_padding = true
decorations = "None" decorations = "None"
opacity = 0.9
## Fully opaque, no blur. YoRHa surfaces are flat and opaque —
## glass and translucency read as a different design language entirely.
opacity = 1.0
blur = false blur = false
startup_mode = "Windowed" startup_mode = "Windowed"
dynamic_title = true dynamic_title = true
@@ -24,10 +27,11 @@ decorations_theme_variant = "None"
columns = 82 columns = 82
lines = 24 lines = 24
## Blank space added around the window in pixels. ## Blank space added around the window in pixels. Roomier than the old 3/0 —
## the text needs to sit inside the border, not against it.
[window.padding] [window.padding]
x = 3 x = 12
y = 0 y = 8
## SCROLLING ------------------------------------------------------ ## SCROLLING ------------------------------------------------------
[scrolling] [scrolling]
@@ -103,7 +107,7 @@ ipc_socket = true
## GENERAL -------------------------------------------------------- ## GENERAL --------------------------------------------------------
## Import additional configuration files. ## Import additional configuration files.
import = ["~/.config/alacritty/colors.toml", "~/.config/alacritty/fonts.toml"] import = ["~/.config/alacritty/yorha.toml", "~/.config/alacritty/fonts.toml"]
[[keyboard.bindings]] [[keyboard.bindings]]
key = "Return" key = "Return"
+4 -2
View File
@@ -4,9 +4,11 @@ foreground = "#cdd6f4"
dim_foreground = "#7f849c" dim_foreground = "#7f849c"
bright_foreground = "#cdd6f4" bright_foreground = "#cdd6f4"
# Cursor in the shell accent (Mocha blue) rather than stock rosewater, so the
# terminal caret matches the focus colour everywhere else on the desktop.
[colors.cursor] [colors.cursor]
text = "#1e1e2e" text = "#11111b"
cursor = "#f5e0dc" cursor = "#89b4fa"
[colors.vi_mode_cursor] [colors.vi_mode_cursor]
text = "#1e1e2e" text = "#1e1e2e"
+86
View File
@@ -0,0 +1,86 @@
## Takemi — matched to ~/.config/quickshell/config/Theme.qml and
## ~/.config/hypr/modules/takemi.conf.
##
## Near-black ground, white text, crimson as the only saturated colour. The
## sixteen ANSI slots still have to be told apart by syntax highlighters, so the
## non-crimson hues survive — but every one of them is pulled down and
## desaturated so nothing competes with the accent. Nothing here is neon.
[colors.primary]
background = "#0a0a0c"
foreground = "#ffffff"
dim_foreground = "#6b6b70"
bright_foreground = "#ffffff"
## Crimson block cursor, black text punched out of it.
[colors.cursor]
text = "#000000"
cursor = "#ff2d40"
[colors.vi_mode_cursor]
text = "#000000"
cursor = "#ffffff"
## Selection is a solid crimson block — the same "active" signal the shell uses.
[colors.selection]
text = "#000000"
background = "#ff2d40"
[colors.search.matches]
foreground = "#000000"
background = "#c8c8ce"
[colors.search.focused_match]
foreground = "#000000"
background = "#ff2d40"
[colors.footer_bar]
foreground = "#000000"
background = "#c8c8ce"
[colors.hints.start]
foreground = "#000000"
background = "#ff2d40"
[colors.hints.end]
foreground = "#000000"
background = "#c8c8ce"
## Red is the accent, exactly. The rest are muted enough to sit behind it.
[colors.normal]
black = "#14141a"
red = "#ff2d40"
green = "#7f9b6a"
yellow = "#d6a24a"
blue = "#6f7d99"
magenta = "#c0566d"
cyan = "#7f9aa0"
white = "#c8c8ce"
[colors.bright]
black = "#3a3a44"
red = "#ff6b78"
green = "#9bb886"
yellow = "#f0c070"
blue = "#8d9bb8"
magenta = "#e07a8e"
cyan = "#9db8bd"
white = "#ffffff"
[colors.dim]
black = "#0f0f13"
red = "#8f1826"
green = "#5c704d"
yellow = "#9c7635"
blue = "#515c70"
magenta = "#8c3f4f"
cyan = "#5c7075"
white = "#6b6b70"
[[colors.indexed_colors]]
index = 16
color = "#ff6b78"
[[colors.indexed_colors]]
index = 17
color = "#ffffff"
+67
View File
@@ -0,0 +1,67 @@
# YoRHa dark — matched to ~/.config/quickshell/config/Theme.qml.
#
# The ground and the text are the space-1 wallpaper's void and sage grey. The
# eight hues are the same few steps of earth NieR's world is painted in —
# rust, moss, sand, dusk — pulled down to sit in that void without glowing.
# Selection and search invert, the way NieR's cursor does.
[colors.primary]
background = "#0f110e"
foreground = "#ccd2cb"
dim_foreground = "#79847a"
bright_foreground = "#eef1ea"
[colors.cursor]
text = "#0f110e"
cursor = "#eef1ea"
[colors.vi_mode_cursor]
text = "#0f110e"
cursor = "#bcc1bf"
[colors.search.matches]
foreground = "#0f110e"
background = "#9fa7a2"
[colors.search.focused_match]
foreground = "#0f110e"
background = "#eef1ea"
[colors.footer_bar]
foreground = "#0f110e"
background = "#9fa7a2"
[colors.hints.start]
foreground = "#0f110e"
background = "#c9b27c"
[colors.hints.end]
foreground = "#0f110e"
background = "#9fa7a2"
[colors.selection]
text = "#0f110e"
background = "#bcc1bf"
[colors.normal]
black = "#20241f"
red = "#c8684f"
green = "#8fa37e"
yellow = "#c9b27c"
blue = "#7f9aa8"
magenta = "#a48ba0"
cyan = "#86a8a0"
white = "#bcc1bf"
# Bright black doubles as a panel fill in TUIs (Claude Code's side pane, fzf
# previews), so it sits one step under the outline grey rather than at it:
# light enough for comments, dark enough not to read as a slab.
[colors.bright]
black = "#464d44"
red = "#d98068"
green = "#a5b893"
yellow = "#d8c491"
blue = "#96afbc"
magenta = "#b8a0b4"
cyan = "#9cbdb4"
white = "#eef1ea"
+2 -2
View File
@@ -2,7 +2,7 @@
#* Name of a btop++/bpytop/bashtop formatted ".theme" file, "Default" and "TTY" for builtin themes. #* Name of a btop++/bpytop/bashtop formatted ".theme" file, "Default" and "TTY" for builtin themes.
#* Themes should be placed in "../share/btop/themes" relative to binary or "$HOME/.config/btop/themes" #* Themes should be placed in "../share/btop/themes" relative to binary or "$HOME/.config/btop/themes"
color_theme = "/home/narl/.config/btop/themes/catppuccin_mocha.theme" color_theme = "/home/narl/.config/btop/themes/yorha.theme"
#* If the theme set background should be shown, set to False if you want terminal background transparency. #* If the theme set background should be shown, set to False if you want terminal background transparency.
theme_background = false theme_background = false
@@ -68,7 +68,7 @@ update_ms = 1000
#* Processes sorting, "pid" "program" "arguments" "threads" "user" "memory" "cpu lazy" "cpu direct", #* Processes sorting, "pid" "program" "arguments" "threads" "user" "memory" "cpu lazy" "cpu direct",
#* "cpu lazy" sorts top process over time (easier to follow), "cpu direct" updates top process directly. #* "cpu lazy" sorts top process over time (easier to follow), "cpu direct" updates top process directly.
proc_sorting = "memory" proc_sorting = "cpu direct"
#* Reverse sorting order, True or False. #* Reverse sorting order, True or False.
proc_reversed = false proc_reversed = false
-83
View File
@@ -1,83 +0,0 @@
# Main background, empty for terminal default, need to be empty if you want transparent background
theme[main_bg]="#1e1e2e"
# Main text color
theme[main_fg]="#cdd6f4"
# Title color for boxes
theme[title]="#cdd6f4"
# Highlight color for keyboard shortcuts
theme[hi_fg]="#89b4fa"
# Background color of selected item in processes box
theme[selected_bg]="#45475a"
# Foreground color of selected item in processes box
theme[selected_fg]="#89b4fa"
# Color of inactive/disabled text
theme[inactive_fg]="#7f849c"
# Color of text appearing on top of graphs, i.e uptime and current network graph scaling
theme[graph_text]="#f5e0dc"
# Background color of the percentage meters
theme[meter_bg]="#45475a"
# Misc colors for processes box including mini cpu graphs, details memory graph and details status text
theme[proc_misc]="#f5e0dc"
# CPU, Memory, Network, Proc box outline colors
theme[cpu_box]="#cba6f7" #Mauve
theme[mem_box]="#a6e3a1" #Green
theme[net_box]="#eba0ac" #Maroon
theme[proc_box]="#89b4fa" #Blue
# Box divider line and small boxes line color
theme[div_line]="#6c7086"
# Temperature graph color (Green -> Yellow -> Red)
theme[temp_start]="#a6e3a1"
theme[temp_mid]="#f9e2af"
theme[temp_end]="#f38ba8"
# CPU graph colors (Teal -> Lavender)
theme[cpu_start]="#94e2d5"
theme[cpu_mid]="#74c7ec"
theme[cpu_end]="#b4befe"
# Mem/Disk free meter (Mauve -> Lavender -> Blue)
theme[free_start]="#cba6f7"
theme[free_mid]="#b4befe"
theme[free_end]="#89b4fa"
# Mem/Disk cached meter (Sapphire -> Lavender)
theme[cached_start]="#74c7ec"
theme[cached_mid]="#89b4fa"
theme[cached_end]="#b4befe"
# Mem/Disk available meter (Peach -> Red)
theme[available_start]="#fab387"
theme[available_mid]="#eba0ac"
theme[available_end]="#f38ba8"
# Mem/Disk used meter (Green -> Sky)
theme[used_start]="#a6e3a1"
theme[used_mid]="#94e2d5"
theme[used_end]="#89dceb"
# Download graph colors (Peach -> Red)
theme[download_start]="#fab387"
theme[download_mid]="#eba0ac"
theme[download_end]="#f38ba8"
# Upload graph colors (Green -> Sky)
theme[upload_start]="#a6e3a1"
theme[upload_mid]="#94e2d5"
theme[upload_end]="#89dceb"
# Process box color gradient for threads, mem and cpu usage (Sapphire -> Mauve)
theme[process_start]="#74c7ec"
theme[process_mid]="#b4befe"
theme[process_end]="#cba6f7"
+60
View File
@@ -0,0 +1,60 @@
# YoRHa dark — matched to ~/.config/quickshell/config/Theme.qml and
# ~/.config/alacritty/yorha.toml.
#
# The void ground and sage text of the space-1 wallpaper. Boxes are hairline
# grey, not four competing hues; the light marks what is selected. Every
# gradient climbs from the suit's grey up to the light, and only temperature
# and load run on into the hull's rust, the shell's one colour for "wrong".
theme[main_bg]="#0f110e"
theme[main_fg]="#ccd2cb"
theme[title]="#eef1ea"
theme[hi_fg]="#eef1ea"
theme[selected_bg]="#20241f"
theme[selected_fg]="#eef1ea"
theme[inactive_fg]="#545c52"
theme[graph_text]="#9fa7a2"
theme[meter_bg]="#20241f"
theme[proc_misc]="#9fa7a2"
theme[cpu_box]="#383e35"
theme[mem_box]="#383e35"
theme[net_box]="#383e35"
theme[proc_box]="#383e35"
theme[div_line]="#2a2f28"
theme[temp_start]="#8e978d"
theme[temp_mid]="#c9b27c"
theme[temp_end]="#c8684f"
theme[cpu_start]="#79847a"
theme[cpu_mid]="#bcc1bf"
theme[cpu_end]="#c8684f"
theme[free_start]="#545c52"
theme[free_mid]="#79847a"
theme[free_end]="#9fa7a2"
theme[cached_start]="#545c52"
theme[cached_mid]="#7f9aa8"
theme[cached_end]="#96afbc"
theme[available_start]="#545c52"
theme[available_mid]="#86a8a0"
theme[available_end]="#9cbdb4"
theme[used_start]="#79847a"
theme[used_mid]="#bcc1bf"
theme[used_end]="#eef1ea"
theme[download_start]="#545c52"
theme[download_mid]="#8fa37e"
theme[download_end]="#a5b893"
theme[upload_start]="#545c52"
theme[upload_mid]="#c9b27c"
theme[upload_end]="#d8c491"
theme[process_start]="#79847a"
theme[process_mid]="#9fa7a2"
theme[process_end]="#eef1ea"
+10 -10
View File
@@ -1,22 +1,22 @@
[global] [global]
corner_radius = 4 corner_radius = 0
transparency = 90 transparency = 90
frame_color = "#f38ba8" frame_color = "#545c52"
frame_width = 1 frame_width = 1
separator_color= frame separator_color= frame
highlight = "#89dceb" highlight = "#eef1ea"
[urgency_low] [urgency_low]
background = "#1e1e2e" background = "#0f110e"
foreground = "#cdd6f4" foreground = "#ccd2cb"
timeout = 4 timeout = 4
[urgency_normal] [urgency_normal]
background = "#1e1e2e" background = "#0f110e"
foreground = "#cdd6f4" foreground = "#ccd2cb"
timeout = 6 timeout = 6
[urgency_critical] [urgency_critical]
background = "#1e1e2e" background = "#0f110e"
foreground = "#cdd6f4" foreground = "#ccd2cb"
frame_color = "#f38ba8" frame_color = "#c8684f"
+100
View File
@@ -0,0 +1,100 @@
# Print an optspec for argparse to handle cmd's options that are independent of any subcommand.
function __fish_claude_account_global_optspecs
string join \n h/help V/version
end
function __fish_claude_account_needs_command
# Figure out if the current invocation already has a command.
set -l cmd (commandline -opc)
set -e cmd[1]
argparse -s (__fish_claude_account_global_optspecs) -- $cmd 2>/dev/null
or return
if set -q argv[1]
# Also print the command, so this can be used to figure out what it is.
echo $argv[1]
return 1
end
return 0
end
function __fish_claude_account_using_subcommand
set -l cmd (__fish_claude_account_needs_command)
test -z "$cmd"
and return 1
contains -- $cmd[1] $argv
end
complete -c claude-account -n "__fish_claude_account_needs_command" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_needs_command" -s V -l version -d 'Print version'
complete -c claude-account -n "__fish_claude_account_needs_command" -f -a "status" -d 'Show every account, every proxy, every name and everything that is wrong'
complete -c claude-account -n "__fish_claude_account_needs_command" -f -a "list" -d 'List the accounts, one per line'
complete -c claude-account -n "__fish_claude_account_needs_command" -f -a "switch" -d 'Make an account the one `claude` runs as'
complete -c claude-account -n "__fish_claude_account_needs_command" -f -a "add" -d 'Register another account, with a profile and a proxy of its own'
complete -c claude-account -n "__fish_claude_account_needs_command" -f -a "remove" -d 'Forget an account. Its profile directory is left where it is'
complete -c claude-account -n "__fish_claude_account_needs_command" -f -a "current" -d 'Print the name of the active account'
complete -c claude-account -n "__fish_claude_account_needs_command" -f -a "dir" -d 'Print the profile directory of an account, for `CLAUDE_CONFIG_DIR`'
complete -c claude-account -n "__fish_claude_account_needs_command" -f -a "env" -d 'Print the shell lines that put this shell into an account'
complete -c claude-account -n "__fish_claude_account_needs_command" -f -a "url" -d 'Print the address a name resolves to'
complete -c claude-account -n "__fish_claude_account_needs_command" -f -a "setup" -d 'Install everything at once: the wrapper, a proxy per account, the router'
complete -c claude-account -n "__fish_claude_account_needs_command" -f -a "adopt" -d 'Take over the proxy services that were written by hand'
complete -c claude-account -n "__fish_claude_account_needs_command" -f -a "doctor" -d 'Report every problem and exit non-zero when there is one'
complete -c claude-account -n "__fish_claude_account_needs_command" -f -a "shim" -d 'The `claude` wrapper in ~/.local/bin'
complete -c claude-account -n "__fish_claude_account_needs_command" -f -a "headroom" -d 'The proxy of one account'
complete -c claude-account -n "__fish_claude_account_needs_command" -f -a "router" -d 'The router that resolves <name>.headroom.localhost'
complete -c claude-account -n "__fish_claude_account_needs_command" -f -a "completions" -d 'Tab completion, with the account names in it'
complete -c claude-account -n "__fish_claude_account_using_subcommand status" -s v -l verbose -d 'Add the last lines each proxy logged'
complete -c claude-account -n "__fish_claude_account_using_subcommand status" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand list" -l names -d 'Print the names alone, for scripts and for tab completion'
complete -c claude-account -n "__fish_claude_account_using_subcommand list" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand switch" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand add" -l dir -d 'A profile directory elsewhere' -r -F
complete -c claude-account -n "__fish_claude_account_using_subcommand add" -l port -d 'A port for its proxy; the next free one by default' -r
complete -c claude-account -n "__fish_claude_account_using_subcommand add" -l switch -d 'Make it the account `claude` runs as straight away'
complete -c claude-account -n "__fish_claude_account_using_subcommand add" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand remove" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand current" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand dir" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand env" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand url" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand setup" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand adopt" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand doctor" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand shim; and not __fish_seen_subcommand_from install uninstall" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand shim; and not __fish_seen_subcommand_from install uninstall" -f -a "install" -d 'Write ~/.local/bin/claude'
complete -c claude-account -n "__fish_claude_account_using_subcommand shim; and not __fish_seen_subcommand_from install uninstall" -f -a "uninstall" -d 'Remove it again'
complete -c claude-account -n "__fish_claude_account_using_subcommand shim; and __fish_seen_subcommand_from install" -l force -d 'Overwrite a `claude` that this tool did not write'
complete -c claude-account -n "__fish_claude_account_using_subcommand shim; and __fish_seen_subcommand_from install" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand shim; and __fish_seen_subcommand_from uninstall" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand headroom; and not __fish_seen_subcommand_from install start stop restart uninstall port logs" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand headroom; and not __fish_seen_subcommand_from install start stop restart uninstall port logs" -f -a "install" -d 'Write the service for an account and start it'
complete -c claude-account -n "__fish_claude_account_using_subcommand headroom; and not __fish_seen_subcommand_from install start stop restart uninstall port logs" -f -a "start" -d 'Start the proxy of an account'
complete -c claude-account -n "__fish_claude_account_using_subcommand headroom; and not __fish_seen_subcommand_from install start stop restart uninstall port logs" -f -a "stop" -d 'Stop it'
complete -c claude-account -n "__fish_claude_account_using_subcommand headroom; and not __fish_seen_subcommand_from install start stop restart uninstall port logs" -f -a "restart" -d 'Stop and start it'
complete -c claude-account -n "__fish_claude_account_using_subcommand headroom; and not __fish_seen_subcommand_from install start stop restart uninstall port logs" -f -a "uninstall" -d 'Stop it and remove its service'
complete -c claude-account -n "__fish_claude_account_using_subcommand headroom; and not __fish_seen_subcommand_from install start stop restart uninstall port logs" -f -a "port" -d 'Give an account another port, and write it everywhere it is named'
complete -c claude-account -n "__fish_claude_account_using_subcommand headroom; and not __fish_seen_subcommand_from install start stop restart uninstall port logs" -f -a "logs" -d 'Print what one proxy logged'
complete -c claude-account -n "__fish_claude_account_using_subcommand headroom; and __fish_seen_subcommand_from install" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand headroom; and __fish_seen_subcommand_from start" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand headroom; and __fish_seen_subcommand_from stop" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand headroom; and __fish_seen_subcommand_from restart" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand headroom; and __fish_seen_subcommand_from uninstall" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand headroom; and __fish_seen_subcommand_from port" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand headroom; and __fish_seen_subcommand_from logs" -s n -l lines -d 'How many lines' -r
complete -c claude-account -n "__fish_claude_account_using_subcommand headroom; and __fish_seen_subcommand_from logs" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand router; and not __fish_seen_subcommand_from serve install uninstall port list" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand router; and not __fish_seen_subcommand_from serve install uninstall port list" -f -a "serve" -d 'Serve the names until stopped'
complete -c claude-account -n "__fish_claude_account_using_subcommand router; and not __fish_seen_subcommand_from serve install uninstall port list" -f -a "install" -d 'Install the router as a user service'
complete -c claude-account -n "__fish_claude_account_using_subcommand router; and not __fish_seen_subcommand_from serve install uninstall port list" -f -a "uninstall" -d 'Stop it and remove its service'
complete -c claude-account -n "__fish_claude_account_using_subcommand router; and not __fish_seen_subcommand_from serve install uninstall port list" -f -a "port" -d 'Move the router to another port'
complete -c claude-account -n "__fish_claude_account_using_subcommand router; and not __fish_seen_subcommand_from serve install uninstall port list" -f -a "list" -d 'List the names that resolve'
complete -c claude-account -n "__fish_claude_account_using_subcommand router; and __fish_seen_subcommand_from serve" -l port -r
complete -c claude-account -n "__fish_claude_account_using_subcommand router; and __fish_seen_subcommand_from serve" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand router; and __fish_seen_subcommand_from install" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand router; and __fish_seen_subcommand_from uninstall" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand router; and __fish_seen_subcommand_from port" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand router; and __fish_seen_subcommand_from list" -s h -l help -d 'Print help'
complete -c claude-account -n "__fish_claude_account_using_subcommand completions" -l install -d 'Write the file where the shell reads it, instead of printing it'
complete -c claude-account -n "__fish_claude_account_using_subcommand completions" -s h -l help -d 'Print help'
# The accounts as they are right now, asked for when Tab is pressed.
complete -c claude-account -n '__fish_seen_subcommand_from switch dir env url remove start stop restart logs install uninstall port' -f -a '(claude-account list --names)'
+1
View File
@@ -0,0 +1 @@
complete --keep-order --exclusive --command enqs-guard --arguments "(COMPLETE=fish enqs-guard -- (commandline --current-process --tokenize --cut-at-cursor) (commandline --current-token))"
+1
View File
@@ -0,0 +1 @@
complete --keep-order --exclusive --command enqs-headroom --arguments "(COMPLETE=fish enqs-headroom -- (commandline --current-process --tokenize --cut-at-cursor) (commandline --current-token))"
+1
View File
@@ -0,0 +1 @@
complete --keep-order --exclusive --command enqs-jail --arguments "(COMPLETE=fish enqs-jail -- (commandline --current-process --tokenize --cut-at-cursor) (commandline --current-token))"
-49
View File
@@ -1,49 +0,0 @@
# This file was created by fish when upgrading to version 4.3, to migrate
# theme variables from universal to global scope.
# Don't edit this file, as it will be written by the web-config tool (`fish_config`).
# To customize your theme, delete this file and see
# help interactive#syntax-highlighting
# or
# man fish-interactive | less +/^SYNTAX.HIGHLIGHTING
# for appropriate commands to add to ~/.config/fish/config.fish instead.
# See also the release notes for fish 4.3.0 (run `help relnotes`).
set --global fish_color_autosuggestion 6c7086
set --global fish_color_cancel f38ba8
set --global fish_color_command 89b4fa
set --global fish_color_comment 7f849c
set --global fish_color_cwd f9e2af
set --global fish_color_cwd_root red
set --global fish_color_end fab387
set --global fish_color_error f38ba8
set --global fish_color_escape eba0ac
set --global fish_color_gray 6c7086
set --global fish_color_history_current --bold
set --global fish_color_host 89b4fa
set --global fish_color_host_remote a6e3a1
set --global fish_color_keyword f38ba8
set --global fish_color_match --background=brblue
set --global fish_color_normal cdd6f4
set --global fish_color_operator f5c2e7
set --global fish_color_option a6e3a1
set --global fish_color_param f2cdcd
set --global fish_color_quote a6e3a1
set --global fish_color_redirection f5c2e7
set --global fish_color_search_match --background=313244
set --global fish_color_selection --background=313244
set --global fish_color_status f38ba8
set --global fish_color_user 94e2d5
set --global fish_color_valid_path --underline
set --global fish_pager_color_background
set --global fish_pager_color_completion cdd6f4
set --global fish_pager_color_description 6c7086
set --global fish_pager_color_prefix f5c2e7
set --global fish_pager_color_progress 6c7086
set --global fish_pager_color_secondary_background
set --global fish_pager_color_secondary_completion
set --global fish_pager_color_secondary_description
set --global fish_pager_color_secondary_prefix
set --global fish_pager_color_selected_background
set --global fish_pager_color_selected_completion
set --global fish_pager_color_selected_description
set --global fish_pager_color_selected_prefix
+51
View File
@@ -0,0 +1,51 @@
# YoRHa dark — matched to ~/.config/quickshell/config/Theme.qml and
# ~/.config/alacritty/yorha.toml.
#
# Commands are the planet light, arguments the sage text, strings moss,
# anything wrong the hull red. Selection and the pager's current row invert —
# a light slab with the void punched out of it, the same cursor the shell
# draws.
#
# Note that `fish_config` writing a theme from the web UI will recreate
# fish_frozen_theme.fish, which sorts before this file and so would lose.
set --global fish_color_normal ccd2cb
set --global fish_color_command eef1ea --bold
set --global fish_color_keyword c9b27c
set --global fish_color_param bcc1bf
set --global fish_color_option 9cbdb4
set --global fish_color_quote 8fa37e
set --global fish_color_escape c9b27c
set --global fish_color_operator 9fa7a2
set --global fish_color_redirection 9fa7a2
set --global fish_color_end 79847a
set --global fish_color_comment 545c52
set --global fish_color_error c8684f --bold
set --global fish_color_cancel c8684f
set --global fish_color_gray 545c52
set --global fish_color_autosuggestion 545c52
set --global fish_color_selection 0f110e --background=bcc1bf
set --global fish_color_search_match 0f110e --background=eef1ea
set --global fish_color_match 0f110e --background=bcc1bf
set --global fish_color_history_current --bold
set --global fish_color_valid_path --underline
set --global fish_color_cwd c9b27c --bold
set --global fish_color_cwd_root c8684f --bold
set --global fish_color_user 9cbdb4
set --global fish_color_host eef1ea --bold
set --global fish_color_host_remote 8fa37e
set --global fish_color_status c8684f
set --global fish_pager_color_progress 79847a --background=161915
set --global fish_pager_color_background
set --global fish_pager_color_prefix eef1ea --bold
set --global fish_pager_color_completion ccd2cb
set --global fish_pager_color_description 79847a
set --global fish_pager_color_secondary_background --background=0c0d0b
set --global fish_pager_color_secondary_completion ccd2cb
set --global fish_pager_color_secondary_description 79847a
set --global fish_pager_color_secondary_prefix eef1ea --bold
set --global fish_pager_color_selected_background --background=eef1ea
set --global fish_pager_color_selected_completion 0f110e
set --global fish_pager_color_selected_description 0f110e
set --global fish_pager_color_selected_prefix 0f110e --bold
-3
View File
@@ -38,6 +38,3 @@ if status is-login && test -z "$DISPLAY" -a -z "$WAYLAND_DISPLAY"
end end
end end
# Claude environments
alias claude="CLAUDE_CONFIG_DIR=~/.claude command claude"
alias claude-work="CLAUDE_CONFIG_DIR=~/.claude-work command claude"
+6 -2
View File
@@ -1,9 +1,13 @@
function fish_prompt --description 'Write out the prompt' function fish_prompt --description 'Write out the prompt'
set -l last_status $status set -l last_status $status
set -l normal (set_color normal) set -l normal (set_color normal)
set -l status_color (set_color brgreen) # White at rest, crimson when the last command failed — the prompt
# arrow is the one place the shell says "something is wrong", so it is
# the one place that earns the accent. Was brgreen/brpurple, which
# spent colour on states that are not worth any.
set -l status_color (set_color ffffff)
set -l cwd_color (set_color $fish_color_cwd) set -l cwd_color (set_color $fish_color_cwd)
set -l vcs_color (set_color brpurple) set -l vcs_color (set_color 6b6b70)
set -l prompt_status "" set -l prompt_status ""
# Since we display the prompt on a new line allow the directory names to be longer. # Since we display the prompt on a new line allow the directory names to be longer.
+52
View File
@@ -0,0 +1,52 @@
# name: 'Takemi'
# url: 'local — matched to ~/.config/quickshell/config/Theme.qml'
# preferred_background: 0a0a0c
# Same rules as the shell and the editor: white does the work, crimson is the
# only saturated colour and it means either "load-bearing" (keyword, prefix,
# selected) or "wrong" (error, status, root). Everything else is grey, or a hue
# pulled far enough down that it reads as grey with a temperature.
#
# Kept in sync by hand with conf.d/takemi_theme.fish, which is what actually
# takes effect at startup. This file exists so `fish_config` can preview it.
fish_color_normal ffffff
fish_color_command ffffff --bold
fish_color_keyword ff2d40
fish_color_param c8c8ce
fish_color_option 8d9bb8
fish_color_quote 7f9b6a
fish_color_escape 9db8bd
fish_color_operator c8c8ce
fish_color_redirection ff6b78
fish_color_end 6b6b70
fish_color_comment 6b6b70 --italics
fish_color_error ff2d40 --bold
fish_color_cancel ff2d40
fish_color_gray 6b6b70
fish_color_autosuggestion 4a4a52
fish_color_selection 000000 --background=ff2d40
fish_color_search_match 000000 --background=c8c8ce
fish_color_match 000000 --background=ff2d40
fish_color_history_current --bold
fish_color_valid_path --underline
fish_color_cwd ffffff --bold
fish_color_cwd_root ff2d40 --bold
fish_color_user c8c8ce
fish_color_host ffffff --bold
fish_color_host_remote ff6b78
fish_color_status ff2d40
fish_pager_color_progress 6b6b70 --background=141418
fish_pager_color_background
fish_pager_color_prefix ffffff --bold
fish_pager_color_completion c8c8ce
fish_pager_color_description 6b6b70
fish_pager_color_secondary_background --background=0d0d10
fish_pager_color_secondary_completion c8c8ce
fish_pager_color_secondary_description 6b6b70
fish_pager_color_secondary_prefix ffffff --bold
fish_pager_color_selected_background --background=ff2d40
fish_pager_color_selected_completion 000000
fish_pager_color_selected_description 000000
fish_pager_color_selected_prefix 000000 --bold
+42 -15
View File
@@ -1,8 +1,20 @@
# fluxo-rs configuration # fluxo-rs configuration
# Location: ~/.config/fluxo/config.toml # Location: ~/.config/fluxo/config.toml
#
# The hardware modules below emit raw pipe-delimited values rather than display
# strings. The Quickshell config (~/.config/quickshell/services/Sys.qml) parses
# them and does its own formatting. The previous human-readable Waybar formats
# are preserved in config.toml.waybar-bak.
#
# Quickshell only consumes cpu, mem, gpu, disk, net, sys and power from fluxo.
# Everything else it gets natively — Quickshell.Services.Mpris, .Pipewire,
# .UPower, .Notifications, Quickshell.Bluetooth — and Brightness.qml reads
# sysfs directly. Those fluxo modules are therefore switched off below: left on,
# they held a libpulse context, a BlueZ D-Bus poll every 2 s and an MPRIS
# marquee ticker firing twice a second, all feeding a bar that does not exist.
[general] [general]
menu_command = "fuzzel --dmenu --prompt \"$FLUXO_PROMPT\"" menu_command = "takemi-dmenu --prompt \"$FLUXO_PROMPT\""
[signals] [signals]
network = 1 network = 1
@@ -20,31 +32,37 @@ backlight = 12
dnd = 13 dnd = 13
[network] [network]
format = "{interface} ({ip}):  {rx:^4.1} MB/s  {tx:^4.1} MB/s" format = "{interface}|{ip}|{rx:.3}|{tx:.3}"
[cpu] [cpu]
format = "CPU: {usage:^4.1}% {temp:^4.1}C" format = "{usage:.1}|{temp:.1}"
[memory] [memory]
format = "MEM: {used:^4.1}/{total:^4.1}GB" format = "{used:.2}|{total:.2}"
[sys] [sys]
format = "UP: {uptime} LOAD: {load1:^3.1} " format = "{uptime}|{load1:.2}|{load5:.2}|{load15:.2}"
[disk] [disk]
format = "{mount} {used:^3.0}/{total:^3.0}G" format = "{mount}|{used:.1}|{total:.1}"
[power] [power]
format = "{percentage:>3}% {icon}" format = "{percentage}"
[audio] [audio]
# Quickshell.Services.Pipewire drives Audio.qml; leaving this on kept a
# libpulse context and its callbacks alive for nothing.
enabled = false
format_sink_unmuted = "{name} {volume:>3}% <span size='large'>{icon}</span>" format_sink_unmuted = "{name} {volume:>3}% <span size='large'>{icon}</span>"
format_sink_muted = "{name} <span size='large'> {icon}</span>" format_sink_muted = "{name} <span size='large'> {icon}</span>"
format_source_unmuted = "{name} {volume:>3}% <span size='large'>{icon}</span>" format_source_unmuted = "{name} {volume:>3}% <span size='large'>{icon}</span>"
format_source_muted = "{name} <span size='large'>{icon}</span>" format_source_muted = "{name} <span size='large'>{icon}</span>"
[bt] [bt]
# Quickshell.Bluetooth drives Bt.qml. This module polled BlueZ over D-Bus every
# 2 seconds regardless.
enabled = false
format_plugin = "{alias} [{left}|{right}] {anc} <span size='large'>󰂰</span>" format_plugin = "{alias} [{left}|{right}] {anc} <span size='large'>󰂰</span>"
format_connected = "{alias} <span size='large'>󰂰</span>" format_connected = "{alias} <span size='large'>󰂰</span>"
format_disconnected = "Disconnected <span size='large'>󰂯</span>" format_disconnected = "Disconnected <span size='large'>󰂯</span>"
@@ -55,9 +73,14 @@ format_active = "<span size='large'>󰊖</span>"
format_inactive = "<span size='large'></span>" format_inactive = "<span size='large'></span>"
[mpris] [mpris]
# Quickshell.Services.Mpris drives Media.qml, and MediaPill/MediaPopout do their
# own eliding and scrolling. This module was the single most expensive thing in
# the daemon: `scroll` woke a ticker every `scroll_speed` ms to advance a marquee
# offset for a Waybar that is not running, and each tick drove the signaler.
enabled = false
format = "{artist} - {title}" format = "{artist} - {title}"
max_length = 20 max_length = 20
scroll = true scroll = true
scroll_speed = 500 scroll_speed = 500
scroll_separator = " /// " scroll_separator = " /// "
@@ -66,20 +89,24 @@ enabled = false
format = "{used:>4.0}G / {total:>4.0}G" format = "{used:>4.0}G / {total:>4.0}G"
[gpu] [gpu]
enabled = false enabled = true
format_amd = "AMD: {usage:>3.0}% {vram_used:>4.1}/{vram_total:>4.1}GB {temp:>4.1}C" format_amd = "{usage:.0}|{vram_used:.2}|{vram_total:.2}|{temp:.1}"
format_intel = "iGPU: {usage:>3.0}%" format_intel = "{usage:.0}|0|0|0"
format_nvidia = "NV: {usage:>3.0}% {vram_used:>4.1}/{vram_total:>4.1}GB {temp:>4.1}C" format_nvidia = "{usage:.0}|{vram_used:.2}|{vram_total:.2}|{temp:.1}"
[keyboard] [keyboard]
enabled = false enabled = false
format = "{layout}" format = "{layout}"
[backlight] [backlight]
enable = true # Was `enable = true` — not a key fluxo reads, so this section was silently
format = " {icon} {percentage}" # running on its default of enabled. Brightness.qml reads
# /sys/class/backlight directly, so the D-Bus watcher here is redundant.
enabled = false
format = "{percentage}"
[dnd] [dnd]
enabled = true # Notifs.qml owns do-not-disturb via Quickshell.Services.Notifications.
enabled = false
format_dnd = "<span size='large'>󰂛</span>" format_dnd = "<span size='large'>󰂛</span>"
format_normal = "<span size='large'>󰂚</span>" format_normal = "<span size='large'>󰂚</span>"
+85
View File
@@ -0,0 +1,85 @@
# fluxo-rs configuration
# Location: ~/.config/fluxo/config.toml
[general]
menu_command = "fuzzel --dmenu --prompt \"$FLUXO_PROMPT\""
[signals]
network = 1
cpu = 2
memory = 3
gpu = 4
sys = 5
disk = 6
game = 7
audio = 8
bt = 9
power = 10
mpris = 11
backlight = 12
dnd = 13
[network]
format = "{interface} ({ip}):  {rx:^4.1} MB/s  {tx:^4.1} MB/s"
[cpu]
format = "CPU: {usage:^4.1}% {temp:^4.1}C"
[memory]
format = "MEM: {used:^4.1}/{total:^4.1}GB"
[sys]
format = "UP: {uptime} LOAD: {load1:^3.1} "
[disk]
format = "{mount} {used:^3.0}/{total:^3.0}G"
[power]
format = "{percentage:>3}% {icon}"
[audio]
format_sink_unmuted = "{name} {volume:>3}% <span size='large'>{icon}</span>"
format_sink_muted = "{name} <span size='large'> {icon}</span>"
format_source_unmuted = "{name} {volume:>3}% <span size='large'>{icon}</span>"
format_source_muted = "{name} <span size='large'>{icon}</span>"
[bt]
format_plugin = "{alias} [{left}|{right}] {anc} <span size='large'>󰂰</span>"
format_connected = "{alias} <span size='large'>󰂰</span>"
format_disconnected = "Disconnected <span size='large'>󰂯</span>"
format_disabled = "<span size='large'>󰂲</span>"
[game]
format_active = "<span size='large'>󰊖</span>"
format_inactive = "<span size='large'></span>"
[mpris]
format = "{artist} - {title}"
max_length = 20
scroll = true
scroll_speed = 500
scroll_separator = " /// "
[pool]
enabled = false
format = "{used:>4.0}G / {total:>4.0}G"
[gpu]
enabled = false
format_amd = "AMD: {usage:>3.0}% {vram_used:>4.1}/{vram_total:>4.1}GB {temp:>4.1}C"
format_intel = "iGPU: {usage:>3.0}%"
format_nvidia = "NV: {usage:>3.0}% {vram_used:>4.1}/{vram_total:>4.1}GB {temp:>4.1}C"
[keyboard]
enabled = false
format = "{layout}"
[backlight]
enable = true
format = " {icon} {percentage}"
[dnd]
enabled = true
format_dnd = "<span size='large'>󰂛</span>"
format_normal = "<span size='large'>󰂚</span>"
-28
View File
@@ -1,28 +0,0 @@
include=~/.config/fuzzel/catppuccin-fuzzel/themes/catppuccin-mocha/red.ini
include=~/.config/fuzzel/catppuccin-fuzzel/themes/catppuccin-mocha/red.ini
[main]
font=FiraCode Nerd Font:size=14
terminal=alacritty
dpi-aware=no
width=45
lines=8
horizontal-pad=20
vertical-pad=20
inner-pad=10
prompt=" "
show-actions=no
layer=overlay
[border]
radius=12
width=2
[colors]
background=1e1e2e80
# Matches the background so the counter becomes invisible for a cleaner look
counter=1e1e2eff
border=f38ba8ff
+57
View File
@@ -0,0 +1,57 @@
/* YoRHa colours for adw-gtk3 — kept in step with
* ~/.config/quickshell/config/Theme.qml and gtk-4.0/gtk.css.
*
* adw-gtk3 draws the libadwaita shapes (soft corners, flat cards) and takes
* every surface from these named colours, so the whole palette lives here.
* Selection is the shell's light slab with ink text; red only for destructive
* and failed. */
@define-color accent_color #eef1ea;
@define-color accent_bg_color #bcc1bf;
@define-color accent_fg_color #090a08;
@define-color destructive_color #c8684f;
@define-color destructive_bg_color #c8684f;
@define-color destructive_fg_color #090a08;
@define-color error_color #c8684f;
@define-color error_bg_color #c8684f;
@define-color error_fg_color #090a08;
@define-color warning_color #c9b27c;
@define-color warning_bg_color #c9b27c;
@define-color warning_fg_color #090a08;
@define-color success_color #8fa37e;
@define-color success_bg_color #8fa37e;
@define-color success_fg_color #090a08;
@define-color window_bg_color #0f110e;
@define-color window_fg_color #ccd2cb;
@define-color view_bg_color #0c0d0b;
@define-color view_fg_color #ccd2cb;
@define-color headerbar_bg_color #161915;
@define-color headerbar_fg_color #ccd2cb;
@define-color headerbar_border_color #2a2f28;
@define-color headerbar_backdrop_color #0f110e;
@define-color headerbar_shade_color rgba(0, 0, 0, 0.36);
@define-color sidebar_bg_color #161915;
@define-color sidebar_fg_color #ccd2cb;
@define-color sidebar_backdrop_color #0f110e;
@define-color sidebar_shade_color rgba(0, 0, 0, 0.36);
@define-color secondary_sidebar_bg_color #0f110e;
@define-color secondary_sidebar_fg_color #ccd2cb;
@define-color secondary_sidebar_backdrop_color #0c0d0b;
@define-color card_bg_color #161915;
@define-color card_fg_color #ccd2cb;
@define-color card_shade_color rgba(0, 0, 0, 0.36);
@define-color dialog_bg_color #161915;
@define-color dialog_fg_color #ccd2cb;
@define-color popover_bg_color #20241f;
@define-color popover_fg_color #ccd2cb;
@define-color popover_shade_color rgba(0, 0, 0, 0.36);
@define-color thumbnail_bg_color #20241f;
@define-color thumbnail_fg_color #ccd2cb;
@define-color shade_color rgba(0, 0, 0, 0.36);
@define-color scrollbar_outline_color rgba(0, 0, 0, 0.5);
+7
View File
@@ -0,0 +1,7 @@
[Settings]
gtk-theme-name=adw-gtk3-dark
gtk-icon-theme-name=Papirus-Dark
gtk-font-name=Noto Sans 11
gtk-cursor-theme-name=Bibata-Modern-Classic
gtk-cursor-theme-size=24
gtk-application-prefer-dark-theme=1
+95
View File
@@ -0,0 +1,95 @@
/* YoRHa colours for libadwaita and adw-gtk3 — kept in step with
* ~/.config/quickshell/config/Theme.qml and gtk-3.0/gtk.css.
*
* libadwaita reads its palette from CSS variables; the named colours below
* the variables cover GTK4 apps that still go through the adw-gtk3 theme. */
:root {
--accent-color: #eef1ea;
--accent-bg-color: #bcc1bf;
--accent-fg-color: #090a08;
--destructive-color: #c8684f;
--destructive-bg-color: #c8684f;
--destructive-fg-color: #090a08;
--error-color: #c8684f;
--error-bg-color: #c8684f;
--error-fg-color: #090a08;
--warning-color: #c9b27c;
--warning-bg-color: #c9b27c;
--warning-fg-color: #090a08;
--success-color: #8fa37e;
--success-bg-color: #8fa37e;
--success-fg-color: #090a08;
--window-bg-color: #0f110e;
--window-fg-color: #ccd2cb;
--view-bg-color: #0c0d0b;
--view-fg-color: #ccd2cb;
--headerbar-bg-color: #161915;
--headerbar-fg-color: #ccd2cb;
--headerbar-border-color: #2a2f28;
--headerbar-backdrop-color: #0f110e;
--headerbar-shade-color: rgba(0, 0, 0, 0.36);
--sidebar-bg-color: #161915;
--sidebar-fg-color: #ccd2cb;
--sidebar-backdrop-color: #0f110e;
--sidebar-shade-color: rgba(0, 0, 0, 0.36);
--secondary-sidebar-bg-color: #0f110e;
--secondary-sidebar-fg-color: #ccd2cb;
--secondary-sidebar-backdrop-color: #0c0d0b;
--card-bg-color: #161915;
--card-fg-color: #ccd2cb;
--card-shade-color: rgba(0, 0, 0, 0.36);
--dialog-bg-color: #161915;
--dialog-fg-color: #ccd2cb;
--popover-bg-color: #20241f;
--popover-fg-color: #ccd2cb;
--popover-shade-color: rgba(0, 0, 0, 0.36);
--thumbnail-bg-color: #20241f;
--thumbnail-fg-color: #ccd2cb;
--shade-color: rgba(0, 0, 0, 0.36);
--scrollbar-outline-color: rgba(0, 0, 0, 0.5);
}
@define-color accent_color #eef1ea;
@define-color accent_bg_color #bcc1bf;
@define-color accent_fg_color #090a08;
@define-color destructive_color #c8684f;
@define-color destructive_bg_color #c8684f;
@define-color destructive_fg_color #090a08;
@define-color error_color #c8684f;
@define-color error_bg_color #c8684f;
@define-color error_fg_color #090a08;
@define-color warning_color #c9b27c;
@define-color warning_bg_color #c9b27c;
@define-color warning_fg_color #090a08;
@define-color success_color #8fa37e;
@define-color success_bg_color #8fa37e;
@define-color success_fg_color #090a08;
@define-color window_bg_color #0f110e;
@define-color window_fg_color #ccd2cb;
@define-color view_bg_color #0c0d0b;
@define-color view_fg_color #ccd2cb;
@define-color headerbar_bg_color #161915;
@define-color headerbar_fg_color #ccd2cb;
@define-color headerbar_border_color #2a2f28;
@define-color headerbar_backdrop_color #0f110e;
@define-color headerbar_shade_color rgba(0, 0, 0, 0.36);
@define-color sidebar_bg_color #161915;
@define-color sidebar_fg_color #ccd2cb;
@define-color sidebar_backdrop_color #0f110e;
@define-color sidebar_shade_color rgba(0, 0, 0, 0.36);
@define-color secondary_sidebar_bg_color #0f110e;
@define-color secondary_sidebar_fg_color #ccd2cb;
@define-color secondary_sidebar_backdrop_color #0c0d0b;
@define-color card_bg_color #161915;
@define-color card_fg_color #ccd2cb;
@define-color card_shade_color rgba(0, 0, 0, 0.36);
@define-color dialog_bg_color #161915;
@define-color dialog_fg_color #ccd2cb;
@define-color popover_bg_color #20241f;
@define-color popover_fg_color #ccd2cb;
@define-color popover_shade_color rgba(0, 0, 0, 0.36);
@define-color thumbnail_bg_color #20241f;
@define-color thumbnail_fg_color #ccd2cb;
@define-color shade_color rgba(0, 0, 0, 0.36);
@define-color scrollbar_outline_color rgba(0, 0, 0, 0.5);
+7
View File
@@ -0,0 +1,7 @@
[Settings]
gtk-theme-name=adw-gtk3-dark
gtk-icon-theme-name=Papirus-Dark
gtk-font-name=Noto Sans 11
gtk-cursor-theme-name=Bibata-Modern-Classic
gtk-cursor-theme-size=24
gtk-application-prefer-dark-theme=1
+10
View File
@@ -0,0 +1,10 @@
{
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
"runtime.version": "Lua 5.4",
"workspace.library": [
"/usr/share/hypr/stubs"
],
"diagnostics.globals": [
"hl"
]
}
-18
View File
@@ -1,18 +0,0 @@
# narls hyprland dotfiles
source = ~/.config/hypr/modules/monitors.conf
source = ~/.config/hypr/modules/programs.conf
source = ~/.config/hypr/modules/autostart.conf
source = ~/.config/hypr/modules/env.conf
source = ~/.config/hypr/modules/theme.conf
source = ~/.config/hypr/modules/input.conf
source = ~/.config/hypr/modules/keybinds.conf
source = ~/.config/hypr/modules/window_rules.conf
+40
View File
@@ -0,0 +1,40 @@
-- narls hyprland dotfiles
--
-- Hyprland 0.55+ reads this file instead of hyprland.conf. Every part lives in
-- lua/, and each require() is its own scope: an error in one file does not stop
-- the others from loading.
--
-- Order matters only in three places: palette must exist before anything
-- paints with it, monitors must exist before the lid handler binds to them,
-- and game mode reads the look and the animations as configured. All three are
-- handled by the requires below, top to bottom.
-- what the desktop looks like
require("lua/palette")
require("lua/look")
require("lua/animations")
-- what it runs on
require("lua/env")
require("lua/monitors")
require("lua/lid")
require("lua/input")
-- what it runs
require("lua/apps")
require("lua/autostart")
-- how windows behave
require("lua/rules/windows")
require("lua/rules/layers")
-- after look and animations: it reads their values to put back
require("lua/gamemode")
-- how it is driven
require("lua/binds/apps")
require("lua/binds/windows")
require("lua/binds/workspaces")
require("lua/binds/shell")
require("lua/binds/media")
require("lua/binds/capture")
-545
View File
@@ -1,545 +0,0 @@
# For more information visit https://wiki.hyprland.org/Hypr-Ecosystem/hyprlock/
source = ~/.config/hypr/catppuccin-hyprland/themes/mocha.conf
$scrPath = ~/.config/hypr/hyprlock/
$imgPath = ~/.config/hypr/hyprlock/assets/
$USER = narl # Your Alias
$host = uname -n #Your Alias
$wifi-mode = false # Change the WiFi output. true = show SSID, false = Connected.
$bt-mode = false # Change the Bluetooth output. true = show Device name, false = Connected.
# Default geolocation provider is IP-API.COM, hardcode it if the location does not match the current location.
# Hardcode your location (If hardcode does not recognize your City, change it to IP Geolocation provider in weatherinfo.sh)
$CITY =
$COUNTRY =
# WALLPAPER
# Just comment and uncomment to select
$wallpaper = screenshot
#$wallpaper = ~/your/wallpaper-path.png
$blur = 2
# ADJUST HERE
$rounding = 12
$shape-rd = -1
$font-text = GeistMono Nerd Font Bold
$font-text0 = GeistMono Nerd Font
$font-display = PP Neue Machina Ultra-Bold
$font-symbol = JetBrainsMono Nerd Font Mono
# Input Var
$weight = 2
$inner-color = rgba($baseAlpha80)
$border-color = rgba($mauveAlpha80)
# Colors
$clock-color = $text
$fg0 = $text
$fg1 = $base
$shape-col0 = $mauve
$shape-col1 = rgba($surface0Alpha80)
$shape-col2 = rgba($surface1Alpha80)
$shadow-pass = 2
$shadow-size = 3
$shadow-color = $crust
$shadow-boost = 1.2
$text-shadow-pass = 1
$text-shadow-boost = 0.5
# GENERAL
general {
no_fade_in = true
grace = 1
disable_loading_bar = false
hide_cursor = true
ignore_empty_input = true
text_trim = true
}
#BACKGROUND
background {
monitor =
path = $wallpaper
blur_passes = $blur
contrast = 0.8916
brightness = 0.7172
vibrancy = 0.1696
vibrancy_darkness = 0
}
# PROFILE PICTURE
image {
monitor =
path = $imgPath/profile.jpg
size = 120 # lesser side if not 1:1 ratio
opacity = 0.25
shadow_pass = $shadow-pass
shadow_size = $shadow-size
shadow_color = $shadow-color
shadow_boost = $shadow-boost
rounding = $rounding # negative values mean circle
border_size = 4
border_color = $mauve
rotate = 0 # degrees, counter-clockwise
position = 20, -20
halign = left
valign = top
zindex = 1
}
# USER INFO
label {
monitor =
text = cmd[update:1000] echo -e "$USER\n󰁥 $($host)\n\n$(uname -r)\nPackages: $(pacman -Q | wc -l) pacman"
shadow_passes = 1
shadow_boost = 0.5
color = $fg0
font_size = 11
font_family = $font-text
position = 170, -35
halign = left
valign = top
}
# WEATHERCAST & LOCATION
label {
monitor =
text = cmd[update:1000] echo "$(bash $scrPath/weatherinfo.sh)"
shadow_passes = 1
shadow_boost = 0.5
color = $fg0
font_size = 11
font_family = $font-text
position = 0, -20
halign = center
valign = top
}
# INPUT FIELD
input-field {
monitor =
size = 275, 55
rounding = $rounding
outline_thickness = $weight
outer_color = $border-color
dots_size = 0.1 # Scale of input-field height, 0.2 - 0.8
dots_spacing = 1 # Scale of dots' absolute size, 0.0 - 1.0
dots_center = true
inner_color = $inner-color
font_color = $fg0
fade_on_empty = false
font_family = $font-text
placeholder_text = <span foreground="##$textAlpha">󰢏 $USER</span>
hide_input = false
check_color = $mauve
fail_color = $red
fail_text = <i>$FAIL <b>($ATTEMPTS)</b></i>
capslock_color = $yellow
position = 0, -240
halign = center
valign = center
zindex = 10
}
# TIME HR
label {
monitor =
text = cmd[update:1000] echo -e "$(date +"%H")" # 24-Hour Format
#text = cmd[update:1000] echo -e "$(date +"%I")" # 12-Hour Format
color = $clock-color
shadow_pass = $shadow-pass
shadow_size = $shadow-size
shadow_color = $shadow-color
shadow_boost = $shadow-boost
font_size = 150
font_family = $font-display
position = 0, -155
halign = center
valign = top
}
# TIME MM
label {
monitor =
text = cmd[update:1000] echo -e "$(date +"%M")"
color = $clock-color
shadow_pass = $shadow-pass
shadow_size = $shadow-size
shadow_color = $shadow-color
shadow_boost = $shadow-boost
font_size = 150
font_family = $font-display
position = 0, -325
halign = center
valign = top
}
# AM/PM for 12-Hour Format
#label {
monitor =
text = cmd[update:1000] echo -e "$(date +"%p")"
color = $clock-color
shadow_pass = $shadow-pass
shadow_size = $shadow-size
shadow_color = $shadow-color
shadow_boost = $shadow-boost
font_size = 16
font_family = $font-display
position = 0, 17
halign = center
valign = center
zindex = 5
}
# AM/PM BG
#shape {
monitor =
size = 70, 40
shadow_passes = $text-shadow-pass
shadow_boost = $text-shadow-boost
color = $shape-col2
rounding = $rounding
border_size =
border_color =
position = 0, 20
halign = center
valign = center
zindex = 1
}
# GREETING
label {
monitor =
text = cmd[update:1000] echo "$(bash $scrPath/greeting.sh)"
shadow_passes = $text-shadow-pass
shadow_boost = $text-shadow-boost
color = $fg0
font_size = 11
font_family = $font-text
position = 0, -55
halign = center
valign = center
}
# TODAY IS
label {
monitor =
text = cmd[update:1000] bash -c 'day=$(date +%A); echo "Today is $day"'
shadow_passes = $text-shadow-pass
shadow_boost = $text-shadow-boost
color = $fg0
font_size = 11
font_family = $font-text
position = 0, -75
halign = center
valign = center
}
# DATE
label {
monitor =
text = cmd[update:1000] bash -c 'day=$(date +%d); case "$day" in 1) suffix="st";; 2) suffix="nd";; 3) suffix="rd";; *) suffix="th";; esac; echo -e "$(date +"%B %e")'$day'$suffix, $(date +%Y)"'
shadow_passes = $text-shadow-pass
shadow_boost = $text-shadow-boost
color = $fg0
font_size = 14
font_family = $font-text
position = 0, -115
halign = center
valign = center
}
# BATTERY
label {
monitor =
text = cmd[update:1000] echo -e "$($scrPath/battery.sh)"
color = $fg1
font_size = 12
font_family = $font-text
position = -37, 29
halign = right
valign = bottom
zindex = 5
}
# NETWORK
label {
monitor =
text = cmd[update:1000] echo -e "$($scrPath/network.sh)"
color = $fg1
font_size = 12
font_family = $font-text
position = 37, 29
halign = left
valign = bottom
zindex = 5
}
# BLUETOOTH
label {
monitor =
text = cmd[update:1000] echo -e "$(~/.config/hypr/hyprlock/bluetooth.sh)"
color = $fg1
font_size = 12
font_family = $font-text
position = 37, 85
halign = left
valign = bottom
zindex = 5
}
# BATTERY BG
shape {
monitor =
size = 90, 40
shadow_passes = $text-shadow-pass
shadow_boost = $text-shadow-boost
color = $shape-col0
rounding = $shape-rd
border_size =
border_color =
position = -20, 20
halign = right
valign = bottom
zindex = 1
}
# NETWORK BG
shape {
monitor =
size = 150, 40
shadow_passes = $text-shadow-pass
shadow_boost = $text-shadow-boost
color = $shape-col0
rounding = $shape-rd
border_size =
border_color =
position = 20, 20
halign = left
valign = bottom
zindex = 1
}
# BLUETOOTH BG
shape {
monitor =
size = 150, 40
shadow_passes = $text-shadow-pass
shadow_boost = $text-shadow-boost
color = $shape-col0
rounding = $shape-rd
border_size =
border_color =
position = 20, 75
halign = left
valign = bottom
zindex = 1
}
# MEDIA BG
image {
monitor =
path = $imgPath/media-bg-dark-25.png
size = 8%
opacity = 0.25
rounding = 5
border_size = 0
rotate = 0
position = 0, -10%
halign = center
valign = center
zindex = 1
}
shape {
monitor =
size = 35%, 8%
shadow_passes = $text-shadow-pass
shadow_boost = $text-shadow-boost
color = $shape-col1
rounding = $rounding
border_size =
border_color =
position = 0, 5%
halign = center
valign = bottom
zindex = 1
}
# PLAYER TITLE
label {
monitor =
text = cmd[update:1000] echo "$($scrPath/playerctl.sh --title)"
color = $fg0
font_size = 14
font_family = $font-text
position = 0, -40%
halign = center
valign = center
zindex = 5
}
# PLAYER ARTIST
label {
monitor =
text = cmd[update:1000] echo "$($scrPath/playerctl.sh --artist)"
color = $fg0
font_size = 11
font_family = $font-text
position = 0, -42%
halign = center
valign = center
zindex = 5
}
# PLAYER ALBUM
label {
monitor =
text = cmd[update:1000] echo "$($scrPath/playerctl.sh --album)"
color = $fg0
font_size = 11
font_family = $font-text0
position = 0, -44%
halign = center
valign = center
zindex = 5
}
# PLAYER STATUS SYMBOL
label {
monitor =
text = cmd[update:1000] echo "$($scrPath/playerctl.sh --status-symbol)"
color = $fg0
font_size = 16
font_family = $font-symbol
position = 33.5%, -38%
halign = left
valign = center
zindex = 5
}
# PLAYER STATUS
label {
monitor =
text = cmd[update:1000] echo "$($scrPath/playerctl.sh --status)"
color = $fg0
font_size = 10
font_family = $font-text
position = 35%, -38%
halign = left
valign = center
zindex = 5
}
# PLAYER SOURCE SYMBOL
label {
monitor =
text = cmd[update:1000] echo "$($scrPath/playerctl.sh --source-symbol)"
color = rgba($subtext0Alpha99)
font_size = 16
font_family = $font-symbol
position = -33.5%, -38%
halign = right
valign = center
zindex = 5
}
# PLAYER SOURCE
label {
monitor =
text = cmd[update:1000] echo "$($scrPath/playerctl.sh --source)"
color = rgba($subtext0Alpha99)
font_size = 10
font_family = $font-text
position = -35%, -38%
halign = right
valign = center
zindex = 5
}
label {
monitor =
text =
color = $fg0
font_size = 24
font_family = $font-symbol
position = 0, 1%
halign = center
valign = bottom
}
-155
View File
@@ -1,155 +0,0 @@
source = $HOME/.config/hypr/catppuccin-hyprland/themes/mocha.conf
$accent = $mauve
$accentAlpha = $mauveAlpha
$font = JetBrainsMono Nerd Font
# GENERAL
general {
hide_cursor = true
no_fade_in = false
grace = 0
disable_loading_bar = true
}
# BACKGROUND
background {
monitor =
path = $HOME/Pictures/reze/reze.png
blur_passes = 3
blur_size = 7
noise = 0.0117
contrast = 0.8916
brightness = 0.8172
vibrancy = 0.1696
vibrancy_darkness = 0.0
color = $base
}
# TIME
label {
monitor =
text = $TIME
color = $text
font_size = 120
font_family = $font
position = 0, 300
halign = center
valign = center
}
# DATE
label {
monitor =
text = cmd[update:43200000] date +"%A, %d %B %Y"
color = $text
font_size = 30
font_family = $font
position = 0, 200
halign = center
valign = center
}
# USER AVATAR (Center)
image {
monitor =
path = $HOME/.face
size = 120
border_color = rgba(255, 255, 255, 0.1)
position = 0, 50
halign = center
valign = center
}
# INPUT FIELD (Center)
input-field {
monitor =
size = 300, 60
outline_thickness = 2
dots_size = 0.2
dots_spacing = 0.2
dots_center = true
outer_color = rgba(255, 255, 255, 0.1)
inner_color = rgba(0, 0, 0, 0.2)
font_color = $text
fade_on_empty = false
placeholder_text = <span foreground="##$textAlpha"><i>󰌾 Logged in as </i><span foreground="##$accentAlpha">$USER</span></span>
hide_input = false
check_color = $accent
fail_color = $red
fail_text = <i>$FAIL <b>($ATTEMPTS)</b></i>
capslock_color = $yellow
position = 0, -60
halign = center
valign = center
}
# --- MUSIC DASHBOARD (TOP RIGHT) ---
# ALBUM ART (At the very top right, 80px high, adjusted -3px for perfect alignment)
image {
monitor =
path = /tmp/hyprlock_art.png
size = 80
rounding = 15
border_color = rgba(255, 255, 255, 0.1)
reload_time = 2
reload_cmd = ~/.config/hypr/scripts/album_art.sh
position = -20, -17
halign = right
valign = top
}
# MUSIC TEXT BOX (To the left of the image, same height)
shape {
monitor =
size = 300, 80
color = rgba(0, 0, 0, 0.3)
rounding = 15
border_size = 1
border_color = rgba(255, 255, 255, 0.1)
position = -110, -20
halign = right
valign = top
}
# MUSIC TEXT (Right aligned and vertically centered inside the 80px box)
label {
monitor =
text = cmd[update:1000] ~/.config/hypr/scripts/songdetail.sh
color = $text
font_size = 14
font_family = $font
position = -125, -37
halign = right
valign = top
text_align = right
}
# --- STATUS DASHBOARD (BOTTOM RIGHT) ---
# SYSTEM INFO BOX (Vertical stack container)
shape {
monitor =
size = 300, 100
color = rgba(0, 0, 0, 0.3)
rounding = 15
border_size = 1
border_color = rgba(255, 255, 255, 0.1)
position = -20, 20
halign = right
valign = bottom
}
# SYSTEM INFO TEXT (3 lines, top of each other, right aligned)
label {
monitor =
text = cmd[update:5000] ~/.config/hypr/scripts/status_info.sh
color = $text
font_size = 14
font_family = $font
position = -35, 35
halign = right
valign = bottom
text_align = right
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

-33
View File
@@ -1,33 +0,0 @@
#!/bin/bash
# Get the current battery percentage
battery_percentage=$(cat /sys/class/power_supply/BAT0/capacity)
# Get the battery status (Charging or Discharging)
battery_status=$(cat /sys/class/power_supply/BAT0/status)
# Define the battery icons for each 10% segment
battery_icons=("󰂃" "󰁺" "󰁻" "󰁼" "󰁽" "󰁾" "󰁿" "󰂀" "󰂁" "󰁹")
# Define the charging icon
charging_icon="󰂄"
# Calculate the index for the icon array
# Ensure the index is within bounds (0 to 9) for battery percentages 0 to 100
icon_index=$((battery_percentage / 10))
# If the battery is 100%, use the last icon (index 9)
if [ "$battery_percentage" -eq 100 ]; then
icon_index=9
fi
# Get the corresponding icon
battery_icon=${battery_icons[$icon_index]}
# Check if the battery is charging
if [ "$battery_status" = "Charging" ]; then
battery_icon="$charging_icon"
fi
# Output the battery percentage and icon
echo "$battery_percentage% $battery_icon"
-47
View File
@@ -1,47 +0,0 @@
#!/bin/bash
# Path to the Hyprland configuration file
config_file="$HOME/.config/hypr/hyprlock.conf"
# Read the bt-mode value from the configuration file
bt_mode=$(grep -oP '^\$bt-mode\s*=\s*\K\S+' ~/.config/hypr/hyprlock.conf)
# Get Bluetooth power status
bluetooth_status=$(bluetoothctl show | grep "Powered:" | awk '{print $2}')
# Check if Bluetooth is powered on
if [ "$bluetooth_status" != "yes" ]; then
echo "󰂯 Bluetooth Off"
exit 0
fi
# Initialize connected devices
connected_devices=$(echo "$connected_devices" | sed 's/[[:space:]]*$//')
# Process Bluetooth devices
while read -r line; do
echo "Processing line: $line" >&2
device_mac=$(echo "$line" | awk '{print $2}')
device_name=$(echo "$line" | cut -d' ' -f3-)
echo "Device MAC: $device_mac" >&2
echo "Device Name: $device_name" >&2
if bluetoothctl info "$device_mac" | grep -q "Connected: yes"; then
connected_devices+="$device_name "
fi
echo "Finished processing $device_mac" >&2
echo "---" >&2
done < <(bluetoothctl devices)
# If no connected devices, show "No Devices"
if [ -z "$connected_devices" ]; then
echo "󰂲 No Devices"
exit 0
fi
# Display output based on bt-mode
if [ "$bt_mode" = "true" ]; then
echo "󰂯 $connected_devices"
else
echo "󰂯 Connected"
fi
-22
View File
@@ -1,22 +0,0 @@
#!/bin/bash
# Only works if you use SWWW for your wallpapers
SWWW_DIR="$HOME/.cache/swww/"
HYPRLOCK="$HOME/.config/hypr/hyprlock.conf"
# Get a Monitor Cache File
FIRST_FILE=$(find "$SWWW_DIR" -type f | head -n 1)
# Check if SWWW file exists
if [ -n "$FIRST_FILE" ] && [ -f "$FIRST_FILE" ]; then
WALLPAPER=$(sed -n '2p' "$FIRST_FILE")
sed -i "s|^\(\$wallpaper[[:space:]]*=[[:space:]]*\).*|\1$WALLPAPER # (screenshot or /path/to/your/wallpaper.jpg)|" "$HYPRLOCK"
echo "Wallpaper path updated to $WALLPAPER"
exit 0
fi
# Error
echo "Fehler: kein Wallpaper gefunden."
exit 1
-32
View File
@@ -1,32 +0,0 @@
#!/bin/bash
# Set your Username
# username="TamarindX"
# Read the username alias from hyprlock.conf
username=$(grep -oP '^\$USER\s*=\s*\K\S+' ~/.config/hypr/hyprlock.conf)
# Check if the username was successfully extracted
if [ -z "$username" ]; then
echo "Username not found in hyprlock.conf."
exit 1
fi
# Get the current hour
hour=$(date +%H)
# Determine the greeting based on the time
if [ "$hour" -ge 5 ] && [ "$hour" -lt 12 ]; then
greeting="Good Morning"
elif [ "$hour" -ge 12 ] && [ "$hour" -lt 17 ]; then
greeting="Good Afternoon"
elif [ "$hour" -ge 17 ] && [ "$hour" -lt 21 ]; then
greeting="Good Evening"
elif [ "$hour" -ge 21 ] && [ "$hour" -lt 24 ]; then
greeting="Good Night"
else
greeting="GO TO SLEEP!"
fi
# Output the combined text
echo -e "Hello, $username! $greeting"
-65
View File
@@ -1,65 +0,0 @@
#!/bin/bash
# Set this variable to control the output
# Set to "true" to show SSID, "false" to show "Connected"
# show_ssid=false
# Read the wifi-mode alias from hyprlock.conf
show_ssid=$(grep -oP '^\$wifi-mode\s*=\s*\K\S+' ~/.config/hypr/hyprlock.conf)
# Check if the SSID was successfully extracted else fallback?!
if [ -z "$show_ssid" ]; then
show_ssid=false
fi
# Check if any Ethernet connection is active
ethernet_connected=$(nmcli -t -f DEVICE,TYPE,STATE dev | grep -E 'ethernet:connected')
# If Ethernet
if [ -n "$ethernet_connected" ]; then
echo "󰈀 Ethernet"
exit 0
fi
# Get Wi-Fi connection status
wifi_status=$(nmcli -t -f WIFI g)
# Check if Wi-Fi is enabled
if [ "$wifi_status" != "enabled" ]; then
echo "󰤮 Wi-Fi Off"
exit 0
fi
# Get active Wi-Fi connection details
wifi_info=$(nmcli -t -f ACTIVE,SSID,SIGNAL dev wifi | grep '^yes')
# If no active connection, show "Disconnected"
if [ -z "$wifi_info" ]; then
echo "󰤮 No Wi-Fi"
exit 0
fi
# Extract SSID
ssid=$(echo "$wifi_info" | cut -d':' -f2)
# Extract signal strength
signal_strength=$(echo "$wifi_info" | cut -d':' -f3)
# Define Wi-Fi icons based on signal strength
wifi_icons=("󰤯" "󰤟" "󰤢" "󰤥" "󰤨") # From low to high signal
# Clamp signal strength between 0 and 100
signal_strength=$((signal_strength < 0 ? 0 : (signal_strength > 100 ? 100 : signal_strength)))
# Calculate the icon index based on signal strength
icon_index=$((signal_strength / 25))
# Get the corresponding icon
wifi_icon=${wifi_icons[$icon_index]}
# Output based on show_ssid variable
if [ "$show_ssid" = true ]; then
echo "$wifi_icon $ssid"
else
echo "$wifi_icon Connected"
fi
-116
View File
@@ -1,116 +0,0 @@
#!/bin/bash
if [ $# -eq 0 ]; then
echo "Usage: $0 --title | --artist | --album | --source | --source-symbol"
exit 1
fi
# Function to get metadata using playerctl
get_metadata() {
key=$1
playerctl metadata --player=%any,chromium,firefox --format "{{ $key }}" 2>/dev/null
}
# Check for arguments
# Function to determine the source and return an icon and text
get_source_info_symbol() {
trackid=$(get_metadata "mpris:trackid")
if [[ "$trackid" == *"firefox"* ]]; then
echo -e "󰈹"
elif [[ "$trackid" == *"spotify"* ]]; then
echo -e ""
elif [[ "$trackid" == *"chromium"* ]]; then
echo -e ""
else
echo ""
fi
}
get_source_info() {
trackid=$(get_metadata "mpris:trackid")
if [[ "$trackid" == *"firefox"* ]]; then
echo -e "Firefox"
elif [[ "$trackid" == *"spotify"* ]]; then
echo -e "Spotify"
elif [[ "$trackid" == *"chromium"* ]]; then
echo -e "Chrome"
else
echo ""
fi
}
# Function to truncate text with ellipsis if necessary
truncate_with_ellipsis() {
text=$1
max_length=$2
if [ ${#text} -gt $max_length ]; then
echo "${text:0:$((max_length - 3))}..."
else
echo "$text"
fi
}
# Parse the argument
case "$1" in
--title)
title=$(get_metadata "xesam:title")
if [ -z "$title" ]; then
echo ""
else
truncate_with_ellipsis "$title" 28 # Limit the output to 50 characters
fi
;;
--artist)
artist=$(get_metadata "xesam:artist")
if [ -z "$artist" ]; then
echo ""
else
truncate_with_ellipsis "$artist" 28 # Limit the output to 50 characters
fi
;;
--status-symbol)
status=$(playerctl status 2>/dev/null)
if [[ $status == "Playing" ]]; then
echo "󰎆"
elif [[ $status == "Paused" ]]; then
echo "󰏥"
else
echo ""
fi
;;
--status)
status=$(playerctl status 2>/dev/null)
if [[ $status == "Playing" ]]; then
echo "Playing Now"
elif [[ $status == "Paused" ]]; then
echo "Paused"
else
echo ""
fi
;;
--album)
album=$(playerctl metadata --player=%any,chromium,firefox --format "{{ xesam:album }}" 2>/dev/null)
if [[ -n $album ]]; then
echo "$album"
else
status=$(playerctl status 2>/dev/null)
if [[ -n $status ]]; then
echo "Not album"
else
truncate_with_ellipsis "$album" 28 # Limit the output to 50 characters
fi
fi
;;
--source-symbol)
get_source_info_symbol
;;
--source)
get_source_info
;;
*)
echo "Invalid option: $1"
echo "Usage: $0 --title | --artist | --album | --source | --source-symbol"
exit 1
;;
esac
-39
View File
@@ -1,39 +0,0 @@
#!/bin/bash
# Get location data using IP Geolocation
# Recommended with API, but you can try w/o API [IPINFO.IO]
# API_TOKEN="YOUR_API_TOKEN"
# location_data=$(curl -s "https://ipinfo.io?token=$API_TOKEN" 2>/dev/null)
# location_data=$(curl -s https://ipinfo.io 2>/dev/null) #[Limited requests. Require API to send 50k request/month]
# Extract city and country code (ISO 3166-1 alpha-2 code)
# CITY=$(echo "$location_data" | jq -r '.city // empty')
# COUNTRY=$(echo "$location_data" | jq -r '.country // empty')
# IPINFO Alternative [IP-API.COM]
location_data=$(curl -s "http://ip-api.com/json/" 2>/dev/null)
# Extract city and country code
CITY=$(echo "$location_data" | jq -r '.city // empty')
COUNTRY=$(echo "$location_data" | jq -r '.countryCode // empty')
# Visit http://ip-api.com/json/ to find other variables
# HARDCODE Location
# CITY=$(grep -oP '^\$CITY\s*=\s*\K.+' ~/.config/hypr/hyprlock.conf)
# COUNTRY=$(grep -oP '^\$COUNTRY\s*=\s*\K.+' ~/.config/hypr/hyprlock.conf)
# Check if CITY and COUNTRY are valid
if [[ -n "$CITY" && -n "$COUNTRY" ]]; then
# Fetch weather info for the detected city from wttr.in
weather_info=$(curl -s "wttr.in/$CITY?format=%c+%C+%t" 2>/dev/null)
# Check if the weather info is valid
if [[ -n "$weather_info" ]]; then
echo "$COUNTRY, $CITY: $weather_info"
else
echo "Weather info unavailable for $COUNTRY, $CITY"
fi
else
echo "Unable to determine your location"
fi
+1 -1
View File
@@ -1,6 +1,6 @@
wallpaper { wallpaper {
monitor = monitor =
path = ~/.wallpapers/takemi_2.jpg path = ~/.wallpapers/space-1.jpg
fit_mode = cover fit_mode = cover
} }
-97
View File
@@ -1,97 +0,0 @@
#!/bin/bash
# Set paths
CONFIG_DIR="$HOME/.config/hypr"
BACKUP_DIR="$CONFIG_DIR/hyprlock-backup-$(date +%Y%m%d%H%M%S)"
REPO_URL="https://github.com/Tamarindtype/googlish-hyprlock-theme.git"
TEMP_DIR="$(mktemp -d)"
read -p "Do you want to see every command executed? [y/N]: " choice
choice=${choice,,}
if [[ "$choice" == "y" ]]; then
SHOW_COMMANDS=true
else
SHOW_COMMANDS=false
fi
run_cmd() {
local desc="$1"
local cmd="$2"
if [[ "$SHOW_COMMANDS" == true ]]; then
echo -e "\n$desc"
echo "-> $cmd"
read -p "Run command? [Y/n]: " confirm
confirm=${confirm,,}
if [[ "$confirm" == "n" ]]; then
echo "⏭ Skipped."
else
eval "$cmd"
fi
else
eval "$cmd"
fi
}
# Backup existing config
if [ -d "$CONFIG_DIR/hyprlock" ] || [ -f "$CONFIG_DIR/hyprlock.conf" ]; then
read -p "Do you want to create a backup of your config? [Y/n]: " choice
choice=${choice,,}
if [[ "$choice" != "n" ]]; then
run_cmd "Creating backup folder" "mkdir -p \"$BACKUP_DIR\""
if [ -d "$CONFIG_DIR/hyprlock" ]; then
run_cmd "Backing up folder" "mv \"$CONFIG_DIR/hyprlock\" \"$BACKUP_DIR/\""
fi
if [ -f "$CONFIG_DIR/hyprlock.conf" ]; then
run_cmd "Backing up file" "mv \"$CONFIG_DIR/hyprlock.conf\" \"$BACKUP_DIR/\""
fi
fi
else
echo "No existing hyprlock config found. Skipping backup."
fi
# Clone the repo
run_cmd "Cloning repository..." "git clone \"$REPO_URL\" \"$TEMP_DIR\""
# Move files
run_cmd "Creating config directory" "mkdir -p \"$CONFIG_DIR\""
if [ -d "$TEMP_DIR/hyprlock" ]; then
run_cmd "Moving hyprlock folder" "mv \"$TEMP_DIR/hyprlock\" \"$CONFIG_DIR/\""
fi
if [ -f "$TEMP_DIR/hyprlock.conf" ]; then
run_cmd "Moving hyprlock.conf" "mv \"$TEMP_DIR/hyprlock.conf\" \"$CONFIG_DIR/\""
fi
# Make scripts executable
if [ -d "$CONFIG_DIR/hyprlock" ]; then
run_cmd "Making scripts executable..." "chmod +x \"$CONFIG_DIR/hyprlock\"/*.sh"
fi
# Optional: Run hyprlock
run_cmd "Running Hyprlock" "hyprlock"
read -p "Do you want to install the 'hyprlock-change-wallpaper' command? [Y/n]: " choice
choice=${choice,,}
if [[ "$choice" != "n" ]]; then
run_cmd "Installing hyprlock-change-wallpaper shortcut" "sudo ln -sf $CONFIG_DIR/hyprlock/change_wallpaper.sh /usr/local/bin/hyprlock-change-wallpaper"
run_cmd "Make command runable" "chmod +x $CONFIG_DIR/hyprlock/change_wallpaper.sh"
if command -v hyprlock-change-wallpaper &>/dev/null; then
echo "hyprlock-change-wallpaper was succesfully installed"
else
echo "Error while installing hyprlock-change-wallpaper."
fi
else
echo "⏭ installation of hyprlock-change-wallpaper skipped"
fi
# Cleanup
run_cmd "Cleaning up temp files..." "rm -rf \"$TEMP_DIR\""
-1
View File
@@ -1 +0,0 @@
+72
View File
@@ -0,0 +1,72 @@
-- https://wiki.hypr.land/Configuring/Advanced-and-Cool/Animations/
--
-- YoRHa motion: calm and mechanical. Things glide in, settle and stop; nothing
-- overshoots, nothing pops. Matches Theme.easeSnap / easeGlide in
-- ~/.config/quickshell/config/Theme.qml.
--
-- The screen is a porthole: the hull (quickshell/bar/Frame.qml) sits on the
-- top layer, so anything that moves past the edge of the opening passes
-- behind the frame instead of over the bar.
--
-- windows resolve in from a hair smaller and fade, the way a NieR menu
-- panel appears rather than arrives; leave the same way, faster.
-- workspaces drift: the old one sinks a little way behind the rim as it
-- fades, the new one rises into its place. A short, slow
-- vertical drift rather than a full-screen sweep, which on a
-- 2560px panel is a lot of motion for "you are somewhere else
-- now". Direction follows workspace order.
-- scratchpad a drawer: SUPER+S lowers it out from under the bar, the same
-- gesture as the dashboard and the popouts, and raises it back.
-- layers only fade; quickshell animates its own surfaces.
--
-- Nothing overshoots and the border angle never animates.
hl.config({
animations = {
enabled = true,
},
})
-- ------------------------------------------------------------------- curves
-- outQuint: fast off the mark, long quiet settle.
hl.curve("glide", { type = "bezier", points = { { 0.22, 1.00 }, { 0.36, 1.00 } } })
-- Material standard, for things that move from one place to another.
hl.curve("standard", { type = "bezier", points = { { 0.40, 0.00 }, { 0.20, 1.00 } } })
-- Leaving: ease in, so the exit gathers pace and is gone.
hl.curve("exit", { type = "bezier", points = { { 0.55, 0.00 }, { 0.90, 0.35 } } })
hl.curve("linear", { type = "bezier", points = { { 0, 0 }, { 1, 1 } } })
-- easeInOutSine-ish with a long tail: starts without a jolt, settles slowly.
-- For motion big enough that a fast start would read as a shove.
hl.curve("drift", { type = "bezier", points = { { 0.35, 0.00 }, { 0.15, 1.00 } } })
-- --------------------------------------------------------------------- tree
-- speed is in ds (1ds = 100ms). Unset leaves inherit their parent.
hl.animation({ leaf = "global", enabled = true, speed = 4, bezier = "glide" })
hl.animation({ leaf = "border", enabled = true, speed = 4, bezier = "glide" })
hl.animation({ leaf = "borderangle", enabled = false })
hl.animation({ leaf = "windows", enabled = true, speed = 4, bezier = "glide" })
hl.animation({ leaf = "windowsIn", enabled = true, speed = 4, bezier = "glide", style = "popin 94%" })
hl.animation({ leaf = "windowsOut", enabled = true, speed = 2.4, bezier = "exit", style = "popin 96%" })
hl.animation({ leaf = "windowsMove", enabled = true, speed = 4, bezier = "standard" })
hl.animation({ leaf = "fade", enabled = true, speed = 3.4, bezier = "glide" })
hl.animation({ leaf = "fadeIn", enabled = true, speed = 3.4, bezier = "glide" })
hl.animation({ leaf = "fadeOut", enabled = true, speed = 2.2, bezier = "exit" })
hl.animation({ leaf = "fadeSwitch", enabled = true, speed = 3, bezier = "glide" })
hl.animation({ leaf = "fadeShadow", enabled = true, speed = 3.4, bezier = "glide" })
hl.animation({ leaf = "fadeDim", enabled = true, speed = 3, bezier = "glide" })
-- Quickshell animates its own surfaces; the compositor only fades them, so the
-- two motions never stack.
hl.animation({ leaf = "layers", enabled = true, speed = 2.6, bezier = "glide" })
hl.animation({ leaf = "layersIn", enabled = true, speed = 2.6, bezier = "glide", style = "fade" })
hl.animation({ leaf = "layersOut", enabled = true, speed = 2, bezier = "exit", style = "fade" })
hl.animation({ leaf = "workspaces", enabled = true, speed = 5.5, bezier = "drift", style = "slidefadevert 18%" })
hl.animation({ leaf = "workspacesIn", enabled = true, speed = 5.5, bezier = "drift", style = "slidefadevert 18%" })
hl.animation({ leaf = "workspacesOut", enabled = true, speed = 4.5, bezier = "drift", style = "slidefadevert 18%" })
hl.animation({ leaf = "specialWorkspace", enabled = true, speed = 4.5, bezier = "glide", style = "slidevert" })
hl.animation({ leaf = "specialWorkspaceIn", enabled = true, speed = 4.5, bezier = "glide", style = "slidevert" })
hl.animation({ leaf = "specialWorkspaceOut", enabled = true, speed = 3.5, bezier = "exit", style = "slidevert" })
+24
View File
@@ -0,0 +1,24 @@
-- The programs the binds reach for. Exported so lua/binds/* name them once.
return {
terminal = "alacritty",
fileManager = "nautilus",
-- The launcher lives in the Quickshell process (quickshell/launcher/), so
-- the bind toggles it over IPC instead of spawning anything. Nothing to
-- start up means it is on screen on the same frame as the keypress.
menu = "qs ipc call shell launcher",
-- Clipboard history. Opens the launcher already in its ":" mode, which
-- reads cliphist itself and copies the chosen entry back — so the picker is
-- the same window, with the same keys, as everything else.
clipboard = "qs ipc call shell prompt :",
-- Proton Drive search. --fzf re-queries the daemon on every keystroke, so
-- results appear as you type; a dmenu could only filter a list it was
-- handed once, which made searching a two-step. It spawns its own terminal
-- (class pdfs-prompt, floated in lua/rules/windows.lua).
pdfs = "pdfs-prompt --gtk",
-- bluetooth menu
bluetooth = "fluxo bt menu",
}
+31
View File
@@ -0,0 +1,31 @@
-- https://wiki.hypr.land/Configuring/Basics/Autostart/
--
-- Everything that used to be exec-once now runs off the start event.
hl.on("hyprland.start", function()
-- Takemi shell — bar, popouts, notifications, OSD, power menu and lock
-- screen. Config lives in ~/.config/quickshell. It owns
-- org.freedesktop.Notifications, so dunst must stay masked
-- (systemctl --user mask dunst.service).
hl.exec_cmd("uwsm app -- qs")
hl.exec_cmd("uwsm app -- nm-applet --indicator")
-- hyprpaper is not started here: its systemd unit is enabled for
-- graphical-session.target, and a second copy here ran alongside it.
-- clipboard history, text and images
hl.exec_cmd("wl-paste --type text --watch cliphist store")
hl.exec_cmd("wl-paste --type image --watch cliphist store")
-- pdfs-prompt has no hidden/resident mode, so the first search of the
-- session is cold. Run one throwaway query once the Proton Drive daemon
-- is up to pull the binary into the page cache and warm the daemon's index.
hl.exec_cmd("uwsm app -- pdfs-prompt --preload")
-- hl.exec_cmd("uwsm app -- nextcloud --background")
-- hl.exec_cmd("uwsm app -- rclone mount google_drive: ~/gdrive")
-- hl.exec_cmd("uwsm app -- protonvpn-app")
-- hl.exec_cmd("uwsm app -- /usr/bin/discord --enable-features=UseOzonePlatform --ozone-platform=wayland --start-minimized")
-- hl.exec_cmd("uwsm app -- /usr/lib/xdg-desktop-portal-hyprland")
-- hl.exec_cmd("sleep 5 && ~/.config/hypr/scripts/replay-ctrl.sh start")
end)
+14
View File
@@ -0,0 +1,14 @@
-- Launching things.
-- https://wiki.hypr.land/Configuring/Basics/Binds/
local apps = require("lua/apps")
local mod = "SUPER" -- Sets "Windows" key as main modifier
hl.bind(mod .. " + RETURN", hl.dsp.exec_cmd(apps.terminal), { description = "terminal" })
hl.bind(mod .. " + E", hl.dsp.exec_cmd(apps.fileManager), { description = "file manager" })
hl.bind(mod .. " + SPACE", hl.dsp.exec_cmd(apps.menu), { description = "app launcher" })
hl.bind(mod .. " + M", hl.dsp.exec_cmd(apps.pdfs), { description = "pdf picker" })
hl.bind(mod .. " + B", hl.dsp.exec_cmd(apps.bluetooth), { description = "bluetooth menu" })
-- Clipboard history. On release so the picker does not inherit the held ALT.
hl.bind("ALT + m", hl.dsp.exec_cmd(apps.clipboard), { release = true, description = "clipboard history" })
+22
View File
@@ -0,0 +1,22 @@
-- Screenshots, replay buffer, webcam. Everything that records something.
local scripts = os.getenv("HOME") .. "/.config/hypr/scripts/"
-- ------------------------------------------------------------- screenshots
-- PRINT selects a region, CTRL grabs the focused window, SHIFT saves to disk
-- instead of the clipboard.
-- All four go through scripts/shot.sh, which also posts a notification with
-- the capture as its thumbnail (Open, Edit with satty, Save).
local shot = scripts .. "shot.sh "
hl.bind("PRINT", hl.dsp.exec_cmd(shot .. "region copy"), { description = "screenshot an area to the clipboard" })
hl.bind("SHIFT + PRINT", hl.dsp.exec_cmd(shot .. "region save"), { description = "screenshot an area to a file" })
hl.bind("CTRL + PRINT", hl.dsp.exec_cmd(shot .. "window copy"), { description = "screenshot the window to the clipboard" })
hl.bind("CTRL + SHIFT + PRINT", hl.dsp.exec_cmd(shot .. "window save"), { description = "screenshot the window to a file" })
-- ------------------------------------------------------- replay and camera
-- On release, so the shortcut is not caught mid-chord.
local on_release = { release = true }
hl.bind("ALT + z", hl.dsp.exec_cmd(scripts .. "replay-ctrl.sh toggle"), on_release)
hl.bind("ALT + SHIFT + z", hl.dsp.exec_cmd(scripts .. "replay-ctrl.sh save"), on_release)
hl.bind("ALT + SHIFT + c", hl.dsp.exec_cmd(scripts .. "droidcam-ctrl.sh toggle"), on_release)
+21
View File
@@ -0,0 +1,21 @@
-- Laptop multimedia keys: volume, mic, brightness, transport.
-- `locked` keeps them working over the lock screen.
-- Volume and brightness repeat when held.
local held = { locked = true, repeating = true }
-- wpctl rather than `fluxo vol`, which goes up to 150%; `-l 1` stops at 100%.
hl.bind("XF86AudioRaiseVolume", hl.dsp.exec_cmd("wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+"), held)
hl.bind("XF86AudioLowerVolume", hl.dsp.exec_cmd("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-"), held)
hl.bind("XF86AudioMute", hl.dsp.exec_cmd("fluxo vol mute"), held)
hl.bind("XF86AudioMicMute", hl.dsp.exec_cmd("fluxo mic mute"), held)
hl.bind("XF86MonBrightnessUp", hl.dsp.exec_cmd("brightnessctl s +10%"), held)
hl.bind("XF86MonBrightnessDown", hl.dsp.exec_cmd("brightnessctl s 10%-"), held)
-- Transport. Requires playerctl.
local locked = { locked = true }
hl.bind("XF86AudioNext", hl.dsp.exec_cmd("playerctl next"), locked)
hl.bind("XF86AudioPrev", hl.dsp.exec_cmd("playerctl previous"), locked)
hl.bind("XF86AudioPlay", hl.dsp.exec_cmd("playerctl play-pause"), locked)
hl.bind("XF86AudioPause", hl.dsp.exec_cmd("playerctl play-pause"), locked)
+28
View File
@@ -0,0 +1,28 @@
-- Takemi shell (quickshell) IPC. Config lives in ~/.config/quickshell.
local mod = "SUPER"
local function shell(call, opts)
hl.bind(opts.key, hl.dsp.exec_cmd("qs ipc call shell " .. call), { description = opts.description })
end
shell("lock", { key = mod .. " + CTRL + L", description = "lock screen" })
shell("power", { key = mod .. " + P", description = "power menu" })
shell("ask", { key = mod .. " + A", description = "ask claude" })
shell("dashboard", { key = mod .. " + D", description = "dashboard" })
shell("overview", { key = mod .. " + W", description = "overview of every workspace" })
-- Alt+Tab: hold Alt, tap Tab through focus history, let go to switch. The
-- overview sees the release itself; the release bind is a backstop for the
-- case where Alt comes up before the overview has the keyboard.
shell("overviewNext", { key = "ALT + Tab", description = "switch windows" })
shell("overviewPrev", { key = "ALT + SHIFT + Tab", description = "switch windows, backwards" })
hl.bind("ALT + ALT_L", hl.dsp.exec_cmd("qs ipc call shell overviewCommit"), { release = true })
shell("toggleDnd", { key = mod .. " + SHIFT + N", description = "toggle do not disturb" })
shell("toggleStayAwake", { key = mod .. " + SHIFT + I", description = "keep the screen awake" })
shell("clearNotifications", { key = mod .. " + CTRL + SHIFT + N", description = "clear notifications" })
shell("reloadConfig", { key = mod .. " + SHIFT + R", description = "reload shell config" })
-- Not a shell call, but it lives with the other session toggles.
local mirror = require("lua/mirror")
hl.bind(mod .. " + SHIFT + P", mirror.toggle, { description = "mirror laptop to all screens" })
+32
View File
@@ -0,0 +1,32 @@
-- Focus, layout and groups — everything that acts on the window under you.
local mod = "SUPER"
-- ------------------------------------------------------------------- window
hl.bind(mod .. " + SHIFT + Q", hl.dsp.window.close())
hl.bind(mod .. " + V", hl.dsp.window.float())
hl.bind(mod .. " + F", hl.dsp.window.fullscreen())
-- hl.bind(mod .. " + t", hl.dsp.layout("togglesplit")) -- dwindle
-- -------------------------------------------------------------------- focus
local directions = { h = "l", l = "r", k = "u", j = "d" }
for key, direction in pairs(directions) do
hl.bind(mod .. " + " .. key, hl.dsp.focus({ direction = direction }))
hl.bind(mod .. " + SHIFT + " .. key:upper(), hl.dsp.window.move({ direction = direction, group_aware = true }))
end
-- ------------------------------------------------------------------- groups
hl.bind(mod .. " + n", hl.dsp.group.toggle())
-- Alt+Tab belongs to the overview (lua/binds/shell.lua), so tabs in a group
-- cycle on Super+Tab.
hl.bind(mod .. " + Tab", hl.dsp.group.next(), { description = "next window in group" })
hl.bind(mod .. " + SHIFT + Tab", hl.dsp.group.prev(), { description = "previous window in group" })
-- -------------------------------------------------------------------- mouse
hl.bind(mod .. " + mouse:272", hl.dsp.window.drag(), { mouse = true })
hl.bind(mod .. " + mouse:273", hl.dsp.window.resize(), { mouse = true })
-- ----------------------------------------------------------------- keyboard
-- cycle keyboard layout (dvorak-intl <-> us-intl), see lua/input.lua
hl.bind(mod .. " + ALT + SPACE", hl.dsp.exec_cmd("hyprctl switchxkblayout all next"))
+18
View File
@@ -0,0 +1,18 @@
-- Workspaces, including the scratchpad.
local mod = "SUPER"
-- Switch with mod + [0-9], move the active window with mod + SHIFT + [0-9].
for i = 1, 10 do
local key = i % 10 -- 10 maps to key 0
hl.bind(mod .. " + " .. key, hl.dsp.focus({ workspace = i }))
hl.bind(mod .. " + SHIFT + " .. key, hl.dsp.window.move({ workspace = i }))
end
-- Scroll through existing workspaces with mod + scroll
hl.bind(mod .. " + mouse_down", hl.dsp.focus({ workspace = "e+1" }))
hl.bind(mod .. " + mouse_up", hl.dsp.focus({ workspace = "e-1" }))
-- Scratchpad: S holds anything virtual. Chat opens wherever it is launched.
hl.bind(mod .. " + S", hl.dsp.workspace.toggle_special("virtual"))
hl.bind(mod .. " + SHIFT + S", hl.dsp.window.move({ workspace = "special:virtual" }))
+38
View File
@@ -0,0 +1,38 @@
-- https://wiki.hypr.land/Configuring/Advanced-and-Cool/Environment-variables/
--
-- Note: this session starts through uwsm, which would rather have these in
-- ~/.config/uwsm/env (toolkit, cursor, GPU) and ~/.config/uwsm/env-hyprland
-- (HYPR*, AQ_*). They are kept here for now so the config stays self-contained.
-- cursor
hl.env("HYPRCURSOR_THEME", "Bibata-Modern-Classic")
hl.env("HYPRCURSOR_SIZE", "24")
hl.env("XCURSOR_THEME", "Bibata-Modern-Classic")
hl.env("XCURSOR_SIZE", "24")
-- session
hl.env("XDG_CURRENT_DESKTOP", "Hyprland")
-- toolkits
hl.env("QT_QPA_PLATFORMTHEME", "qt6ct")
hl.env("MOZ_ENABLE_WAYLAND", "1")
hl.env("ELECTRON_OZONE_PLATFORM_HINT", "wayland")
-- AMD graphics / video acceleration
hl.env("LIBVA_DRIVER_NAME", "radeonsi")
hl.env("VDPAU_DRIVER", "radeonsi")
hl.env("AMD_VULKAN_ICD", "RADV")
-- misc
hl.env("EDITOR", "nvim")
-- hl.env("INTEL_DEBUG", "noccs")
-- hl.env("WLR_DRM_NO_ATOMIC", "1")
-- hl.env("debug:full_cm_proto", "true")
-- headroom
hl.env("HEADROOM_ROLLOUT_CHANNEL", "beta")
hl.env("HEADROOM_OUTPUT_SHAPER", "1")
+124
View File
@@ -0,0 +1,124 @@
-- Game mode: while a fullscreen game is on screen, neither the compositor nor
-- the shell spends anything on the desktop it covers.
--
-- on <=> some monitor shows a game, fullscreen, on its active workspace
--
-- Like lua/lid.lua, the answer is re-derived from live state on every trigger
-- and nothing is remembered, so a reload mid-game simply finds the game again.
--
-- A game is a window that says so through the content-type protocol
-- (gamescope does), or one whose class gives it away: Proton names every Steam
-- game steam_app_<id>. Add to GAME_CLASSES for anything else.
--
-- The shell is told through its IPC and pauses its own polling and animation
-- (quickshell/services/GameMode.qml).
-- Lua patterns, matched against the window class.
local GAME_CLASSES = {
"^steam_app_%d+$",
"^gamescope$",
}
-- What game mode sets. What it puts back is read from the config as loaded, so
-- lua/look.lua and lua/animations.lua stay the only place those are decided. A
-- reload resets every option before the config runs, so this never reads game
-- mode's own values back.
local GAME = {
-- Nothing under a fullscreen window is worth animating, and leaving the
-- game should be instant.
["animations.enabled"] = false,
["decoration.shadow.enabled"] = false,
-- Hand the game's frames straight to the display instead of compositing
-- them every refresh. Only while playing: with it on all the time, some
-- ordinary fullscreen apps flicker or show the wrong colours.
["render.direct_scanout"] = 1,
}
local NORMAL = {}
for option in pairs(GAME) do
NORMAL[option] = hl.get_config(option)
end
-- ------------------------------------------------------------- reading state
local function is_game(win)
if win.content_type == "game" then
return true
end
local class = win.class or ""
for _, pattern in ipairs(GAME_CLASSES) do
if class:match(pattern) then
return true
end
end
return false
end
-- `ignore` is a window being closed: during window.close it can still be
-- listed, and counting it would keep game mode on after the game has gone.
-- Fullscreen 2 is real fullscreen; a maximised game (1) still shows the hull.
local function playing(ignore)
for _, win in ipairs(hl.get_windows()) do
if win ~= ignore
and win.mapped
and win.fullscreen >= 2
and win.monitor
and win.monitor.active_workspace == win.workspace
and is_game(win)
then
return true
end
end
return false
end
-- ---------------------------------------------------------------- reconciling
-- A fresh config is the normal one, so only a game found at load needs
-- applying. The shell heard the last "false" before the reload.
local active = false
local function tell_shell()
hl.exec_cmd("qs ipc call shell gameMode " .. tostring(active))
end
local function reconcile(ignore)
local want = playing(ignore)
if want == active then
return
end
active = want
hl.config(active and GAME or NORMAL)
tell_shell()
end
-- ------------------------------------------------------------------ triggers
for _, event in ipairs({
"window.open",
"window.fullscreen",
"window.move_to_workspace",
-- Proton sets the class after the window opens.
"window.class",
"workspace.active",
"monitor.removed",
}) do
hl.on(event, function()
reconcile()
end)
end
hl.on("window.close", function(win)
reconcile(win)
end)
-- A shell started mid-game (Super+Shift+R, or a crash) begins with game mode
-- off. Its bar mapping is the sign it is up and listening.
hl.on("layer.opened", function(layer)
if active and layer.namespace == "takemi-bar" then
tell_shell()
end
end)
reconcile()
+32
View File
@@ -0,0 +1,32 @@
-- https://wiki.hypr.land/Configuring/Basics/Variables/#input
hl.config({
input = {
-- index 0 = dvorak-intl (default), index 1 = us-intl
-- switch with $mainMod ALT, SPACE (see lua/binds/windows.lua)
kb_layout = "us,us",
kb_variant = "dvorak-intl,intl",
kb_model = "",
kb_rules = "",
kb_options = "caps:backspace",
follow_mouse = 0,
accel_profile = "flat",
sensitivity = 0, -- -1.0 - 1.0, 0 means no modification.
touchpad = {
natural_scroll = false,
},
},
})
-- Layouts are inherited from the global input block so that layout switching
-- applies to these too. Only bind resolution differs here.
-- https://wiki.hypr.land/Configuring/Advanced-and-Cool/Devices/
for _, name in ipairs({
"at-translated-set-2-keyboard",
"topre-corporation-hhkb-professional",
}) do
hl.device({ name = name, resolve_binds_by_sym = true })
end
+122
View File
@@ -0,0 +1,122 @@
-- Laptop lid handling.
--
-- One rule, applied from several triggers:
--
-- panel off <=> lid is shut AND something else is plugged in
--
-- Both halves are re-checked every time, so the session can never end up with
-- zero outputs. Undocking with the lid shut brings the panel back; booting
-- alone after a docked shutdown just works, because nothing about the previous
-- session is remembered.
--
-- That last part is the fix for the old design: scripts/lid_handler.sh wrote a
-- lid_state.conf that got sourced on the next start, so a laptop that was shut
-- while docked came up believing it still had an external display and blanked
-- its own panel. Lid state now comes from the kernel instead, and the external
-- check is a live one.
local monitors = require("lua/monitors")
-- ------------------------------------------------------------- reading state
-- The kernel's own view of the hinge. Present on this machine as
-- /proc/acpi/button/lid/LID/state; the device name differs per vendor, hence
-- the glob. Returns nil if the file is missing or unparseable.
local function acpi_lid_closed()
local pipe = io.popen("cat /proc/acpi/button/lid/*/state 2>/dev/null")
if not pipe then
return nil
end
local out = pipe:read("a") or ""
pipe:close()
if out:find("closed", 1, true) then
return true
elseif out:find("open", 1, true) then
return false
end
return nil
end
-- Whatever the last switch event said, used only when ACPI has nothing for us.
local last_switch_closed = nil
local function lid_closed()
local acpi = acpi_lid_closed()
if acpi ~= nil then
return acpi
end
return last_switch_closed == true
end
-- ---------------------------------------------------------------- reconciling
-- nil until the first decision, so the first pass always applies.
local panel_off = nil
-- `ignore` is the output being unplugged: during monitor.removed it can still
-- be listed, and counting it would keep the panel switched off.
local function reconcile(ignore)
-- Nothing enumerated yet means we are still parsing the config on a cold
-- start. Leave the defaults from lua/monitors.lua alone and wait for the
-- start event.
local all = hl.get_monitors()
if #all == 0 then
return
end
local externals = 0
for _, mon in ipairs(monitors.externals_present()) do
if mon.name ~= ignore then
externals = externals + 1
end
end
local want_off = lid_closed() and externals > 0
if want_off == panel_off then
return
end
panel_off = want_off
if want_off then
monitors.disable_builtin()
else
monitors.enable_builtin()
end
monitors.apply_external()
-- Quickshell follows monitor hotplug by itself, wallpaper included.
end
-- ------------------------------------------------------------------ triggers
hl.bind("switch:on:Lid Switch", function()
last_switch_closed = true
reconcile()
end, { locked = true })
hl.bind("switch:off:Lid Switch", function()
last_switch_closed = false
reconcile()
end, { locked = true })
-- Docking and undocking. Removal passes the output that is going away.
hl.on("monitor.added", function()
reconcile()
end)
hl.on("monitor.removed", function(mon)
reconcile(mon and mon.name)
end)
-- Cold start: outputs are not up yet at this point, so this settles once they
-- are. On `hyprctl reload` the start event does not fire again, but the
-- monitor list is already populated, so run it inline too.
hl.on("hyprland.start", function()
reconcile()
end)
reconcile()
+124
View File
@@ -0,0 +1,124 @@
-- YoRHa dark — NieR:Automata's interface language on the space-1 wallpaper's
-- palette, matched to ~/.config/quickshell.
--
-- Everything static about how the desktop is painted. Motion lives next door
-- in lua/animations.lua.
local c = require("lua/palette")
hl.config({
-- https://wiki.hypr.land/Configuring/Basics/Variables/
general = {
-- Measured from the quickshell hull's opening, not the screen edge: the
-- hull reserves its own exclusive zones. 10px clears the opening's 18px
-- corner radius, so a tiled window never touches the rim.
gaps_in = 5,
gaps_out = 10,
border_size = 1,
col = {
active_border = c.activeBorder,
-- Overlay, not outline: an idle window's frame is structure,
-- and the outline grey was bright enough to compete with the
-- focused border.
inactive_border = c.overlay,
},
resize_on_border = false,
-- Please see https://wiki.hypr.land/Configuring/Advanced-and-Cool/Tearing/
-- before you turn this on
allow_tearing = false,
layout = "dwindle",
},
decoration = {
-- Concentric with the porthole: the opening's corners are 18px and
-- the windows sit 10px inside them, so 8px keeps every gap the same
-- width all the way round the curve. Matches Theme.frameRadius -
-- gaps_out; change one and the other has to follow.
rounding = 8,
rounding_power = 2,
-- Fully opaque, both states. Flat ink on flat ground.
active_opacity = 1.0,
inactive_opacity = 1.0,
-- A soft, wide shadow: windows float in the opening rather than
-- being printed onto it. Dark against the planet light, invisible
-- against the void, which is how a shadow behaves out there.
shadow = {
enabled = true,
sharp = false,
range = 24,
render_power = 3,
offset = { 0, 6 },
color = "rgba(000000aa)",
color_inactive = "rgba(00000066)",
},
-- Off. Every surface here is opaque, so there is nothing to blur, and
-- blur would cost three render passes for no visible result.
blur = {
enabled = false,
},
},
misc = {
force_default_wallpaper = 0,
disable_hyprland_logo = true,
-- 0 = off, 1 = always, 2 = fullscreen only (G7 is a 240Hz VRR panel)
vrr = 0,
font_family = "JetBrainsMono Nerd Font",
background_color = c.base,
focus_on_activate = true,
-- The shell powers displays down after idle (quickshell/services/Idle.qml)
-- and back up when idle ends; these wake them even if it is not running.
key_press_enables_dpms = true,
mouse_move_enables_dpms = true,
},
group = {
col = {
border_active = c.activeBorder,
border_inactive = c.overlay,
},
-- Tabs are thin rounded lines, not titled slabs. The bar already
-- writes the focused title and its place in the group ("2/3", in
-- quickshell/bar/ActiveWindow.qml), so titles here only repeated it in
-- a second font. Hyprland paints a tab's indicator in the same colour
-- as its fill, so with no fill the current tab is simply the light one.
groupbar = {
render_titles = false,
gradients = false,
indicator_height = 3,
indicator_gap = 0,
rounding = 2,
round_only_edges = false,
keep_upper_gap = false,
gaps_in = 4,
gaps_out = 0,
col = {
active = c.accent,
inactive = c.overlay,
locked_active = c.alarm,
locked_inactive = c.overlay,
},
},
},
-- https://wiki.hypr.land/Configuring/Layouts/Dwindle-Layout/
dwindle = {
preserve_split = true,
},
-- https://wiki.hypr.land/Configuring/Layouts/Master-Layout/
master = {
new_status = "master",
},
})
+106
View File
@@ -0,0 +1,106 @@
-- Presentation mode: every external output shows the laptop panel.
--
-- Toggled from lua/binds/shell.lua. Mirroring is just a `mirror` field on the
-- monitor rule, so switching it off clears that field and re-applies the specs
-- from lua/monitors.lua to put the Samsung back where it belongs.
--
-- Anything plugged in while mirroring is on gets mirrored too, which is the
-- usual order at a talk: press the key, then find the HDMI cable.
local monitors = require("lua/monitors")
local M = {}
local function notify(text)
hl.notification.create({ text = text, timeout = 3000 })
end
-- The panel is off while docked with the lid shut, see lua/lid.lua. Mirroring
-- a disabled output would leave every screen black.
local function builtin()
for _, mon in ipairs(hl.get_monitors()) do
if mon.name == monitors.laptop then
return mon
end
end
return nil
end
-- A mirroring output drops out of hl.get_monitors(), so the only place left
-- to find it is the panel's own `mirrors` list. That list is also the state:
-- a Lua flag would reset on every config reload while the outputs stay
-- mirrored.
local function mirroring(panel)
local names = {}
for _, mon in ipairs(panel and panel.mirrors or {}) do
table.insert(names, mon.name)
end
return names
end
-- Outputs keep their own mode while mirroring, and the panel image gets
-- rescaled to fit it every frame. The Dells at 1920x1200 flicker with that,
-- so match the panel's resolution; Hyprland falls back to the preferred mode
-- if the output cannot do it.
local function mirror_all(panel)
local mode = panel.width .. "x" .. panel.height
for _, mon in ipairs(monitors.externals_present()) do
hl.monitor({ output = mon.name, mirror = monitors.laptop, mode = mode, position = "auto" })
end
end
-- The bar, popouts and wallpaper stay on the outputs they were created for,
-- so respawn the shell the same way lua/autostart.lua starts it.
local function restart_clients()
hl.exec_cmd("sh -c 'qs kill; exec uwsm app -- qs'")
end
-- hl.monitor() only takes effect after the calling handler returns, so
-- anything that has to see the new layout goes on a timer.
local function later(fn)
hl.timer(fn, { timeout = 500, type = "oneshot" })
end
-- Clearing `mirror` alone brings the image back, but Hyprland never
-- re-announces the output to clients: hyprpaper and Quickshell keep seeing
-- only the panel, however often they restart. Cycling the output through
-- disabled makes it a fresh wl_output. Undeclared outputs get their auto
-- defaults back, apply_external() then overrides what lua/monitors.lua knows
-- better.
local function unmirror_all(names)
for _, name in ipairs(names) do
hl.monitor({ output = name, mirror = "", disabled = true })
end
later(function()
for _, name in ipairs(names) do
hl.monitor({ output = name, disabled = false, mode = "preferred", position = "auto" })
end
monitors.apply_external()
later(restart_clients)
end)
end
function M.toggle()
local panel = builtin()
local mirrored = mirroring(panel)
if #mirrored > 0 then
unmirror_all(mirrored)
notify("Mirroring off")
elseif panel then
mirror_all(panel)
later(restart_clients)
notify("Mirroring " .. monitors.laptop .. " to all screens")
else
notify("Laptop panel is off, open the lid first")
end
end
hl.on("monitor.added", function()
local panel = builtin()
if #mirroring(panel) > 0 then
mirror_all(panel)
end
end)
return M
+62
View File
@@ -0,0 +1,62 @@
-- https://wiki.hypr.land/Configuring/Basics/Monitors/
--
-- Declares every display once and exports the table, so lua/lid.lua can turn
-- the laptop panel on and off without duplicating any of these numbers.
--
-- Load-time behaviour is deliberately dumb: the built-in panel is always
-- enabled here. Deciding to switch it off needs to know whether anything else
-- is actually plugged in, and during a cold start the outputs are not
-- enumerated yet — so that decision lives in lua/lid.lua, which re-runs it on
-- every lid and hotplug event. Worst case the panel flickers on for a moment
-- while docked; the alternative failure is a black screen.
local M = {}
M.laptop = "eDP-1"
M.external = {
-- samsung home monitor. Other modes it has run at, if you ever need them:
-- "2560x1440@144", "1920x1080@144", "1280x720@240"
{ output = "desc:Samsung Electric Company LC27G7xT H4ZRA00734", mode = "2560x1440@240", position = "-2560x0", scale = 1 },
}
-- `disabled = false` is load-bearing, not decoration. hl.monitor() merges into
-- whatever rule the output already has, so a spec that simply omits the flag
-- leaves an earlier `disabled = true` in place: the panel stays dark and the
-- mode/position are applied to a monitor nobody can see. Opening the lid while
-- docked hit exactly that.
M.builtin = { output = M.laptop, mode = "1920x1080@60", position = "0x0", scale = 1, disabled = false }
-- Anything not declared above falls back to Hyprland's auto placement, so a
-- borrowed projector or a monitor at the office still lights up.
function M.apply_external()
for _, mon in ipairs(M.external) do
hl.monitor(mon)
end
end
function M.enable_builtin()
hl.monitor(M.builtin)
end
function M.disable_builtin()
hl.monitor({ output = M.laptop, disabled = true })
end
-- Every connected output except the laptop panel. Empty during early startup,
-- which callers have to treat as "do not know yet", never as "nothing here".
function M.externals_present()
local found = {}
for _, mon in ipairs(hl.get_monitors()) do
if mon.name ~= M.laptop then
table.insert(found, mon)
end
end
return found
end
M.enable_builtin()
M.apply_external()
return M
+54
View File
@@ -0,0 +1,54 @@
-- YoRHa dark palette — kept in step with ~/.config/quickshell/config/Theme.qml.
--
-- The wallpaper's grey-green void for the ground, its planet light as the
-- accent, the hull's red indicator as the alarm. Nothing else carries hue.
--
-- Returned as a table, so every module that paints something does
-- local c = require("lua/palette")
-- and there is exactly one place the colours are written.
local c = {}
-- ---------------------------------------------------------------- THE KNOBS
-- Two colours in this setup: the planet light for anything live (window
-- borders, group indicators, focus), the hull red for anything wrong. Change these lines and every
-- painted edge recolours with them.
--
-- Keep them in step with `accent` / `alarm` in
-- ~/.config/quickshell/config/Theme.qml, which is what colours the shell.
-- Those are the only places the colours are written.
c.accent = "rgb(eef1ea)"
c.alarm = "rgb(c8684f)"
c.accentDim = "rgb(8e978d)"
c.accentSoft = "rgb(bcc1bf)"
-- ------------------------------------------------------------------ neutrals
c.ink = "rgb(090a08)"
c.base = "rgb(0f110e)"
c.mantle = "rgb(0c0d0b)"
c.surface = "rgb(161915)"
c.surfaceAlt = "rgb(20241f)"
c.overlay = "rgb(383e35)"
c.outline = "rgb(545c52)"
c.text = "rgb(ccd2cb)"
c.subtext = "rgb(9fa7a2)"
c.muted = "rgb(79847a)"
-- Semantic aliases, so existing references resolve without inventing hues.
c.primary = c.text
c.primaryDim = "rgb(8e978d)"
c.glow = c.accent
c.blue = c.accent
c.deep = c.surfaceAlt
c.warn = c.alarm
c.ok = c.text
-- The focused window is lit by the planet: full light at the top-left, the
-- suit's grey at the far corner. One family, so it reads as light falling
-- across the frame rather than as two colours.
c.activeBorder = { colors = { c.accent, c.accentDim }, angle = 135 }
return c
+23
View File
@@ -0,0 +1,23 @@
-- https://wiki.hypr.land/Configuring/Basics/Window-Rules/#layer-rules
--
-- Quickshell surfaces: the bar, popouts, the OSD and the launcher. Note
-- decoration.blur is off in lua/look.lua, so these only take effect if blur is
-- switched back on.
for _, namespace in ipairs({ "bottom", "top", "takemi-launcher" }) do
hl.layer_rule({
match = { namespace = namespace },
blur = true,
ignore_alpha = 0.5,
})
end
-- The hull shares the top layer with the bar and the rail, which draw on it,
-- so they are ordered above it whatever order they map in. See
-- quickshell/bar/Frame.qml.
for _, namespace in ipairs({ "takemi-bar", "takemi-rail" }) do
hl.layer_rule({
match = { namespace = namespace },
order = -1,
})
end
+82
View File
@@ -0,0 +1,82 @@
-- https://wiki.hypr.land/Configuring/Basics/Window-Rules/
--
-- Rules are evaluated top to bottom and the last match wins, so order matters.
-- Named rules are all evaluated before anonymous ones.
-- ==========================================
-- MATCHERS
-- ==========================================
-- Dialogs and utility apps that should float in the center
local DIALOG_TITLES = "^(Open Form|Open File|Select a File|Choose a file|Open Workspace|Choose Directory.*|Save As.*|Save File.*|branchdialog|pinentry-gtk-2|Confirm to replace files|File Operation Progress|Open Files.*|Anmelden.*|File Upload.*|TRuDI-Export laden)$"
-- pdfs-prompt is the Proton Drive search: io.narl.* is its GTK window, plain
-- pdfs-prompt the terminal `pdfs-prompt --fzf` spawns for itself. Every
-- terminal it knows how to start sets that same class, so this one rule holds
-- whichever is installed.
local DIALOG_CLASSES = "^(pavucontrol|blueman-manager|nm-connection-editor|org.pulseaudio.pavucontrol|io.narl.proton-drive-linux-prompt|pdfs-prompt)$"
local STEAM = "^(steam)$"
local GAMESCOPE = "^(gamescope)$"
-- ==========================================
-- GENERAL & FIXES
-- ==========================================
-- Ignore maximize requests from apps. You'll probably like this.
-- hl.window_rule({ match = { class = ".*" }, suppress_event = "maximize" })
-- Fix some dragging issues with XWayland
-- hl.window_rule({
-- match = { class = "^$", title = "^$", xwayland = true, float = true, fullscreen = false, pin = false },
-- no_focus = true,
-- })
hl.window_rule({ match = { class = "^$", title = "^$" }, no_blur = true })
-- Hold idle off while any window is fullscreen. Players do not all ask for
-- that themselves (Brave goes through D-Bus, which nothing here listens to),
-- and the shell's dim and lock (quickshell/services/Idle.qml) respect inhibitors.
hl.window_rule({ match = { class = ".*" }, idle_inhibit = "fullscreen" })
-- ==========================================
-- FLOATING & CENTERED DIALOGS
-- ==========================================
-- Float, size, center, no blur — one rule per matcher instead of four.
for _, match in ipairs({ { title = DIALOG_TITLES }, { class = DIALOG_CLASSES } }) do
hl.window_rule({
match = match,
float = true,
size = { 800, 600 },
center = true,
no_blur = true,
})
end
-- ==========================================
-- WORKSPACE ASSIGNMENTS
-- ==========================================
-- hl.window_rule({ match = { class = "^(firefox)$" }, workspace = "1" })
-- hl.window_rule({ match = { class = "^(kitty)$" }, workspace = "2" })
-- hl.window_rule({ match = { class = "^(Code)$" }, workspace = "3" })
hl.window_rule({ match = { class = GAMESCOPE }, workspace = "1" })
-- Special workspaces
hl.window_rule({ match = { class = "^(Spotify|spotify)$" }, workspace = "special:virtual" })
-- ==========================================
-- STEAM & GAMING
-- ==========================================
-- hl.window_rule({ match = { class = STEAM, title = "^(Steam)$" }, workspace = "3 silent" })
-- Fixed sizes for specific Steam windows
hl.window_rule({ match = { class = STEAM, title = "^(Friends List)$" }, size = { 400, 800 } })
hl.window_rule({ match = { class = STEAM, title = "^(Steam Settings)$" }, size = { 1000, 800 } })
hl.window_rule({ match = { class = STEAM, title = "^(Add Non-Steam Game)$" }, size = { 1000, 800 } })
-- Float and disable blur for Steam windows that aren't the main window
hl.window_rule({
match = { class = STEAM, title = "negative:^(Steam)$" },
float = true,
no_blur = true,
-- center = true,
})
-- Allow tearing for games started with Gamescope
hl.window_rule({ match = { class = GAMESCOPE }, immediate = true })
-16
View File
@@ -1,16 +0,0 @@
# Autostart necessary processes (like notifications daemons, status bars, etc.)
# Or execute your favorite apps at launch like this:
# exec-once = uwsm app -- waybar
# exec-once = uwsm app -- hyprpanel
# exec-once = uwsm app -- nextcloud --background
exec-once = uwsm app -- nm-applet --indicator
# exec-once = uwsm app -- rclone mount google_drive: ~/gdrive
# exec-once = uwsm app -- protonvpn-app
exec-once = uwsm app -- hyprpaper
# exec-once = uwsm app -- /usr/bin/discord --enable-features=UseOzonePlatform --ozone-platform=wayland --start-minimized &> /dev/null
# exec-once = sleep 5 && ~/.config/hypr/scripts/replay-ctrl.sh start
# exec-once = uwsm app -- /usr/lib/xdg-desktop-portal-hyprland
exec-once = wl-paste --type text --watch cliphist store # Stores only text data
exec-once = wl-paste --type image --watch cliphist store # Stores only image data
-18
View File
@@ -1,18 +0,0 @@
# See https://wiki.hyprland.org/Configuring/Environment-variables/
# env = INTEL_DEBUG,noccs
# env = WLR_DRM_NO_ATOMIC,1
env = HYPRCURSOR_THEME,Bibata-Modern-Classic
env = HYPRCURSOR_SIZE,24
env = XCURSOR_SIZE,24
env = XCURSOR_THEME,Bibata-Modern-Classic
env = XDG_CURRENT_DESKTOP,Hyprland
# env = debug:full_cm_proto,true
env = QT_QPA_PLATFORMTHEME,qt6ct
env = LIBVA_DRIVER_NAME,radeonsi
env = VDPAU_DRIVER,radeonsi
env = AMD_VULKAN_ICD,RADV
env = MOZ_ENABLE_WAYLAND,1
env = ELECTRON_OZONE_PLATFORM_HINT,wayland
env = EDITOR,nvim
-32
View File
@@ -1,32 +0,0 @@
# https://wiki.hyprland.org/Configuring/Variables/#input
input {
# index 0 = dvorak-intl (default), index 1 = us-intl
# switch with $mainMod ALT, SPACE (see keybinds.conf)
kb_layout = us,us
kb_variant = dvorak-intl,intl
kb_model =
kb_rules =
kb_options = caps:backspace
follow_mouse = 0
accel_profile = flat
sensitivity = 0 # -1.0 - 1.0, 0 means no modification.
touchpad {
natural_scroll = false
}
}
# Layouts are inherited from the global input block so that layout
# switching applies to these too. Only bind resolution differs here.
device {
name = at-translated-set-2-keyboard
resolve_binds_by_sym = 1
}
device {
name = topre-corporation-hhkb-professional
resolve_binds_by_sym = 1
}
-98
View File
@@ -1,98 +0,0 @@
$mainMod = SUPER # Sets "Windows" key as main modifier
bindl=,switch:on:Lid Switch,exec,~/.config/hypr/scripts/lid_handler.sh close
bindl=,switch:off:Lid Switch,exec,~/.config/hypr/scripts/lid_handler.sh open
bindr = ALT, m, exec, cliphist list | fuzzel --dmenu | cliphist decode | wl-copy
bind = , PRINT, exec, grim -g "$(slurp)" - | wl-copy
bind = SHIFT, PRINT, exec, grim -g "$(slurp)" ~/Pictures/Screenshots/$(date +'%Y-%m-%d_%H-%M-%S').png
bind = CTRL, PRINT, exec, ~/.config/hypr/scripts/screenshot_window.sh copy
bind = CTRL SHIFT, PRINT, exec, ~/.config/hypr/scripts/screenshot_window.sh save
bindr = ALT SHIFT, z, exec, ~/.config/hypr/scripts/replay-ctrl.sh save
bindr = ALT, z, exec, ~/.config/hypr/scripts/replay-ctrl.sh toggle
bindr = ALT SHIFT, c, exec, ~/.config/hypr/scripts/droidcam-ctrl.sh toggle
# general binds
bind = $mainMod, M, exec, pdfs-prompt
bind = $mainMod, B, exec, fluxo bt menu
bind = $mainMod, RETURN, exec, $terminal
bind = $mainMod SHIFT, Q, killactive,
bind = $mainMod CTRL, L, exec, hyprlock
bind = $mainMod, E, exec, $fileManager
bind = $mainMod, V, togglefloating,
bind = $mainMod, F, fullscreen,
bind = $mainMod, SPACE, exec, $menu
bind = $mainMod, P, exec, ~/.config/hypr/scripts/powermenu.sh
# cycle keyboard layout (dvorak-intl <-> us-intl)
bind = $mainMod ALT, SPACE, exec, hyprctl switchxkblayout all next
# bind = $mainMod, t, togglesplit, # dwindle
bind = $mainMod, n, togglegroup
# Move focus with mainMod + arrow keys
bind = $mainMod, h, movefocus, l
bind = $mainMod, l, movefocus, r
bind = $mainMod, k, movefocus, u
bind = $mainMod, j, movefocus, d
bind = $mainMod SHIFT, H, movewindoworgroup, l
bind = $mainMod SHIFT, L, movewindoworgroup, r
bind = $mainMod SHIFT, K, movewindoworgroup, u
bind = $mainMod SHIFT, J, movewindoworgroup, d
# Switch workspaces with mainMod + [0-9]
bind = $mainMod, 1, workspace, 1
bind = $mainMod, 2, workspace, 2
bind = $mainMod, 3, workspace, 3
bind = $mainMod, 4, workspace, 4
bind = $mainMod, 5, workspace, 5
bind = $mainMod, 6, workspace, 6
bind = $mainMod, 7, workspace, 7
bind = $mainMod, 8, workspace, 8
bind = $mainMod, 9, workspace, 9
bind = $mainMod, 0, workspace, 10
# Move active window to a workspace with mainMod + SHIFT + [0-9]
bind = $mainMod SHIFT, 1, movetoworkspace, 1
bind = $mainMod SHIFT, 2, movetoworkspace, 2
bind = $mainMod SHIFT, 3, movetoworkspace, 3
bind = $mainMod SHIFT, 4, movetoworkspace, 4
bind = $mainMod SHIFT, 5, movetoworkspace, 5
bind = $mainMod SHIFT, 6, movetoworkspace, 6
bind = $mainMod SHIFT, 7, movetoworkspace, 7
bind = $mainMod SHIFT, 8, movetoworkspace, 8
bind = $mainMod SHIFT, 9, movetoworkspace, 9
bind = $mainMod SHIFT, 0, movetoworkspace, 10
# groups
# bind = ALT, Tab, changegroupactive, prev
bind = ALT, Tab, changegroupactive, next
# Example special workspace (scratchpad)
bind = $mainMod, S, togglespecialworkspace, virtual
bind = $mainMod SHIFT, S, movetoworkspace, special:virtual
bind = $mainMod, D, togglespecialworkspace, discord
bind = $mainMod SHIFT, D, movetoworkspace, special:discord
# Scroll through existing workspaces with mainMod + scroll
bind = $mainMod, mouse_down, workspace, e+1
bind = $mainMod, mouse_up, workspace, e-1
# Move/resize windows with mainMod + LMB/RMB and dragging
bindm = $mainMod, mouse:272, movewindow
bindm = $mainMod, mouse:273, resizewindow
# Laptop multimedia keys for volume and LCD brightness
bindel = ,XF86AudioRaiseVolume, exec, fluxo vol up 5
bindel = ,XF86AudioLowerVolume, exec, fluxo vol down 5
bindel = ,XF86AudioMute, exec, fluxo vol mute
bindel = ,XF86AudioMicMute, exec, fluxo mic mute
bindel = ,XF86MonBrightnessUp, exec, brightnessctl s +10%
bindel = ,XF86MonBrightnessDown, exec, brightnessctl s 10%-
# Requires playerctl
bindl = , XF86AudioNext, exec, playerctl next
bindl = , XF86AudioPause, exec, playerctl play-pause
bindl = , XF86AudioPlay, exec, playerctl play-pause
bindl = , XF86AudioPrev, exec, playerctl previous
-13
View File
@@ -1,13 +0,0 @@
# laptop screen
monitor = eDP-1, 1920x1080@60, 0x0, 1
# samsung home monitor
# monitor = desc:Samsung Electric Company LC27G7xT H4ZRA00734, 1280x720@240, 0x0, 1
monitor = desc:Samsung Electric Company LC27G7xT H4ZRA00734, 2560x1440@240, -2560x0, 1
# monitor = desc:Samsung Electric Company LC27G7xT H4ZRA00734, 2560x1440@144, 0x0, 1
# monitor = desc:Samsung Electric Company LC27G7xT H4ZRA00734, 1920x1080@144, 0x0, 1
# monitor = desc:Samsung Electric Company LC27G7xT H4ZRA00734, 2560x1440@60, 0x0, 1
# lid_state fallback
source = ~/.config/hypr/lid_state.conf
-7
View File
@@ -1,7 +0,0 @@
# See https://wiki.hyprland.org/Configuring/Keywords/
# Set programs that you use
$terminal = alacritty
$fileManager = nautilus
$menu = fuzzel
-125
View File
@@ -1,125 +0,0 @@
# catppuccin mocha theme
source = ~/.config/hypr/catppuccin-hyprland/themes/mocha.conf
# Refer to https://wiki.hyprland.org/Configuring/Variables/
# https://wiki.hyprland.org/Configuring/Variables/#general
general {
gaps_in = 5
gaps_out = 5, 5, 5, 5
border_size = 3
# https://wiki.hyprland.org/Configuring/Variables/#variable-types for info about colors
col.active_border = $mauve $lavender 45deg
col.inactive_border = rgb($surface2Alpha)
# Set to true enable resizing windows by clicking and dragging on borders and gaps
resize_on_border = false
# Please see https://wiki.hyprland.org/Configuring/Tearing/ before you turn this on
allow_tearing = false
layout = dwindle
}
# https://wiki.hyprland.org/Configuring/Variables/#decoration
decoration {
rounding = 12
rounding_power = 3
# Change transparency of focused and unfocused windows
active_opacity = 1.0
inactive_opacity = 1.0
shadow {
enabled = true
range = 4
render_power = 3
color = rgba(1a1a1aee)
}
# https://wiki.hyprland.org/Configuring/Variables/#blur
blur {
enabled = true
size = 3
passes = 1
vibrancy = 0.1696
}
}
# https://wiki.hyprland.org/Configuring/Variables/#animations
animations {
enabled = yes, please :)
# Default animations, see https://wiki.hyprland.org/Configuring/Animations/ for more
bezier = easeOutQuint,0.23,1,0.32,1
bezier = easeInOutCubic,0.65,0.05,0.36,1
bezier = linear,0,0,1,1
bezier = almostLinear,0.5,0.5,0.75,1.0
bezier = quick,0.15,0,0.1,1
animation = global, 1, 10, default
animation = border, 1, 5.39, easeOutQuint
animation = windows, 1, 4.79, easeOutQuint
animation = windowsIn, 1, 4.1, easeOutQuint, popin 87%
animation = windowsOut, 1, 1.49, linear, popin 87%
animation = fadeIn, 1, 1.73, almostLinear
animation = fadeOut, 1, 1.46, almostLinear
animation = fade, 1, 3.03, quick
animation = layers, 1, 3.81, easeOutQuint
animation = layersIn, 1, 4, easeOutQuint, fade
animation = layersOut, 1, 1.5, linear, fade
animation = fadeLayersIn, 1, 1.79, almostLinear
animation = fadeLayersOut, 1, 1.39, almostLinear
animation = workspaces, 1, 1.94, almostLinear, fade
animation = workspacesIn, 1, 1.21, almostLinear, fade
animation = workspacesOut, 1, 1.94, almostLinear, fade
}
# See https://wiki.hyprland.org/Configuring/Master-Layout/ for more
master {
new_status = master
}
# https://wiki.hyprland.org/Configuring/Variables/#misc
misc {
force_default_wallpaper = 0 # Set to 0 or 1 to disable the anime mascot wallpapers
disable_hyprland_logo = true # If true disables the random hyprland logo / anime girl background. :(
# 0 = off, 1 = always, 2 = fullscreen only (G7 is a 240Hz VRR panel)
vrr = 2
font_family = FiraCode Nerd Font
}
group {
# The border around the grouped windows
col.border_active = $mauve $lavender 45deg
col.border_inactive = $surface1
groupbar {
font_family = JetBrainsMono Nerd Font
font_size = 11
# Increase the height so the text has room to breathe
height = 24
# Disable the default 3D gradient for a modern flat look
gradients = false
# --- Active Tab ---
# Bright background with very dark text for perfect readability
col.active = $mauve
text_color = $crust
# --- Inactive Tab ---
# Dark surface background with dimmed, soft text
col.inactive = $surface0
text_color_inactive = $subtext0
# Colored indicator bar, roughly half the groupbar height
indicator_height = 15
}
}
-89
View File
@@ -1,89 +0,0 @@
# See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules
# ==========================================
# VARIABLES
# ==========================================
# Dialogs and utility apps that should float in the center
$dialog_titles = ^(Open Form|Open File|Select a File|Choose a file|Open Workspace|Choose Directory.*|Save As.*|Save File.*|branchdialog|pinentry-gtk-2|Confirm to replace files|File Operation Progress|Open Files.*|Anmelden.*|File Upload.*|TRuDI-Export laden)$
$dialog_classes = ^(pavucontrol|blueman-manager|nm-connection-editor|org.pulseaudio.pavucontrol|io.narl.proton-drive-linux-prompt)$
# App classes
$steam = ^(steam)$
$gamescope = ^(gamescope)$
# ==========================================
# GENERAL & FIXES
# ==========================================
# Ignore maximize requests from apps. You'll probably like this.
# windowrule = suppress_event maximize, match:class .*
# Fix some dragging issues with XWayland
# windowrule = no_focus 1, match:class ^$, match:title ^$, match:xwayland 1, match:floating 1, match:fullscreen 0, match:pinned 0
windowrule = no_blur 1, match:class ^$, match:title ^$
# ==========================================
# FLOATING & CENTERED DIALOGS
# ==========================================
# Make them float
windowrule = float 1, match:title $dialog_titles
windowrule = float 1, match:class $dialog_classes
# Set to 800x600
windowrule = size 800 600, match:title $dialog_titles
windowrule = size 800 600, match:class $dialog_classes
# Center them on the screen
windowrule = center 1, match:title $dialog_titles
windowrule = center 1, match:class $dialog_classes
# Disable blur
windowrule = no_blur 1, match:title $dialog_titles
windowrule = no_blur 1, match:class $dialog_classes
# ==========================================
# WORKSPACE ASSIGNMENTS
# ==========================================
# Normal Workspaces
# windowrule = workspace 1, match:class ^(firefox)$
# windowrule = workspace 2, match:class ^(kitty)$
# windowrule = workspace 3, match:class ^(Code)$
windowrule = workspace 1, match:class $gamescope
# Special Workspaces
windowrule = workspace special:virtual, match:class ^(Spotify|spotify)$
windowrule = workspace special:discord, match:class ^(discord|vesktop)$
# ==========================================
# APP-SPECIFIC RULES: STEAM & GAMING
# ==========================================
# windowrule = workspace 3 silent, match:class $steam, match:title ^(Steam)$
# Fixed sizes for specific Steam windows
windowrule = size 400 800, match:title ^(Friends List)$, match:class $steam
windowrule = size 1000 800, match:title ^(Steam Settings)$, match:class $steam
windowrule = size 1000 800, match:title ^(Add Non-Steam Game)$, match:class $steam
# Float and disable blur for Steam windows that aren't the main window
windowrule = float 1, match:class $steam, match:title negative:^(Steam)$
windowrule = no_blur 1, match:class $steam, match:title negative:^(Steam)$
# windowrule = center 1, match:class $steam, match:title negative:^(Steam)$
# Allow tearing for games started with Gamescope
windowrule = immediate 1, match:class $gamescope
# Layer rules
# waybar blur
layerrule = blur on, match:namespace bottom
layerrule = blur on, match:namespace top
layerrule = ignore_alpha 0.5, match:namespace bottom
layerrule = ignore_alpha 0.5, match:namespace top
# launcher blur
layerrule = blur on, match:namespace launcher
layerrule = ignore_alpha 0.5, match:namespace launcher
-53
View File
@@ -1,53 +0,0 @@
#!/bin/bash
# ~/.config/hypr/scripts/lid_handler.sh
# The file that tells Hyprland to keep the lid off during reloads
LID_STATE_FILE="$HOME/.config/hypr/lid_state.conf"
restart_ui() {
# Spawn in background and detach
systemctl restart --user waybar hyprpaper
}
if [[ "$1" == "close" ]]; then
# Check if ANY external monitor is connected and active
if hyprctl monitors all | grep -qE "Monitor (DP|HDMI|Type-C)-"; then
# Prevent laptop screen from turning on during manual config reloads
echo "monitor=eDP-1, disable" > "$LID_STATE_FILE"
# Extract the CURRENT live settings of all external monitors using jq
# This grabs the active resolution, refresh rate, position, and scale.
LIVE_MONITORS=$(hyprctl -j monitors | jq -c '.[] | select(.name != "eDP-1")')
# 3. Disable the laptop screen
hyprctl keyword monitor "eDP-1, disable"
# 4. Re-apply the live settings to external monitors so they don't reset
echo "$LIVE_MONITORS" | while read -r mon_json; do
NAME=$(echo "$mon_json" | jq -r '.name')
WIDTH=$(echo "$mon_json" | jq -r '.width')
HEIGHT=$(echo "$mon_json" | jq -r '.height')
REFRESH=$(echo "$mon_json" | jq -r '.refreshRate')
X=$(echo "$mon_json" | jq -r '.x')
Y=$(echo "$mon_json" | jq -r '.y')
SCALE=$(echo "$mon_json" | jq -r '.scale')
# Formats it exactly as Hyprland expects: DP-1, 1920x1080@240, 0x0, 1
hyprctl keyword monitor "$NAME, ${WIDTH}x${HEIGHT}@${REFRESH}, ${X}x${Y}, $SCALE"
done
# restart ui
restart_ui
fi
elif [[ "$1" == "open" ]]; then
# Clear the override file so the laptop screen is allowed to turn on again
echo "" > "$LID_STATE_FILE"
# Let Hyprland reload itself.
# This automatically re-enables eDP-1 based on your hardcoded hyprland.conf!
hyprctl reload
restart_ui
fi
-18
View File
@@ -1,18 +0,0 @@
#!/bin/bash
options="Shutdown\nReboot\nLock\nLogout"
selected=$(echo -e $options | fuzzel --dmenu --prompt='Power:')
case $selected in
"Shutdown")
systemctl poweroff
;;
"Reboot")
systemctl reboot
;;
"Lock")
hyprlock
;;
"Logout")
hyprctl dispatch exit
;;
esac
-49
View File
@@ -1,49 +0,0 @@
#!/bin/sh
# Get active window info in JSON format
WINDOW_INFO=$(hyprctl activewindow -j)
# Extract window class name (app name)
APP_NAME=$(echo "$WINDOW_INFO" | jq -r '.class')
# If app name is empty, null, or invalid, default to "unknown"
if [ -z "$APP_NAME" ] || [ "$APP_NAME" = "null" ]; then
APP_NAME="unknown"
fi
# Sanitize app name: convert to lowercase and replace characters that aren't letters, numbers, hyphens, or underscores with underscores
APP_NAME_SAFE=$(echo "$APP_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9_-]/_/g')
# Extract geometry: "x,y wxh"
GEOMETRY=$(echo "$WINDOW_INFO" | jq -r '"\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"')
# Check if geometry is valid
if [ -z "$GEOMETRY" ] || [ "$GEOMETRY" = "null,null nullxnull" ]; then
notify-send -u normal "Screenshot Error" "No active window found to screenshot."
exit 1
fi
ACTION=$1
case "$ACTION" in
copy)
grim -g "$GEOMETRY" - | wl-copy
notify-send -u low "Screenshot" "Active window ($APP_NAME) copied to clipboard"
;;
save)
SCREENSHOTS_DIR="$HOME/Pictures/Screenshots"
APP_DIR="$SCREENSHOTS_DIR/$APP_NAME_SAFE"
mkdir -p "$APP_DIR"
FILE_NAME="$(date +'%Y-%m-%d_%H-%M-%S').png"
FILE_PATH="$APP_DIR/$FILE_NAME"
grim -g "$GEOMETRY" "$FILE_PATH"
notify-send -u low "Screenshot" "Saved active window to $APP_NAME_SAFE/$FILE_NAME"
;;
*)
echo "Usage: $0 {copy|save}"
exit 1
;;
esac
exit 0
+73
View File
@@ -0,0 +1,73 @@
#!/bin/sh
# Screenshots with a receipt: every capture lands on the clipboard and comes
# back as a notification with its own thumbnail, so a keypress that used to
# vanish silently now shows what it took.
#
# shot.sh region copy pick an area, copy it
# shot.sh region save pick an area, save it to ~/Pictures/Screenshots
# shot.sh window copy the focused window, copy it
# shot.sh window save the focused window, save it
#
# A copy is kept in $XDG_RUNTIME_DIR only so the notification has a picture
# to show; its Save action moves it into ~/Pictures/Screenshots. Edit opens
# satty when it is installed. Cancelling the region picker does nothing.
set -eu
target=${1:-region}
mode=${2:-copy}
dir="$HOME/Pictures/Screenshots"
stamp=$(date +'%Y-%m-%d_%H-%M-%S')
case "$target" in
region)
geometry=$(slurp) || exit 0
name="$stamp.png"
;;
window)
info=$(hyprctl activewindow -j)
geometry=$(printf '%s' "$info" | jq -r 'if .at then "\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])" else empty end')
if [ -z "$geometry" ]; then
notify-send -a Screenshot "Screenshot" "No window is focused."
exit 1
fi
app=$(printf '%s' "$info" | jq -r '.class // "unknown"' | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9_-]/_/g')
name="${app}_$stamp.png"
;;
*)
echo "usage: shot.sh region|window copy|save" >&2
exit 2
;;
esac
if [ "$mode" = save ]; then
mkdir -p "$dir"
file="$dir/$name"
else
file="${XDG_RUNTIME_DIR:-/tmp}/shot-$name"
fi
grim -g "$geometry" "$file"
wl-copy --type image/png < "$file"
title=$([ "$mode" = save ] && echo "Screenshot saved" || echo "Screenshot copied")
body=$([ "$mode" = save ] && echo "${file#"$HOME"/}" || echo "On the clipboard")
set -- --action=open=Open
command -v satty >/dev/null 2>&1 && set -- "$@" --action=edit=Edit
[ "$mode" = copy ] && set -- "$@" --action=save=Save
# notify-send waits for the notification to close and prints the action
# that was chosen, so run it in the background and act on the answer.
(
choice=$(notify-send -a Screenshot -i "$file" -h "string:image-path:$file" \
"$@" "$title" "$body") || exit 0
case "$choice" in
open) xdg-open "$file" ;;
edit) satty --filename "$file" --output-filename "$dir/edited_$name" --copy-command wl-copy ;;
save) mkdir -p "$dir" && mv "$file" "$dir/$name" \
&& notify-send -a Screenshot -i "$dir/$name" -h "string:image-path:$dir/$name" \
"Screenshot saved" "${dir#"$HOME"/}/$name" ;;
esac
) >/dev/null 2>&1 &
+3 -1
View File
@@ -21,7 +21,8 @@ x-scheme-handler/claude-cli=claude-code-url-handler.desktop
x-scheme-handler/msteams=teams-for-linux.desktop x-scheme-handler/msteams=teams-for-linux.desktop
x-scheme-handler/slack=slack.desktop x-scheme-handler/slack=slack.desktop
audio/flac=mpv.desktop audio/flac=mpv.desktop
x-scheme-handler/codex=ChatGPT.desktop x-scheme-handler/codex=chatgpt.desktop
x-scheme-handler/proton-inbox=proton-mail.desktop
[Added Associations] [Added Associations]
application/pdf=org.pwmt.zathura-pdf-poppler.desktop; application/pdf=org.pwmt.zathura-pdf-poppler.desktop;
@@ -40,3 +41,4 @@ application/xhtml+xml=zen.desktop;
application/x-extension-xhtml=zen.desktop; application/x-extension-xhtml=zen.desktop;
application/x-extension-xht=zen.desktop; application/x-extension-xht=zen.desktop;
audio/flac=mpv.desktop; audio/flac=mpv.desktop;
text/plain=org.neovim.nvim.desktop;
+1 -1
Submodule nvim updated: 8d657d0f8d...19e0db60a1.
+2 -2
View File
@@ -3,6 +3,7 @@ aalib
abseil-cpp abseil-cpp
acl acl
acpi acpi
adw-gtk-theme
adwaita-cursors adwaita-cursors
adwaita-fonts adwaita-fonts
adwaita-icon-theme adwaita-icon-theme
@@ -87,7 +88,6 @@ calibre
cargo-audit cargo-audit
catppuccin-gtk-theme-mocha catppuccin-gtk-theme-mocha
catppuccin-qt5ct-git catppuccin-qt5ct-git
catppuccinifier-cli-git
cbindgen cbindgen
cblas cblas
cdparanoia cdparanoia
@@ -111,6 +111,7 @@ cups-pk-helper
curl curl
cxxbridge cxxbridge
cython cython
darkly-bin
dav1d dav1d
db5.3 db5.3
dbus dbus
@@ -178,7 +179,6 @@ fribidi
fuse-common fuse-common
fuse2 fuse2
fuse3 fuse3
fuzzel
fzf fzf
gawk gawk
gc gc
+542
View File
@@ -0,0 +1,542 @@
# Takemi design hierarchy redesign
## Outcome
Turn the existing Persona 5 shell into a three-level visual system:
1. **Chrome** — quiet, glanceable, and subordinate to applications.
2. **Transient** — expressive pop-outs with distinct compositions and obvious controls.
3. **Cinematic** — theatrical power and lock scenes with focused hierarchy.
Preserve the crimson/black/white palette, services, keybinds, one-slab bar/rail,
`P5Panel`, session-lock security, and current functional coverage. This plan changes
visual hierarchy and interaction clarity, not the desktop stack.
## Phase 1 execution override — 2026-08-11
For the current Phase 1 implementation, the user explicitly removed reduced-motion
work and screen-reader metadata from scope. This override supersedes Phase 1.1, the
`Accessible.*` parts of Phase 1.4, and their related definition-of-done and
verification requirements for this execution only.
- Do not create durable motion preferences or change animation policy.
- Do not add or alter `Accessible.*` metadata or accessible action handlers.
- Keep keyboard focus, visible focus treatment, keyboard navigation, root-disabled
input behavior, semantic color/treatment tokens, and the surface hierarchy in
scope.
- Defer the remaining overridden requirements to a separately approved milestone.
## Implementation progress — 2026-08-11
- **Phase 1 foundation: complete for the approved scope.** Added explicit surface
presets and semantic treatment tokens; made `P5Button` and `P5Slider`
keyboard-capable with visible focus and root-gated disabled behavior. The user
explicitly excluded durable reduced-motion work and `Accessible.*` metadata, so
those items remain deferred rather than incomplete regressions.
- **Phase 2 calm chrome: complete.** Bar and rail use the quiet `ChromeSurface`
tier without permanent texture; the clock omits seconds; CPU/MEM/GPU are visibly
labelled; MediaPill uses elided static text and a static play/pause mark; rail
clicks consistently pin detail panels and undisclosed secondary state actions
were removed; delayed Persona tooltips show state and scroll hints without
taking focus or overlapping an opening detail panel.
- **Audio vertical slice: complete.** Output percentage is dominant, output/input
are distinct bands, application streams are subordinate, and mute plus mixer
actions are explicit. Pinned popouts use `PopupWindow.grabFocus` as their sole
focus/dismiss owner, remap when promoted from hover, focus the first Audio
control, support Tab and Escape, and synchronously release coordinator ownership.
- **Verification status.** Changed QML passes individual Qt 6 `qmllint` and the
repository diff passes whitespace validation. Tooltip placement/suppression and
hover open/close were exercised in a live Quickshell session and passed. Pinned
click/remap, outside-click dismissal, Escape, and compositor-close synchronization
remain untested and require interaction QA before the milestone is considered
runtime-verified.
## Non-goals
- Do not replace Quickshell, Hyprland, Fluxo, PAM, or the service layer.
- Do not add new accent hues or generic glass/blur cards.
- Do not reproduce Persona 5 screens literally; keep this a usable desktop shell.
- Do not build a second “control center” that duplicates every existing pop-out.
- Do not rewrite tray-menu recursion or the popup coordinator unless verification
exposes a concrete defect.
## Definition of done
- Permanent chrome remains readable but no longer competes with application content.
- Each primary rail icon has one obvious click action and a concise hover label.
- Selected, active, destructive, error, and urgent states are distinguishable by
treatment as well as color.
- All small text meets WCAG AA; black-on-crimson replaces white-on-crimson where needed.
- Pinned pop-outs and modal controls are keyboard reachable and visibly focused.
- Power actions that end the session require a deliberate confirmation step.
- Lock has idle, editing, busy, failure, and visible success compositions.
- Reduced motion survives process restarts and stops all nonessential loops.
- `qmllint`, live reload, interaction checks, and 1920×1080 plus 1366×768 visual
captures pass without new warnings.
---
## Phase 0 — API and pattern lock
### Allowed APIs
- **Quickshell 0.3.0 `PopupWindow`:** `visible`, `anchor`, `grabFocus`, `screen`.
Configure placement through `anchor.item/window`, `edges`, `gravity`, `margins`,
`adjustment`; call `anchor.updateAnchor()` when a visible trigger moves.
[Official PopupWindow docs](https://quickshell.org/docs/v0.3.0/types/Quickshell/PopupWindow/)
and [PopupAnchor docs](https://quickshell.org/docs/v0.3.0/types/Quickshell/PopupAnchor/).
- **Popup bounds:** combine `PopupAdjustment.Flip | Slide | Resize` where content
can genuinely resize. Use `QsWindow.mask: Region { item: ... }` for transparent
overshoot bleed only after testing transformed entry animation.
[PopupAdjustment](https://quickshell.org/docs/v0.3.0/types/Quickshell/PopupAdjustment/),
[Region](https://quickshell.org/docs/v0.3.0/types/Quickshell/Region/).
- **Pinned dismissal:** current `HyprlandFocusGrab.active/windows/onCleared` is valid
only after a visible window exists (`components/Popout.qml:83-97`). Do not combine
it casually with `PopupWindow.grabFocus`; choose one owner per opening mode.
[HyprlandFocusGrab](https://quickshell.org/docs/v0.3.0/types/Quickshell.Hyprland/HyprlandFocusGrab/).
- **Durable preferences:** `FileView` + `JsonAdapter`, using
`Quickshell.statePath("design-preferences.json")`, `watchChanges`, `reload()`, and
`writeAdapter()`. `PersistentProperties` survives reload only, not restart.
[FileView](https://quickshell.org/docs/v0.3.0/types/Quickshell.Io/FileView/),
[JsonAdapter](https://quickshell.org/docs/v0.3.0/types/Quickshell.Io/JsonAdapter/).
- **Accessibility:** `Accessible.role/name/description/focusable/focused/pressed`,
`Accessible.onPressAction`, `onIncreaseAction`, and `onDecreaseAction`; decorative
duplicates use `Accessible.ignored: true`.
[Qt Accessible](https://doc.qt.io/qt-6/qml-qtquick-accessible.html).
- **Focus:** `FocusScope`, `focusPolicy: Qt.StrongFocus`, `activeFocusOnTab`,
`KeyNavigation`, `Keys`, and `forceActiveFocus(Qt.PopupFocusReason)`.
[Qt focus model](https://doc.qt.io/qt-6/qtquick-input-focus.html).
- **Disabled controls:** disable the root `Item`; this suppresses pointer and keyboard
input and propagates to children. Explicitly bind `HoverHandler.enabled` when a
disabled control must not react visually.
[Qt Item](https://doc.qt.io/qt-6/qml-qtquick-item.html).
- **System contrast:**
`Application.styleHints.accessibility.contrastPreference === Qt.HighContrast`.
Qt 6.11 has no documented reduced-motion preference, so motion policy is owned by
`Preferences`.
- **Secure lock:** retain `WlSessionLock`/`WlSessionLockSurface` in
`lock/Lock.qml:12-28`; `Overlay + Exclusive` is not a secure replacement.
[WlSessionLock](https://quickshell.org/docs/v0.3.0/types/Quickshell.Wayland/WlSessionLock/).
### Repository patterns to copy
- Copy singleton structure from `config/Theme.qml:1-16` and `services/Actions.qml:1-10`.
- Copy popup show/hide ownership from `components/Popout.qml:58-116,150-190`.
- Copy keyboard roving selection from `overlays/PowerMenu.qml:242-261`.
- Copy development-safe lock preview boundary from `lock/Lock.qml:7-27` and
`lockpreview.qml`; never style by repeatedly locking the real session.
- Copy one-slab chrome composition from `bar/Bar.qml:40-63` and `bar/Rail.qml:43-64`.
### Verification
- Record `qs --version` and Qt version in the implementation PR: expected Quickshell
0.3.0 and Qt 6.11.1.
- Run `qmllint` on each copied API in a minimal changed component before broad rollout.
- Use `qs ipc show` to confirm existing IPC surface before adding typed functions.
### Guards
- No deprecated `PopupWindow.parentWindow`, `relativeX`, or `relativeY`.
- No direct `QsWindow.width/height`; use implicit sizes.
- No `WlrKeyboardFocus.Exclusive` as a lock substitute.
- No stock `QsMenuAnchor` or default Qt tooltip visuals inside the custom theme.
- No claimed system reduced-motion detection; it does not exist in the documented API.
---
## Phase 1 — Establish the semantic design foundation
### 1.1 Durable preferences
Create `quickshell/config/Preferences.qml` using the Phase 0 `FileView`/`JsonAdapter`
pattern. Persist:
- `reducedMotion: false`
- `tooltips: true`
- `highIntensityChrome: false` only as a migration escape hatch; default remains calm
Expose typed setters through the existing `IpcHandler` in
`services/Actions.qml:60-68`, such as `setReducedMotion(enabled: bool): void`. Do not
build a settings UI in this phase.
### 1.2 Three explicit surface presets
- Add `components/ChromeSurface.qml`: a thin `P5Panel` preset with opaque flat fill,
one keyline, no halftone, no drop, and an active slash only when requested.
- Refine `popouts/PopoutSurface.qml:7-23` as the transient preset: halftone becomes
opt-in by composition rather than present on every panel; retain the hard offset
and stronger border.
- Keep `components/P5Backdrop.qml` cinematic-only. Bar, rail, tooltips, cards, and OSD
must not instantiate it.
Do not add a generic numeric “intensity” prop to `P5Panel`; explicit presets make
the hierarchy visible in code and prevent arbitrary intermediate tiers.
### 1.3 Semantic treatments
Document and encode in `config/Theme.qml`:
| State | Fill | Mark | Text | Motion |
|---|---|---|---|---|
| Rest | surface | neutral keyline | white | none |
| Hover | surface | short red wipe | white | one short lunge |
| Focus/selected | white | black keyline | black | snap once |
| Active | surface | persistent red slash | white | none |
| Destructive/error | solid red | black keyline | black | shake only on error |
| Urgent | surface | red pulse mark | white | bounded pulse |
| Disabled | surface | muted outline | muted | none |
Replace small white-on-crimson labels with black-on-crimson; measured contrast improves
from 3.68:1 to 5.70:1. Do not add a new status color.
### 1.4 Base-control contract
- Convert `components/P5Button.qml` to a `FocusScope` or focusable root with one
activation path shared by `TapHandler`, Enter, Space, and
`Accessible.onPressAction`. Add `description`, visible focus state, and a real
disabled state.
- Replace `P5Slider.enabledControl` with root `enabled` and update its call sites.
Gate tap, drag, hover, accessible increase/decrease, and left/right keys through
the same enabled root.
- Add semantic names and roles to `RailIcon`, `StatusIcon`, notification actions,
media transport, calendar navigation, and tray-menu rows. Mark split-text ghosts,
masks, texture layers, and duplicate glyphs ignored.
### Verification
- Focus every base control with Tab and confirm a visible non-color-only state.
- Trigger each through pointer, Enter/Space, and its accessible action; assert one
signal per activation.
- Disable each control and assert no `clicked`/`moved` signal and no hover animation.
- Recompute all foreground/background contrast pairs; normal text must be ≥4.5:1.
- Restart Quickshell and confirm preferences persist.
### Guards
- Do not treat `Accessible` metadata as keyboard support; implement both.
- Do not attach semantics to every decorative child.
- Do not use `MouseArea.enabled` as the component-wide disabled gate.
- Do not spread raw state colors through callers; state treatment belongs in base controls.
---
## Phase 2 — Quiet and clarify permanent chrome
### 2.1 Bar
Update `bar/Bar.qml` and `bar/BarPill.qml` to use `ChromeSurface` and remove all
always-on texture. Keep the single slab and hairline dividers.
- `bar/Clock.qml`: remove permanent seconds; reveal them only inside Calendar.
- `bar/Resources.qml`: render tiny `CPU`, `MEM`, and `GPU` labels beside values so
`2.6G` is not contextless; remove the currently dead `Metric.label` path only after
its visible replacement exists.
- `bar/MediaPill.qml`: keep title/artist but replace the three continuously animated
equalizer bars with one static play-state mark in normal mode. Allow the loop only
when high-intensity chrome is explicitly enabled and reduced motion is off.
- Preserve active-window elision and current collision calculation in
`bar/Bar.qml:90-96`.
### 2.2 Rail interaction grammar
Make left click mean **open/pin details** for network, Bluetooth, audio, battery,
notifications, and power. Remove undisclosed right/middle-click state changes from
`bar/Rail.qml:76-132,151-171`; move those actions into their corresponding pop-outs.
Retain scrolling only for volume, microphone, and brightness because it directly
maps to a continuous readout. Mention that gesture in the tooltip.
### 2.3 Persona tooltip
Add `components/P5Tooltip.qml` as a small anchored `PopupWindow`, not a stock Qt
tooltip. Copy anchor/delay/hide behavior from `components/Popout.qml`, but use:
- calm `ChromeSurface`
- 350–450ms delay
- one-line name + current state
- optional second micro-line such as `SCROLL TO ADJUST`
- no focus grab, no halftone, no scale loop
- `PopupAdjustment.Flip | Slide`
Add `label` and `hint` to `RailIcon`; tooltips honor `Preferences.tooltips` and never
become the only source of an accessible name.
### 2.4 Pinned keyboard opening
Add explicit `openHover()` and `openPinned()` paths to `components/Popout.qml`.
`openPinned()` must set its focus mode before mapping; if a hover popup is already
visible, close and remap rather than assuming a live `grabFocus` change works. Start
with Audio as the vertical slice. Choose either `PopupWindow.grabFocus` or the current
`HyprlandFocusGrab` for pinned dismissal after runtime comparison; never leave both
owning the same close event.
### Verification
- Screenshot clean desktop against both a quiet and high-contrast wallpaper.
- First-time test: identify every rail icon and predict left click from tooltip.
- Verify no secondary pointer action remains unless it is printed in the tooltip.
- Open Audio by hover and click: hover must remain passive; pinned mode must accept
keyboard focus, Esc-close, and outside-click close exactly once.
- Move/rescale the rail while a popup is open and call `anchor.updateAnchor()` from
the actual geometry-change path if it drifts.
### Guards
- Do not reintroduce separate floating cards into bar or rail.
- Do not increase bar/rail footprint.
- Do not put stripes, halftone, or large display text in permanent chrome.
- Do not make a tooltip interactive.
---
## Phase 3 — Give transient surfaces distinct macro-compositions
Retain shared popup lifecycle, width discipline, coordinator, header baseline, and
body controls. Redesign one family at a time; capture and approve each family before
moving on so a bad motif is not copied eight times.
### 3.1 Control pop-outs
- **Audio (`AudioPopout.qml`)** — make current volume the headline number; output and
microphone form two clear bands; applications become a restrained mixer list.
Put Mute and `Open audio mixer` inside the panel.
- **Network (`NetPopout.qml`)** — dossier composition: current connection/IP at top,
download/upload fully named, network list as clipped paper rows. Use action labels
`Turn Wi-Fi on/off` and `Network settings`.
- **Bluetooth (`BtPopout.qml`)** — share list-row mechanics with Network but not its
exact silhouette. Use action copy `Turn on/off`, `Start/stop scanning`; pair/connect
status must not rely on color alone.
- **Battery (`BatteryPopout.qml`)** — oversized percentage crossing one edge, with
`Plugged in`/`On battery`, remaining time, draw, and health ordered underneath.
Keep Suspend as the one explicit action.
### 3.2 Information pop-outs
- **Media (`MediaPopout.qml`)** — let album art define one side of the silhouette;
keep metadata in a solid legibility zone; label transport actions accessibly.
- **Calendar (`CalendarPopout.qml`)** — large selected day/month as the composition,
grid subordinate; move seconds here from the permanent bar; label previous/next.
- **Notifications (`NotifPopout.qml`, `NotifCard.qml`)** — overlapping-paper rhythm
only at the list level, not on every inner action. Rename `n HELD` to `n IN HISTORY`;
make DND copy an action (`Silence notifications` / `Resume notifications`).
- **System (`SysPopout.qml`)** — treat it as a diagnostic poster: four primary metrics
first, hardware detail second. Expand `PROCS`, `MEM`, `VRAM`, `Root`, and label load
averages `1 / 5 / 15 min`.
### 3.3 Shared transient rules
- `PopoutHeader` supplies alignment and focus traversal, not the full silhouette.
- Only one dominant numeral/image/headline per panel.
- Maximum one halftone region and one red slash per panel.
- Lists use consistent 32–40px targets and explicit selected/focused/disabled states.
- Empty and loading layouts preserve the panel silhouette instead of collapsing.
- Fix muted OSD honesty: show stored percentage beside `MUTED`, or omit percentage;
never display a fabricated 0%.
### Verification
- Capture all eight pop-outs at rest plus empty/loading/error/disabled states where applicable.
- Test with longest realistic SSID, device, track, app, and notification strings.
- Verify list focus order, Enter/Space activation, Esc close, and disabled-row skipping.
- Ensure only one transient window stays open through a rapid rail sweep.
- Test popup bounds at every screen edge and both target resolutions.
### Guards
- Do not create eight copies of the same card grid.
- Do not vary edge angle randomly inside one surface.
- Do not use display italic for body copy or dense device lists.
- Do not put action wording in state form (`Wi-Fi On`) when clicking performs the inverse.
---
## Phase 4 — Focus the cinematic scenes
### 4.1 Power menu
In `overlays/PowerMenu.qml`:
- Keep the left headline/right action-stack asymmetry.
- Increase backdrop opacity enough that application text no longer competes.
- Use one stripe band or wedge, not a full-screen stripe field plus every other motif.
- Separate Lock/Suspend/Hibernate from Log Out/Reboot/Shut Down with a labeled break
and greater spacing.
- Keep white fill/black text for normal selection. Use solid crimson/black text for
destructive selection.
- Add `pendingAction`: the first activation of Log Out/Reboot/Shut Down replaces the
action region with `CONFIRM LOG OUT`, `CONFIRM REBOOT`, or `CONFIRM SHUT DOWN`
plus Cancel; second activation executes.
Escape clears confirmation first and closes the menu only when no action is pending.
- Keep roving Up/Down/J/K navigation and update focus order for confirmation.
### 4.2 Lock screen
Refactor `lock/LockFace.qml` into explicit visual states while retaining the single
PAM context and `WlSessionLock` shell:
- **Idle:** clock is dominant; status is quiet; auth field is present but subordinate.
- **Editing:** first user edit shifts emphasis to auth and dims/moves clock/status.
- **Busy:** `CHECKING…`, disabled input, bounded sweep.
- **Failure:** `PASSWORD INCORRECT` or PAM message, solid error treatment, one shake.
- **Success:** paint a short `ACCESS GRANTED` state before unlocking; skip delay under
reduced motion.
Replace `WHO GOES THERE`, `WRONG`, and `PAM ERROR` with explicit copy. Resolve the
username safely from `USER` then `LOGNAME`; if both are empty, do not call PAM with a
literal placeholder—show `AUTHENTICATION USER UNAVAILABLE`.
Keep responsive scaling, per-monitor wallpaper, and `lockpreview.qml`. Ensure the
password input exposes `Accessible.EditableText`, name `Password`, `editable: true`,
and `passwordEdit: true`.
### Verification
- Power: pointer and keyboard through safe action, destructive confirmation, cancel,
outside click, and repeated open/close.
- Lock preview: capture all five states at 1920×1080 and 1366×768.
- Real lock test only after preview and PAM-path review; test wrong password, correct
password, empty username environment simulation, and Quickshell reload behavior.
- Confirm `WlSessionLock.secure` path remains unchanged.
### Guards
- Never execute destructive action on the first activation.
- Never fake authentication success in preview or bypass PAM.
- Never replace session lock with a focus-exclusive overlay.
- Do not let theatrical copy obscure what the user must do.
---
## Phase 5 — Motion, contrast, and runtime calm
### 5.1 Motion policy
In `Theme.qml`, derive `motionScale` from `Preferences.reducedMotion`. Apply it to:
- every `Behavior`
- popup enter/exit sequences
- media equalizer and marquee loops
- icon pulses
- toast countdown/entry
- OSD, power, and lock scene transitions
When reduced motion is on, set final state directly or disable `Behavior`; do not merely
shorten loops to 1ms. Essential progress indicators may remain static with clear text.
### 5.2 High contrast
Read `Application.styleHints.accessibility.contrastPreference`. In high contrast:
- increase keyline/border separation
- disable low-opacity texture behind text
- retain shape/icon differences among selection, active, danger, and urgency
- do not change the three-color identity
### 5.3 Polling cleanup
After visual behavior is stable, move `sysReader` from the 2-second batch to the
20-second batch in `services/Sys.qml:78-102`; uptime/load/process count do not drive
permanent animation. Keep CPU/MEM/GPU at 2 seconds because the bar renders them.
Measure before making further demand-driven polling changes; do not introduce a
consumer-count abstraction without evidence that it materially reduces work.
### Verification
- Restart with reduced motion and verify zero infinite animations via source grep and
live observation; all final states must still appear.
- Toggle high contrast and re-run contrast checks.
- Compare Fluxo process-launch frequency before/after for 60 seconds.
- Confirm notification timing, popup dismissal, and lock success still complete when
animation is disabled.
### Guards
- Do not claim to follow an OS reduced-motion setting.
- Do not gate functional timers such as notification lifetime or polling behind motion.
- Do not add blur, gradients, or extra texture to compensate for removed motion.
---
## Phase 6 — Integrated verification and cutover
### Automated checks
1. `qmllint -I quickshell` on every changed QML file individually; the current tool can
exit 255 without diagnostics on some recursive/singleton batches, so record files
and results rather than treating one giant invocation as authoritative.
2. `git diff --check`.
3. Grep for:
- white small text on solid accent
- `enabledControl`
- pointer handlers without an accessible name on shared controls
- infinite animations not gated by reduced motion
- deprecated popup properties
- old copy: `WHO GOES THERE|PAM error|HELD|PROCS|VRAM`
4. Reload through `qs ipc call shell reloadConfig` and inspect `qs log`.
### Interaction matrix
Test each bar/rail/pop-out action with pointer, keyboard where focusable, outside-click,
Esc, disabled state, and service unavailable. Specifically test tray menus with nested
submenus, Wi-Fi off, no Bluetooth adapter, no battery, no player, no notifications,
and missing album art.
### Visual matrix
Capture:
- 1920×1080 and 1366×768
- calm and high-contrast wallpaper
- default, reduced-motion, and high-contrast preferences
- desktop, every pop-out, tray menu, OSD, toast, power normal/confirm, and all lock states
Compare against these invariants:
- Chrome has no texture and never dominates content.
- Transient panels each have one dominant composition.
- Cinematic scenes retain the strongest typography and atmosphere.
- Nothing important is encoded by color alone.
- No text clips, crosses a cut edge, or falls below contrast requirements.
### Cutover sequence
Implement in six reviewable commits matching Phases 1–6. Do not delete the old visual
path until its phase passes screenshots and interaction tests. Remove migration aliases
(`enabledControl`, high-intensity escape hatch) only in the final cleanup commit after
all call sites and preferences have migrated.
## Expected file map
### New
- `quickshell/config/Preferences.qml`
- `quickshell/components/ChromeSurface.qml`
- `quickshell/components/P5Tooltip.qml`
### Foundation edits
- `quickshell/config/Theme.qml`
- `quickshell/components/P5Panel.qml`
- `quickshell/components/P5Button.qml`
- `quickshell/components/P5Slider.qml`
- `quickshell/components/Popout.qml`
- `quickshell/components/Icon.qml`
- `quickshell/components/Marquee.qml`
- `quickshell/services/Actions.qml`
### Surface edits
- `quickshell/bar/{Bar,BarPill,Rail,RailIcon,StatusIcon,Clock,Resources,MediaPill,Tray}.qml`
- `quickshell/popouts/{PopoutSurface,PopoutHeader,AudioPopout,NetPopout,BtPopout,BatteryPopout,MediaPopout,CalendarPopout,NotifPopout,SysPopout,TrayMenuList}.qml`
- `quickshell/components/NotifCard.qml`
- `quickshell/overlays/{Osd,Toasts,PowerMenu}.qml`
- `quickshell/lock/LockFace.qml`
- `quickshell/services/Sys.qml`
## Recommended execution order
Execute Phase 1 first and review its state matrix before any visual restyling. Then use
Audio as the full vertical slice through Phases 2–3. Once its tooltip, pinned focus,
semantic states, reduced motion, and transient composition pass, repeat that proven
pattern across the remaining surfaces. Leave real session-lock testing until the lock
preview and power confirmation work are complete.
+4
View File
@@ -0,0 +1,4 @@
[ColorScheme]
active_colors=#ffccd2cb, #ff20241f, #ff383e35, #ff2a2f28, #ff0c0d0b, #ff161915, #ffccd2cb, #ffeef1ea, #ffccd2cb, #ff0c0d0b, #ff0f110e, #ff090a08, #ffbcc1bf, #ff090a08, #ffeef1ea, #ff9fa7a2, #ff161915, #ff0f110e, #ff20241f, #ffccd2cb, #ff79847a, #ffbcc1bf
inactive_colors=#ffccd2cb, #ff20241f, #ff383e35, #ff2a2f28, #ff0c0d0b, #ff161915, #ffccd2cb, #ffeef1ea, #ffccd2cb, #ff0c0d0b, #ff0f110e, #ff090a08, #ff383e35, #ffccd2cb, #ffeef1ea, #ff9fa7a2, #ff161915, #ff0f110e, #ff20241f, #ffccd2cb, #ff79847a, #ff383e35
disabled_colors=#ff545c52, #ff161915, #ff2a2f28, #ff20241f, #ff0c0d0b, #ff161915, #ff545c52, #ff79847a, #ff545c52, #ff0c0d0b, #ff0f110e, #ff090a08, #ff20241f, #ff545c52, #ff79847a, #ff545c52, #ff161915, #ff0f110e, #ff20241f, #ff545c52, #ff464d44, #ff20241f
+32
View File
@@ -0,0 +1,32 @@
[Appearance]
color_scheme_path=/home/narl/.config/qt6ct/colors/yorha.conf
custom_palette=true
icon_theme=Papirus-Dark
standard_dialogs=default
style=Darkly
[Fonts]
fixed="JetBrainsMono Nerd Font,10,-1,5,400,0,0,0,0,0,0,0,0,0,0,1"
general="Noto Sans,11,-1,5,400,0,0,0,0,0,0,0,0,0,0,1"
[Interface]
activate_item_on_single_click=1
buttonbox_layout=0
cursor_flash_time=1000
dialog_buttons_have_icons=1
double_click_interval=400
gui_effects=@Invalid()
keyboard_scheme=2
menus_have_icons=true
show_shortcuts_in_context_menus=true
stylesheets=@Invalid()
toolbutton_style=4
underline_shortcut=1
wheel_scroll_lines=3
[SettingsWindow]
geometry=@ByteArray(\x1\xd9\xd0\xcb\0\x3\0\0\0\0\a\x80\0\0\0\0\0\0\xe\xef\0\0\x4l\0\0\a\x80\0\0\0\0\0\0\xe\xef\0\0\x4l\0\0\0\x1\x2\0\0\0\a\x80\0\0\a\x80\0\0\0\0\0\0\xe\xef\0\0\x4l)
[Troubleshooting]
force_raster_widgets=1
ignored_applications=@Invalid()
+484
View File
@@ -0,0 +1,484 @@
# Takemi — YoRHa dark
A NieR:Automata flavoured Quickshell desktop for Hyprland, kept dark and lit
like its wallpaper: a lone figure at a porthole, the void outside, one planet's
light. The planet white for anything live, the hull red for anything wrong,
grey-green for everything else. Hairlines, small dots, soft rounded corners,
letter-spaced capitals, and selection shown by inverting the row. The bar, the
rail and a thin border are one hull around the desktop, and the desktop is the
porthole cut out of it.
Replaces Waybar, dunst and hyprlock.
## Running
```sh
qs # starts the shell (Hyprland does this via autostart.lua)
qs -p lockpreview.qml # the lock screen in an ordinary window, for styling
```
Hyprland starts it from `~/.config/hypr/lua/autostart.lua`.
## Layout
Two surfaces. Status moves off the bar and onto a vertical rail, because on a
1080p panel horizontal space is cheap and vertical space is not: a 48px rail
costs 2.5% of the width, where another 48px strip along the bottom would have
cost 4.4% of the height on top of what the bar already takes.
```
████████████████████████████████████████████████
█ ◉ │ 1 2 3 │ window title ♪ TRACK │ CPU │ 20:04 █ bar — 40px, part of the hull
█╭──────────────────────────────────────────╮ ▐▌█
█│ │ ▐▌█ rail — 48px, part of the hull
█│ windows │ ▐▌█
█│ │ ▐▌█
█╰──────────────────────────────────────────╯ █ frame — 8px left and bottom
████████████████████████████████████████████████
```
- **hull** — `bar/Frame.qml`: one surface round the whole screen, with the
desktop as a rounded opening. The bar and the rail sit on it.
- **bar** — identity and workspaces on the left (a ring after them while the
`Super+S` scratchpad holds windows, filled while it is open); the focused window, the
player, the CPU/MEM/GPU readout and the clock on the right.
- **rail** — network, bluetooth, audio, mic, backlight, battery, tray,
notifications, power. While something records the mic, uses a camera or
shares the screen, a lit icon heads the rail (`services/Privacy.qml`, read
from PipeWire). Popouts grow leftward out of it, into the screen.
Modules are divided by hairlines. `BarPill` is chromeless by default — a module
is *content inside* the hull, not a surface of its own — and marks hover with
an accent underline. Pass `standalone: true` for the rare thing that really is
its own panel.
The shell uses three deliberate surface tiers. **Chrome** (the hull and
`ChromeSurface`) is the calm, texture-free bar, rail, and tooltip layer. **Transient**
(`PopoutSurface`) is reserved for expressive menus and detail panels, with one
clear composition and stronger separation from the desktop. **Scene**
surfaces — the lock screen and the power menu — have no panel at all: the
desktop or the wallpaper dims, and one small group of type (the mark, a line of
thin display type, a short log, a menu or a field) stands in the dark.
```
shell.qml entrypoint — hull, bar, rail, dashboard, power menu, toasts, OSD per screen
config/ palette, type scale, geometry, motion curves
components/ the primitives: panels, sliders, list rows, display text
services/ data sources, all singletons
bar/ the two surfaces and their modules
popouts/ hover panels hanging off rail and bar modules
overlays/ power menu, notification toasts, the volume/brightness/keyboard OSD, idle dim
lock/ session lock
overview/ every monitor, workspace and window at once
backdrop/ the wallpaper, drawn by the shell
assets/ the dot and scanline tiles, generated with ImageMagick
```
`Panel` is the base surface everything sits on. It does not auto-size —
callers set implicit sizes from their own layout (`row.implicitWidth +
contentPad * 2`), which keeps the shape maths out of binding loops.
## Popouts
Hover panels (`components/Popout.qml`) are part of the hull, not cards
floating near their trigger. Each one sits flush against the frame edge it
opens from, is painted in the hull colour, joins the frame through concave
fillets, carries the rim round its open side, and slides out from behind the
frame. `PopoutSurface` notices it is inside a `Popout` and leaves the fill and
the frame to the hull; in the launcher and the ask panel it is still a
free-standing panel with corner ticks.
Placement is explicit: centred on the trigger, then — if that would land the
panel within `dockReach` of one of the opening's corners — docked into the
corner instead, attached to the bar and the rail at once (`dock` is `top`,
`right`, `topRight` or `bottomRight`). Without docking, a panel near the rail
left a thin strip of opening between itself and the rail.
## Notifications
The shell is the notification server (`services/Notifs.qml`). New ones arrive
as toasts in a corner drawer (`overlays/Toasts.qml`); the bell on the rail
opens the log (`popouts/NotifPopout.qml`), which is grouped by sender. A
sender with one notification is just that entry. A sender with several gets a
header — its name, a count, a mark to clear only that sender — over its
newest entry and a `+ 2 more` line; a click on either opens the rest.
## Dashboard
`dashboard/Dashboard.qml` is a drawer that slides down out of the underside of
the bar — clock click, `SUPER + D`, or `qs ipc call shell dashboard`. It is
painted in the hull colour and joins the bar through two concave fillets, so it
reads as more of the hull rather than as a floating card (caelestia's idea).
Four columns divided by hairlines: operator and time, the month, the system
meters, and what is playing. Escape or focusing anything else puts it away; a
`HyprlandFocusGrab` handles the latter, and `Actions.dashboardClosedAt` stops
the clock click that cleared the grab from opening it straight back up.
Under the columns, past a hairline, a row of switches: Wi-Fi, Bluetooth, do
not disturb and stay awake, plus night light when `hyprsunset` is installed
(it runs at 4200 K for as long as the switch is on). On is the light slab
with ink type, the same as a selection everywhere else; off is a hairline.
## Game mode
While a fullscreen game is on screen, Hyprland switches off its animations and
shadows and turns on direct scanout (`~/.config/hypr/lua/gamemode.lua`). It
also tells the shell through `qs ipc call shell gameMode true`. The game covers
the hull, but covered surfaces still render, so the shell stops what it does by
itself: the fluxo stream, the backlight poll, the media tick and equaliser, and
the rail and workspace pulses. Toasts wait in the log, except critical ones.
Both sides come back when the game leaves fullscreen, closes or is switched
away from.
A game is a window with the `game` content type, or one whose class matches
`GAME_CLASSES` in `gamemode.lua` (`steam_app_<id>` and `gamescope` so far).
## Modules
| Module | Where | Left click | Right click | Middle | Scroll | Hover |
|---|---|---|---|---|---|---|
| Sigil | bar | app menu | reload shell | | | |
| Workspaces | bar | switch | | | | |
| Clock | bar | dashboard | | | | calendar |
| Network | rail | pin details | | | | state + networks |
| Bluetooth | rail | pin details | | | | state + devices |
| Volume | rail | pin audio | | | volume | state + scroll hint + audio |
| Mic | rail | pin audio | | | mic volume | state + scroll hint + audio |
| Brightness | rail | | | | brightness | state + scroll hint |
| Battery | rail | pin panel | | | | power detail |
| Tray | rail | activate | app menu | secondary | | |
| Bell | rail | pin history + mark read | | | | state + history |
| Power | rail | power menu | | | | |
| Media | bar | play/pause | next | raise player | next/prev | full player |
| CPU/MEM/GPU | bar | btop | | | | resource dashboard |
Popouts open on a short dwell and stay while the pointer is over the trigger or
the panel. Hover openings remain passive. Clicking remaps a detail panel as a
keyboard-focused popup: Tab moves through its controls, Escape dismisses it,
and an outside click closes it. A shell-wide coordinator keeps only one
transient surface mapped at a time, including tray context menus, so sweeping
across the rail cannot stack several animated panels over one another.
Audio is the reference vertical-slice popout: current output percentage leads,
output and input have distinct bands, and application streams form a subordinate
mixer list. Output/input mute actions and **Open audio mixer** are explicit
buttons inside the panel; scrolling the rail icons remains the fast level
adjustment.
The power menu is fully keyboard-driven as well as clickable: `Up`/`Down` or
`J`/`K` moves the active slab, `Enter`/`Space` confirms it, and `Escape`
dismisses the screen.
## Keybinds
Set in `~/.config/hypr/lua/binds/shell.lua`:
- `Super+Ctrl+L` — lock
- `Super+P` — power menu
- `Super+Shift+N` — toggle do not disturb
- `Alt+Tab` / `Alt+Shift+Tab` — overview, walking focus history; let go of Alt to switch
- `Super+W` — overview, without the hold
- `Super+Tab` / `Super+Shift+Tab` — next / previous window in a group
- `Super+Shift+I` — stay awake (no dim, lock or screen-off until toggled back)
- `Super+Ctrl+Shift+N` — clear notifications
- `Super+A` — ask panel
- `Super+Shift+R` — reload the shell
All of these go through Quickshell's IPC, so they work from any script:
```sh
qs ipc call shell lock
qs ipc call shell power
qs ipc call shell toggleDnd
qs ipc call shell toggleStayAwake
qs ipc call shell wallpaper ~/.wallpapers/space-1.jpg
qs ipc call shell ask
qs ipc call shell askAbout "why is the sky blue"
qs ipc call shell clearNotifications
qs ipc call shell reloadConfig
```
## Ask
`Super+A` drops a panel off the top edge and hands whatever you type to
`claude -p`, streamed token by token. It is the subscription that answers —
the CLI already holds the credentials, so nothing here has an API key and
nothing here has to be given one.
The panel is a chat, not a coding session. Settings sources, MCP servers and
project files are switched off by default, which keeps a question asked from
the desktop from arriving with the last project's hooks, `CLAUDE.md` and tone
attached. The thread survives closing the panel; `Ctrl+L` starts a new one.
| Key | Does |
|---|---|
| `Enter` | ask |
| `Shift+Enter` | newline |
| `Ctrl+M` | next model, written back to `ask.json` |
| `Ctrl+Y` | copy the last answer |
| `Ctrl+L` | new conversation |
| `Ctrl+S` | stop the answer |
| `Escape` | hide, keeping the thread |
Configured in `ask.json`, watched and reloaded live:
```json
{
"model": "sonnet",
"systemPrompt": "…",
"keepContext": true,
"settingSources": "",
"allowedTools": ["WebSearch", "WebFetch"],
"disallowedTools": ["Bash", "Edit", "Write"],
"workingDirectory": "/home/narl",
"maxTurns": 6
}
```
`settingSources` is the escape hatch: set it to `"user,project,local"` and the
panel becomes your ordinary Claude Code, hooks and all.
## Data sources
Hardware telemetry comes from `fluxo`, whose hardware modules are configured to
emit raw pipe-delimited values rather than display strings — `services/Sys.qml`
parses them and does its own formatting. The previous human-readable Waybar
formats are preserved in `~/.config/fluxo/config.toml.waybar-bak`.
Everything else is native: PipeWire for audio, BlueZ for bluetooth,
NetworkManager for Wi-Fi, UPower for battery, MPRIS for media, and Quickshell's
own notification server. These are event-driven, so the bar and OSD react the
instant a volume key is pressed instead of on the next poll.
## Overview
`overview/Overview.qml`. Each monitor is a row of small portholes, one per
workspace at that monitor's aspect ratio, with live captures of the windows
where they sit (`ScreencopyView`, so windows on hidden workspaces show too).
The monitor's name heads its row once there is more than one. A group shows
the member in front with a `1/3` tag; picking a hidden member puts it in
front.
`Alt+Tab` opens it with the previous window picked, more Tabs walk back
through focus history, and letting go of Alt switches. Hyprland keeps the
Alt+Tab bind while the overview has the keyboard, so those presses arrive
over IPC (`overviewNext` / `overviewPrev`); the Alt release comes to the
overview itself, with a release bind on `ALT_L` as a backstop. Hover picks
only once the pointer moves, so a resting pointer cannot override the
keyboard. Enter, a click, or `1`–`9` for a workspace also go; Escape or a
click on the dark cancels.
Focus is dispatched a moment after the overview lets go of the keyboard:
Hyprland ignores focus changes while a layer holds it exclusively.
## Wallpaper
`backdrop/Backdrop.qml` draws it on the background layer; there is no
hyprpaper. The picture is drawn 8% larger than the screen and drifts with the
workspace: 1 sits at the top of it and each step down moves it a fixed
distance, a little slower than Hyprland's own slide, so the windows arrive
first and the scene behind catches up. It never moves on its own, so an idle
desktop costs the compositor nothing. While the workspace holds windows it
settles a shade darker.
The choice is one line in `quickshell/wallpaper` (`~` allowed), watched live.
`qs ipc call shell wallpaper <path>` rewrites it.
## Idle
`services/Idle.qml`, on Wayland's idle-notify protocol instead of hypridle:
| After | Does |
|---|---|
| 4 min | the screen dims slowly (`overlays/IdleDim.qml`); any input lifts it |
| 5 min | lock |
| 6 min | displays off; any input wakes them |
| 15 min | suspend, on battery only |
Every stage respects idle inhibitors, so a fullscreen video holds them all
off. `Super+Shift+I` does the same by hand and puts a cup on the rail while it
is on.
## Lock screen
Authenticates through `/etc/pam.d/hyprlock`, which on this box is just
`auth include login`. The visuals live in `lock/LockFace.qml` so they can be
styled in an ordinary window (`qs -p lockpreview.qml`) without locking the
session.
It is the wallpaper, barely softened, with the face set in the dark of the
porthole: the mark, the time in light display type, the date, "Take your
time." glowing faintly with the odd flicker (`components/GlowText.qml`), a
status log
(battery, network, what is playing, unread notices), and a rounded field that
fills with dots as you type. The dots breathe while PAM checks; a wrong
password turns them and the field's rim to rust and nudges the field once.
If the shell ever dies while the session is locked, the compositor keeps the
session secured by protocol and there is no unlocker left to talk to — recovery
means restarting Hyprland from a TTY.
## What was retired
- `waybar.service` — disabled. Its config is still in `~/.config/waybar`.
- `hyprpaper` — replaced by `backdrop/`. Turn its unit off with
`systemctl --user disable --now hyprpaper`.
- `hypridle` — never installed; `services/Idle.qml` does the job.
- `dunst.service` — masked, so it cannot be D-Bus activated and steal
`org.freedesktop.Notifications`. Undo with
`systemctl --user unmask dunst.service`.
- `hyprlock` — no longer bound. `hyprlock.conf` is untouched.
## Optional
`power-profiles-daemon` is not installed (TLP is), so the power-profile
switching in `services/Battery.qml` stays dormant. Nothing else depends on it.
The display face is Noto Sans, upright and widely tracked — the nearest free
face to NieR's FOT-Rodin that is already installed. Change `Theme.fontDisplay`
and everything follows.
## Palette discipline
YoRHa dark: NieR:Automata's interface language, painted from the wallpaper
(`~/.wallpapers/space-1.jpg`) rather than from the game's beige. Two hues, and
**hue is meaning**; nothing gets a colour for decoration.
- **Light (`accent`, `#eef1ea`)** means live: the focused workspace, the
selected row, the hovered control, a channel that is on, an unread badge. It
is the planet's white, barely a colour at all — NieR marks attention with
brightness and inversion, not with hue.
- **Red (`alarm`, `#c8684f`)** means wrong or destructive: critical battery, an
urgent workspace, a critical notification, reboot and shut down. It is the
hull's indicator lights, the only warm thing in the wallpaper.
- **Grey-green** is everything else, and that includes *off*. A muted speaker,
a muted mic and Do Not Disturb drop to `muted` grey with their off glyph.
- **Ink** is the text on an inverted slab and the faint drop under display
type.
**Selection is the NieR cursor.** Focus and selection invert the row: a slab of
the light with ink punched out of it. Hover lifts the surface one step and
brings the marker up to full light, but never inverts. Destructive-active is
the same slab in red. The `treatment*` tokens in `Theme.qml` encode all of
this; use them rather than picking colours per widget.
**Dots, not slashes.** The current row, a button under the pointer, the end of
a header rule and a notification's urgency carry a small dot (`Theme.marker`).
Borders are structure and stay neutral.
**Flicker, not print.** Display text (`SplitText`) is upright, tracked by
`Theme.tracking`, on a one-pixel soft drop. The two ghosts either side of it
appear only while `glitch()` runs — NieR's brief data-corruption flicker.
**Texture is rare.** Chrome (bar, rail, tooltips), transient panels (popouts,
OSD, toasts) and the two scene surfaces are flat. Depth comes from the hull's
dark, the rim hairline and the wallpaper, not from patterns.
**Type has a floor.** Nothing renders below `Theme.fsMicro` (10px).
**Changing the palette.** The colours are written once per program:
| File | Covers |
|---|---|
| `quickshell/config/Theme.qml` | the shell: `accent`, `alarm`, the neutral ramp, the hull |
| `hypr/lua/palette.lua` | window borders, groups |
| `alacritty/yorha.toml` | the terminal |
| `fish/conf.d/yorha_theme.fish` | the prompt and pager |
| `nvim/lua/yorha.lua` | the editor and its statusline |
| `btop/themes/yorha.theme` | the resource monitor |
| `gtk-3.0/gtk.css`, `gtk-4.0/gtk.css` | GTK and libadwaita apps |
| `qt6ct/colors/yorha.conf` | Qt apps |
| `zathura/yorha` | the document viewer |
Everything else derives. `glow` *is* `accent`, and `primary` is plain `text`.
`Theme.heat()` is a continuous text → light → red ramp, so a CPU figure that
has gone red means the same thing as a battery that has gone red. Tray icons
are flattened to one colour (subtext, the accent on hover) for the same reason:
app brand colours wreck the palette faster than anything else in the shell.
nm-applet and blueman are hidden from the tray, since the rail already shows
network and bluetooth, but keep running as the secret and pairing agents.
## The rest of the desktop
- **Hyprland** — `hypr/lua/look.lua`. Windows round at 8px, concentric with
the opening's 18px corners at a 10px gap (`Theme.frameRadius - gaps_out`);
change one and the others follow. No blur, both opacities at `1.0`, 1px
borders running from the light into the suit's grey. The shadow is soft and
wide, so windows float in the opening. Group tabs are thin rounded lines with
no titles, the current one in the light; the bar writes the title once and
adds the window's place in the group (`2/3`). Motion
(`hypr/lua/animations.lua`) glides and settles with no overshoot: windows
resolve in from a hair smaller, workspaces drift a short way vertically and
fade (sliding behind the hull), the `SUPER+S` scratchpad lowers out from
under the bar like the dashboard, and layers only fade, since quickshell
animates its own surfaces.
- **Alacritty** — `alacritty/yorha.toml`, selected by the `import` in
`alacritty.toml`. Muted earth hues on the void; selection inverts.
Alacritty only live-reloads when `alacritty.toml` itself changes, so touch
it after editing the palette.
The Catppuccin and `takemi.toml` files are left in place, unreferenced.
- **Neovim** — `nvim/lua/yorha.lua` (loaded by `colors/yorha.lua`, with a
matching lualine theme). Syntax in the terminal's earth hues, floats as hull
surfaces with rounded rims, menus selecting with the light slab.
- **btop** — `btop/themes/yorha.theme`. Hairline boxes; gradients climb from
grey to the light, and only temperature and CPU load run on into rust.
- **GTK** — the `adw-gtk3-dark` theme (package `adw-gtk-theme`) for the
libadwaita shapes, recoloured by `gtk-3.0/gtk.css` and `gtk-4.0/gtk.css`.
libadwaita apps such as Nautilus read the same overrides as CSS variables.
Selection is the light slab with ink text; only destructive actions are red.
The theme name is also in `xsettingsd/xsettingsd.conf` and has to be set with
`gsettings` for portal-aware apps.
- **Type** — one sans and one mono everywhere: Noto Sans for interface text
and JetBrainsMono Nerd Font for figures and code, in the shell
(`Theme.fontDisplay` / `fontMono`), GTK (`settings.ini` and the gsettings
`font-name` / `monospace-font-name`), qt6ct and Hyprland's own text.
- **Qt** — qt6ct with `qt6ct/colors/yorha.conf`, the same palette in
`QPalette` roles, drawn with the Darkly style (package `darkly-bin`) for
rounded controls. Fusion is the fallback if Darkly is missing.
- **zathura** — `zathura/yorha`, included from `zathurarc`. Pages are
recoloured onto the hull with text in bone; images keep their colours.
Search hits are a soft bone wash, and the current hit is sand.
- **Fish** — `fish/conf.d/yorha_theme.fish`.
- **The launcher** — no external theming to keep in step, because it is part of
this shell (`launcher/`). Fuzzel is gone; `takemi-dmenu` stands in for
`fuzzel --dmenu` where a script needs a picker.
- **No glass anywhere.** Alacritty is `opacity = 1.0, blur = false`, and
Hyprland's `decoration:blur` is disabled.
There are **no gradients in the chrome**; every fill is flat. `Theme.sheen`
still exists as a no-op property so old call sites need not be chased down.
## Geometry discipline
**The hull.** Borrowed from caelestia's screen border: the bar, the rail and a
thin band down the left and along the bottom (`Theme.frame`) are one surface,
drawn by `bar/Frame.qml`, and the desktop is a rounded opening cut out of it
(`Theme.frameRadius`). Read against the wallpaper, you are inside the ship and
the windows sit in the porthole. The hull is on the top layer, over the
windows, with a transparent opening and no input region, so a window moving
past the opening's edge — a workspace switch, the scratchpad drawer — passes
behind the frame rather than over the bar. `hypr/lua/rules/layers.lua` orders
the bar and the rail above it. The bar and the rail draw
no surface of their own and ignore exclusive zones; Frame reserves all four
zones itself, so every piece is placed from the screen edge.
**Soft, everywhere else.** The porthole is the shape the desktop is built
around, and every smaller surface is a gentler echo of it. `Theme.skew` and
`Theme.cut` are `0`; `Panel` and `Skew` draw `Geom.roundedRect` with
`Theme.radiusS` (pills, chips, meter tracks), `radiusM` (rows, buttons) or
`radiusL`. `radiusL` is `frameRadius - padS`: anything inset by `padS` inside
one of the frame's curves — a hovered notification in the drawer's corner —
gets a corner concentric with it, so the gap stays even and nothing pokes past
the curve. Free-standing panels (launcher, ask) take `frameRadius` itself.
Toggles, transport buttons, workspace pips and today's date are pills or
discs; album art is rounded via `ClippingRectangle`.
**Motion glides.** No overshoot anywhere: things ease in on `OutCubic` or
`OutExpo` and settle. Nothing scales up or nudges sideways on hover either;
hover brightens and marks, the way NieR's menus do. Meters are one continuous
rounded line that glides to its level, and the bar's player shows a
five-line signal meter.
Each surface is **one continuous slab**, with modules divided by hairline rules
rather than by gaps. A row of separate floating cards reads as clutter; that is
what the dividers exist to avoid.
+11
View File
@@ -0,0 +1,11 @@
{
"model": "sonnet",
"fallbackModel": "",
"systemPrompt": "You are a desktop answer panel. Answer the question directly and compactly, in markdown, with no preamble. Prefer a few sentences over a list, and a list over an essay. Say plainly when you do not know.",
"keepContext": true,
"settingSources": "",
"allowedTools": ["WebSearch", "WebFetch"],
"disallowedTools": ["Bash", "Edit", "Write", "NotebookEdit", "Agent", "Task"],
"workingDirectory": "/home/narl",
"maxTurns": 6
}
+416
View File
@@ -0,0 +1,416 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Wayland
import "root:/config"
import "root:/components"
import "root:/popouts"
import "root:/services"
// The ask panel. It comes down off the top edge, takes the keyboard, and hands
// a question to Claude.
//
// Deliberately not the launcher: the launcher is a list you pick from in one
// keystroke, this is a conversation you read. The shape says so — full width of
// the question, answer below it, and the thing drops from the top rather than
// rising into the middle so the two are never mistaken for each other mid-type.
PanelWindow {
id: root
required property var modelData
screen: modelData
// One screen at a time, latched at open — same reason as the launcher: the
// window takes focus, and a prompt should not hop monitors mid-question.
readonly property bool open: Actions.askOpen
&& Actions.askScreen === modelData.name
visible: root.open || closeAnim.running
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.namespace: "takemi-ask"
WlrLayershell.keyboardFocus: root.open
? WlrKeyboardFocus.Exclusive
: WlrKeyboardFocus.None
anchors { top: true; bottom: true; left: true; right: true }
color: "transparent"
exclusionMode: ExclusionMode.Ignore
readonly property int panelWidth: Math.min(860, root.width - Theme.padXL * 2)
readonly property int maxTranscript: Math.round(root.height * 0.55)
onOpenChanged: {
if (root.open) {
PopupCoordinator.dismissAll();
input.text = Actions.askSeed;
openAnim.restart();
input.forceActiveFocus();
input.cursorPosition = input.text.length;
if (Actions.askSeed !== "") root.send();
} else {
closeAnim.restart();
}
}
function send(): void {
const q = input.text.trim();
if (q === "" || Ask.busy) return;
Ask.ask(q);
input.text = "";
}
function copyLast(): void {
for (let i = Ask.turns.length - 1; i >= 0; i--) {
if (Ask.turns[i].role === "assistant" && Ask.turns[i].text !== "") {
Actions.copyText(Ask.turns[i].text);
return;
}
}
}
// -------------------------------------------------------------- backdrop
Rectangle {
id: backdrop
anchors.fill: parent
color: Theme.alpha(Theme.base, 0.6)
opacity: 0
TapHandler { onTapped: Actions.closeAsk() }
}
// ----------------------------------------------------------------- panel
Item {
id: shell
width: root.panelWidth
height: header.height + divider.height + body.height + hint.height
anchors.horizontalCenter: parent.horizontalCenter
y: 0
opacity: 0
// The drop. The offset is animated, never bound: binding it to the
// panel's own height means every height change — every streamed line —
// snaps the panel back off the top of the screen.
transform: Translate { id: drop; y: 0 }
PopoutSurface {
anchors.fill: parent
tone: Ask.busy ? Theme.accent : Theme.primary
padding: 0
slashWidth: Ask.busy ? 8 : 5
// -------------------------------------------------------- header
Item {
id: header
width: parent.width
height: 62
anchors.top: parent.top
Row {
anchors.fill: parent
anchors.leftMargin: Theme.padL
anchors.rightMargin: Theme.padL
spacing: Theme.padM
// Pulses rather than spins. Native text rendering is
// subpixel-antialiased, so a rotated glyph smears into
// colour fringes; scale keeps it on the pixel grid.
Icon {
anchors.verticalCenter: parent.verticalCenter
text: "󰭹"
color: Ask.busy ? Theme.accent : Theme.primary
font.pixelSize: Theme.fsTitle
pulsing: Ask.busy
pulseScale: 1.12
}
TextInput {
id: input
anchors.verticalCenter: parent.verticalCenter
width: parent.width - x - modelChip.width - Theme.padM
color: Theme.text
font.family: Theme.fontMono
font.pixelSize: Theme.fsLarge
selectionColor: Theme.accent
selectedTextColor: Theme.ink
clip: true
focus: true
renderType: Text.NativeRendering
Keys.priority: Keys.BeforeItem
Keys.onPressed: event => root.handleKey(event)
Text {
anchors.verticalCenter: parent.verticalCenter
visible: input.text === ""
text: Ask.busy ? "thinking…"
: Ask.turns.length > 0 ? "follow up…" : "ask anything"
color: Theme.muted
font: input.font
renderType: Text.NativeRendering
}
}
// The model, which is also the control for it: Ctrl+M walks
// the ring and writes the choice back to ask.json, so the
// panel and the file never disagree.
Item {
id: modelChip
anchors.verticalCenter: parent.verticalCenter
width: modelText.width + Theme.padM * 2
height: 24
Skew {
anchors.fill: parent
color: Theme.surface
}
Text {
id: modelText
anchors.centerIn: parent
text: Ask.model.toUpperCase()
color: Theme.accent
font.family: Theme.fontMono
font.pixelSize: Theme.fsMicro
font.weight: Font.Bold
font.letterSpacing: 2
renderType: Text.NativeRendering
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: Ask.cycleModel()
}
}
}
}
Skew {
id: divider
anchors.top: header.bottom
width: parent.width
height: Theme.stroke
color: Theme.alpha(Theme.outline, 0.8)
lean: 0
}
// --------------------------------------------------- transcript
Item {
id: body
anchors.top: divider.bottom
width: parent.width
height: Math.min(transcript.contentHeight + Theme.padM * 2,
root.maxTranscript)
visible: Ask.turns.length > 0 || Ask.error !== ""
clip: true
Behavior on height {
NumberAnimation { duration: Theme.durBase; easing.type: Easing.OutExpo }
}
ListView {
id: transcript
anchors.fill: parent
anchors.margins: Theme.padM
anchors.leftMargin: Theme.padL
anchors.rightMargin: Theme.padL
model: Ask.turns
spacing: Theme.padM
boundsBehavior: Flickable.StopAtBounds
// Stay at the bottom while an answer streams in, but leave
// the view alone the moment it is scrolled by hand.
property bool pinned: true
onMovementEnded: pinned = atYEnd
onContentHeightChanged: if (pinned) positionViewAtEnd()
ScrollBar.vertical: ScrollBar { policy: ScrollBar.AsNeeded }
delegate: Column {
required property var modelData
width: transcript.width
spacing: Theme.padS
Text {
visible: modelData.role === "user"
text: "YOU"
color: Theme.muted
font.family: Theme.fontMono
font.pixelSize: Theme.fsMicro
font.letterSpacing: 3
renderType: Text.NativeRendering
}
Text {
width: parent.width
text: modelData.text
textFormat: modelData.role === "assistant"
? Text.MarkdownText : Text.PlainText
color: modelData.role === "user" ? Theme.subtext : Theme.text
linkColor: Theme.accent
font.family: Theme.fontMono
font.pixelSize: Theme.fsBody
wrapMode: Text.Wrap
renderType: Text.NativeRendering
onLinkActivated: link => Actions.run(["xdg-open", link])
}
}
footer: Text {
width: transcript.width
visible: Ask.error !== ""
topPadding: visible ? Theme.padM : 0
text: Ask.error
color: Theme.alarm
font.family: Theme.fontMono
font.pixelSize: Theme.fsSmall
wrapMode: Text.Wrap
renderType: Text.NativeRendering
}
}
}
// -------------------------------------------------------- hints
Item {
id: hint
anchors.top: body.visible ? body.bottom : divider.bottom
width: parent.width
height: 28
Text {
anchors.left: parent.left
anchors.leftMargin: Theme.padL
anchors.verticalCenter: parent.verticalCenter
color: Theme.muted
font.family: Theme.fontMono
font.pixelSize: Theme.fsMicro
font.letterSpacing: 3
renderType: Text.NativeRendering
text: Ask.busy
? "⏎ QUEUE CTRL+S STOP ESC HIDE"
: "⏎ ASK CTRL+M MODEL CTRL+Y COPY CTRL+L NEW ESC HIDE"
}
Text {
anchors.right: parent.right
anchors.rightMargin: Theme.padL
anchors.verticalCenter: parent.verticalCenter
visible: Ask.turns.length > 0
text: Math.floor(Ask.turns.length / 2) + " TURN"
+ (Ask.turns.length > 2 ? "S" : "")
color: Theme.muted
font.family: Theme.fontMono
font.pixelSize: Theme.fsMicro
font.letterSpacing: 1
renderType: Text.NativeRendering
}
}
}
}
// ------------------------------------------------------------------ keys
function handleKey(event: var): void {
if (!root.open) return;
const ctrl = (event.modifiers & Qt.ControlModifier) !== 0;
if (event.key === Qt.Key_Escape || (ctrl && event.key === Qt.Key_C)) {
// Hide, do not clear. The thread outlives the panel so that closing
// it to read something and reopening to ask about that is one
// conversation, not two.
Actions.closeAsk();
event.accepted = true;
return;
}
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
// Shift+Enter is a newline; a multi-line question is still one
// question. Plain Enter sends.
if ((event.modifiers & Qt.ShiftModifier) === 0) {
root.send();
event.accepted = true;
}
return;
}
if (ctrl && event.key === Qt.Key_L) {
Ask.clear();
input.text = "";
event.accepted = true;
return;
}
if (ctrl && event.key === Qt.Key_S) {
Ask.stop();
event.accepted = true;
return;
}
if (ctrl && event.key === Qt.Key_M) {
Ask.cycleModel();
event.accepted = true;
return;
}
if (ctrl && event.key === Qt.Key_Y) {
root.copyLast();
event.accepted = true;
return;
}
if (ctrl && event.key === Qt.Key_U) {
input.text = "";
event.accepted = true;
return;
}
// The transcript scrolls on the arrows, because the input is one line
// and has nowhere for them to go.
if (event.key === Qt.Key_PageDown || (ctrl && event.key === Qt.Key_J)) {
transcript.flick(0, -900);
event.accepted = true;
return;
}
if (event.key === Qt.Key_PageUp || (ctrl && event.key === Qt.Key_K)) {
transcript.flick(0, 900);
event.accepted = true;
return;
}
}
// ------------------------------------------------------------- animation
ParallelAnimation {
id: openAnim
NumberAnimation {
target: backdrop; property: "opacity"
to: 1; duration: Theme.durBase; easing.type: Easing.OutQuad
}
NumberAnimation {
target: shell; property: "opacity"
to: 1; duration: Theme.durFast
}
NumberAnimation {
target: drop; property: "y"
from: -shell.height; to: 0
duration: Theme.durSlow; easing.type: Easing.OutCubic
}
}
ParallelAnimation {
id: closeAnim
NumberAnimation {
target: backdrop; property: "opacity"
to: 0; duration: Theme.durFast
}
NumberAnimation {
target: shell; property: "opacity"
to: 0; duration: Theme.durFast
}
NumberAnimation {
target: drop; property: "y"
to: -shell.height; duration: Theme.durFast; easing.type: Easing.InQuad
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 B

+90
View File
@@ -0,0 +1,90 @@
import QtQuick
import Quickshell
import Quickshell.Hyprland
import Quickshell.Wayland
import "root:/config"
import "root:/services"
// The wallpaper, seen through the porthole. It is drawn a little larger than
// the screen and drifts when the workspace changes: windows slide past
// vertically (hypr/lua/animations.lua) and the scene behind them moves a few
// pixels the same way and a little later, the way distant things do out of a
// moving window. Only on switches — it never moves on its own, so an idle
// desktop costs the compositor nothing.
//
// It also settles a shade darker while the workspace holds windows, so the
// band of wallpaper round them reads as the far side of the glass rather than
// as part of the desktop.
PanelWindow {
id: root
required property var modelData
screen: modelData
WlrLayershell.layer: WlrLayer.Background
WlrLayershell.namespace: "takemi-backdrop"
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
exclusionMode: ExclusionMode.Ignore
anchors { top: true; bottom: true; left: true; right: true }
color: Theme.ink
mask: Region {}
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen)
readonly property HyprlandWorkspace workspace: root.monitor?.activeWorkspace ?? null
// Overscan: how much bigger than the screen the picture is drawn, which is
// the room it has to drift in.
readonly property real overscan: 1.08
readonly property real slack: root.height * (root.overscan - 1)
// Workspaces 1–10 spread across the slack, so 1 sits at the top of the
// picture and each step down moves it a fixed distance. Special and
// named workspaces (negative ids) leave it where it was.
property real drift: 0
readonly property int wsId: root.workspace?.id ?? 1
onWsIdChanged: if (root.wsId > 0) root.drift = Math.min(root.wsId - 1, 9) / 9
readonly property bool occupied: (root.workspace?.toplevels?.values?.length ?? 0) > 0
Image {
id: picture
width: root.width * root.overscan
height: root.height * root.overscan
x: -(width - root.width) / 2
y: -root.drift * root.slack
source: Wallpaper.forMonitor(root.screen?.name ?? "")
sourceSize: Qt.size(width, height)
fillMode: Image.PreserveAspectCrop
asynchronous: true
smooth: true
cache: false
opacity: status === Image.Ready ? 1 : 0
Behavior on opacity {
NumberAnimation { duration: Theme.durLazy; easing.type: Easing.OutCubic }
}
// Longer than Hyprland's 550ms workspace slide, on the same curve, so
// the windows arrive first and the scene catches up.
Behavior on y {
NumberAnimation {
duration: 900
easing.type: Easing.BezierSpline
easing.bezierCurve: [0.35, 0.0, 0.15, 1.0, 1.0, 1.0]
}
}
}
Rectangle {
anchors.fill: parent
color: Theme.ink
opacity: root.occupied ? 0.18 : 0
Behavior on opacity {
NumberAnimation { duration: Theme.durLazy; easing.type: Easing.InOutSine }
}
}
Component.onCompleted: root.drift = Math.min(Math.max(root.wsId, 1) - 1, 9) / 9
}
+205
View File
@@ -0,0 +1,205 @@
import QtQuick
import Quickshell
import Quickshell.Hyprland
import "root:/config"
import "root:/components"
// Focused window title. Rapid application title churn is settled before it is
// painted, then the stable caption lands like a misregistered print layer.
Item {
id: root
required property var screenRef
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(screenRef)
readonly property HyprlandToplevel toplevel: Hyprland.activeToplevel
// Only speak for the monitor this bar lives on.
readonly property bool mine: toplevel !== null
&& (root.monitor === null || toplevel.monitor === null
|| toplevel.monitor.id === root.monitor.id)
readonly property string incomingTitle: mine && toplevel.title ? toplevel.title : "—"
// Where the focused window sits in its group, as "2/3", or "" when it is
// not grouped. Hyprland's group bar draws only indicator lines (see
// hypr/lua/look.lua), so the bar is the one place the title is written.
readonly property string groupLabel: {
const ipc = root.mine ? root.toplevel.lastIpcObject : null;
const members = ipc?.grouped ?? [];
if (members.length < 2) return "";
const bare = a => String(a).replace(/^0x/, "");
const at = members.findIndex(m => bare(m) === bare(root.toplevel.address));
return (at < 0 ? "?" : at + 1) + "/" + members.length;
}
// lastIpcObject is a snapshot, refreshed only on request. Ask again
// whenever focus or group membership may have changed.
Connections {
target: Hyprland
function onRawEvent(event) {
const n = event.name;
if (n === "activewindowv2" || n === "togglegroup"
|| n === "moveintogroup" || n === "moveoutofgroup"
|| n === "closewindow" || n === "openwindow")
Hyprland.refreshToplevels();
}
}
property string displayedTitle: "—"
// Which toplevel the landing transition last played for. The landing is a
// focus-change stamp, so it has to key off window identity rather than off
// the caption text — see commitTitle().
property var landedToplevel: null
// The bar shrinks this when the three clusters would otherwise collide.
property real maxWidth: 240
implicitWidth: Math.min(maxWidth,
Math.max(70, label.implicitWidth + 4 + (groupTag.visible ? groupTag.width + Theme.padS : 0)))
implicitHeight: 20
Behavior on implicitWidth {
NumberAnimation { duration: Theme.durSlow; easing.type: Easing.OutExpo }
}
Item {
id: titleStage
anchors.fill: parent
clip: true
Text {
id: label
anchors.verticalCenter: parent.verticalCenter
width: parent.width - (groupTag.visible ? groupTag.width + Theme.padS : 0)
text: root.displayedTitle
color: root.mine ? Theme.subtext : Theme.muted
elide: Text.ElideRight
font.pixelSize: Theme.fsSmall
font.family: Theme.fontDisplay
font.weight: Font.DemiBold
renderType: Text.NativeRendering
}
// Accent duplicate: visible only during the landing transition. The
// small opposing offset gives the title NieR's brief corruption flicker
// without leaving permanent motion in the bar.
Text {
id: ghost
anchors.verticalCenter: parent.verticalCenter
width: label.width
text: root.displayedTitle
color: Theme.accent
opacity: 0
elide: Text.ElideRight
font: label.font
renderType: Text.NativeRendering
}
Text {
id: groupTag
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
visible: root.groupLabel !== ""
text: root.groupLabel
color: Theme.muted
font.family: Theme.fontMono
font.pixelSize: Theme.fsMicro
renderType: Text.NativeRendering
}
}
// Adopt the settled caption, and stamp it only when focus actually moved.
//
// The landing used to restart on any title change, which is not the same
// thing: plenty of windows rewrite their own title on a timer — a terminal
// running a task with a spinner or a percentage, a browser tab with a live
// clock — and each rewrite restarted seven overlapping animations totalling
// over half a second. At a title churning twice a second on a 240 Hz screen
// the bar never stopped animating, and repainting it that hard measured at
// roughly 18% of a CPU core with nothing else happening. Keying the stamp to
// window identity restores what the effect was described as doing.
function commitTitle(): void {
const focusMoved = root.toplevel !== root.landedToplevel;
if (root.displayedTitle !== root.incomingTitle)
root.displayedTitle = root.incomingTitle;
else if (!focusMoved)
return;
if (focusMoved) {
root.landedToplevel = root.toplevel;
titleLanding.restart();
}
}
// Browser tabs, terminals and editors can update titles several times in a
// frame. Keep the old caption steady until the stream settles instead of
// restarting an animation for every intermediate value.
Timer {
id: titleSettle
interval: 120
repeat: false
onTriggered: root.commitTitle()
}
onIncomingTitleChanged: titleSettle.restart()
// Focus can move to a window whose caption happens to match the outgoing
// one, which changes no title and so would otherwise never land.
onToplevelChanged: titleSettle.restart()
Component.onCompleted: {
displayedTitle = incomingTitle;
// Adopt the current window silently, so a config reload does not play a
// focus-change stamp for a focus that did not change.
landedToplevel = toplevel;
}
// A quick accent wipe under the title on focus change.
Rectangle {
id: wipe
anchors.bottom: parent.bottom
height: 2
width: 0
color: Theme.accent
opacity: 0
}
SequentialAnimation {
id: titleLanding
ParallelAnimation {
NumberAnimation {
target: wipe; property: "width"
from: 0; to: root.width
duration: Theme.durBase; easing.type: Easing.OutExpo
}
NumberAnimation {
target: wipe; property: "opacity"
from: 0; to: 1; duration: Theme.durFast
}
NumberAnimation {
target: label; property: "x"
from: -10; to: 0
duration: Theme.durBase; easing.type: Easing.OutCubic
}
NumberAnimation {
target: label; property: "opacity"
from: 0.35; to: 1; duration: Theme.durFast
}
NumberAnimation {
target: ghost; property: "x"
from: 9; to: -3
duration: Theme.durBase; easing.type: Easing.OutExpo
}
NumberAnimation {
target: ghost; property: "opacity"
from: 0.8; to: 0; duration: Theme.durBase
}
}
NumberAnimation {
target: wipe; property: "opacity"
to: 0; duration: Theme.durSlow
}
}
}
+112
View File
@@ -0,0 +1,112 @@
import QtQuick
import Quickshell
import Quickshell.Wayland
import "root:/config"
import "root:/components"
import "root:/services"
// One bar per monitor, and one surface.
//
// Identity and workspaces on the left, the player, metrics and clock on the
// right, all inside a single continuous slab and separated by hairline rules
// rather than by gaps. Nothing floats and nothing has its own card. Status
// lives on the rail, which is likewise one slab — two surfaces, not a dozen
// islands.
PanelWindow {
id: root
required property var modelData
screen: modelData
WlrLayershell.layer: WlrLayer.Top
WlrLayershell.namespace: "takemi-bar"
anchors {
top: true
left: true
right: true
}
// Flush with the screen edge and drawn on nothing: the hull behind it is
// painted by Frame, which is what joins the bar, the rail and the border
// into one surface. Exclusive zones are Frame's job too, so every piece
// of the hull is placed from the screen edge rather than from whichever
// piece the compositor happened to arrange first.
exclusionMode: ExclusionMode.Ignore
implicitHeight: Theme.barHeight
color: "transparent"
readonly property real edgeInset: Theme.padM
// ------------------------------------------------------------------ left
Row {
id: leftGroup
anchors.left: parent.left
anchors.leftMargin: root.edgeInset
anchors.verticalCenter: parent.verticalCenter
spacing: 0
Sigil {
id: sigil
screenRef: root.modelData
}
Divider {}
Workspaces {
id: spaces
anchors.verticalCenter: parent.verticalCenter
screenRef: root.modelData
}
Divider {}
ActiveWindow {
anchors.verticalCenter: parent.verticalCenter
screenRef: root.modelData
maxWidth: Math.max(120, root.width
- leftGroup.x - rightGroup.width - sigil.implicitWidth
- spaces.implicitWidth - root.edgeInset * 2 - Theme.padXL)
}
}
// ----------------------------------------------------------------- right
Row {
id: rightGroup
anchors.right: parent.right
anchors.rightMargin: root.edgeInset
anchors.verticalCenter: parent.verticalCenter
spacing: 0
MediaPill {
screenRef: root.modelData
}
Divider {}
Resources {
screenRef: root.modelData
}
Divider {}
Clock {
screenRef: root.modelData
}
}
// A hairline rule between modules. This is what divides the bar — gaps
// between cards are what made it look scattered.
component Divider: Item {
anchors.verticalCenter: parent.verticalCenter
implicitWidth: Theme.padM * 2
implicitHeight: Theme.barHeight
Rectangle {
anchors.centerIn: parent
width: 1
height: 16
color: Theme.alpha(Theme.outline, 0.6)
}
}
}
+174
View File
@@ -0,0 +1,174 @@
import QtQuick
import "root:/config"
import "root:/components"
// Shared chrome for every bar module.
//
// The bar is one continuous slab, so by default a module draws no panel of its
// own — it is content inside the bar's surface, and marks hover with an accent
// underline rather than by lighting up a card. That is what stops the bar from
// reading as a row of floating islands.
//
// `standalone: true` brings back the individual leaning slab, for the few
// places that genuinely are separate surfaces.
Item {
id: root
property color accent: Theme.accent
property bool active: false
property bool slash: false
property real lean: Theme.skew
property bool leanLeft: false
property real padding: Theme.padM
property bool interactive: true
property bool halftone: false
property bool standalone: false
property real chop: Theme.cut
property var chopCorners: [1, 3]
readonly property bool hovered: hover.hovered
readonly property real leanInset: standalone ? panelLoader.item?.leanInset ?? 0 : 0
// The hover rule's own strip, reserved at the bottom of the module.
//
// The rule used to be drawn into the same box as the content, so anything
// taller than about 32px — the media pill's art, the metric meters — had a
// accent line struck through its bottom edge on hover. Reserving the band
// and centring content in what is left is what keeps the rule under the
// module instead of across it.
// The band reserved the full rule plus its inset — 7px off a 40px module,
// all of it taken from the bottom — so every module's content was centred
// 3.5px above the slab's own centre and the whole bar read as top-heavy.
// The rule is pushed closer to the edge now and the band keeps only enough
// clearance to stop the tallest content (the media pill's 24px art) from
// touching it, so content sits within a pixel of the slab's centre line.
readonly property real ruleHeight: 2
readonly property real ruleInset: 3
readonly property real ruleBand: root.standalone ? 0 : 2
// Modules with a notion of progress (the player's track position) draw it in
// the same band, so it can never be struck through the content. A module
// cannot do this for itself: anything it declares becomes a child of the
// content holder, whose bottom edge is above the band, and the rule lands
// across the text instead of under it. Negative means "no progress".
property real progress: -1
// Total horizontal inset a module must add on top of its content width.
readonly property real contentPad: standalone
? (panelLoader.item?.contentPad ?? root.padding)
: root.padding
default property alias content: holder.data
signal clicked()
signal rightClicked()
signal middleClicked()
signal scrolled(real delta)
implicitHeight: Theme.barHeight
Loader {
id: panelLoader
anchors.fill: parent
active: root.standalone
sourceComponent: Panel {
lean: root.lean
leanLeft: root.leanLeft
chop: root.chop
chopCorners: root.chopCorners
padding: root.padding
halftone: root.halftone
halftoneColor: root.accent
fill: root.active ? Theme.surfaceAlt : Theme.surface
fillOpacity: 1.0
border: root.hovered || root.active
? root.accent
: Theme.alpha(Theme.outline, 0.9)
borderWidth: root.hovered || root.active ? Theme.stroke : 1
slash: root.slash || root.active
slashColor: root.accent
}
}
Item {
id: holder
anchors.fill: parent
anchors.leftMargin: root.contentPad
anchors.rightMargin: root.contentPad
anchors.bottomMargin: root.ruleBand
}
// Hover mark for in-bar modules: an accent rule under the content, wiping
// out from the leading edge. Sheared to match the one angle everything uses.
Item {
visible: !root.standalone && root.interactive
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.bottomMargin: root.ruleInset
height: root.ruleHeight
clip: true
// Track progress, shown until the pointer arrives and the hover rule
// takes the band over.
//
// Deliberately untweened. MPRIS position is polled once a second, and a
// 480 ms ease on a 1 Hz input meant this rule was mid-animation roughly
// half of every second — about 115 animated frames per second on a
// 240 Hz screen, each one rebuilding this Skew's Shape geometry through
// the curve renderer and re-rendering the pill's layer textures with it.
// Stepping once per second costs 1 frame instead of 115, matches the rate
// the underlying data actually arrives at, and makes a seek land exactly
// where it was dropped instead of gliding there.
Skew {
height: parent.height
width: parent.width * Math.max(0, Math.min(1, root.progress))
color: Theme.accent
visible: root.progress >= 0 && !root.hovered && !root.active
}
Skew {
height: parent.height
width: root.hovered || root.active ? parent.width : 0
color: root.accent
Behavior on width {
NumberAnimation { duration: Theme.durBase; easing.type: Easing.OutExpo }
}
}
}
HoverHandler {
id: hover
enabled: root.interactive
cursorShape: Qt.PointingHandCursor
}
TapHandler {
enabled: root.interactive
acceptedButtons: Qt.LeftButton
onTapped: root.clicked()
}
TapHandler {
enabled: root.interactive
acceptedButtons: Qt.RightButton
onTapped: root.rightClicked()
}
TapHandler {
enabled: root.interactive
acceptedButtons: Qt.MiddleButton
onTapped: root.middleClicked()
}
WheelHandler {
enabled: root.interactive
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
onWheel: event => root.scrolled(event.angleDelta.y)
}
}
+124
View File
@@ -0,0 +1,124 @@
import QtQuick
import Quickshell
import "root:/config"
import "root:/components"
import "root:/popouts"
import "root:/services"
// Centre-stage clock. Hovering drops the calendar; clicking drops the
// dashboard, which carries the calendar too.
BarPill {
id: root
required property var screenRef
accent: Theme.accent
slash: true
padding: Theme.padS
implicitWidth: layout.implicitWidth + contentPad * 2
SystemClock {
id: clock
precision: SystemClock.Minutes
}
onClicked: Actions.toggleDashboard()
Row {
id: layout
anchors.centerIn: parent
spacing: Theme.padM
Column {
anchors.verticalCenter: parent.verticalCenter
spacing: -1
Text {
text: Qt.formatDateTime(clock.date, "ddd").toUpperCase()
color: Theme.accent
font.family: Theme.fontMono
font.pixelSize: Theme.fsMicro
font.weight: Font.Bold
font.letterSpacing: 3
renderType: Text.NativeRendering
}
Text {
text: Qt.formatDateTime(clock.date, "dd MMM").toUpperCase()
color: Theme.subtext
font.family: Theme.fontMono
font.pixelSize: Theme.fsMicro
font.letterSpacing: 1
renderType: Text.NativeRendering
}
}
// A hairline between the date and the time.
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 1
height: 18
color: Theme.alpha(Theme.outline, 0.6)
}
// The display face's digits are not all the same width, so a clock sized to
// its own text changed width as the minutes rolled over — 11:11 is
// several px narrower than 08:48 — and shoved the rest of the bar
// around. Reserve the widest possible reading and centre inside it.
TextMetrics {
id: timeMetrics
text: "88:88"
font.family: Theme.fontDisplay
font.pixelSize: Theme.fsTitle
font.weight: Theme.weightDisplay
font.letterSpacing: 0.5
}
// Archivo Black reserves descender room the time never uses — "12:04"
// is all cap height — so centring the line box left the digits sitting
// above everything beside them. Centre the ink instead.
// `tightBoundingRect` is measured from the baseline, so the ink's top
// inside the line box is `ascent + ink.y`.
readonly property real timeInkOffset: {
const ink = timeMetrics.tightBoundingRect;
if (ink.height <= 0) return 0;
const inkCentre = timeFont.ascent + ink.y + ink.height / 2;
return Math.round(timeMetrics.boundingRect.height / 2 - inkCentre);
}
FontMetrics {
id: timeFont
font: timeMetrics.font
}
SplitText {
id: timeText
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: layout.timeInkOffset
width: timeMetrics.advanceWidth
text: Qt.formatDateTime(clock.date, "HH:mm")
pixelSize: Theme.fsTitle
horizontalAlignment: Text.AlignHCenter
}
}
// Glitch the time on the hour, because it should feel like something happened.
property int lastHour: -1
Connections {
target: clock
function onDateChanged() {
const h = clock.date.getHours();
if (root.lastHour !== -1 && h !== root.lastHour) timeText.glitch();
root.lastHour = h;
}
}
CalendarPopout {
id: calendar
anchorItem: root
// Not while the dashboard is down: it already shows the month.
triggerHovered: root.hovered && !Actions.dashboardOpen
clockDate: clock.date
}
}
+124
View File
@@ -0,0 +1,124 @@
import QtQuick
import QtQuick.Shapes
import Quickshell
import Quickshell.Wayland
import "root:/config"
// The hull: one surface for the bar, the rail and the thin band down the left
// and along the bottom, with the desktop as a rounded opening cut out of it.
// The idea is caelestia's screen border; the reading is the wallpaper's — you
// are inside the ship, and the windows sit in the porthole.
//
// The hull lives on the top layer, over every window, and its opening is
// transparent with no input region, so it covers nothing inside the opening
// and catches no clicks. Being above the windows is the point: a workspace
// switch slides windows *behind* the frame, the way things pass behind the
// rim of a porthole, instead of drawing over the bar. A fullscreen window
// still covers it, since Hyprland draws fullscreen above the top layer.
// hypr/lua/rules/layers.lua orders it under the bar and the rail.
//
// The bar and the rail draw no surface of their own and ignore exclusive
// zones, so every piece is placed from the screen edge. The four zones are
// reserved here instead, by windows that draw nothing — that way the usable
// area is exactly the opening, whichever surface the compositor arranges
// first.
Scope {
id: root
required property var modelData
PanelWindow {
id: hull
screen: root.modelData
WlrLayershell.layer: WlrLayer.Top
WlrLayershell.namespace: "takemi-frame"
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
exclusionMode: ExclusionMode.Ignore
anchors {
top: true
bottom: true
left: true
right: true
}
color: "transparent"
mask: Region {}
// The opening, in window coordinates.
readonly property real x0: Theme.frame
readonly property real y0: Theme.barHeight
readonly property real x1: width - Theme.railWidth
readonly property real y1: height - Theme.frame
readonly property real r: Theme.frameRadius
readonly property string opening: {
const x0 = hull.x0, y0 = hull.y0, x1 = hull.x1, y1 = hull.y1, r = hull.r;
return `M ${x0 + r} ${y0} H ${x1 - r} A ${r} ${r} 0 0 1 ${x1} ${y0 + r}`
+ ` V ${y1 - r} A ${r} ${r} 0 0 1 ${x1 - r} ${y1}`
+ ` H ${x0 + r} A ${r} ${r} 0 0 1 ${x0} ${y1 - r}`
+ ` V ${y0 + r} A ${r} ${r} 0 0 1 ${x0 + r} ${y0} Z`;
}
Shape {
anchors.fill: parent
preferredRendererType: Shape.CurveRenderer
// The hull itself: the whole screen, minus the opening.
ShapePath {
fillColor: Theme.hull
fillRule: ShapePath.OddEvenFill
strokeColor: "transparent"
PathSvg {
path: `M 0 0 H ${hull.width} V ${hull.height} H 0 Z ` + hull.opening
}
}
// The rim — a hairline round the opening, the lit edge of the
// porthole in the wallpaper.
ShapePath {
fillColor: "transparent"
strokeColor: Theme.rim
strokeWidth: 1
PathSvg {
path: hull.opening
}
}
}
}
ExclusionZone {
anchors.top: true
exclusiveZone: Theme.barHeight
}
ExclusionZone {
anchors.right: true
exclusiveZone: Theme.railWidth
}
ExclusionZone {
anchors.bottom: true
exclusiveZone: Theme.frame
}
ExclusionZone {
anchors.left: true
exclusiveZone: Theme.frame
}
component ExclusionZone: PanelWindow {
screen: root.modelData
WlrLayershell.layer: WlrLayer.Bottom
WlrLayershell.namespace: "takemi-frame-zone"
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
color: "transparent"
mask: Region {}
implicitWidth: 1
implicitHeight: 1
}
}
Loaded 100 of 196 files, more files were not shown because too many files have changed in this diff. Show more