v0.32.6_fix2
This commit is contained in:
1 parent
e1ac899ec0
commit
fc8711d4d8
39 files changed
+695
-232
No files matched your search
@@ -29,6 +29,7 @@ const ctx = {
|
||||
bytes: v => `${v} B`, currentPanel: 'overview', performance: { now: () => now },
|
||||
};
|
||||
vm.createContext(ctx);
|
||||
vm.runInContext(fs.readFileSync('src/admin/web/app/rate-sampling.js', 'utf8'), ctx);
|
||||
vm.runInContext(source, ctx);
|
||||
const module = window.rustProxyBackendDashboard;
|
||||
const data = {
|
||||
|
||||
@@ -122,7 +122,7 @@ with sync_playwright() as p:
|
||||
assert history['before'] == 1 and history['live'] == 46, history
|
||||
assert history['afterSystem'] == 46 and history['afterLive'] == 47, history
|
||||
assert history['newest'] == 500 and '500 B' in history['label'], history
|
||||
assert 'renderNetworkInterfaceCharts(d.network_interfaces || [], appendSample);' in source
|
||||
assert 'renderNetworkInterfaceCharts(live.network_interfaces || [], appendSample);' in source
|
||||
refresh = (WEB / 'app/refresh.js').read_text()
|
||||
assert 'interval: () => options.system_info_seconds' in refresh
|
||||
assert 'renderDiagnostics(latestStatus, false, true);' in refresh
|
||||
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Real diagnostic renderer/CSS in Chromium mobile emulation (not iOS Safari)."""
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
from bs4 import BeautifulSoup
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
WEB = ROOT / 'src/admin/web'
|
||||
source = (WEB / 'app/runtime.js').read_text()
|
||||
soup = BeautifulSoup((WEB / 'index.html').read_text(), 'html.parser')
|
||||
process = soup.select_one('[data-dashboard-widget="process_diagnostics"]')
|
||||
network = soup.select_one('[data-dashboard-widget="network_diagnostics"]')
|
||||
assert process and network
|
||||
# Run the real renderDiagnostics and interface component as shipped, without
|
||||
# creating a fake SSE server or depending on Rust/Cargo in this test environment.
|
||||
start = source.index('let systemSamples =')
|
||||
end = source.index('function renderHeaderRules(', start)
|
||||
renderer = source[start:end]
|
||||
html = ('<!doctype html><html data-theme="ultra_dark" data-corner-style="rounded" '
|
||||
'data-dashboard-density="compact"><head><meta name="viewport" '
|
||||
'content="width=device-width, initial-scale=1"><style>'
|
||||
+ (WEB / 'style.css').read_text() + '</style></head>'
|
||||
'<body style="margin:0"><div style="height:1100px">Earlier dashboard cards</div>'
|
||||
'<section id="panel-overview"><div id="dashboard-overview-grid" '
|
||||
'class="stats-grid dashboard-overview-grid is-aligned">'
|
||||
+ str(process) + str(network) + '</div></section>'
|
||||
'<div class="vhost-policy-pills"><small class="pill">H1</small>'
|
||||
'<small class="vhost-backend-protocol-badge" data-observed="true">AUTO → H2</small></div>'
|
||||
'<div style="height:900px">Footer</div></body></html>')
|
||||
stubs = '''
|
||||
const dummy = new Map();
|
||||
const $ = id => document.getElementById(id) || (() => {
|
||||
if (!dummy.has(id)) dummy.set(id, { textContent:'-', value:'', hidden:false,
|
||||
classList:{ toggle(){} }, setAttribute(){}, removeAttribute(){}, append(){},
|
||||
replaceChildren(){}, closest(){ return {hidden:false}; } });
|
||||
return dummy.get(id);
|
||||
})();
|
||||
const envelope = {config:{cache:{enabled:true,cold_enabled:false}}};
|
||||
const t = key => ({networkInterfacesVisibleCount:'{visible} / {total}',
|
||||
networkInterfaceRate:'Rate',networkInterfacesHistory:'Traffic',hideMetric:'Hide',
|
||||
showMetric:'Show',hiddenInterfaces:'Hidden',networkNoInterfaces:'None',
|
||||
networkNoVisibleInterfaces:'None visible',notEnabled:'Disabled',
|
||||
activeListeners:'Listeners',restartPending:'Restart',realSample:'Sampled',
|
||||
threads:'threads',unavailable:'Unavailable',unlimited:'Unlimited'})[key] || key;
|
||||
const bytes = value => `${Number(value)} B`;
|
||||
const locale = () => 'en-US';
|
||||
const uptime = seconds => `${seconds}s`;
|
||||
const formatDateTime = seconds => String(seconds);
|
||||
const action = (text, fn) => { const node=document.createElement('button'); node.textContent=text;
|
||||
node.addEventListener('click',fn); return node; };
|
||||
const el = (tag, text, cls) => {const node=document.createElement(tag);
|
||||
if(text!==undefined) node.textContent=text; if(cls) node.className=cls; return node;};
|
||||
const updateCacheMemorySizing = () => {};
|
||||
const rustProxyRateSampling = {cadence:()=>3,difference:(previous,values,at)=>({next:{values,at},
|
||||
deltas:previous?values.map((v,i)=>v-previous.values[i]):null,seconds:3})};
|
||||
'''
|
||||
|
||||
with sync_playwright() as p:
|
||||
browser = p.chromium.launch(headless=True, executable_path=shutil.which('chromium'), args=['--no-sandbox'])
|
||||
ctx = browser.new_context(viewport={'width':393,'height':844}, device_scale_factor=3,
|
||||
is_mobile=True, has_touch=True)
|
||||
page = ctx.new_page()
|
||||
page.set_content(html, wait_until='domcontentloaded')
|
||||
page.on('pageerror', lambda error: print('BROWSER ERROR:', error))
|
||||
page.add_script_tag(content=stubs + renderer)
|
||||
result = page.evaluate('''() => {
|
||||
const system = {source:'sysinfo', platform:'Linux', process_threads:5,
|
||||
process_cpu_percent:5,process_rss_bytes:100,cpu_percent:15,
|
||||
network_rx_bytes_per_second:20, network_tx_bytes_per_second:10,
|
||||
sampled_at_ms:Date.now(),network_interfaces:[
|
||||
{name:'eth0',rx_bytes_per_second:10,tx_bytes_per_second:5}]};
|
||||
const s = {system,cache:{hits:50,misses:30,bypasses:10,resident_bytes:4096},
|
||||
metrics:{http3_requests:2,mean_duration_ms:0.5},uptime_seconds:100,
|
||||
listeners:{active_endpoints:[]},proxy_listen:null,tls_listen:null};
|
||||
renderDiagnostics(s,true,true);
|
||||
const process = document.querySelector('[data-dashboard-widget="process_diagnostics"]');
|
||||
const network = document.querySelector('[data-dashboard-widget="network_diagnostics"]');
|
||||
const target = document.getElementById('network-interface-charts');
|
||||
const firstRow = target.firstElementChild;
|
||||
const initialDetail = document.getElementById('process-cpu').textContent;
|
||||
const initialHistory = processCpuSamples.length;
|
||||
window.scrollTo(0, Math.round(network.getBoundingClientRect().top + window.scrollY + 125));
|
||||
const y = window.scrollY, height = process.getBoundingClientRect().height;
|
||||
for (let i=0; i<35; i++) {
|
||||
s.live_system = {sampled_at_ms:Date.now(),process_cpu_percent:7+i,
|
||||
process_rss_bytes:150+i, cpu_percent:22+i,
|
||||
network_rx_bytes_per_second:100+i*20,network_tx_bytes_per_second:10+i,
|
||||
network_interfaces:[{name:'eth0',rx_bytes_per_second:100+i*20,
|
||||
tx_bytes_per_second:10+i}]};
|
||||
renderDiagnostics(s,true,false);
|
||||
}
|
||||
const unchangedDetails = document.getElementById('process-cpu').textContent === initialDetail;
|
||||
const recentChart = document.getElementById('process-cpu-chart-value').textContent;
|
||||
const iface = target.querySelector('.network-interface-rate').textContent;
|
||||
const beforeSystem = processCpuSamples.length;
|
||||
const y2 = window.scrollY;
|
||||
s.system = {...system, process_cpu_percent:93,process_rss_bytes:3000,
|
||||
network_rx_bytes_per_second:9000, network_tx_bytes_per_second:900};
|
||||
renderDiagnostics(s,false,true);
|
||||
const slowlyUpdated = document.getElementById('process-cpu').textContent;
|
||||
const pill = document.querySelector('.pill');
|
||||
const origin = document.querySelector('.vhost-backend-protocol-badge');
|
||||
const roundedOrigin = getComputedStyle(origin).borderTopLeftRadius;
|
||||
document.documentElement.dataset.cornerStyle = 'default';
|
||||
const defaultPillRadius = getComputedStyle(pill).borderTopLeftRadius;
|
||||
const defaultOriginRadius = getComputedStyle(origin).borderTopLeftRadius;
|
||||
document.documentElement.dataset.cornerStyle = 'rounded';
|
||||
return {initialDetail,initialHistory,unchangedDetails,recentChart,iface,
|
||||
beforeSystem,afterSystem:processCpuSamples.length,slowlyUpdated,
|
||||
rowStable:target.firstElementChild===firstRow,
|
||||
heightDelta:process.getBoundingClientRect().height-height,
|
||||
scrollDelta:y2-y,postSystemScrollDelta:window.scrollY-y,
|
||||
nestedScroll:getComputedStyle(target).overflowY,
|
||||
nestedMaxHeight:getComputedStyle(target).maxHeight,
|
||||
pillRadius:getComputedStyle(pill).borderTopLeftRadius,
|
||||
originRadius:roundedOrigin,defaultPillRadius,defaultOriginRadius,
|
||||
originColor:getComputedStyle(origin).color};
|
||||
}''')
|
||||
assert result['initialDetail'] == '5%', result
|
||||
assert result['initialHistory'] == 1, result
|
||||
assert result['unchangedDetails'] and result['recentChart'] == '41%', result
|
||||
assert result['beforeSystem'] == 36 == result['afterSystem'], result
|
||||
assert result['slowlyUpdated'] == '93%', result
|
||||
assert result['rowStable'] and '780 B' in result['iface'], result
|
||||
assert abs(result['heightDelta']) < 2 and abs(result['scrollDelta']) < 2, result
|
||||
assert abs(result['postSystemScrollDelta']) < 2, result
|
||||
assert result['nestedScroll'] == 'visible' and result['nestedMaxHeight'] == 'none', result
|
||||
assert result['pillRadius'] == '8px' and result['originRadius'] == '8px', result
|
||||
assert result['defaultPillRadius'] == result['defaultOriginRadius'], result
|
||||
assert result['originColor'] != '', result
|
||||
print('PASS: 35 live CPU/RSS/network samples, slow Process metadata, stable mobile layout/scroll, badge radii', result)
|
||||
browser.close()
|
||||
@@ -114,10 +114,13 @@ with sync_playwright() as browser_tool:
|
||||
assert moves == 0, (width, moves)
|
||||
assert abs(page.evaluate('window.scrollY')-before) <= 2
|
||||
print(f'PASS: {width}px touch emulation, two metric columns, no overflow or scroll jumps')
|
||||
for style,surface in (('square','0px'), ('subtle','5px'), ('rounded','12px'), ('extra','18px')):
|
||||
for style,surface,control in (('square','0px','0px'), ('subtle','5px','4px'),
|
||||
('rounded','12px','8px'), ('extra','18px','12px')):
|
||||
page.evaluate('(s) => document.documentElement.dataset.cornerStyle=s', style)
|
||||
for selector in ('#vhost-test','#entity-test','#badge-test','.stat'):
|
||||
for selector in ('#vhost-test','#entity-test','.stat'):
|
||||
radius = page.locator(selector).first.evaluate('node=>getComputedStyle(node).borderTopLeftRadius')
|
||||
assert radius == surface, (style,selector,radius)
|
||||
print('PASS: vhost, entity, backend badge and metric corner presets')
|
||||
radius = page.locator('#badge-test').evaluate('node=>getComputedStyle(node).borderTopLeftRadius')
|
||||
assert radius == control, (style,'origin protocol badge',radius)
|
||||
print('PASS: vhost, entity, badge/control and metric corner presets')
|
||||
chromium.close()
|
||||
@@ -21,6 +21,7 @@ const context = {
|
||||
document: { querySelector: selector => widgets.get(selector.match(/data-dashboard-widget="([^"]+)"/)?.[1]) || null },
|
||||
};
|
||||
vm.createContext(context);
|
||||
vm.runInContext(fs.readFileSync('src/admin/web/app/rate-sampling.js', 'utf8'), context);
|
||||
vm.runInContext(source, context);
|
||||
const render = context.window.rustProxyExtraOverview.render;
|
||||
const text = id => field(`overview-extra-${id}`).textContent;
|
||||
|
||||
@@ -16,7 +16,8 @@ function $(id) {
|
||||
let now = 1000;
|
||||
const ctx = {window:{},currentPanel:'overview',performance:{now:()=>now},fmt:(n,places=0)=>Number(n).toFixed(places),fmtBytes:n=>`${n} B`,$,
|
||||
document:{querySelector:selector=>nodes.get(selector.match(/data-dashboard-widget="([^"]+)"/)?.[1])||null}};
|
||||
vm.createContext(ctx);vm.runInContext(source,ctx);
|
||||
vm.createContext(ctx);vm.runInContext(fs.readFileSync('src/admin/web/app/rate-sampling.js', 'utf8'), ctx);
|
||||
vm.runInContext(source,ctx);
|
||||
const render=ctx.window.rustProxyWideOverview.render;
|
||||
const text=id=>$(id).textContent;
|
||||
const baseline = { revision:1,
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
// Counter-derived chart regression: pauses must not become fake 0/s samples.
|
||||
// Real Cache / Transform chart functions run against simulated DOM and clock.
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import vm from 'node:vm';
|
||||
|
||||
const samplingSource = fs.readFileSync('src/admin/web/app/rate-sampling.js', 'utf8');
|
||||
const panels = {
|
||||
cache: {
|
||||
path: 'src/admin/web/cache-panel.js', stop: ' function configure()',
|
||||
counters: (hit, miss) => ({ hits: hit, misses: miss, bypasses: 0 }),
|
||||
graph: 'cache-live-hit-line', rate: 'cache-live-hit-rate',
|
||||
keys: ['hits', 'misses', 'bypasses'], seconds: 5,
|
||||
},
|
||||
transform: {
|
||||
path: 'src/admin/web/transform-panel.js', stop: ' function countersFor(data)',
|
||||
counters: (hit, miss) => ({ applied: hit, cache_hits: miss, skipped: 0, errors: 0 }),
|
||||
graph: 'transform-live-applied-line', rate: 'transform-live-applied-rate',
|
||||
keys: ['applied', 'cache_hits', 'skipped', 'errors'], seconds: 3,
|
||||
},
|
||||
};
|
||||
|
||||
function fixture(name, config) {
|
||||
const source = fs.readFileSync(config.path, 'utf8');
|
||||
const end = source.indexOf(config.stop);
|
||||
assert(end > 0, `unable to isolate ${name} live chart`);
|
||||
const documentListeners = new Map(), windowListeners = new Map(), nodes = new Map();
|
||||
let now = 1000;
|
||||
const node = id => {
|
||||
if (!nodes.has(id)) nodes.set(id, {
|
||||
value: id.endsWith('-scope') ? '*' : '', textContent: '',
|
||||
attrs: {}, setAttribute(key, value) { this.attrs[key] = value; },
|
||||
});
|
||||
return nodes.get(id);
|
||||
};
|
||||
const document = {
|
||||
hidden: false,
|
||||
addEventListener(name, listener) { documentListeners.set(name, listener); },
|
||||
};
|
||||
const window = {
|
||||
addEventListener(name, listener) { windowListeners.set(name, listener); },
|
||||
rustProxyRefresh: { current: () => ({ overview_seconds: 2, cache_seconds: config.seconds }) },
|
||||
};
|
||||
const ctx = {
|
||||
document, window, $: node, performance: { now: () => now },
|
||||
sparklinePoints: samples => samples.join(','),
|
||||
};
|
||||
vm.createContext(ctx);
|
||||
vm.runInContext(samplingSource, ctx);
|
||||
vm.runInContext(source.slice(0, end) + '\nwindow.liveChartTest = { renderLiveChart, resetLiveChart, liveChartSamples };\n})();', ctx);
|
||||
const chart = window.liveChartTest;
|
||||
const line = () => node(config.graph).attrs.points;
|
||||
const rate = () => node(config.rate).textContent;
|
||||
const length = () => chart.liveChartSamples[config.keys[0]].length;
|
||||
const sample = (stamp, hits, misses) => {
|
||||
now = stamp;
|
||||
chart.renderLiveChart(config.counters(hits, misses));
|
||||
};
|
||||
return { ctx, chart, line, rate, length, sample, document, documentListeners, windowListeners };
|
||||
}
|
||||
|
||||
for (const [name, config] of Object.entries(panels)) {
|
||||
const f = fixture(name, config);
|
||||
f.sample(1000, 100, 50); // no deltas exist before the first observation
|
||||
assert.equal(f.length(), 0, `${name}: first reading is a baseline, not 0/s`);
|
||||
assert.equal(f.rate(), '-');
|
||||
f.sample(1000 + config.seconds * 1000, 130, 65);
|
||||
assert.equal(f.rate(), `${(30 / config.seconds).toFixed(1)}/s`);
|
||||
assert.equal(f.length(), 1);
|
||||
f.sample(1000 + config.seconds * 2000, 130, 65);
|
||||
assert.equal(f.rate(), '0.0/s', `${name}: real idle interval is still zero`);
|
||||
assert.equal(f.length(), 2);
|
||||
|
||||
const graphBefore = f.line();
|
||||
const delayed = 1000 + config.seconds * 2000 + 120000;
|
||||
f.sample(delayed, 500, 200);
|
||||
assert.equal(f.rate(), '-', `${name}: long pause cannot be averaged into a misleading rate`);
|
||||
assert.equal(f.length(), 2, `${name}: do not append fake drop`);
|
||||
assert.equal(f.line(), graphBefore, `${name}: keep old chart path`);
|
||||
f.sample(delayed + config.seconds * 1000, 530, 215);
|
||||
assert.equal(f.rate(), `${(30 / config.seconds).toFixed(1)}/s`, `${name}: next fresh interval resumes`);
|
||||
assert.equal(f.length(), 3);
|
||||
|
||||
f.document.hidden = true;
|
||||
f.documentListeners.get('visibilitychange')?.();
|
||||
f.document.hidden = false;
|
||||
f.documentListeners.get('visibilitychange')?.();
|
||||
const afterBackground = delayed + config.seconds * 1000 + 200;
|
||||
f.sample(afterBackground, 533, 219);
|
||||
assert.equal(f.length(), 3, `${name}: even a short background hide must not append a rate`);
|
||||
assert.equal(f.rate(), '-');
|
||||
f.sample(afterBackground + config.seconds * 1000, 533, 219);
|
||||
assert.equal(f.rate(), '0.0/s', `${name}: genuine zero after resume`);
|
||||
assert.equal(f.length(), 4);
|
||||
|
||||
f.windowListeners.get('pagehide')?.();
|
||||
f.sample(afterBackground + config.seconds * 2000, 540, 220);
|
||||
assert.equal(f.length(), 4, `${name}: pagehide / BFCache must rebase`);
|
||||
f.sample(afterBackground + config.seconds * 3000, 550, 230);
|
||||
assert.equal(f.length(), 5);
|
||||
f.sample(afterBackground + config.seconds * 4000, 1, 1);
|
||||
assert.equal(f.length(), 5, `${name}: counters reset must not be interpreted as 0/s`);
|
||||
f.sample(afterBackground + config.seconds * 5000, 3, 3);
|
||||
assert.equal(f.length(), 6, `${name}: counter reset recovery`);
|
||||
}
|
||||
|
||||
// All remaining rate charts share this exact continuity check, including
|
||||
// configured 30-second Cache refresh (not falsely treated as a pause).
|
||||
{
|
||||
const listeners = new Map(), document = { hidden: false, addEventListener(key, fn) { listeners.set(key, fn); } };
|
||||
const context = { window: {}, document };
|
||||
vm.createContext(context);
|
||||
vm.runInContext(samplingSource, context);
|
||||
const sample = vm.runInContext('rustProxyRateSampling', context);
|
||||
let baseline = sample.difference(null, [100], 1000, 30).next;
|
||||
let value = sample.difference(baseline, [130], 31000, 30);
|
||||
assert.equal(value.deltas[0], 30, '30-second chart interval is valid');
|
||||
baseline = value.next;
|
||||
value = sample.difference(baseline, [200], 124000, 30);
|
||||
assert.equal(value.deltas, null, 'large delay is a gap');
|
||||
document.hidden = true;
|
||||
listeners.get('visibilitychange')?.();
|
||||
document.hidden = false;
|
||||
assert.equal(sample.continuous(value.next, 124001, 30), false, 'hide invalidates even a short elapsed period');
|
||||
}
|
||||
console.log('PASS: Cache/Transform history survives hidden tabs, long pauses, BFCache and counter resets; real 0/s and configured cadence remain intact.');
|
||||
@@ -95,7 +95,7 @@ function fixture({ withoutSse = false } = {}) {
|
||||
|
||||
const h = fixture();
|
||||
h.registerViews();
|
||||
assert.equal(h.refresh.current().overview_seconds, 2);
|
||||
assert.equal(h.refresh.current().overview_seconds, 3);
|
||||
assert.equal(h.refresh.current().system_info_seconds, 60);
|
||||
assert.equal(h.refresh.current().traffic_seconds, 5);
|
||||
assert.equal(h.refresh.current().cache_seconds, 5);
|
||||
@@ -103,7 +103,7 @@ assert.equal(h.refresh.current().live_enabled, true);
|
||||
assert.equal(h.streams.length, 0);
|
||||
h.refresh.start(); await h.advance();
|
||||
assert.equal(h.streams.length, 1, "one shared SSE connection");
|
||||
assert.equal(h.streams[0].url, "/api/events?interval=2");
|
||||
assert.equal(h.streams[0].url, "/api/events?interval=3");
|
||||
assert.equal(h.count("/api/traffic"), 0, "no Traffic on Overview");
|
||||
assert.equal(h.count("/api/cache"), 0, "no Cache on Overview");
|
||||
assert.equal(h.count("/api/system-info"), 1, "Overview uses slow diagnostics");
|
||||
@@ -231,7 +231,7 @@ const invalid = runInNewContext(scheduler + ";normalizeRefreshPreferences({ live
|
||||
liveFallback: false, stopLive() {}, startLive() {}, $: () => null
|
||||
});
|
||||
assert.equal(invalid.live_enabled, true);
|
||||
assert.equal(invalid.overview_seconds, 2);
|
||||
assert.equal(invalid.overview_seconds, 3);
|
||||
assert.equal(invalid.traffic_seconds, 5);
|
||||
assert.equal(invalid.cache_seconds, 0);
|
||||
assert.equal(invalid.system_info_seconds, 300);
|
||||
|
||||
@@ -26,7 +26,7 @@ const scriptContext = { window, document: { querySelector(selector) {
|
||||
] } }, latestStatus: null, currentPanel: 'overview',
|
||||
performance: { now: () => clock }, t: key => key, fmt: n => `${n}`, bytes: n => `${n} B`, Math,
|
||||
};
|
||||
vm.runInNewContext(src, scriptContext);
|
||||
vm.runInNewContext(fs.readFileSync('src/admin/web/app/rate-sampling.js', 'utf8') + '\n' + src, scriptContext);
|
||||
const widget = window.rustProxyStreamDashboard;
|
||||
const status = { streams: { active: [
|
||||
{ id: 'tcp', enabled: true, protocol: 'tcp' },
|
||||
|
||||
@@ -65,6 +65,7 @@ const applyLayout = prefsSource.slice(prefsSource.indexOf('function applyDashboa
|
||||
const applyPreferences = prefsSource.slice(prefsSource.indexOf('function applyAdminPreferences'), prefsSource.indexOf('try { applyAdminTheme('));
|
||||
vm.runInContext(`${registryPrelude}\n${constants}\n${normalize}\nlet adminPreferences = null;\n${applyLayout}\n${applyPreferences}\nthis.getPrefs = () => adminPreferences;\nthis.applyAdminPreferencesTest = applyAdminPreferences;`, context);
|
||||
window.rustProxyPreferences = { current: () => context.getPrefs() };
|
||||
vm.runInContext(fs.readFileSync('src/admin/web/app/rate-sampling.js', 'utf8'), context);
|
||||
vm.runInContext(streamSource, context);
|
||||
|
||||
// First load (or page reload): defaults hide the new widgets.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from pathlib import Path
|
||||
|
||||
APP_PARTS = ("core.js", "session-resume.js", "refresh.js", "preferences.js", "dashboard-layout-editor.js", "dashboard-inline-layout.js", "stream-dashboard.js", "backend-dashboard.js", "config-history.js", "runtime.js", "settings.js")
|
||||
APP_PARTS = ("core.js", "session-resume.js", "refresh.js", "rate-sampling.js", "preferences.js", "dashboard-layout-editor.js", "dashboard-inline-layout.js", "stream-dashboard.js", "backend-dashboard.js", "config-history.js", "runtime.js", "settings.js")
|
||||
PANEL_PARTS = (
|
||||
"management.js", "drafts.js", "integrations.js", "vhosts.js",
|
||||
"web-server-profiles.js", "web-server.js", "docker.js", "streams.js",
|
||||
|
||||
Reference in new issue
Block a user