Files
2026-09-05 22:03:25 +08:00

105 lines
4.0 KiB
JavaScript

// SPDX-License-Identifier: Apache-2.0
'use strict';
'require uci';
'require network';
'require view';
'require view.daede.backend as backend';
'require view.daede.styles as styles';
'require view.daede.widgets as widgets';
'require view.daede.dae as daeView';
'require view.daede.daed as daedView';
return view.extend({
_loadContext: function() {
return backend.detectBackend().then(function(ctx) {
return uci.load(ctx.backend.uci).catch(function() {}).then(function() {
/* enumerate L3 devices so the dae LAN/WAN fields offer a dropdown
(still free-text, so undetectable WANs can be typed) */
return network.getDevices().catch(function() { return []; }).then(function(devs) {
ctx.netDevs = (devs || []).map(function(d) { return d.getName(); })
.filter(function(n) { return n && n !== 'dae0'; }).sort();
return ctx;
});
});
});
},
load: function() {
return this._loadContext();
},
render: function(ctx) {
const self = this;
const themeHref = Array.prototype.map.call(document.styleSheets, function(sheet) { return sheet.href || ''; }).join(' ');
document.documentElement.setAttribute('data-daede-theme', /\/argon\//.test(themeHref) ? 'argon' : 'bootstrap');
/* themes signal dark differently — BootstrapDark sets data-darkmode,
Argon just loads a dark stylesheet with no flag. Read the first opaque
background up the tree; a transparent body (CSS not applied yet) must
NOT count as black, else argon light is misdetected as dark. */
try {
const probe = [document.body, document.documentElement];
for (let i = 0; i < probe.length; i++) {
const m = getComputedStyle(probe[i]).backgroundColor.match(/[\d.]+/g);
if (!m) continue;
const a = m.length >= 4 ? parseFloat(m[3]) : 1;
if (a < 0.1) continue; // transparent — keep looking
if (0.299 * m[0] + 0.587 * m[1] + 0.114 * m[2] < 128)
document.documentElement.setAttribute('data-darkmode', 'true');
break; // first opaque background decides
}
} catch (e) {}
const redrawBackend = function(name, message) {
self._backendHint = message;
return self._loadContext()
.then(function(nextCtx) { return self.render(nextCtx); })
.then(function(nextRoot) {
const current = document.querySelector('.dd-config-page');
if (!current) return;
Array.prototype.forEach.call(current.querySelectorAll('.dd-status-card'), function(card) {
if (card._ddCleanup) card._ddCleanup();
});
current.replaceWith(nextRoot);
if (self._backendHintTimer) clearTimeout(self._backendHintTimer);
self._backendHintTimer = setTimeout(function() {
self._backendHint = '';
const hint = document.querySelector('.dd-config-page .dd-backend-help');
if (hint) hint.textContent = '';
}, 3500);
});
};
const listenAddr = uci.get('daed', 'config', 'listen_addr') || backend.BACKENDS.daed.defaultListen;
const children = [
E('style', {}, styles.CSS),
widgets.renderStatusCard(ctx, listenAddr),
widgets.renderBackendSwitcher(ctx, redrawBackend, self._backendHint)
].filter(function(node) { return !!node; });
if (!ctx.installed[ctx.name]) {
children.push(E('div', { 'class': 'dd-card dd-warning' }, _('Selected backend is not installed. Install dae or daed from the package feed first.')));
} else if (ctx.name === 'dae') {
children.push(daeView.renderDaeImportBanner());
children.push(daeView.renderDaeForms(ctx));
children.push(daeView.renderDaeEditor());
} else {
children.push(daedView.renderDaedSettings());
}
return Promise.all(children.map(function(child) {
return child && child.then ? child : Promise.resolve(child);
})).then(function(nodes) {
return E('div', { 'class': 'dd-wrap dd-config-page' }, nodes.filter(function(n) { return !!n; }));
});
},
/* this view drives its own save buttons (dae form / daed settings / manual
editor) — suppress LuCI's global Save/Apply/Reset footer so there is one
unambiguous primary action per backend */
handleSave: null,
handleSaveApply: null,
handleReset: null
});