Configure scootnotify
How the cards look, where they land, and how long they stay. Every size
here is in logical pixels, drawn at the output’s scale: width_px = 360
is 360 pixels on a 1x output and 720 on a 2x one. The file
is $XDG_CONFIG_HOME/scoot/notify.; every key is optional, and a
missing file is the built-in defaults. A typo’d key refuses the whole
file at start (the daemon runs on defaults and says why); daemon --check refuses loudly instead:
scootnotify daemon --check --config ./notify.tomlwidth_px = 360max_visible = 5margin_px = 12anchor = "top-right"# output = "HDMI-1"body_lines = 5border_px = 2# radius = 8pause_when_idle_secs = 60timeout_low_ms = 5000timeout_normal_ms = 10000timeout_max_ms = 3600000max_live = 128rate_per_sec = 20rate_burst = 40history_max = 64dnd_allows_critical = truelook = truePlacement
Section titled “Placement”To move the stack, set where it anchors and how far it stands off the edges:
width_px: the card width in logical pixels. Type: integer, 10 to 1024. Default:360. Example:width_px = 400. Reload:msg reload.margin_px: the stack’s distance from the screen edges in logical pixels. Type: integer, 0 to 128. Default:12. Example:margin_px = 24. Reload:msg reload.anchor: the screen corner. Type:top-left,top-right,bottom-leftorbottom-right. Default:top-right. Example:anchor = "bottom-right". Reload:msg reload.output: the output the stack shows on. Type: string (1 to 256 printable bytes) or absent. Default: absent, the compositor’s choice. Example:output = "HDMI-1". Reload:msg reload. On hotplug the stack moves if its output left; with zero outputs the cards wait with their clocks stopped.
Symptom: the stack sits on the wrong monitor. Set
outputto the name scoot reports for the monitor. A name with no output left falls back to the compositor’s choice and warns once.
Timeouts and urgency
Section titled “Timeouts and urgency”To change how long cards stay, set the per-urgency defaults and the clamp. The clock starts on the frame that first presents the card, not at arrival, so a card held by the lock or by missing outputs expires normally once shown. Cards already on screen keep expiring through a manual lock.
timeout_low_ms: low-urgency expiry forexpire_timeout = -1, in milliseconds. Type: integer, at mosttimeout_max_ms. Default:5000. Example:timeout_low_ms = 3000. Reload:msg reload.timeout_normal_ms: normal-urgency expiry forexpire_timeout = -1, in milliseconds. Type: integer, at mosttimeout_max_ms. Default:10000. Example:timeout_normal_ms = 8000. Reload:msg reload.timeout_max_ms: what a positiveexpire_timeoutis honored up to, in milliseconds. Type: integer, 1 to 86400000. Default:3600000(one hour). Example:timeout_max_ms = 600000. Reload:msg reload.pause_when_idle_secs: seconds without input after which every expiry clock pauses (0 disables). Type: integer, 0 to 3600. Default:60. Example:pause_when_idle_secs = 120. Reload:msg reload. Only real input counts (a playing video does not), and an agent driving the session keeps cards flowing.
Symptom: a card never expires. Critical cards never expire unless the client asked with a positive timeout. Hover and input idling pause the clock; the card goes when the pointer leaves and input returns.
Limits
Section titled “Limits”max_visible: cards on screen at once; past it a “+N more” pill covers the rest. Type: integer, 1 to 16. Default:5. Example:max_visible = 3. Reload:msg reload.body_lines: body lines per card before the ellipsis. Type: integer, 1 to 16. Default:5. Example:body_lines = 8. Reload:msg reload.border_px: the urgency border width in logical pixels (0 draws none). Type: integer, 0 to 8. Default:2. Example:border_px = 3. Reload:msg reload. Low drawsborder, normalfocus, criticalurgent.radius: the card corner radius in logical pixels. Type: integer, 0 to 64. Default: unset, which shows the look’sshape.(radius 8when no look is on). Example:radius = 12. Reload:msg reload. A value set here wins over the look.max_live: live notifications at most; past it the oldest non-critical one closes with reason 4. Type: integer, 1 to 1024. Default:128. Example:max_live = 64. Reload:msg reload.rate_per_sec,rate_burst: the per-sender token bucket, notifications per second and burst. Type: integers, 1 to 1000. Defaults:20,40. Example:rate_per_sec = 10. Reload:msg reload. Past the bucket aNotifystill gets its id, but the card goes unshown.history_max: history ring entries kept, oldest first. Type: integer, 0 to 256 (0 keeps none). Default:64. Example:history_max = 100. Reload:msg reload(truncates to the new cap, newest kept).dnd_allows_critical: critical cards show while DND holds the rest. Type: boolean. Default:true. Example:dnd_allows_critical = false. Reload:msg reload.look: whether the look layer themes the cards (see The look layer). Type: boolean. Default:true. Example:look = false. Reload:msg reload.
History
Section titled “History”Every closed card lands in the in-memory ring (newest last, at most
history_max), and so does every card that skips the screen (held by
DND, hidden by a rule, or past the rate bucket). transient
notifications never enter. Entries keep app, summary, body, urgency,
category, time and an icon name or path — never pixels. The ring
survives a reload, never a restart. Read it with
scootnotify msg history, bring the newest back with
scootnotify msg restore (a new card with a new id, even under DND).
Symptom: the unread count never drops. Turning DND off shows nothing retroactively. Each
restoremarks one entry seen; the count is live cards plus unseen history.
Do not disturb
Section titled “Do not disturb”While DND is on, new cards skip the screen and go straight to history,
except critical ones (while dnd_allows_critical holds) and cards a
rule lets through (dnd_bypass). A held card’s expiry never starts, so
it stays unread until seen. The mode survives a reload, never a
restart.
scootnotify msg dnd on # hold everything but criticalsscootnotify msg dnd toggle # the bar's click runs thisscootnotify msg dnd status # or with no argument: on or offThe bar feed
Section titled “The bar feed”On every change of DND or of the unread count (an arrival, a
dismissal, an expiry, a restore, a DND toggle), and only then, the
daemon sends set notifications <payload> to the bar’s control
socket. The bar needs a notifications push module placed (see
Button, push and exec modules);
without one the daemon logs guidance once per run and leaves the bar
alone. A bar started later shows its placeholder until the owed send
lands when its socket appears.
The payload is {text, class, tooltip, icon?}: DND n (or DND)
with class muted while DND is on, the count n with class urgent
(any counted card is critical) or normal otherwise, and {text: ""} when idle. The count is live cards plus unseen history.
bar_icon_unread: the icon sent beside the unread count (a per-update icon, so it overrides the module’s static icon while set). Type: empty or exactly one character. Default: `` (no icon; the key stays out of the payload). Example:bar_icon_unread = "●". Reload:msg reload.bar_icon_dnd: the icon sent while DND is on. Type: empty or exactly one character. Default: `` (no icon; the key stays out of the payload). Example:bar_icon_dnd = "☾". Reload:msg reload.
Symptom: the bar never shows a count. The module is not placed, or the bar is not running: the daemon says which on its stderr, once for the module, once per failed change for the bar. A running bar that still shows its placeholder is waiting for the next change, unless a send is owed, which lands when the bar’s socket appears.
Symptom: the count survives dismissing everything. Turning DND off shows nothing retroactively, but it reshapes the bar at once (
DND nbecomesn). Eachrestoremarks one entry seen; the count is live cards plus unseen history.
[[rule]] tables match cards and steer them, in file order. A rule
with no match key matches everything. Every matching rule applies;
for the scalar actions the later rule wins; every matching exec
runs, at most 4 children at once.
[[rule]]app_name = "Mail"category = "email."set_urgency = "normal"timeout_ms = 8000
[[rule]]app_name = "Volume"hide = true
[[rule]]app_name = "Thunderbird"exec = ["play", "/usr/share/sounds/new-mail.wav"]Match keys (all optional, all must hold):
app_name: exact app name. Type: string, 1 to 128 bytes. Example:app_name = "Mail". Reload:msg reload.desktop_entry: exact desktop entry. Type: string, 1 to 128 bytes. Example:desktop_entry = "firefox". Reload:msg reload.category: exact category, or aclass.prefix when it ends with a dot. Type: string, 1 to 128 bytes. Example:category = "device.matches" device.. Reload:added msg reload.urgency:low,normalorcritical. Example:urgency = "critical". Reload:msg reload.summary_contains: a plain substring of the summary, never a regex. Type: string, 1 to 128 bytes. Example:summary_contains = "Download complete". Reload:msg reload.
Action keys:
set_urgency: the card’s urgency, over the hint’s (not namedurgency, since that is the match key). Type:low,normalorcritical. Example:set_urgency = "low". Reload:msg reload.timeout_ms: expiry over the resolved one. Type: integer, 0 to 86400000 (0 never expires). Example:timeout_ms = 30000. Reload:msg reload.hide: straight to history, never on screen. Type: boolean, defaultfalse. Example:hide = true. Reload:msg reload.history:falsekeeps the card out of history, shown or not. Type: boolean, defaulttrue. Example:history = false. Reload:msg reload.output: pins this card’s output, over the file’s. Type: string (1 to 256 printable bytes). Example:output = "HDMI-1". Reload:msg reload.dnd_bypass: shows through DND, like a critical card. Type: boolean, defaultfalse. Example:dnd_bypass = true. Reload:msg reload.exec: an argv run on arrival, never through a shell, with the card’s fields inSCOOTNOTIFY_APP,SCOOTNOTIFY_SUMMARY,SCOOTNOTIFY_BODY,SCOOTNOTIFY_URGENCY,SCOOTNOTIFY_CATEGORY,SCOOTNOTIFY_DESKTOP_ENTRYandSCOOTNOTIFY_ID. At most 16 words, each 1 to 4096 bytes. A ruleexecnever fires for areplaces_idupdate. At most 4 run at once; past that the spawn refuses with a log line. Example:exec = ["play", "ding.. Reload:wav"] msg reload. A sound hook is the expected use: the daemon plays no sound itself.
At most 32 [[rule]] tables; a 33rd refuses the file, like any
out-of-range value.
Symptom: a rule never fires. Match keys are exact (except
categorypast a trailing dot, andsummary_contains). Checkmsg list --jsonfor the card’s realapp_nameandcategory: senders often name themselves differently than the app you see.
Symptom: the sound never plays. Check the daemon log for
cannot run: the program must exist and run (a shell line needssh -c, quoted by you). Past 4 running children the spawn refuses until one exits.
Every key above applies live: edit the file, then scootnotify msg reload. A refused file keeps the running config and cards, and says
why. msg reload --look re-reads only the look layer, never touching
the file, so a look swap works under a half-edited file.
The look layer
Section titled “The look layer”Colors, font, opacity and corners come from the active look, not from
this file. Depth, motion, card styles and title weight
come from the look too, unless this file sets them: see
Effects over any look. The daemon reads
<state>/look/notify. at start (a dangling or missing link means
no layer, plus a warning), with the user’s file winning per key over
the look over the built-ins. The thirteen color roles map to the
cards.
To keep the built-in colors whatever look is active, set look = false in the file. The layer is then not read at start, a plain msg reload applies the built-ins, and msg reload --look answers ok and
changes nothing, so a look swap does not report a failure. Set it
back to true and msg reload to re-read the layer. The switch is
all or nothing: the layer carries colors, font size, opacity and
the icon theme, the file carries geometry, timeouts and rules, and
to change one value of the look, override it in the look’s
[apps. table. With the Nix profile, look = false is
what theme. writes.
| Element | Role |
|---|---|
| Card fill | surface (default background), at opacity. |
| Summary, body | foreground |
| App name, age, “+N more” | dim |
| Border, normal | focus (default accent) |
| Border, low | border (default dim) |
| Border, critical | urgent |
| Progress bar | selection (default accent) |
| Action button outline / hovered fill | focus / hover |
| Link text (underlined, never clickable in v1) | accent |
| Font | fonts., found by name (see Fonts), at fonts. |
| Corners | the file’s radius if set, else shape.; or card. |
| Shadow, fill | effects., effects. (see Depth) |
| Motion | effects., effects., effects., and card., card. (see Motion) |
| Accent marker, outline, per-app color | card., card., card. (see Card styles) |
| Summary weight and spacing | title., title. (see Title weight and spacing) |
The corner radius is the look’s shape., unless the file sets
radius (see Limits), which wins. A look that sets no
shape. has radius 0, so its cards are square, as its bar and
launcher are.
Effects over any look
Section titled “Effects over any look”Depth, motion, card styles and title weight can also be set in
notify., in [effects], [card] and [title] tables. They win per
key over whatever look is active, so one setting turns a shadow or a spring
on for every look without copying a look. A key the file does not set stays
the look’s, and a key the look does not set stays the built-in default.
[effects]shadow = "soft"motion = "spring"motion-duration-ms = 240
[card]mark = "stripe"
[title]weight = 600| Table and key | Value | Same as the look’s |
|---|---|---|
[effects] shadow |
none, soft, deep or glow |
effects. (Depth) |
[effects] fill |
solid or gradient |
effects. (Depth) |
[effects] motion |
none, ease-out or spring |
effects. (Motion) |
[effects] motion-duration-ms |
whole number, 0 to 1000 | effects. |
[effects] motion-bounce |
number, 0 to 1 | effects. |
[card] enter, exit |
slide, fade, scale or drop |
card., card. |
[card] mark |
none, stripe, dot or badge |
card. (Card styles) |
[card] shape |
rounded or pill |
card. |
[card] app-accent |
true or false |
card. |
[title] weight |
whole number, 100 to 900 | title. (Title weight and spacing) |
[title] spacing |
whole number, -2 to 8 | title. |
All keys are optional and unset by default. They use the look’s own token
names (with hyphens), unlike the snake_case keys elsewhere in this file.
Only these keys are accepted in these tables: anything else, including
font-size or a color, refuses the file like any other typo, and so does
a top-level shadow or motion.
A value outside its list or range does not refuse the file. The daemon
logs notify. and the key keeps the
look’s value (or the built-in default when the look sets none). daemon --check prints the same line and still exits 0.
These keys apply with look = false too: they are yours, over the
built-ins. A plain msg reload re-reads them. msg reload --look and
scoot look apply do not re-read the file, but the values the daemon is
running with still win over the new look.
With the Nix profile, programs. writes these
tables for you (see Look and theme).
Symptom:
daemon --checksaysconfig refused: [effects] has no key. The key is not one of the five effect tokens.[effects]takesshadow,fill,motion,motion-duration-msandmotion-bounce;enter,exit,mark,shapeandapp-accentgo in[card], andweightandspacingin[title].
Symptom: a key is set here and the cards do not change. Run
scootnotify msg reload. If the daemon logsrefused ..., the value is outside its list or range. The tables live in the user’s file, not in: value ignored <state>/look/notify., which a look swap rewrites.toml
A look can give the cards a shadow and a gradient fill. Both are off by
default. Set them in look.:
[effects]shadow = "soft" # none | soft | deep | glowfill = "gradient" # solid | gradientTo override one for the notifier alone, put the key in the look’s
[apps. table (shadow = "glow"). The look renders them as
top-level shadow and fill keys in notify.. A value outside the
lists is refused by scoot look validate; a hand-edited
notify. with one is reported in the daemon log and ignored, and the
card keeps its default.
shadow |
What it draws | Room it needs |
|---|---|---|
none |
Nothing. The default; builds and paints nothing. | none |
soft |
A low shadow: 4 px down, 12 px blur, 36% black. | 8 px above, 12 px at the sides, 16 px below |
deep |
A heavy shadow: 10 px down, 28 px blur, 55% black. | 18 px above, 28 px at the sides, 38 px below |
glow |
A 16 px halo, centered, in the card’s border color (border, focus or urgent by urgency), at 51%. |
16 px on every side |
fill |
What it draws |
|---|---|
solid |
The surface color, flat. The default. |
gradient |
Light at the top. On a dark surface the top edge is lightened about 10% toward white and fades to surface at the bottom; on a light one the bottom is darkened about 8%. The card keeps the look’s opacity.. |
The shadow’s room comes out of margin_px: with the default 12 px
margin and soft, a top-right stack sits 12 px from the screen edge, as
it does without a shadow (a stack anchored to the bottom needs 16 px). A margin smaller than the room moves the cards that
much further in, since a surface cannot start off screen. The shadow
falls outside the cards only (a translucent card does not show its own
shadow through its body), is never clickable (the gaps and the room
around the stack click through), and is painted when the stack repaints,
not while it is idle.
soft and deep are black, so they show over light and mid-tone
backgrounds and nearly vanish over a near-black wallpaper; glow reads on
any background.
scootnotify and scootlaunch read these tokens (see the launcher’s
depth); scootbar ignores [effects],
and an [apps. shadow key refuses at validation.
Symptom: a look sets
shadowand the cards have none. The daemon logsshadow refused ... value ignoredorshadow is not a shadow stylefor a value outsidenone,soft,deepandglow. Check<state>/look/notify.for atoml shadowline, and that the config has nolook = false.
Symptom: the cards sit further from the screen edge than
margin_px. The shadow needs room that comes out of the margin. Withmargin_pxbelow the room (for a top-right stack, 12 px forsoftand 28 px fordeep), the cards sit that much further in. Raisemargin_px, or pick a lightershadow.
Card styles
Section titled “Card styles”A look can mark each card with an accent, give the cards a pill outline
and color each app’s cards differently. All three are off by default.
In every look, a progress bar or buttons on a card with an icon start
below the icon, so such a card (a volume OSD, say) is up to 24 px taller.
They are notifier keys, in the look’s [apps. table, quoted:
[apps.scootnotify]"card.mark" = "stripe" # none | stripe | dot | badge"card.shape" = "pill" # rounded | pill"card.app-accent" = true # true | falseThe look renders them into notify. as a [card] table with
mark, shape and app-accent, only when the look sets them. A value
outside its list is refused by scoot look validate; a hand-edited
notify. with one is reported in the daemon log and ignored, and the
key keeps its default.
card. |
What it draws |
|---|---|
none |
No marker. The default. |
stripe |
A 3 px bar down the card’s leading (left) edge, just inside the border, cut by the card’s rounded corners. Takes no room. |
dot |
An 8 px dot before the app name on the app · age line. The text moves 14 px right to make room; the card does not grow. |
badge |
The icon slot on a tinted tile. A card with no icon gets the slot too, showing the app’s initial, so every card is 48 px tall at least and its text starts 58 px in. |
The marker’s color is the look’s accent. With card. on it
is the app’s own color instead (below).
card. gives every app a color hashed from its name: the same
app is the same color on every run and every machine, and Firefox and
firefox share one. The hue is spread around the wheel at medium
saturation and lightness, then darkened (on a light-on-dark look) or
lightened (on a dark-on-light one) until the look’s foreground reaches
a 4.5:1 contrast ratio on it, WCAG AA for normal text. That is what makes
the letter on a badge tile readable; on a dark look it also means
accents are deep rather than bright. An app with no name has no accent and
uses the look’s accent. card. with card.
draws nothing.
card. |
What it draws |
|---|---|
rounded |
The corner radius from the config file, else the look’s. The default. |
pill |
Ends rounded to half the card’s height, to at most a 36 px radius: a one- or two-line card is a true pill, a tall one keeps round ends of that size. The content moves 8 px further in on each side so text, the progress bar and the buttons stay clear of the curve. The shadow follows the shape. |
A stripe on a pill shows only where the curved end is nearly
straight, a short sliver at its middle; dot and badge suit a pill
better.
A card that is moving carries its marker with it, and the marker, the tile and the pill are all inside the card’s shadow and clipped by its shape.
A card takes clicks only where it is drawn: a click in the cut corner of a pill, or of a rounded card, reaches the window behind it instead of invoking or dismissing the card. The card’s shadow takes none either.
Symptom: a look sets
card.and the cards have none. The daemon logsmark card.ormark refused ... value ignored card.for a value outsidemark is not a card mark none,stripe,dotandbadge. Check<state>/look/notify.for atoml [card]table, and that the config has nolook = false.
Symptom: every app’s marker is the same color.
card.is off, or the app’s name is empty (app-accent msg list --jsonshows theapp_namethe sender gave).
Symptom: the dot or the badge crowds the text. Both reserve room at layout, so a look swap needs a daemon that re-laid out the cards:
scoot look applydoes, and plainmsg reload --lookdoes too.
scootnotify has no fontconfig. It picks the card font in this order:
--font PATH.- The look’s
font-file: the filescoot look applyresolved the look’s UI family to (withfc-match, or the look’s own[fonts.entry), written intofiles] notify.. When the family is not installed it is the fallback file the apply note names. It is read as given: no directory is walked, so it works for a font installed in a directory only fontconfig was configured with, and for a font carried in the look. Atoml [apps.override drops the key, since it names a different family.scootnotify] font - The look’s
fontfamily (fonts.), found by file name inui $XDG_DATA_HOME/fonts(~/.),local/share/fonts ~/.,fonts $XDG_DATA_DIRS/fonts,~/.,nix-profile/share/fonts /etc/profiles/per-user/$USER/share/fonts,/usr/share/fonts,/usr/local/share/fontsand NixOS’s/run/current-system/sw/share/X11/fontsand..., up to six directories deep./share/fonts DroidSansM Nerd Font PropofindsDroidSansMNerdFontPropo-Regular.; case, spaces, hyphens and underscores do not matter, and onlyotf .andttf .files named exactly the family, the family plusotf Regular, or the family plusBoldcount. - The first well-known file (DejaVu Sans, then Noto Sans).
Steps 3 and 4 are the fallbacks for a notify. with no font-file
(one you wrote by hand, or a look applied by an older scoot), and for a
font-file that cannot be used; the log says which.
The bold face is the Bold file next to the regular one. For a family
found in the font directories (step 3) it is the Bold file anywhere
under the same font directory root as the regular file. For every other
file (--font, font-file, a well-known file) it is the Bold file in
the same folder: DejaVuSans. or NotoSans-Regular. take
DejaVuSans-Bold. or NotoSans-Bold..
It draws the summary and <b> runs, and wraps and ellipsizes them with
their own width. A family with no bold file draws the regular
face twice, 1 px apart. With no look font set, a
DejaVuSans-Bold. beside the well-known DejaVuSans. is used.
The files are read at start and when a msg reload --look changes the
family or the file, never when a card draws. A --font file keeps its font through a
look reload.
Symptom: the cards ignore the look’s font. The daemon logs
the look's font ... has no .and draws with the default font, sottf or . otf file in the font directories notify.has notoml font-file. Runscoot look applyagain (it resolves the family withfc-matchand writesfont-file), or install the family as a.orttf .file in one of the directories above, or pointotf --fontat the file. With no default font on the machine either, the daemon runs headless and the log names the family.
Title weight and spacing
Section titled “Title weight and spacing”The summary (the first, bold line of a card) can be set apart from the
body. title. and title. apply to the summary only; the
app · age line and the body keep their weight, and bold inside a body
keeps drawing bold.
[apps.scootnotify]"title.weight" = 800 # 100 to 900"title.spacing" = 1 # -2 to 8, pixels after each characterThe look renders them into notify. as a [title] table with
weight and spacing, only when the look sets them.
title. |
What it draws |
|---|---|
| 100 to 500 | The summary in the regular weight, like the body. |
| 501 to 700 | Bold. The default is 700. |
| 701 to 900 | Heavier: the bold face drawn once more, 1 px to the right, so the strokes are a pixel thicker again. |
A weight is three steps, not nine: scootnotify has no variable-font support. With a bold file beside the regular one (see Fonts) the bold steps draw that face; without one they draw the regular face again, 1 px to the right, once for bold and twice for heavier. Weights 100 to 500 never use the bold file.
title. adds that many logical pixels after every character of the
summary (negative tightens). The summary wraps and ellipsizes with the
spacing included, so a wider summary breaks earlier instead of running out
of the card.
Symptom:
title.makes no visible change between 600 and 700. They are the same step. The steps start at 501 and 701.weight
Motion
Section titled “Motion”By default cards appear and disappear at once and the stack jumps when a card closes. A look can make them move: a new card slides, fades, grows or drops in, a closed card fades out, and the cards beside it spring into the space it left. Motion is off by default.
[effects]motion = "spring" # none | ease-out | springmotion-duration-ms = 380 # 0 to 1000motion-bounce = 0.6 # 0 to 1, spring only
[apps.scootnotify]"card.enter" = "slide" # slide | fade | scale | drop"card.exit" = "fade" # slide | fade | scale | drop| Key | Value | Default | What it does |
|---|---|---|---|
motion |
none, ease-out or spring |
none |
The curve every move follows. none moves nothing and costs nothing. |
motion-duration-ms |
whole number, 0 to 1000 | 250 |
How long one move takes. 0 is the same as none. |
motion-bounce |
number, 0 to 1 | 1 |
How far a spring overshoots its target: 0 is the ease-out curve, 1 is the compositor’s own spring (about 9% past the target before it settles). Ignored by ease-out. |
card. |
slide, fade, scale or drop |
slide |
How a new card arrives. |
card. |
slide, fade, scale or drop |
fade |
How a closed or expired card leaves. |
Every style fades the card; the style adds a move. slide comes in from
the edge the stack is anchored to (40 px), scale grows from 88% of its
size, drop falls 24 px from above. A card leaving runs its style
backwards, always without overshoot, whatever motion is.
motion, motion-duration-ms and motion-bounce go in [effects] to
apply to every app that reads them, or in [apps. to
override them for the notifier alone. card. and card. are
notifier keys: they go in [apps., quoted. The look renders
them into notify. as motion, motion-duration-ms and
motion-bounce at the top level and enter and exit in a [card]
table, only when the look sets them. A value outside its list or range is
refused by scoot look validate; a hand-edited notify. with one is
reported in the daemon log and ignored, and that key keeps its default.
Cards move on the compositor’s frame callbacks, so the motion is as
smooth as the display and costs nothing while the stack sits still: no
timer runs and no frame is requested once the last card lands. A close
only changes what is drawn: the notification is closed, and its
NotificationClosed signal sent, at the moment of the close, not when the
card has faded out. A card that is leaving cannot be clicked. A card that
is moving is clicked where it is drawn, not where it is going.
While a card travels, the surface stays as tall as the motion needs (up to the distance a card moves, plus a little for a spring’s overshoot) and returns to the stack’s height when it lands. A spring settling toward the screen edge the stack is anchored to stops at the edge instead of overshooting off it.
The radial-burst look ships with a glow shadow, a spring and a scale entrance;
ginger-night, the default look, sets no motion. ginger-night also draws
the stripe marker; radial-burst the badge marker with per-app color, the
pill shape and a heavier, spaced summary.
There is no reduced-motion setting to follow: scoot has none yet, and the
compositor’s [animation] enabled is a separate opt-in for window
slides. To turn card motion off, set motion = "none" in the look or in
[apps., then scoot look apply (or scootnotify msg reload --look).
Symptom: a look sets
motionand the cards still jump. The daemon logsmotion refused ... value ignoredormotion is not a motionfor a value outsidenone,ease-outandspring. Check<state>/look/notify.for atoml motionline, thatmotion-duration-msis not0, and that the config has nolook = false.
Symptom: a card slides in with no overshoot.
motion-bounceonly shapes aspring. Withease-out, or a bounce of0, the card lands without passing its place.
Symptom: the stack stutters while cards move. Motion repaints at the compositor’s frame rate, so a compositor that serves few frames (a software-rendered session over a slow link) shows few. A shorter
motion-duration-msormotion = "ease-out"finishes sooner;motion = "none"moves nothing.
Symptom: the cards ignore a look swap.
scoot look applyreloads the running daemon live (plainmsg reloadre-reads the file too). If the look changed but the cards did not, the fragment refused: the daemon warns per key and keeps the last good look.
Symptom: the cards keep the built-in colors under a look. Check the file for
look = false, which the Nix optiontheme.writes. With it set the daemon logstargets. notifications. enable = false look = false in the config: the look layer is not appliedon eachscoot look apply.
Symptom: no icon on the card. PNG
image-dataand reachable PNG paths draw; a theme name draws the app’s initial as a letter tile in v1, and anything refused (a FIFO, an absurd size, a missing file) is a tile too, never an error.
