{# Copyright 2008 Steven Barth Copyright 2012 David Menting Copyright 2008-2022 Jo-Philipp Wich Copyright 2026 footstrap theme Licensed to the public under the Apache License 2.0. i18n, and the rule the other templates point at. A msgid is a GLOBAL name: LuCI serves one merged catalogue — load_catalog() loads every *..lmo and a lookup returns the first archive holding the hash — so readdir order decides who owns a string, and the Appearance layout toggle rendered "Максимум" on a Russian router because another catalogue translates `Top` as "maximum" (issue #6). Anything left bare is a name anyone may take, and contexting cannot be selective. So every Appearance label carries the `footstrap` msgctxt, while the chrome's own strings — Skip to content, Menu, Collapse menu, Search, Log out — are deliberately msgctxt-free, because a bare msgid is how a string inherits a translation in the ~40 languages this theme ships no catalogue for. Skip to content and Log out are luci-base msgids verbatim; the other three are ours alone and stay bare so a future luci-base string can adopt them. Same for the login/notice sentences and for System/Memory/Storage in fs-overview.js, which match the stock headings. -#} {% import { getuid, getspnam } from 'luci.core'; const boardinfo = ubus.call('system', 'board') ?? {}; const empty_password = (getuid() == 0 && getspnam('root')?.pwdp === ''); /* The router's saved Appearance defaults (/etc/config/footstrap): head.ut stamps them before paint and the client's localStorage overrides every one. Absent = the built-in defaults. `uci` and `config` come from the render scope dispatcher.uc builds per request: `uci` is already a cursor and `config.main` is already `uci.get_all('luci','main')`. Opening a second cursor here would allocate one per page render and re-read a package the dispatcher has already loaded. */ const fsd = uci.get_all('footstrap', 'settings') ?? {}; /* Every option saveAsDefault() writes, listed once and read back in a loop. A hand-written * literal with one line per axis drops an axis silently: Save to router reports success, the * file on disk is correct, and "Reset to router" lands on the BUILT-IN look because the value * the server hands the client was never there. tools/axes.mjs checks this list against * snapshotAxes(). * * '' rather than a raw undefined when unset, so head.ut's `!= ''` guards fire: int(undefined) * is 0, a valid value, and an axis whose 0 is a real choice would default to it. */ const FS_AXES = [ 'layout', 'darkmode', 'palette', 'wallpaper', 'density', 'tint', 'accent', 'good', 'warn', 'danger', 'card', 'control', 'bar', 'line', 'rounding', 'autocollapse', 'tint_strength', 'photo_dim', 'pattern_size', 'pattern_strength', 'pattern_ink', 'content_width', /* the two uploaded images' cache-bust tokens (head.ut whitelists both to hex); the files themselves are served. Not axes — they have no browser layer — but they reach the client the same way. */ 'login_bg', 'pattern', /* the fonts, same shape: no browser layer, no Appearance control, no entry in snapshotAxes(). font_sans/font_mono are font-family stacks (head.ut whitelists the charset); `fonts` is the cache-bust token of the @font-face sheet the admin installs beside the faces. All three are written with `uci set`, never by Save as default. */ 'font_sans', 'font_mono', 'fonts' ]; const fs_defaults = {}; for (let k in FS_AXES) fs_defaults[k] = fsd[k] || ''; /* the one value with a fallback of its own: the legacy migration seed luci.main.footstrap_layout=top, which an admin may set to give the router a default top-nav theme (a shell script cannot write localStorage). `config.main?.`, not `config.main.`: ucode raises a hard reference error on a property read through null, and this template renders every page, so a dispatcher that stopped defaulting that section would answer 500 everywhere. `ucode -T -c` compiles the template and never runs it, so the CI gate cannot see the difference. */ fs_defaults.layout = fsd.layout || config.main?.footstrap_layout || ''; /* Optional companion packages announce themselves in the theme's own config — `uci add_list footstrap.settings.plugin=` from the package's uci-defaults — rather than being named in this tree. The chrome requires each name and knows nothing else about it, so the edge points inwards exactly as fs-router.onNavigate does. Whitelisted to the shape of a LuCI module name before it is printed into a