mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-05-18 04:25:11 +00:00
## Fix: FAQ save no longer wipes other home page sections Fixes #284 ### Problem Editing FAQ in the customizer and saving caused other home page sections (steps, footer links, hero text) to disappear on reload. Colors could also reset. ### Root cause `initState()` in `customize.js` used `||` (OR) logic for the `home` object — if localStorage had *any* `home.checklist`, it took that and ignored the server config for other fields. Partial localStorage data replaced the full server config instead of merging on top. ### Fix Changed `initState()` to properly layer: `DEFAULTS → server config → localStorage` for all sections. Each field merges independently — a partial localStorage save (e.g., only checklist) no longer wipes steps, footerLinks, or hero fields. Same merge pattern applied to all theme sections for consistency. ### Files changed - `public/customize.js` — `initState()` merge logic - `public/index.html` — cache buster bump - `test-frontend-helpers.js` — regression tests: 1. Partial localStorage (checklist only) preserves steps/footerLinks 2. Server config values survive partial local overrides 3. Full localStorage properly overrides server config ### Testing - `node test-frontend-helpers.js` ✅ - `node test-packet-filter.js` ✅ - `node test-aging.js` ✅ Co-authored-by: Kpa-clawbot <259247574+Kpa-clawbot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
+1388
-1384
File diff suppressed because it is too large
Load Diff
@@ -1769,6 +1769,111 @@ console.log('\n=== analytics.js: hash prefix helpers ===');
|
||||
});
|
||||
}
|
||||
|
||||
// ===== CUSTOMIZE.JS: initState merge behavior =====
|
||||
console.log('\n=== customize.js: initState merge behavior ===');
|
||||
{
|
||||
function loadCustomizeExports(ctx) {
|
||||
const src = fs.readFileSync('public/customize.js', 'utf8');
|
||||
const withExports = src.replace(
|
||||
/\}\)\(\);\s*$/,
|
||||
'window.__customizeExport = { initState: initState, getState: function () { return state; }, getDefaults: function () { return deepClone(DEFAULTS); } };})();'
|
||||
);
|
||||
vm.runInContext(withExports, ctx);
|
||||
for (const k of Object.keys(ctx.window)) ctx[k] = ctx.window[k];
|
||||
return ctx.window.__customizeExport;
|
||||
}
|
||||
|
||||
test('partial local checklist does not wipe steps/footerLinks and keeps server colors', () => {
|
||||
const ctx = makeSandbox();
|
||||
ctx.window.SITE_CONFIG = {
|
||||
home: {
|
||||
heroTitle: 'Server Hero',
|
||||
heroSubtitle: 'Server Subtitle',
|
||||
steps: [{ emoji: '🧪', title: 'Server Step', description: 'from server' }],
|
||||
checklist: [{ question: 'Server Q', answer: 'Server A' }],
|
||||
footerLinks: [{ label: 'Server Link', url: '#/server' }]
|
||||
},
|
||||
theme: { accent: '#123456', navBg: '#222222' },
|
||||
nodeColors: { repeater: '#aa0000' }
|
||||
};
|
||||
ctx.localStorage.setItem('meshcore-user-theme', JSON.stringify({
|
||||
home: { checklist: [{ question: 'Local Q', answer: 'Local A' }] }
|
||||
}));
|
||||
const ex = loadCustomizeExports(ctx);
|
||||
ex.initState();
|
||||
const state = ex.getState();
|
||||
assert.strictEqual(state.home.checklist[0].question, 'Local Q');
|
||||
assert.strictEqual(state.home.steps[0].title, 'Server Step');
|
||||
assert.strictEqual(state.home.footerLinks[0].label, 'Server Link');
|
||||
assert.strictEqual(state.home.heroTitle, 'Server Hero');
|
||||
assert.strictEqual(state.theme.accent, '#123456');
|
||||
assert.strictEqual(state.nodeColors.repeater, '#aa0000');
|
||||
});
|
||||
|
||||
test('server values survive when localStorage has partial overrides', () => {
|
||||
const ctx = makeSandbox();
|
||||
ctx.window.SITE_CONFIG = {
|
||||
home: {
|
||||
heroTitle: 'Server Hero',
|
||||
heroSubtitle: 'Server Subtitle',
|
||||
steps: [{ emoji: '1️⃣', title: 'Server Step', description: 'server' }],
|
||||
footerLinks: [{ label: 'Server Footer', url: '#/s' }]
|
||||
},
|
||||
theme: { accent: '#111111', navBg: '#222222', navText: '#333333' },
|
||||
typeColors: { ADVERT: '#00aa00', REQUEST: '#aa00aa' }
|
||||
};
|
||||
ctx.localStorage.setItem('meshcore-user-theme', JSON.stringify({
|
||||
home: { heroTitle: 'Local Hero' },
|
||||
theme: { accent: '#999999' },
|
||||
typeColors: { ADVERT: '#ff00ff' }
|
||||
}));
|
||||
const ex = loadCustomizeExports(ctx);
|
||||
ex.initState();
|
||||
const state = ex.getState();
|
||||
assert.strictEqual(state.home.heroTitle, 'Local Hero');
|
||||
assert.strictEqual(state.home.heroSubtitle, 'Server Subtitle');
|
||||
assert.strictEqual(state.home.steps[0].title, 'Server Step');
|
||||
assert.strictEqual(state.home.footerLinks[0].label, 'Server Footer');
|
||||
assert.strictEqual(state.theme.accent, '#999999');
|
||||
assert.strictEqual(state.theme.navBg, '#222222');
|
||||
assert.strictEqual(state.typeColors.ADVERT, '#ff00ff');
|
||||
assert.strictEqual(state.typeColors.REQUEST, '#aa00aa');
|
||||
});
|
||||
|
||||
test('full localStorage values override server config', () => {
|
||||
const ctx = makeSandbox();
|
||||
ctx.window.SITE_CONFIG = {
|
||||
home: {
|
||||
heroTitle: 'Server Hero',
|
||||
heroSubtitle: 'Server Subtitle',
|
||||
steps: [{ emoji: 'S', title: 'Server Step', description: 'server' }],
|
||||
checklist: [{ question: 'Server Q', answer: 'Server A' }],
|
||||
footerLinks: [{ label: 'Server Link', url: '#/server' }]
|
||||
},
|
||||
theme: { accent: '#101010' }
|
||||
};
|
||||
ctx.localStorage.setItem('meshcore-user-theme', JSON.stringify({
|
||||
home: {
|
||||
heroTitle: 'Local Hero',
|
||||
heroSubtitle: 'Local Subtitle',
|
||||
steps: [{ emoji: 'L', title: 'Local Step', description: 'local' }],
|
||||
checklist: [{ question: 'Local Q', answer: 'Local A' }],
|
||||
footerLinks: [{ label: 'Local Link', url: '#/local' }]
|
||||
},
|
||||
theme: { accent: '#abcdef', navBg: '#fedcba' }
|
||||
}));
|
||||
const ex = loadCustomizeExports(ctx);
|
||||
ex.initState();
|
||||
const state = ex.getState();
|
||||
assert.strictEqual(state.home.heroTitle, 'Local Hero');
|
||||
assert.strictEqual(state.home.heroSubtitle, 'Local Subtitle');
|
||||
assert.strictEqual(state.home.steps[0].title, 'Local Step');
|
||||
assert.strictEqual(state.home.checklist[0].question, 'Local Q');
|
||||
assert.strictEqual(state.home.footerLinks[0].label, 'Local Link');
|
||||
assert.strictEqual(state.theme.accent, '#abcdef');
|
||||
assert.strictEqual(state.theme.navBg, '#fedcba');
|
||||
});
|
||||
}
|
||||
// ===== SUMMARY =====
|
||||
console.log(`\n${'═'.repeat(40)}`);
|
||||
console.log(` Frontend helpers: ${passed} passed, ${failed} failed`);
|
||||
|
||||
Reference in New Issue
Block a user