The theming-demo hooks module

Sample mod for the $.theme API. It:
$.theme.vars (separate light and dark palettes for --accent, --primary, --bg-sidebar),$.theme.css sheet (accent border and title),Not installed by default. Copy this folder to ~/.pawn/mods/theming-demo and allow it in the review dialog; the dialog lists the Change colors and styles permission. Switch the theme off without disabling the mod from the mods menu (Theme toggle).
Docs: docs/agent/MODS.md.
hooks/register.js 58 lines1// Palettes for the whole app. Only `--name` custom properties are accepted.
2const PALETTES = {
3 forest: {
4 light: { '--accent': '#2f6b3a', '--primary': '#2f6b3a', '--bg-sidebar': '#eef3ec' },
5 dark: { '--accent': '#8fd19e', '--primary': '#8fd19e', '--bg-sidebar': '#121a14' }
6 },
7 dusk: {
8 light: { '--accent': '#6b3fa0', '--primary': '#6b3fa0', '--bg-sidebar': '#f3eef8' },
9 dark: { '--accent': '#c3a6ff', '--primary': '#c3a6ff', '--bg-sidebar': '#17121f' }
10 }
11}
12let current = 'forest'
13
14// Each $.theme / $.slot call is written out literally so consent shows it.
15function apply($) {
16 const p = PALETTES[current]
17 $.theme.vars({ id: 'palette-light', mode: 'light', vars: p.light })
18 $.theme.vars({ id: 'palette-dark', mode: 'dark', vars: p.dark })
19 // Scoped: this sheet only reaches elements this mod drew through $.slot.*.
20 $.theme.css({
21 id: 'card',
22 css: [
23 ':scope { border-left: 3px solid var(--accent); border-radius: 6px; }',
24 '.mod-slot-sidebar-name { color: var(--accent); font-weight: 600; }'
25 ].join('\n')
26 })
27 const el = $.ui.resolve({})
28 $.slot.sidebar({
29 id: 'theme',
30 title: 'Theme demo',
31 tree: [
32 el.Text({ text: 'palette: ' + current }),
33 el.Button({ id: 'swap', text: 'Swap palette' }),
34 el.Button({ id: 'off', text: 'Clear theme' })
35 ]
36 })
37}
38
39export function register(on) {
40 on('session.start', async ($, e, next) => {
41 if (e.plugin !== $.plugin.name) return next(e)
42 apply($)
43 return next(e)
44 })
45
46 on('ui.press', { plugin: 'theming-demo' }, async ($, e, next) => {
47 if (e.id === 'swap') {
48 current = current === 'forest' ? 'dusk' : 'forest'
49 apply($)
50 }
51 if (e.id === 'off') {
52 for (const t of $.theme.list()) $.theme.remove({ id: t.id })
53 $.ui.toast('Theme cleared. Swap palette to bring it back.')
54 }
55 return next(e)
56 })
57}
58