Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions public/api/widget-data/gh-diskhealth-md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
"bays": [
{ "device_id": "1b8f4a20-6c35-4d17-91ea-70c2f5b8d413", "device_status": 0, "hasSmart": true, "model_name": "ST8000VN004-2M2101", "device_name": "sda", "temp": 38, "capacity": 8001563222016 },
{ "device_id": "2c9e5b31-7d46-4e28-a2fb-81d306c9e524", "device_status": 0, "hasSmart": true, "model_name": "ST8000VN004-2M2101", "device_name": "sdb", "temp": 39, "capacity": 8001563222016 },
{ "device_id": "3da06c42-8e57-4f39-b30c-92e417daf635", "device_status": 1, "hasSmart": true, "model_name": "TOSHIBA MG08ACA16TE", "device_name": "sdc", "temp": 47, "capacity": 16000900661248 },
{ "device_id": "3da06c42-8e57-4f39-b30c-92e417daf635", "device_status": 0, "hasSmart": true, "model_name": "TOSHIBA MG08ACA16TE", "device_name": "sdc", "temp": 47, "capacity": 16000900661248 },
{ "device_id": "4eb17d53-9f68-4a40-c41d-a3f528eb1746", "device_status": 0, "hasSmart": true, "model_name": "TOSHIBA MG08ACA16TE", "device_name": "sdd", "temp": 41, "capacity": 16000900661248 },
{ "device_id": "5fc28e64-a079-4b51-d52e-b40639fc0857", "device_status": 2, "hasSmart": true, "model_name": "WDC WD80EAZZ-00BKLB0", "device_name": "sde", "temp": 55, "capacity": 8001563222016 },
{ "device_id": "60d39f75-b18a-4c62-e63f-c5174a0d1968", "device_status": 0, "hasSmart": true, "model_name": "WDC WD80EAZZ-00BKLB0", "device_name": "sdf", "temp": 40, "capacity": 8001563222016 },
{ "device_id": "71e4a086-c29b-4d73-f740-d6285b1e2a79", "device_status": 3, "hasSmart": true, "model_name": "WDC WD40EFRX-68N32N0", "device_name": "sdg", "temp": 43, "capacity": 4000787030016 },
{ "device_id": "71e4a086-c29b-4d73-f740-d6285b1e2a79", "device_status": 0, "hasSmart": true, "model_name": "WDC WD40EFRX-68N32N0", "device_name": "sdg", "temp": 43, "capacity": 4000787030016 },
{ "device_id": "82f5b197-d3ac-4e84-a851-e7396c2f3b8a", "device_status": 0, "hasSmart": true, "model_name": "CT1000MX500SSD1", "device_name": "sdh", "temp": 33, "capacity": 1000204886016 },
{ "device_id": "93a6c208-e4bd-4f95-b962-f84a7d304c9b", "device_status": 0, "hasSmart": false, "model_name": "Samsung SSD 870 EVO 500GB", "device_name": "sdi", "temp": null, "capacity": 500107862016 },
null
Expand Down
17 changes: 9 additions & 8 deletions public/js/admin-app-form.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { clr, el, inp as inpById, q as qSel, qa, qi, tgt } from '/js/utils.js?v=b1cfbd45';
import { clr, el, inp as inpById, q as qSel, qa, qi, tgt } from '/js/utils.js?v=c5766a9d';
import { html, raw, setHtml } from '/js/html.js?v=c71f8903';
import { loadLocalIcons, resolveIcon, iconChain, cdnIconRef, splitIconRef } from '/js/icons.js?v=9c8c550c';
import { state } from '/js/admin-state.js?v=831e219e';
import { state } from '/js/admin-state.js?v=af772a1b';
import { DOCK_MAX } from '/js/limits.js?v=31048a24';
import {
isDockBlocked,
Expand All @@ -10,21 +10,22 @@ import {
failureIsMissingApiPath,
nextActiveIndex,
sameIconName,
} from '/js/admin-logic.js?v=cbb7417d';
} from '/js/admin-logic.js?v=fc7f0836';
import { t } from '/js/i18n.js?v=1f1ea9c1';
import {
toast,
responseError,
apiGet,
apiPost,
PE_SVG,
initInlineEdit,
reveal,
setTogDisabled,
swapContent,
} from '/js/admin-shared.js?v=3870d0d0';
import { createListbox } from '/js/listbox.js?v=6e7b7060';
} from '/js/admin-shared.js?v=a81b9cbe';
import { createListbox } from '/js/listbox.js?v=9a8ae607';
import { MAX_LABELS } from '/js/badge-logic.js?v=9e6d9d4b';
import { renderColorControl, BADGE_DEFAULT } from '/js/admin-color-control.js?v=844bdeb2';
import { renderColorControl, BADGE_DEFAULT } from '/js/admin-color-control.js?v=0d4d1038';
import { badgeErrorAdvice, TONE } from '/js/admin-error.js?v=f82486b2';
import { fluidHoverClear, fluidHoverKb } from '/js/fluid-hover.js?v=cb886e86';
import { iconSvg } from '/js/icon-set.js?v=34af798f';
Expand Down Expand Up @@ -707,8 +708,8 @@ function wireIcon() {
const form = new FormData();
form.append('icon', file, file.name);
const r = await fetch('/api/icons/upload', { method: 'POST', body: form });
if (!r.ok) throw new Error(await responseError(r));
const d = await r.json();
if (!r.ok) throw new Error(d.error || `HTTP ${r.status}`);
await loadLocalIcons();
state.siurl = d.filename;
const ipIn = inpById('ip-in');
Expand Down Expand Up @@ -985,7 +986,7 @@ async function fetchBadge() {
params,
headers,
skipTls,
itemId: state.eid !== null ? state.items[state.eid]?.id : undefined,
itemId: state.eid ?? undefined,
});
state.fnums = r.numbers || [];
if (st) {
Expand Down
13 changes: 6 additions & 7 deletions public/js/admin-auth.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { apiGet, apiPost } from '/js/admin-shared.js?v=3870d0d0';
import { apiGet, apiPost } from '/js/admin-shared.js?v=a81b9cbe';
import { t } from '/js/i18n.js?v=1f1ea9c1';
import { pwStrength } from '/js/password-strength.js?v=42f45ac7';
import { el, inp as inpById, qa } from '/js/utils.js?v=b1cfbd45';
import { pwStrength } from '/js/password-strength.js?v=389e0ed0';
import { el, inp as inpById, qa } from '/js/utils.js?v=c5766a9d';
import { blockingScreenFor } from '/js/config-recovery.js?v=706fc9a7';

export async function checkAuth(onLogin) {
try {
Expand All @@ -14,7 +15,7 @@ export async function checkAuth(onLogin) {
showLoginScreen(onLogin);
return false;
}
return true;
return !blockingScreenFor(e);
}
}

Expand Down Expand Up @@ -70,15 +71,13 @@ export function wirePasswordStrength(inputId, barsId, hintId) {
const bars = qa('.pwbar', el(barsId));
const hint = el(hintId);
if (!inp || !bars?.length) return;
const dim = 'rgba(255,255,255,.1)';
inp.addEventListener('input', () => {
const { score, labelKey, color } = pwStrength(inp.value);
bars.forEach((b, i) => {
b.style.background = inp.value && i < score ? color : dim;
b.style.background = inp.value && i < score ? color : '';
});
if (hint) {
hint.textContent = inp.value && labelKey ? t(labelKey) : '';
hint.style.color = color;
}
});
}
16 changes: 8 additions & 8 deletions public/js/admin-color-control.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { PE_SVG, initInlineEdit, toast, reveal } from '/js/admin-shared.js?v=3870d0d0';
import { PE_SVG, initInlineEdit, toast, reveal } from '/js/admin-shared.js?v=a81b9cbe';
import { t } from '/js/i18n.js?v=1f1ea9c1';
import { html, raw, setHtml } from '/js/html.js?v=c71f8903';
import { qa, q } from '/js/utils.js?v=b1cfbd45';
import { qa, q } from '/js/utils.js?v=c5766a9d';
import { iconSvg } from '/js/icon-set.js?v=34af798f';
import { HUE_NAMES, ROLE_NAMES, TILE_KEYWORDS, pageTheme, tileColor } from '/js/palette.js?v=3fb8ae43';

Expand Down Expand Up @@ -145,7 +145,7 @@ const _swatchFill = v => tileColor(v, pageTheme());
swatchColors?: string[], label?: string }} [opts] */
export function renderColorControl(
container,
{ value = '#0289ff', idPrefix, onChange, variant, swatchColors = CC_SWATCHES, label = 'Color' } = {},
{ value = '#0289ff', idPrefix, onChange, variant, swatchColors = CC_SWATCHES, label = t('common.color') } = {},
) {
const top = variant === 'tile' ? TILE_KEYWORDS : variant === 'badge' ? ROLE_NAMES : [];
const hues = variant ? HUE_NAMES : [];
Expand All @@ -169,10 +169,10 @@ export function renderColorControl(
<div class="row cc-row"><span class="rl">${label}</span><div class="cc-sw">${swatches}</div></div>
<div class="row-wrap reveal cc-tune"><div class="reveal-in">
${hueRow}
${slider('Hue', 'hsb-range hsb-hue', `${idPrefix}-h`, 360, init.h, _ccIco.hueLo, _ccIco.hueHi)}
${slider('Saturation', 'hsb-range', `${idPrefix}-s`, 100, init.s, _ccIco.satLo, _ccIco.satHi)}
${slider('Brightness', 'hsb-range', `${idPrefix}-v`, 100, init.v, _ccIco.brLo, _ccIco.brHi)}
<div class="row ie-row" id="${idPrefix}-code-row"><span class="rl">${t('appearance.colorCode')}</span><span class="rv is-ph">#rrggbb or any CSS color</span><input id="${idPrefix}-hex" type="text" class="d-none"><button class="pe" type="button">${raw(PE_SVG)}</button></div>
${slider(t('appearance.hue'), 'hsb-range hsb-hue', `${idPrefix}-h`, 360, init.h, _ccIco.hueLo, _ccIco.hueHi)}
${slider(t('appearance.saturation'), 'hsb-range', `${idPrefix}-s`, 100, init.s, _ccIco.satLo, _ccIco.satHi)}
${slider(t('appearance.brightness'), 'hsb-range', `${idPrefix}-v`, 100, init.v, _ccIco.brLo, _ccIco.brHi)}
<div class="row ie-row" id="${idPrefix}-code-row"><span class="rl">${t('appearance.colorCode')}</span><span class="rv is-ph">${t('appearance.colorCodePh')}</span><input id="${idPrefix}-hex" type="text" class="d-none"><button class="pe" type="button">${raw(PE_SVG)}</button></div>
</div></div>`,
);
/* The markup carries no style attribute, so the page keeps style-src without
Expand Down Expand Up @@ -280,7 +280,7 @@ export function renderColorControl(
);
initInlineEdit(`${idPrefix}-code-row`, `${idPrefix}-hex`, {
root: container,
placeholder: '#rrggbb or any CSS color',
placeholder: t('appearance.colorCodePh'),
onCommit(val) {
if (mode !== 'color' && val === kwName(mode)) return;
const { value: parsed, ok } = normalizeColorInput(val);
Expand Down
6 changes: 3 additions & 3 deletions public/js/admin-drag.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
it is near, and which classes say so. */

import { applyDrop, canJoinFolder, folderRowZone } from '/js/admin-drag-logic.js?v=6b767e76';
import { state } from '/js/admin-state.js?v=831e219e';
import { snapshotItems } from '/js/admin-save-logic.js?v=60a82419';
import { qa } from '/js/utils.js?v=b1cfbd45';
import { state } from '/js/admin-state.js?v=af772a1b';
import { snapshotItems } from '/js/admin-save-logic.js?v=8389782f';
import { qa } from '/js/utils.js?v=c5766a9d';

/** @type {(before: unknown) => void} */
let _save = () => {};
Expand Down
14 changes: 7 additions & 7 deletions public/js/admin-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
The filter and the collapsed folders are in admin-state.js, because the page
reads and writes them too. */

import { collapsedFolders, filter, state } from '/js/admin-state.js?v=831e219e';
import { snapshotItems } from '/js/admin-save-logic.js?v=60a82419';
import { reorderItems } from '/js/admin-logic.js?v=cbb7417d';
import { initDrag, wireRowDrag } from '/js/admin-drag.js?v=7fe4a0bf';
import { paintIcon } from '/js/admin-shared.js?v=3870d0d0';
import { clr, el, qa, setUserText } from '/js/utils.js?v=b1cfbd45';
import { collapsedFolders, filter, state } from '/js/admin-state.js?v=af772a1b';
import { snapshotItems } from '/js/admin-save-logic.js?v=8389782f';
import { reorderItems } from '/js/admin-logic.js?v=fc7f0836';
import { initDrag, wireRowDrag } from '/js/admin-drag.js?v=18af71d8';
import { paintIcon } from '/js/admin-shared.js?v=a81b9cbe';
import { clr, el, qa, setUserText } from '/js/utils.js?v=c5766a9d';
import { html, raw, setHtml } from '/js/html.js?v=c71f8903';
import { t } from '/js/i18n.js?v=1f1ea9c1';
import { sizeLabel } from '/js/admin-widget-form.js?v=bcfb87e2';
import { sizeLabel } from '/js/admin-widget-form.js?v=ef53bce9';
import { widgetGlyph } from '/js/widget-glyphs.js?v=648cc374';
import { iconSvg } from '/js/icon-set.js?v=34af798f';

Expand Down
4 changes: 4 additions & 0 deletions public/js/admin-logic.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ export function clearsStoredPassword({ enabled, wasEnabled, passwordSet }) {
return !enabled && !!wasEnabled && !!passwordSet;
}

export function needsCurrentPassword({ enabled, wasEnabled, passwordSet, newPassword }) {
return !!wasEnabled && !!passwordSet && (!enabled || !!(newPassword || '').length);
}

/* 'unavailable' must not fall through to the custom editor. The server
withholds that widget's config, so empty fields read as lost settings. */
export function widgetConfigMode(type, reg) {
Expand Down
29 changes: 26 additions & 3 deletions public/js/admin-save-logic.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,21 +50,44 @@ export function snapshotItems(items) {
/** Run `write` and undo the local change when it did not reach the server.
Without this the list shows a dashboard the server does not have. `write`
reports failure by resolving false; a throw is re-raised after the restore.
`superseded` reports a later change still waiting to save. Its write carries
this change too, so restoring here would drop the later change.

@template T
@param {{ write: () => Promise<boolean|void>, snapshot: T,
restore: (snapshot: T) => void }} opts
restore: (snapshot: T) => void, superseded?: () => boolean }} opts
@returns {Promise<boolean>} */
export async function saveWithRevert({ write, snapshot, restore }) {
export async function saveWithRevert({ write, snapshot, restore, superseded = () => false }) {
let ok = false;
try {
ok = (await write()) !== false;
} finally {
if (!ok) restore(snapshot);
if (!ok && !superseded()) restore(snapshot);
}
return ok;
}

/** Run writes one at a time, in the order asked. A write asked for while
another runs waits for it instead of being dropped.

@returns {{ run: <R>(write: () => Promise<R>) => Promise<R>, pending: () => number }} */
export function serialWrites() {
/** @type {Promise<unknown>} */
let tail = Promise.resolve();
let pending = 0;
return {
run(write) {
pending++;
const p = tail.then(write).finally(() => {
pending--;
});
tail = p.catch(() => {});
return p;
},
pending: () => pending,
};
}

/** Put `item` where the item with `id` currently is, or append it. Match by id,
never by position: a position goes stale as soon as items move, and writing
past the end grows the array with holes that JSON turns into nulls.
Expand Down
Loading
Loading