Files
command-center/agent/managed-skills/noctalia-bar-layout-edit/SKILL.md
T

7.9 KiB

name, description
name description
noctalia-bar-layout-edit Edit/verify Noctalia v5 bar layout and clock widgets (multi-timezone instances, capsule groups); GUI-repo propagation chain, settings.toml symlink audit, and stash-clobber recovery on Frank's dotfiles workstations

Bar widget layout on Frank's noctalia v5 workstations is GUI-override state, not config.toml.

Where

  • Layout file: ~/.local/state/noctalia/settings.toml -> [bar.<name>] keys start / center / end (arrays of widget ids; [widget.<id>] subtables hold per-widget settings).
  • On Frank's boxes this file MUST be a symlink into the command-center repo (e.g. ~/.omp/dotfiles/noctalia/.local/state/noctalia/settings.toml) - edit the repo file, not the link path (atomic-rename writes through the link path would replace the symlink).
  • Layer order: built-in defaults < ~/.config/noctalia/config.toml (+[include]) < settings.toml (wins). Noctalia writes through symlinks and keeps them.
  • Defaults when a key is absent: start = ["launcher","wallpaper","workspaces"], center = ["clock"], end = ["notifications","clipboard","network","bluetooth","tray","battery","session","control-center","clock"].

Clobber window and recovery (2026-10-18 incident law)

  • GUI-built layout is working-tree-only until the hourly live-sync heartbeat commits it. A merge/sync session that "clears the dirty tree" can git stash those edits, and later stash operations can destroy the entries - customizations then vanish from the live bar with the repo copy stale.
  • Never blame the GUI or noctalia first: diff live file vs HEAD, check heartbeat commits (git log -- dotfiles/noctalia/.local/state/noctalia/settings.toml), then the stash chain.
  • Destroyed stashes are recoverable: git fsck --lost-found lists dangling commits; git show <dangling>:dotfiles/noctalia/.local/state/noctalia/settings.toml dumps each candidate; pick by [bar.default] richness (capsule groups, extra clock instances). Validate a candidate in a sandboxed XDG_*_HOME before touching the live file.
  • After recovery, commit the file immediately so the window closes.

Widgets (v5 census, verified 2026-10-17)

  • Full roster in settings.toml widget_order plus docs: activity, backlinks, battery, bluetooth, brief, bright, cat, cheatsheet, chart, clipby, clipboard, control-center, cpu, controls, cava (now playing), disk, gpu, help, keyboard, language, launcher, mediaplayer, memory, mic, network, night-light, noctalia-palette, notification-history, notifications, niri-colors, power-section, power-profile, screenrecord, scratchpad, screenshot, session, shell (custom shell command), slowouts, slowouts-visible (per-output bar assignment), speedtest, swayosd-*, taskbar, tray, voiceline, volume, wallpaper, weather, workspaces.
  • clock widget: format (chrono strftime), schema (12/24), multi_line, show_seconds, popup, and timezone = IANA name. Multiple clock widget instances are supported: ids like clock, clock-utc, clock-sea each get a [widget.<id>] subtable -> one capsule per instance. This is how multi-timezone (Seattle work + UTC dev) lands: add clock + clock-utc to end (or center), set timezone = "Etc/UTC" on the utc one.
  • cat widget: shows random ASCII art, rows setting (4-10).
  • shell widget: command, run interval (seconds), icon, terminal; output lands in its capsule popup - can wrap any CLI (hyprstatus-style).
  • taskbar widget: shows window buttons only on outputs where niri-bridged is enabled (noctalia-launcher enables it). Buttons render icon-only (app icon from icon theme; fallback = first letter of title); the window title string is NOT shown in the bar. Hover tooltip carries title + app_name. Per-widget settings: show_icons, show_actions, disable_middle_click, preferred_app_names (map app_name -> icon for missing icons).
  • Widget ids usable in [bar.*].start|center|end are restricted to "bar widgets" (is_bar_widget allowlist in bar.rs); non-bar widget ids are silently dropped at load. group:<id> entries render capsule groups.
  • Capsule groups: [bar.<name>.capsule_groups] id = ["widget",...].
  • Niri layout law for bars: window width must stay odd (strut = (W-1)/2); bars reserve via layer-shell, niri's reserved area = layer surface size, so bar height changes need no niri edit.

Bar styles (GUI-visible, settings.toml keys)

  • [bar.*]: thickness, font_family, font_weight, font_size, opacity, border_width, border_offset, rounded, paddings, icon_ratio, follow_layout, output, exclusive, layer (bottom = below window layer; overlay = above windows), position (top/bottom).
  • Per-monitor bars: add a [bar.<name>] with output = "<monitor>" and follow_layout = true (niri) -> per-workspace strut follows tall windows on that monitor. This is the mechanism for "bar shrinks when a window is tall" (the bar is a layer-shell strut, NOT a niri layer rule).
  • The GUI "Bar" page is the reference list; anything absent from settings.toml is a built-in default.

Slowouts (window-height-aware bars/panels)

  • slowouts = hidden engine (not a bar capsule): [slowouts] keys enabled, duration, easing, trigger_offset; per-monitor [slowouts.<monitor-name>] overrides (e.g. DP-1) with the same keys plus edge.
  • position = "top" + exclusive = false + slowouts on the monitor -> panel hides when a window touches its edge, re-reveals otherwise. slowouts-visible = toggle capsule.
  • Slowouts needs niri workspace-autoresize ON (niri workspace-autoresize = "per-workspace" in cfg/layout.kdl output ... { workspace-autoresize per-workspace }): noctalia computes hide from workspace height < panel bottom edge; uniform-strut layouts never trigger it.
  • Per-output bars: slowouts config keys are monitor names, so one [bar.<x>] with output + exclusive=false + layer=overlay per slowout monitor is the supported shape.

Layout modes and window title decorations

  • [layout]: mode (tiling|floating|monocle), pseudotiled, focus_bar (window-title bar at top, center, layer-shell; style via [ui.controls...]/bar-ish settings), window_follows_cursor, gaps, border_width, rounded, blur, opacity, borderless_csd, show_native_decoration.
  • focus_bar is the noctalia analog of niri's drawn window title, but ONE global bar (no per-window arrows). True per-window title tags = niri draw-with-border on window rules; noctalia adds no per-window decoration (it's a bar app + portal, composites nothing).
  • Floating mode: right-click desktop / SUPER+Shift+F / toggle widget; widgets floating-directories, floating-files, pins (sticky notes), canvas, sketchpad, post-its. Float windows snap to screen quarters (SUPER+Ctrl+Alt+Arrows) and tile into current layout (SUPER+Shift+T). [layout.floating.files] = icon/view/sort/preview size.
  • [bar.*].window_display = disabled|icons|text|combined - per-window chips in the bar strip (niri only; this is what niri-taskbar could not do). Values: disabled|icons|centered|inside. Requires compositor per-window workspace assignment (niri: yes; LabWC: no -> stays flat).
  • The workspaces widget itself has no window display; styles are regular/minimal/focus_hint only.

Law

  • Full widget/setting tables: https://docs.noctalia.dev/noctalia/bar/ and .../bar/widgets// . Commit bar changes atomically to the command-center repo (checkout is the deployment).
  • Ask before applying. Frank asks "how do I X" questions about the live bar; answer with the exact snippet + file, apply ONLY on explicit go. A settings.toml edit is a live-deployment change, not a demo.
  • Git M on settings.toml usually carries noctalia's own runtime churn (wallpaper path, nightlight, panel positions) alongside your edit - mention it when committing.
  • Right after building layout in the GUI, commit settings.toml (or trigger scripts/live-sync.sh) before any branch/merge work: the uncommitted-GUI-edit window is exactly where the 2026-10-18 clobber happened.