mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-01 16:44:23 +08:00
💐 Sync 2026-09-05 10:39:08
This commit is contained in:
1 parent
3d42220d7f
commit
eee3184ac2
17 files changed
+474
-236
No files matched your search
@@ -296,7 +296,14 @@ function build() {
|
||||
const node = w.render();
|
||||
node.setAttribute('aria-label', label);
|
||||
const push = () => apply(parseInt(w.getValue(), 10));
|
||||
node.addEventListener('widget-update', push);
|
||||
/* `live: false` for an axis whose value moves THIS CONTROL. Content width widens the column
|
||||
* the Appearance page is drawn in, so applying mid-drag slides the handle out from under
|
||||
* the pointer, the pointer catches up, and one small drag runs the value to the maximum —
|
||||
* reported from a router, and not a thing any other slider here can do: rounding and the
|
||||
* tint sliders repaint around a control that stays where it is. On release the page still
|
||||
* follows, and a keyboard user is unaffected either way: an arrow key on a range input
|
||||
* fires `change` as well as `input`, so the value applies at once. */
|
||||
if (o.live !== false) node.addEventListener('widget-update', push);
|
||||
node.addEventListener('widget-change', push);
|
||||
return node;
|
||||
};
|
||||
@@ -343,7 +350,10 @@ function build() {
|
||||
* so it is a proper noun and stays untranslated, like the two above it */
|
||||
bootstrap: 'Bootstrap',
|
||||
/* names the OTHER package again, luci-theme-openwrt-2020, whose colourway this is */
|
||||
'2020': 'OpenWrt 2020'
|
||||
'2020': 'OpenWrt 2020',
|
||||
/* names the OpenWrt forum (forum.openwrt.org), whose Discourse colourway this is —
|
||||
* a proper noun like the three above it, not the English common noun "forum" */
|
||||
forum: 'Forum'
|
||||
}, bump(repaint(axes.applyPalette)), label)),
|
||||
|
||||
group(_('Density', 'footstrap'), (label) => selectCtl(prefs.currentDensity(), {
|
||||
@@ -352,6 +362,16 @@ function build() {
|
||||
large: _('Large', 'footstrap')
|
||||
}, bump(prefs.applyDensity), label)),
|
||||
|
||||
/* issue #44: the content column's own cap, separate from Density (which moves type and
|
||||
* air, not the column's ceiling). The slider STARTS at the 1280px the theme has always
|
||||
* shipped and only ever widens — there is no reason to offer a column narrower than the
|
||||
* one every page was designed against, and keeping 1280 as the left end also keeps
|
||||
* --fs-content-min (500px, what the sidebar-to-bar fold is measured against) out of reach
|
||||
* by construction rather than by a rule someone has to remember. */
|
||||
group(_('Content width', 'footstrap'),
|
||||
(label) => sliderCtl(axes.currentContentWidth(), 1280, 3840,
|
||||
bump(axes.applyContentWidth), label, { step: 40, live: false })),
|
||||
|
||||
group(_('Rounding', 'footstrap'),
|
||||
(label) => sliderCtl(axes.currentRadius(), 0, 20, bump(axes.applyRadius), label)),
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
'require baseclass';
|
||||
'require rpc';
|
||||
'require fs-prefs as prefs';
|
||||
'require fs-fit as fit';
|
||||
|
||||
/* fs-axes — the nineteen Appearance axes and the Save-as-default machinery.
|
||||
*
|
||||
@@ -79,8 +80,14 @@ function colorAxis(key, attr, hueProp, colorProp) {
|
||||
* in how the number formats onto the property, which is the one varying argument. The prefs.sd() field
|
||||
* name is passed explicitly because one instance needs a rename rather than a spelling
|
||||
* ('fs-radius' -> rounding), and a factory right for four keys out of five is the trap enumAxis and
|
||||
* colorAxis name above. */
|
||||
function propAxis(key, sdKey, prop, min, max, dfl, fmt) {
|
||||
* colorAxis name above.
|
||||
*
|
||||
* `after`, like listAxis's, is optional and runs once the property is set: every propAxis instance
|
||||
* but Content width only repaints a rule that reads the token, so none of the other five pass it.
|
||||
* Content width moves --fs-content-max, which shellGeometry() (fs-chrome.js) reads to decide
|
||||
* data-narrow, so its applier must re-run the fit or the chrome goes on deciding against the width
|
||||
* the column had a moment ago. */
|
||||
function propAxis(key, sdKey, prop, min, max, dfl, fmt, after) {
|
||||
const inRange = (n) => (typeof n === 'number' && n >= min && n <= max);
|
||||
const def = () => { const d = prefs.sd(sdKey); return inRange(d) ? d : dfl; };
|
||||
return {
|
||||
@@ -96,6 +103,7 @@ function propAxis(key, sdKey, prop, min, max, dfl, fmt) {
|
||||
prefs.lsSet(key, String(v));
|
||||
if (v === dfl) root.style.removeProperty(prop);
|
||||
else root.style.setProperty(prop, fmt(v));
|
||||
if (after) after();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -111,7 +119,7 @@ function propAxis(key, sdKey, prop, min, max, dfl, fmt) {
|
||||
* Legacy names ('rvht'/'roman'/'github') are migrated by head.ut before paint, so they never reach
|
||||
* currentPalette() on a loaded page; the stray fallthrough covers them anyway. */
|
||||
|
||||
const PALETTES = [ 'hicontrast', 'bootstrap', '2020' ]; /* the non-default values; 'footstrap' = bare :root */
|
||||
const PALETTES = [ 'hicontrast', 'bootstrap', '2020', 'forum' ]; /* the non-default values; 'footstrap' = bare :root */
|
||||
const PALETTE = prefs.listAxis('fs-palette', 'data-palette', PALETTES, 'footstrap');
|
||||
const currentPalette = PALETTE.current, applyPalette = PALETTE.apply;
|
||||
|
||||
@@ -209,6 +217,19 @@ const currentDanger = DANGER.current, applyDanger = DANGER.apply;
|
||||
const RADIUS = propAxis('fs-radius', 'rounding', '--fs-radius-base', 0, 20, FS_RADIUS_DEFAULT, (v) => (v + 'px'));
|
||||
const currentRadius = RADIUS.current, applyRadius = RADIUS.apply, radiusDefault = RADIUS.def;
|
||||
|
||||
/* Content width (issue #44): how far the reader lets the column grow past --fs-content-max's own
|
||||
* 1280px. A propAxis like Rounding, pointed at that one token — the range comment lives in
|
||||
* 02-tokens.css, next to the token it bounds, and is not restated here.
|
||||
*
|
||||
* The one propAxis instance that moves chrome geometry: shellGeometry() (fs-chrome.js) reads
|
||||
* --fs-content-max on every fit, so dragging the slider must re-run it through the SAME
|
||||
* fit.schedule() every other geometry-affecting axis calls, or the sidebar goes on folding at the
|
||||
* width the column had before the drag. */
|
||||
const FS_CWIDTH_DEFAULT = 1280;
|
||||
const CWIDTH = propAxis('fs-content-width', 'content_width', '--fs-content-max', 1280, 3840,
|
||||
FS_CWIDTH_DEFAULT, (v) => (v + 'px'), () => fit.schedule());
|
||||
const currentContentWidth = CWIDTH.current, applyContentWidth = CWIDTH.apply, contentWidthDefault = CWIDTH.def;
|
||||
|
||||
/* Layout axis: horizontal top bar (the default) vs vertical sidebar. One template, one renderer —
|
||||
* CSS morphs the chrome off :root[data-layout] and toggling re-renders nothing; menu-footstrap.js
|
||||
* observes the attribute and folds the accordion into dropdowns or restores it.
|
||||
@@ -222,7 +243,7 @@ const AXIS_KEYS = [
|
||||
'fs-accent', 'fs-good', 'fs-warn', 'fs-danger', 'fs-card', 'fs-control',
|
||||
'fs-bar', 'fs-line', 'fs-radius', 'fs-menu-autocollapse', 'fs-tint-strength',
|
||||
'fs-density', 'fs-photo-dim', 'fs-pattern-size', 'fs-pattern-strength',
|
||||
'fs-pattern-ink'
|
||||
'fs-pattern-ink', 'fs-content-width'
|
||||
];
|
||||
/* Tint strength: a multiplier on the tint chroma (03-palettes.css), 100% being the designed
|
||||
* strength and 200% the cap. 0 is not quite "no tint" — the relative colour that applies the tint
|
||||
@@ -291,7 +312,8 @@ function snapshotAxes() {
|
||||
photo_dim: String(currentPhotoDim()),
|
||||
pattern_size: String(currentPatternSize()),
|
||||
pattern_strength: String(currentPatternStrength()),
|
||||
pattern_ink: currentPatternInk()
|
||||
pattern_ink: currentPatternInk(),
|
||||
content_width: String(currentContentWidth())
|
||||
};
|
||||
}
|
||||
/* The resolved router default (the uci value if set, else the built-in) in snapshotAxes() string
|
||||
@@ -326,7 +348,8 @@ function _resolvedDefault() {
|
||||
photo_dim: String(photoDimDefault()),
|
||||
pattern_size: String(patternSizeDefault()),
|
||||
pattern_strength: String(patternStrengthDefault()),
|
||||
pattern_ink: PINK.def()
|
||||
pattern_ink: PINK.def(),
|
||||
content_width: String(contentWidthDefault())
|
||||
};
|
||||
}
|
||||
let _savedDefault = _resolvedDefault();
|
||||
@@ -401,6 +424,7 @@ function resetToBuiltin() {
|
||||
applyPatternSize(FS_PSIZE_DEFAULT);
|
||||
applyPatternStrength(FS_PSTR_DEFAULT);
|
||||
applyPatternInk('theme');
|
||||
applyContentWidth(FS_CWIDTH_DEFAULT);
|
||||
}
|
||||
|
||||
/* ---- the two uploaded wallpapers, browser side ----
|
||||
@@ -473,6 +497,7 @@ return baseclass.extend({
|
||||
currentPatternSize, applyPatternSize,
|
||||
currentPatternStrength, applyPatternStrength,
|
||||
currentPatternInk, applyPatternInk,
|
||||
currentContentWidth, applyContentWidth, contentWidthDefault,
|
||||
|
||||
currentPattern, patternUrl, applyPattern,
|
||||
currentLoginBg, loginBgUrl, applyLoginBg,
|
||||
|
||||
@@ -138,9 +138,15 @@ const GEOM_DFLT = { contentMin: 500, sidebarW: 224, railW: 68, contentPad: 56, c
|
||||
let _geom = null, _geomDensity = null, _geomWarned = false;
|
||||
function shellGeometry() {
|
||||
const density = document.documentElement.getAttribute('data-density') || '';
|
||||
/* contentMax moves with data-content-width (02-tokens.css), the other three widths do not, but
|
||||
* they are one memo and the axis's own applier calls fit.schedule() same as Density's — folded
|
||||
* into the same key rather than a second one, or a content-width change alone would hit the
|
||||
* density-only key and go on answering the previous contentMax until density also changed */
|
||||
const cwidth = document.documentElement.getAttribute('data-content-width') || '';
|
||||
const key = density + '|' + cwidth;
|
||||
/* the gutter is re-asked even on a memo hit: it moves with the width, not the density */
|
||||
if (_geom && _geomDensity === density) return _geom;
|
||||
_geomDensity = density;
|
||||
if (_geom && _geomDensity === key) return _geom;
|
||||
_geomDensity = key;
|
||||
const px = (name, dflt) => resolveLen(name, dflt);
|
||||
const g = {
|
||||
contentMin: px('--fs-content-min', GEOM_DFLT.contentMin),
|
||||
|
||||
@@ -192,10 +192,10 @@ _mqDark.addEventListener('change', () => {
|
||||
|
||||
/* ---- the four axis shapes, each written once ----
|
||||
*
|
||||
* Fifteen axes are four shapes, so the shape lives in a factory and each instance is one line:
|
||||
* Sixteen axes are four shapes, so the shape lives in a factory and each instance is one line:
|
||||
* enumAxis (pattern ink), colorAxis (tint, accent, good, warn, danger), surfaceAxis (cards,
|
||||
* controls, bar, borders), propAxis (rounding, tint strength, photo dim, pattern size, pattern
|
||||
* strength). Same contract throughout: `current()` is localStorage ?? def(), `def()` is the router
|
||||
* strength, content width). Same contract throughout: `current()` is localStorage ?? def(), `def()` is the router
|
||||
* default alone, `apply()` stores the choice explicitly. None use `this` — every export is a
|
||||
* detached method reference, so a `this` here would throw on the first call.
|
||||
*
|
||||
@@ -272,6 +272,11 @@ const DENSITIES = [ 'compact', 'large' ]; /* the two non-default values; 'normal
|
||||
const DENSITY = listAxis('fs-density', 'data-density', DENSITIES, 'normal', () => fit.schedule());
|
||||
const currentDensity = DENSITY.current, applyDensity = DENSITY.apply,
|
||||
densityDefault = DENSITY.def;
|
||||
|
||||
/* Content width lives entirely in fs-axes.js now (a propAxis like Rounding, issue #44): unlike
|
||||
* Density it sets no attribute, so nothing here or in fs-chrome.js needs to read one, and
|
||||
* currentContentWidth()/applyContentWidth() have no caller outside the Appearance tab — the one
|
||||
* thing that kept Density's shape in this cold-path file. */
|
||||
/* Background-tint axis: the canvas the cards float on (--fs-bg), so a whole install reads as one
|
||||
* colour and a tab or a screenshot says which router it belongs to. Cards, chrome and the status
|
||||
* colours keep the palette's values — the cue colours the paper, not the UI. On a hue it is mixed
|
||||
|
||||
Reference in new issue
Block a user