shell theme: add glass-workbench as a second compiled-in theme
Phase 5 of the shell theme system (THEME_SYSTEM_PLAN.md §11): a second
builtin, demo 02's flush edge-to-edge bar with pill workspaces, a plain
date+time clock, and cpu/ram chips instead of the media widget.
- bread-theme/assets/shell/glass-workbench/: theme.toml + CSS template,
faithful to bos-ui-demos/02-glass-workbench.html. Accent maps to the
`green` palette token (flat, not a gradient) rather than a hex literal,
so pywal theming still works.
- builtin.rs: generalized from a single hardcoded liquid-motion constant
pair to a small BuiltinTheme registry (builtin::ALL / builtin::find),
so mod.rs's discovery/list()/resolve_builtin no longer special-case one
id. liquid-motion stays the pinned fallback in resolve_builtin().
- manifest.rs: KNOWN_MODULES gains "cpu"/"ram".
- types.rs: new Tokens::bar_border() ("full" default vs "bottom") so a
flush bar can ask for a single hairline instead of an island's full
border.
- Tests: builtin loads, appears in list() alongside liquid-motion, and its
window spec is the flush/edge shape (36px, zero margin, radius 0).
cargo test -p bread-theme --lib: 63 passing (59 prior + 4 new).
This commit is contained in:
parent
53a6c59f2d
commit
96fa79c1d4
6 changed files with 467 additions and 40 deletions
86
bread-theme/assets/shell/glass-workbench/glass-workbench.css
Normal file
86
bread-theme/assets/shell/glass-workbench/glass-workbench.css
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/* CSS template for the glass-workbench builtin (bread-theme/src/shell/
|
||||
* builtin.rs). Same scope and substitution rules as liquid-motion.css: only
|
||||
* the window/workspace/clock chrome the manifest's own concepts model, `{name}`
|
||||
* tokens substituted, `@name` palette references passed through untouched.
|
||||
*
|
||||
* Source: bos-ui-demos/02-glass-workbench.html's <style> block. No
|
||||
* per-digit flip (this theme has no `.clock-digit` markup at all — plain
|
||||
* style is a date label + one time label) and no gradient trail (pill style
|
||||
* never makes `.workspace-trail` visible — see breadbar's
|
||||
* `WorkspaceTrail::place`/`stretch`, which this theme's slot config simply
|
||||
* never calls).
|
||||
*/
|
||||
|
||||
window.breadbar {
|
||||
background-color: alpha(@bg, {bg_alpha});
|
||||
color: @on-bg;
|
||||
border-radius: {radius_bar}px;
|
||||
border: none;
|
||||
border-bottom: 1px solid alpha(@on-bg, 0.07);
|
||||
}
|
||||
window.breadbar > centerbox { padding: 0 {pad}px; }
|
||||
window.breadbar button { min-height: 0; min-width: 0; }
|
||||
|
||||
/* Pill workspaces: solid accent fill when active, dimmed when empty — no
|
||||
* trail overlay is ever shown (place()/stretch() are never called for this
|
||||
* style), so `.workspace-trail` itself needs no rule here.
|
||||
*/
|
||||
.workspace-btn {
|
||||
background: transparent;
|
||||
opacity: 1;
|
||||
color: alpha(@on-bg, 0.4);
|
||||
border-radius: {radius_sm}px;
|
||||
border: none;
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
min-width: 22px;
|
||||
min-height: {chip_height}px;
|
||||
margin: 0;
|
||||
padding: 0 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
transition: background-color 0.22s {spring_settle}, color 0.22s {spring_settle},
|
||||
opacity 0.22s {spring_settle};
|
||||
}
|
||||
.workspace-btn:hover { background: alpha(@on-bg, 0.08); }
|
||||
.workspace-btn.occupied { color: alpha(@on-bg, 0.8); }
|
||||
.workspace-btn:not(.occupied):not(.active) { opacity: 0.35; }
|
||||
.workspace-btn.active {
|
||||
background: @{accent_from};
|
||||
color: @on-accent;
|
||||
opacity: 1;
|
||||
}
|
||||
.workspace-btn.active:hover { background: @{accent_from}; }
|
||||
|
||||
.clock-plain { padding: 0 4px; }
|
||||
.clock-plain-time {
|
||||
font-size: {font_size_base}px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
.date-label {
|
||||
font-size: 12px;
|
||||
opacity: 0.48;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
window.breadbar-osd {
|
||||
background-color: alpha(@bg, 0.72);
|
||||
color: @on-bg;
|
||||
border-radius: {radius_pill}px;
|
||||
border: 1px solid alpha(@on-bg, 0.10);
|
||||
}
|
||||
|
||||
window.breadbar-panel {
|
||||
background-color: alpha(@bg, 0.86);
|
||||
color: @on-bg;
|
||||
border-radius: {radius_card}px;
|
||||
border: 1px solid alpha(@on-bg, 0.12);
|
||||
}
|
||||
|
||||
window.breadbar-dismiss {
|
||||
background-color: alpha(#000000, 0.02);
|
||||
}
|
||||
|
||||
.bread-widget-slot { margin-right: {pad}px; }
|
||||
169
bread-theme/assets/shell/glass-workbench/theme.toml
Normal file
169
bread-theme/assets/shell/glass-workbench/theme.toml
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
# The second compiled-in builtin (bread-theme/src/shell/builtin.rs), demo 02
|
||||
# in THEME_SYSTEM_PLAN.md — "Glass Workbench": a flush edge-to-edge bar with
|
||||
# plain pill workspaces, a plain date+time clock, and cpu/ram chips instead
|
||||
# of the media widget liquid-motion carries. Values are taken from
|
||||
# bos-ui-demos/02-glass-workbench.html's <style> block; where the demo is
|
||||
# silent on a number (bar-adjacent chip sizing, satellite offsets) this file
|
||||
# scales liquid-motion's own numbers down by the same 36/44 bar-height ratio
|
||||
# rather than inventing something unrelated to either source.
|
||||
#
|
||||
# Unlike liquid-motion, this builtin has no "as the code exists today" tether
|
||||
# — it is new, so demo fidelity is the only source of truth. See Phase 5's
|
||||
# task notes for the two exceptions: font (IBM Plex Sans is not installed on
|
||||
# the dev machine, so this renders in fallback until the user installs
|
||||
# ttf-ibm-plex) and colour (the demo's #7a9a88 sage is a *flat* accent, so it
|
||||
# maps to the palette's `green` token for both accent_from and accent_to —
|
||||
# never a literal hex value, or pywal theming breaks).
|
||||
|
||||
name = "Glass Workbench"
|
||||
id = "glass-workbench"
|
||||
|
||||
[tokens]
|
||||
font_family = "IBM Plex Sans"
|
||||
font_fallback = "Inter, Noto Sans, sans-serif"
|
||||
font_size_base = 13
|
||||
radius_bar = 0
|
||||
radius_card = 10
|
||||
radius_sm = 6
|
||||
radius_pill = 999
|
||||
pad = 12
|
||||
bg_alpha = 0.72
|
||||
# The demo defines exactly one easing curve (`--spring: cubic-bezier(.22,
|
||||
# 1.2, .36, 1)`), not liquid-motion's overshoot/settle pair — there's no
|
||||
# digit-flip or trail-stretch here to want a bounce for. Both `spring` and
|
||||
# `spring_settle` point at the demo's one curve rather than inventing a
|
||||
# second value the demo never specifies.
|
||||
spring = "cubic-bezier(0.22, 1.2, 0.36, 1)"
|
||||
spring_settle = "cubic-bezier(0.22, 1.2, 0.36, 1)"
|
||||
# Palette token NAMES, not hex — #7a9a88 is this palette's `green`. Equal
|
||||
# from/to because the demo's accent is flat, not a gradient.
|
||||
accent_from = "green"
|
||||
accent_to = "green"
|
||||
# Demo: `.ws button { height: 20px }`. Liquid-motion's chip_height (32) is
|
||||
# tied to its 44px island; 20px is the demo's own number for this bar's
|
||||
# workspace pills, and is reused for the bar's other small chips (cpu/ram/
|
||||
# wifi/battery/control) so they all sit at the same height on the thinner
|
||||
# 36px flush bar.
|
||||
chip_height = 20
|
||||
# No demo number for bar-icon size; scaled from liquid-motion's 24px by the
|
||||
# same 36/44 bar-height ratio liquid-motion itself uses for its 44px bar.
|
||||
icon_px = 18
|
||||
# Flush edge-to-edge bar: a full border would draw a stray line along the
|
||||
# top/side screen edges a floating island doesn't have to worry about. See
|
||||
# Tokens::bar_border's doc comment.
|
||||
bar_border = "bottom"
|
||||
|
||||
[bar.window]
|
||||
anchors = ["top", "left", "right"]
|
||||
width = "fill"
|
||||
height = 36
|
||||
margin = { top = 0, left = 0, right = 0 }
|
||||
exclusive = "auto"
|
||||
keyboard = "none"
|
||||
layer = "top"
|
||||
|
||||
[bar.slots]
|
||||
# No media module — demo 02 has no media widget at all (plan §1 table).
|
||||
# breadbar must tolerate the omission: `media_widget` is still built and
|
||||
# registered under the "media" module name (main.rs), it just never appears
|
||||
# in any of this theme's slot lists, so it's never appended anywhere.
|
||||
left = ["workspaces"]
|
||||
centre = ["clock"]
|
||||
right = ["cpu", "ram", "wifi", "battery", "control"]
|
||||
drawer = []
|
||||
|
||||
[modules.workspaces]
|
||||
style = "pill"
|
||||
show_empty = true
|
||||
|
||||
[modules.clock]
|
||||
style = "plain"
|
||||
format = "%H:%M"
|
||||
show_date = true
|
||||
|
||||
[launcher]
|
||||
mode = "overlay"
|
||||
width = 560
|
||||
top = "64px"
|
||||
radius = 12
|
||||
icon_px = 32
|
||||
row_anim = "stagger"
|
||||
rule = "hairline"
|
||||
footer = "count_results"
|
||||
# Declared-but-not-consumed, exactly like liquid-motion's own launcher block:
|
||||
# breadbox's Phase 4b-i wiring only reads mode/width/top/radius/icon_px.
|
||||
# row_anim/rule/footer are this theme's demo-derived aspirations breadbox
|
||||
# does not implement yet; sections/modes describe today's actual (trivial,
|
||||
# apps-only) behaviour but are equally unread by any code path.
|
||||
sections = false
|
||||
modes = ["apps"]
|
||||
|
||||
# Same four satellite namespaces as liquid-motion, keyed identically so the
|
||||
# two manifests validate against the same [compositor.*] keyspace. Offsets
|
||||
# are liquid-motion's own numbers re-derived from this bar's actual edge
|
||||
# instead of the island's: liquid-motion's bar bottom edge sits at
|
||||
# margin.top(12) + height(44) = 56px, and its breadbar-notif/-panel top
|
||||
# offset (64) is that plus an 8px gap, while breadbar-dismiss (56) sits
|
||||
# flush against it with no gap. This bar's edge is margin.top(0) +
|
||||
# height(36) = 36px, so the same two relationships give 44 (36 + 8) and 36.
|
||||
[surfaces."breadbar-notif"]
|
||||
anchor = "top_right"
|
||||
offset = [16, 44]
|
||||
width = 320
|
||||
layer = "overlay"
|
||||
|
||||
[surfaces."breadbar-osd"]
|
||||
# Independent of bar height (an OSD pill near the bottom of the screen) —
|
||||
# unchanged from liquid-motion.
|
||||
anchor = "bottom_centre"
|
||||
offset = 80
|
||||
width = 180
|
||||
layer = "overlay"
|
||||
|
||||
[surfaces."breadbar-panel"]
|
||||
anchor = "top_right"
|
||||
offset = [16, 44]
|
||||
width = "auto"
|
||||
layer = "overlay"
|
||||
|
||||
[surfaces."breadbar-dismiss"]
|
||||
anchor = "fill"
|
||||
offset = 36
|
||||
width = "fill"
|
||||
layer = "overlay"
|
||||
|
||||
# Appearance-only, never placement/workspace/focus (plan §12 Layer B
|
||||
# boundary) — identical to liquid-motion's rules. Blur *strength* is global
|
||||
# (plan §9 known limit), so this theme cannot independently match the demo's
|
||||
# per-surface blur radii (20px bar / 16px launcher / 22px popovers / 6px
|
||||
# window borders); only on/off, ignore_alpha and slide direction are
|
||||
# per-namespace knobs this system actually has.
|
||||
[compositor."breadbar"]
|
||||
blur = true
|
||||
ignore_alpha = 0.2
|
||||
blur_popups = true
|
||||
animation = "slide top"
|
||||
|
||||
[compositor."breadbar-osd"]
|
||||
blur = true
|
||||
ignore_alpha = 0.2
|
||||
animation = "slide bottom"
|
||||
|
||||
[compositor."breadbar-notif"]
|
||||
blur = true
|
||||
ignore_alpha = 0.2
|
||||
animation = "slide right"
|
||||
|
||||
[compositor."breadbar-panel"]
|
||||
blur = true
|
||||
ignore_alpha = 0.2
|
||||
animation = "slide right"
|
||||
|
||||
[compositor."breadbar-dismiss"]
|
||||
no_anim = true
|
||||
|
||||
[compositor."breadbox"]
|
||||
blur = true
|
||||
ignore_alpha = 0.2
|
||||
|
||||
# No `css = "..."` overlay — compiled-in builtin, same as liquid-motion.
|
||||
Loading…
Add table
Add a link
Reference in a new issue