mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-05 01:44:16 +08:00
⛄ Sync 2026-08-12 20:59:19
This commit is contained in:
1 parent
99ea32a4ad
commit
5c59dccedc
62 files changed
+2165
-809
No files matched your search
@@ -14,9 +14,13 @@
|
||||
|
||||
So every label in the Appearance page carries the `footstrap` msgctxt (fs-appearance.js), while
|
||||
the CHROME's own strings — Skip to content, Menu, Collapse menu, Search, Log out — are
|
||||
deliberately msgctxt-FREE: inheriting luci-base's translation is a feature in the ~40 languages
|
||||
this theme ships no catalogue for. Same for the login/notice sentences and for
|
||||
System/Memory/Storage in fs-overview.js, which MATCH the stock headings on purpose.
|
||||
deliberately msgctxt-FREE, because a bare msgid is how a string INHERITS a translation in the ~40
|
||||
languages this theme ships no catalogue for. Two of the five collect on that today: Skip to
|
||||
content and Log out are luci-base msgids verbatim (the latter was `Logout`, which is nobody's
|
||||
msgid, so it rendered in English everywhere our own catalogue does not reach). Menu, Collapse menu
|
||||
and Search are ours alone for now 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 on purpose.
|
||||
-#}
|
||||
|
||||
{%
|
||||
@@ -74,8 +78,14 @@
|
||||
fs_defaults[k] = fsd[k] || '';
|
||||
/* the one value with a fallback of its own: the legacy migration seed
|
||||
luci.main.footstrap_layout=top, which uci-defaults sets for a router upgraded from the old
|
||||
top-nav theme (a shell script cannot write localStorage). */
|
||||
fs_defaults.layout = fsd.layout || config.main.footstrap_layout || '';
|
||||
top-nav theme (a shell script cannot write localStorage).
|
||||
|
||||
`config.main?.` and not `config.main.`: ucode raises a hard reference error on a property read
|
||||
through null and this template is the ONE that renders every page, so a dispatcher that stopped
|
||||
defaulting that section would answer 500 everywhere. It defaults it today — dispatcher.uc has
|
||||
`main: uci.get_all('luci', 'main') ?? {}` on both release branches — and the CI gate cannot see
|
||||
the difference either way: `ucode -T -c` COMPILES the template, it never runs it. */
|
||||
fs_defaults.layout = fsd.layout || config.main?.footstrap_layout || '';
|
||||
|
||||
http.prepare_content('text/html; charset=UTF-8');
|
||||
-%}
|
||||
|
||||
@@ -12,8 +12,12 @@
|
||||
break the inline script nor inject markup (defence in depth — writing that file already needs
|
||||
root). Absent/invalid becomes '' (or 0, or -1 for rounding whose 0 is a real value), which the
|
||||
JS reads as "no router default". The client's own localStorage overrides every one of these
|
||||
before paint — the browser choice always wins (see fs-prefs.js). */
|
||||
fs_defaults = fs_defaults ?? {};
|
||||
before paint — the browser choice always wins (see fs-prefs.js).
|
||||
|
||||
`fs_defaults` is a required include parameter, not an optional one: header.ut is the only
|
||||
caller and builds it as an object literal before the include, so there is nothing to default
|
||||
it to — a `?? {}` here only hid that contract from a second caller that would need to honour
|
||||
it. */
|
||||
let _sd_lay = index([ 'sidebar', 'top' ], fs_defaults.layout) >= 0 ? fs_defaults.layout : '';
|
||||
let _sd_dark = index([ 'auto', 'light', 'dark' ], fs_defaults.darkmode) >= 0 ? fs_defaults.darkmode : '';
|
||||
let _sd_pal = index([ 'footstrap', 'hicontrast', 'bootstrap' ], fs_defaults.palette) >= 0 ? fs_defaults.palette : '';
|
||||
@@ -61,8 +65,9 @@
|
||||
builds the url() below; anything else becomes '' = "no background". */
|
||||
let _lb = fs_defaults.login_bg;
|
||||
let _sd_lbg = (type(_lb) == 'string' && match(_lb, /^[a-f0-9]{6,64}$/)) ? _lb : '';
|
||||
/* the router-identity Tint's DENSITY 0-200 (% of the designed chroma); '' (unset) -> -1, like
|
||||
rounding, because 0 is a real value (no tint). */
|
||||
/* the router-identity Tint's STRENGTH 0-200 (% of the designed chroma; the Appearance row is
|
||||
"Tint strength" — "Density" is the other axis entirely). '' (unset) -> -1, like rounding,
|
||||
because 0 is a real value. */
|
||||
let _ts = int(fs_defaults.tint_strength);
|
||||
let _sd_tstr = (fs_defaults.tint_strength != '' && _ts >= 0 && _ts <= 200) ? _ts : -1;
|
||||
/* the File photo's scrim opacity 0-100 (Wallpaper -> File -> Dim), a SHARED router value; unset
|
||||
@@ -150,7 +155,7 @@
|
||||
RTL set rather than only the three LuCI ships catalogues for today (ar, fa, he), because the
|
||||
cost of a language never installed is one array entry. `ltr` is written explicitly for the same
|
||||
reason data-layout is: every rule then matches positively. */
|
||||
let _lang = dispatcher.lang ?? 'en';
|
||||
let _lang = dispatcher.lang;
|
||||
let _lbase = lc(split(_lang, /[-_]/)[0]);
|
||||
let _rtl = index([ 'ar', 'arc', 'ckb', 'dv', 'fa', 'ha', 'he', 'khw', 'ks', 'ps', 'sd', 'ur', 'yi' ], _lbase) >= 0;
|
||||
%}
|
||||
@@ -226,14 +231,28 @@
|
||||
(this browser) wins; absent -> the router default in window.__fsSD; absent -> the
|
||||
built-in. */
|
||||
(() => {
|
||||
/* PER READ, not once around the block. A browser that refuses storage (Firefox with
|
||||
dom.storage.enabled=false, "block all cookies" in Chrome/Safari, several WebViews)
|
||||
throws a SecurityError on the FIRST getItem, and with one try around everything that
|
||||
aborted the whole pre-paint: dark mode still applied — it guards narrowly, three
|
||||
blocks up — while palette, density, rounding, wallpaper, the pattern axes, the five
|
||||
colour axes, the four surfaces, tint strength and photo dim were all skipped, so a
|
||||
router that had saved defaults painted none of them. Storage failing must mean "this
|
||||
browser has no override", which is exactly what the live applier's lsGet already
|
||||
means (fs-prefs.js). The outer try stays as the last resort for everything else. */
|
||||
const lsGet = (k) => { try { return localStorage.getItem(k); } catch (e) { return null; } };
|
||||
try {
|
||||
const root = document.querySelector(':root'),
|
||||
sd = window.__fsSD || {};
|
||||
let p = localStorage.getItem('fs-palette');
|
||||
let p = lsGet('fs-palette');
|
||||
/* legacy 'rvht'/'roman' = default colours + a doodle wallpaper, and legacy
|
||||
'github' = the footstrap default, now stored explicitly. Migrate to the current
|
||||
explicit value, do NOT clear (an absent key means "inherit the router default",
|
||||
which is no longer the same as "the built-in default"). The doodles they used to
|
||||
which is no longer the same as "the built-in default"). The migration is
|
||||
already redundant, and worth knowing before it is carried forward forever: a
|
||||
stray value reads as the built-in default in the live applier too
|
||||
(fs-prefs.js's currentPalette ends `return 'footstrap'`), so deleting this
|
||||
block changes nothing UNLESS that line goes with it. The doodles they used to
|
||||
turn on are gone — the Wallpaper axis now paints an SVG the admin uploads — so
|
||||
the wallpaper half of that migration has nothing to point at and is dropped;
|
||||
`fs-wallpaper` keeps whatever it holds and the whitelist below reads a doodle
|
||||
@@ -256,18 +275,18 @@
|
||||
whitelisted to hex above, so nothing user-controlled reaches the url(). */
|
||||
const pu = sd.pattern;
|
||||
if (pu) root.style.setProperty('--fs-pattern-url', 'url("/luci-static/footstrap/pattern.svg?v=' + pu + '")');
|
||||
const psRaw = localStorage.getItem('fs-pattern-size');
|
||||
const psRaw = lsGet('fs-pattern-size');
|
||||
const ps = (psRaw !== null) ? parseInt(psRaw, 10) : sd.pattern_size;
|
||||
if (ps >= 40 && ps <= 1600 && ps !== 440) root.style.setProperty('--fs-pattern-size', ps + 'px');
|
||||
const pstRaw = localStorage.getItem('fs-pattern-strength');
|
||||
const pstRaw = lsGet('fs-pattern-strength');
|
||||
const pst = (pstRaw !== null) ? parseInt(pstRaw, 10) : sd.pattern_strength;
|
||||
if (pst >= 0 && pst <= 100 && pst !== 20) root.style.setProperty('--fs-pattern-strength', String(pst / 100));
|
||||
let pi = localStorage.getItem('fs-pattern-ink');
|
||||
let pi = lsGet('fs-pattern-ink');
|
||||
if (pi === null) pi = sd.pattern_ink || null;
|
||||
if (pi === 'original') root.setAttribute('data-pattern-ink', 'original');
|
||||
else root.removeAttribute('data-pattern-ink');
|
||||
|
||||
let w = localStorage.getItem('fs-wallpaper');
|
||||
let w = lsGet('fs-wallpaper');
|
||||
if (w === null) w = sd.wallpaper || null;
|
||||
if (w === 'pattern' || w === 'file') root.setAttribute('data-wallpaper', w);
|
||||
else root.removeAttribute('data-wallpaper');
|
||||
@@ -276,7 +295,7 @@
|
||||
type and space ladders). Normal is a bare :root, so the removeAttribute
|
||||
half matters as much as the set — without it a browser that switched back
|
||||
to Normal would paint one frame still compressed. */
|
||||
let dn = localStorage.getItem('fs-density');
|
||||
let dn = lsGet('fs-density');
|
||||
if (dn === null) dn = sd.density || null;
|
||||
if (dn === 'compact' || dn === 'large') root.setAttribute('data-density', dn);
|
||||
else root.removeAttribute('data-density');
|
||||
@@ -285,7 +304,7 @@
|
||||
default is a LITERAL: this runs before the stylesheet and cannot read the
|
||||
CSS token, so axes.mjs is the only thing checking the two agree. sd.rounding
|
||||
is -1 when the router has no default. */
|
||||
const rRaw = localStorage.getItem('fs-radius');
|
||||
const rRaw = lsGet('fs-radius');
|
||||
let r = parseInt(rRaw, 10);
|
||||
if (rRaw === null) r = (sd.rounding >= 0 ? sd.rounding : 12);
|
||||
if (r >= 0 && r <= 20 && r !== 12)
|
||||
@@ -302,7 +321,7 @@
|
||||
to one shape. */
|
||||
const hex = /^#[0-9a-f]{6}$/i;
|
||||
const colour = (key, sdVal, attr, hueProp, colorProp) => {
|
||||
const raw = localStorage.getItem(key);
|
||||
const raw = lsGet(key);
|
||||
const v = String((raw === null ? sdVal : raw) ?? '').trim();
|
||||
if (hex.test(v)) {
|
||||
root.style.removeProperty(hueProp);
|
||||
@@ -327,7 +346,7 @@
|
||||
mode and therefore no attribute, just the token or nothing. Mirrors
|
||||
surfaceAxis() in fs-prefs.js. */
|
||||
const surface = (key, sdVal, prop) => {
|
||||
const raw = localStorage.getItem(key);
|
||||
const raw = lsGet(key);
|
||||
const v = String((raw === null ? sdVal : raw) ?? '').trim().toLowerCase();
|
||||
if (hex.test(v)) root.style.setProperty(prop, v);
|
||||
};
|
||||
@@ -339,19 +358,22 @@
|
||||
window.__fsSD — no localStorage). Set the property whenever a valid token
|
||||
exists; whether it PAINTS is the wallpaper axis above (data-wallpaper="file"),
|
||||
so a router whose default wallpaper is 'file' shows it here, pre-login included.
|
||||
Re-validate the token (defence in depth, same charset as the server sanitiser). */
|
||||
The token is already whitelisted to hex above, the same as sd.pattern before it, so
|
||||
nothing user-controlled reaches the url(); the test is repeated here anyway
|
||||
because this one interpolates into a url() inside a STYLE property, where a
|
||||
second reading costs one regex and the failure would be silent. */
|
||||
const lb = sd.login_bg;
|
||||
if (lb && (/^[a-f0-9]{6,64}$/).test(lb))
|
||||
root.style.setProperty('--fs-login-bg-url', "url('/luci-static/footstrap/bg?v=" + lb + "')");
|
||||
/* the router-identity Tint's density (default 100% = the designed chroma). */
|
||||
const tsRaw = localStorage.getItem('fs-tint-strength');
|
||||
const tsRaw = lsGet('fs-tint-strength');
|
||||
let ts = parseInt(tsRaw, 10);
|
||||
if (tsRaw === null) ts = (sd.tint_strength >= 0 ? sd.tint_strength : 100);
|
||||
if (ts >= 0 && ts <= 200 && ts !== 100)
|
||||
root.style.setProperty('--fs-tint-strength', String(ts / 100));
|
||||
/* the File photo's scrim opacity — an ordinary per-browser axis, same shape as
|
||||
the tint strength above; the 74% default lives in the CSS token. */
|
||||
const pdRaw = localStorage.getItem('fs-photo-dim');
|
||||
const pdRaw = lsGet('fs-photo-dim');
|
||||
let pd = parseInt(pdRaw, 10);
|
||||
if (pdRaw === null) pd = (sd.photo_dim >= 0 ? sd.photo_dim : 74);
|
||||
if (pd >= 0 && pd <= 100 && pd !== 74)
|
||||
@@ -444,9 +466,10 @@
|
||||
lang=en it spends them to deliver 13 bytes (`window.TR={};`): there is no en catalogue,
|
||||
because the msgids already ARE English. The process is the cost, not the data. So emit
|
||||
those 13 bytes inline when this language has no catalogue, and keep the tag when it has.
|
||||
The probe MIRRORS the server: action_translations calls load_catalog(lang,
|
||||
'/usr/lib/lua/luci/i18n'), which loads every `*.<lang>.lmo` there — so a router that does
|
||||
ship an en catalogue still gets the tag. Deciding by language NAME would silently drop it.
|
||||
The probe MIRRORS the server: determine_request_language() (dispatcher.uc) loads this
|
||||
request's catalogue with load_catalog(lang, '/usr/lib/lua/luci/i18n'), which takes every
|
||||
`*.<lang>.lmo` in that directory — so a router that does ship an en catalogue still gets
|
||||
the tag. Deciding by language NAME would silently drop it.
|
||||
Fails OPEN (a throwing probe keeps the tag): a missing catalogue makes every `_()` render
|
||||
English and report nothing, which is the one failure this must never risk. #}
|
||||
{%- let has_catalog = true;
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
-#}
|
||||
{% if (dispatcher.lookup("admin/logout")): %}
|
||||
<a class="fs-logout" href="{{ dispatcher.build_url('admin/logout') }}" title="{{ _('Logout') }}" aria-label="{{ _('Logout') }}">
|
||||
<a class="fs-logout" href="{{ dispatcher.build_url('admin/logout') }}" title="{{ _('Log out') }}" aria-label="{{ _('Log out') }}">
|
||||
{% include('themes/footstrap/partials/icon', { body: '<path d="M14 4h4a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-4"/><path d="M10 12H3m0 0 3-3m-3 3 3 3"/>' }) %}
|
||||
<span class="fs-label">{{ _('Logout') }}</span>
|
||||
<span class="fs-label">{{ _('Log out') }}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
@@ -1,9 +1,11 @@
|
||||
{#
|
||||
The page-search trigger (#fs-search-btn), wired by fs-search.js. It is a `.fs-themerow` — the
|
||||
SAME class the Appearance trigger uses, not a look of its own — so it is one of the right
|
||||
chrome's shared row/button class rather than a look of its own — so it is one of the right
|
||||
cluster's square icon buttons in the bar and the rail, and a full-width labelled row in the
|
||||
vertical sidebar, with no new CSS to keep in step. It sits between the poll pill (#indicators)
|
||||
and Appearance: those three share `order: 2`, so the DOM order here IS the visual order.
|
||||
and Log out, which share `order: 2` with it, so the DOM order here IS the visual order. (The
|
||||
Appearance trigger was the third member until the panel became a tab on System -> System; this is
|
||||
the only .fs-themerow the document carries now.)
|
||||
|
||||
i18n: 'Search' is CHROME and deliberately carries no msgctxt — inheriting luci-base's translation
|
||||
is a feature in the ~40 languages this theme ships no catalogue for. The palette's OWN strings
|
||||
|
||||
@@ -5,10 +5,13 @@
|
||||
Copyright 2008-2012 Jo-Philipp Wich <jow@openwrt.org>
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
|
||||
It exists to pass `blank_page: true`, the only thing it changes about the generic template:
|
||||
without a theme-local sysauth the dispatcher falls back to that one, which includes the header
|
||||
WITHOUT blank_page — so the whole chrome (sidebar, menu, footer) rendered around the login
|
||||
form, every control on it dead.
|
||||
It exists because the generic template includes the header WITHOUT `blank_page: true`: with no
|
||||
theme-local sysauth the dispatcher falls back to that one and the whole chrome (sidebar, menu,
|
||||
footer) rendered around the login form, every control on it dead. Everything else here is a
|
||||
DEPARTURE the notes below each justify — the `fs-login` hook the page CSS needs, the id/for/
|
||||
autocomplete attributes, the alert gating, and an HTTPS-redirect script that also fixes the
|
||||
generic's `split(https_port, …)` typo — and the rule is that none of them may drop a field the
|
||||
backend posts.
|
||||
|
||||
It is NOT a copy of bootstrap's sysauth.ut, which hides the form in a `<section hidden>` and
|
||||
needs a view module to reveal it: tried here once and it gave a BLANK PAGE WITH NO WAY TO LOG
|
||||
@@ -88,8 +91,8 @@
|
||||
sysauth that drops them is not a login form with a missing extra: the field is never
|
||||
posted, so on a router with 2FA enabled NOBODY CAN LOG IN while this theme is active,
|
||||
and the backend's "Too many failed attempts" / "System time is not calibrated" messages
|
||||
never reach the screen either. This template exists only to pass `blank_page: true`, so
|
||||
it must stay a superset of the generic one.
|
||||
never reach the screen either. Whatever else this template changes, it must stay a
|
||||
SUPERSET of the generic one's fields.
|
||||
|
||||
The leading space inside each conditional attribute is load-bearing. ucode's template
|
||||
lexer eats the whitespace immediately BEFORE a `{%` tag and the newline immediately
|
||||
|
||||
Reference in new issue
Block a user