Files
sow-nodebb-theme/prototype/seed.js
T
archvillainetteandClaude Opus 5 ea36116e62 prototype(#53): palette candidates as runtime token swaps
Throwaway. Tokenises the theme's literal colours so candidate palettes can be
swapped in the browser, adds three candidates (Plum Noir one-metal, two-metal
pewter, two-metal plus a champagne inset), a WCAG checker, a seeder and the
screenshots they are judged on.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 16:31:41 +02:00

89 lines
4.8 KiB
JavaScript

// Throwaway seeder for #53: categories + one dense topic, over the REST API.
// node prototype/seed.js (needs the dev stack up on :4567)
const BASE = process.env.BASE || 'http://localhost:4567';
const USER = 'admin', PASS = 'Admin12345!';
let cookie = '';
async function req(path, opts = {}) {
const res = await fetch(BASE + path, {
...opts,
redirect: 'manual',
headers: { 'Content-Type': 'application/json', Cookie: cookie, ...(opts.headers || {}) },
});
const set = res.headers.getSetCookie?.() || [];
if (set.length) cookie = set.map((c) => c.split(';')[0]).join('; ');
const text = await res.text();
try { return { status: res.status, body: JSON.parse(text) }; } catch { return { status: res.status, body: text }; }
}
const CATS = [
['The Gate House', 'Announcements from the Warden of the Gate. Read before you post.'],
['The Velvet Ledger', 'In-character correspondence, letters, and the record of the city.'],
['The Undercroft', 'Rules lawyering, builds, and mechanics. Bring numbers.'],
['Petitions', 'Support, bug reports, and requests to the staff.'],
['The Long Table', 'Off-topic. Keep it civil, keep it quiet.'],
];
const POSTS = [
'The lamplighters have not come to the Ashgate quarter in nine nights. The Watch says this is a matter of budget. The Watch says a great many things.',
'It is a matter of budget. The oil comes up the river and the river is being taxed twice, once by the Crown and once by whoever is currently pretending to be the Crown at the Narrows.',
'Nine nights is long enough that people have started leaving their own lamps out. Which is charity, and also a list of which houses have oil.',
'I walked it last week. There are chalk marks on seven doors between the cooperage and the old chapel. Not guild marks. Not any mark I know.',
'Post the marks. If they are what I think they are then this is not a budget problem and the Watch is not being slow, it is being paid.',
'I am not posting the marks in a public thread. Ask me in the Ledger and I will write it out properly.',
'Seconding that. Last time someone posted a sigil here it was scrubbed off every door in the quarter inside a day and we learned nothing.',
'For what it is worth the chapel bell has also stopped, and nobody has said a word about that either.',
'The bell is a rope problem, not a conspiracy. I fixed a bell in Southcant last winter, same silence, same rumours, one frayed rope.',
'Then the rope was cut, in the same nine nights the lamps went out, in the same quarter. You are describing a coincidence as if it were a comfort.',
'Nobody here has said the word out loud so I will: the last time a quarter went dark and quiet, three families sold their houses in a week and none of them were seen again.',
'They were seen. Just not by anyone willing to say where.',
];
(async () => {
await req('/login'); // prime csrf
const cfg = await req('/api/config');
const csrf = cfg.body.csrf_token;
const h = { 'x-csrf-token': csrf };
const login = await req('/login', { method: 'POST', headers: h, body: JSON.stringify({ username: USER, password: PASS }) });
if (login.status >= 400) throw new Error('login failed: ' + login.status + ' ' + JSON.stringify(login.body));
const cfg2 = await req('/api/config');
const h2 = { 'x-csrf-token': cfg2.body.csrf_token };
const cids = [];
for (const [name, description] of CATS) {
const r = await req('/api/v3/categories', { method: 'POST', headers: h2, body: JSON.stringify({ name, description }) });
if (!r.body?.response?.cid) throw new Error('category failed: ' + JSON.stringify(r.body));
cids.push(r.body.response.cid);
console.log('category', name, '->', r.body.response.cid);
}
// a few short topics so the category list has recent activity
const titles = [
'The lamps in the Ashgate quarter',
'Read this before you post in the Ledger',
'Damage reduction stacking, one more time',
'Cannot log in after the patch',
'What is everyone drinking',
];
let denseTid = null;
for (let i = 0; i < titles.length; i++) {
const r = await req('/api/v3/topics', {
method: 'POST', headers: h2,
body: JSON.stringify({ cid: cids[i % cids.length], title: titles[i], content: POSTS[0] }),
});
const tid = r.body?.response?.tid;
console.log('topic', titles[i], '->', tid);
if (i === 0) denseTid = tid;
}
for (const content of POSTS.slice(1)) {
await req(`/api/v3/topics/${denseTid}`, { method: 'POST', headers: h2, body: JSON.stringify({ content }) });
}
// one quoted reply, so candidate C's paper inset has something to render
await req(`/api/v3/topics/${denseTid}`, {
method: 'POST', headers: h2,
body: JSON.stringify({ content: '> They were seen. Just not by anyone willing to say where.\n\nThat is the whole city in one line, and I would like it on a plaque.' }),
});
console.log('dense topic:', BASE + '/topic/' + denseTid);
})();