Files
ink-website/scripts/capture/seed-vault.cjs
T
Your Name 4cd68793c9
Deploy to GitHub Pages / build (push) Failing after 57s
Deploy to GitHub Pages / deploy (push) Skipped
sdfsdf
2026-10-15 15:05:50 +02:00

264 lines
15 KiB
JavaScript

'use strict';
// Builds a believable notes folder for the website screenshots. Usage: seedVault(dir)
const fs = require('node:fs');
const path = require('node:path');
const w = (root, rel, text) => {
const f = path.join(root, rel);
fs.mkdirSync(path.dirname(f), { recursive: true });
fs.writeFileSync(f, text);
};
const fm = (o) => {
const lines = Object.entries(o).map(([k, v]) => `${k}: ${Array.isArray(v) ? `[${v.join(', ')}]` : v}`);
return `---\n${lines.join('\n')}\n---\n`;
};
// [title, icon, tags, links, one-line body]
const STUBS = [
['Meeting notes', '🗓️', ['work'], ['Roadmap', 'Release checklist'], 'Weekly sync. Decided to ship Linux first and test the Windows installer on a clean machine before announcing it.'],
['Release checklist', '✅', ['work', 'ink'], ['Roadmap', 'Windows build', 'Docs'], 'Version bump, changelog, AppImage, checksum, website config.'],
['Windows build', '🪟', ['work', 'ink'], ['Release checklist', 'Roadmap'], 'NSIS installer is configured. Needs a test pass on a clean Windows install.'],
['Docs', '📖', ['work', 'ink'], ['Roadmap', 'Keyboard shortcuts'], 'Where notes live, Obsidian compatibility, shortcuts, privacy.'],
['Keyboard shortcuts', '⌨️', ['ink'], ['Docs'], 'Ctrl+K palette, Ctrl+N new page, Ctrl+G graph.'],
['Ink website', '🌐', ['work', 'ink'], ['Roadmap', 'Docs', 'Typography'], 'Static site. Calm, dark first, no analytics.'],
['Typography', '🔤', ['learning', 'ideas'], ['Ink website', 'Colour notes'], 'Inter for UI, a mono for code. Keep the measure around 66 characters.'],
['Colour notes', '🎨', ['learning', 'ideas'], ['Typography', 'Ink website'], 'Warm blush accent on near-black. Keep contrast above 4.5:1.'],
['Ideas', '💡', ['ideas'], ['Canvas experiments', 'Plugin ideas', 'Roadmap'], 'Things I might build one day.'],
['Canvas experiments', '🧪', ['ideas', 'ink'], ['Ideas', 'Project map'], 'Alignment guides, grouping, export. What else would make a canvas calmer?'],
['Plugin ideas', '🧩', ['ideas'], ['Ideas'], 'Probably not. Keep the core small.'],
['Reading list', '📚', ['reading'], ['The Pragmatic Programmer', 'A Pattern Language', 'Designing Data-Intensive Applications', 'Meeting notes'], 'Books and articles I want to get to.'],
['The Pragmatic Programmer', '📘', ['reading'], ['Reading list', 'Software craft'], 'Tracer bullets, orthogonality, DRY. Re-read every few years.'],
['A Pattern Language', '🏛️', ['reading', 'learning'], ['Reading list', 'Software craft'], 'Patterns that make places feel alive. Applies to software more than expected.'],
['Designing Data-Intensive Applications', '📗', ['reading', 'learning'], ['Reading list', 'SQLite notes'], 'Storage engines, replication, the trouble with distributed clocks.'],
['Software craft', '🛠️', ['learning'], ['The Pragmatic Programmer', 'A Pattern Language', 'Code review'], 'Small tools, sharp edges, patient iteration.'],
['Code review', '🔍', ['learning', 'work'], ['Software craft'], 'Review the change, not the person. Ask questions first.'],
['SQLite notes', '🗃️', ['learning', 'ink'], ['Designing Data-Intensive Applications', 'Full-text search'], 'FTS5, WAL mode, and why a single file is enough.'],
['Full-text search', '🔎', ['learning', 'ink'], ['SQLite notes', 'Roadmap'], 'Index lives next to the notes and can be rebuilt any time.'],
['Trip to Lisbon', '✈️', ['personal'], ['Packing list', 'Food to try'], 'Four days in April. Trams, tiles and custard tarts.'],
['Packing list', '🎒', ['personal'], ['Trip to Lisbon'], 'Light jacket, walking shoes, charger, notebook.'],
['Food to try', '🥐', ['personal'], ['Trip to Lisbon', 'Sourdough'], 'Pastéis de nata, grilled sardines, ginjinha.'],
['Sourdough', '🍞', ['personal'], ['Food to try'], '500 g flour, 350 g water, 100 g starter, 10 g salt. Fold every 30 minutes.'],
['Garden', '🌱', ['personal'], ['Sourdough', 'Planting calendar'], 'Tomatoes, basil, and a lemon tree that refuses to flower.'],
['Planting calendar', '📆', ['personal'], ['Garden'], 'March: sow tomatoes indoors. May: harden off and plant out.'],
['Running', '🏃', ['personal'], ['Training plan'], 'Easy runs mostly. Pace is a distraction.'],
['Training plan', '📈', ['personal'], ['Running'], 'Three runs a week, one long. Add 10% at most.'],
['Book club', '☕', ['reading', 'personal'], ['Reading list', 'A Pattern Language'], 'First Thursday of the month. Next: The Overstory.'],
['Gift ideas', '🎁', ['personal', 'ideas'], ['Book club'], 'Good pens, a nice notebook, tickets for something.'],
['Home projects', '🔧', ['personal'], ['Garden', 'Gift ideas'], 'Fix the squeaky door. Paint the hallway.'],
['Learning Rust', '🦀', ['learning'], ['Software craft', 'SQLite notes'], 'Ownership clicked after drawing it on a canvas.'],
['Weekly review', '🗒️', ['work', 'personal'], ['Roadmap', 'Meeting notes', 'Training plan'], 'What went well, what did not, what is next.'],
];
function seedVault(root) {
const stubTitles = new Set(STUBS.map((s) => s[0]));
// The hero page.
w(root, 'Roadmap.md', fm({ icon: '🗺️', tags: ['work', 'ink'], created: '2026-09-02' }) + `Where Ink is heading this quarter. Details live in [[Projects]], and every meeting ends up in [[Meeting notes]].
## Q4 goals
- [x] Ship the first public build as an AppImage
- [x] Canvas export to PNG, SVG and PDF
- [x] Version history for pages and canvases
- [x] Windows installer and Linux deb
- [ ] Write the docs, starting with [[Keyboard shortcuts]]
- [ ] Follow the [[Launch plan]]
> [!tip]
> Keep releases small. A calm app needs fewer surprises, not more features.
\`\`\`js
const release = {
version: '1.0.0-beta',
platforms: ['linux', 'windows'],
notes: 'plain .md files, offline, no account',
};
console.log(\`Shipping Ink \${release.version}\`);
\`\`\`
<details>
<summary>Why Linux first?</summary>
The AppImage was the first target built and tested. Windows and the deb followed; macOS is still to do.
</details>
`);
w(root, 'Projects.md', fm({ icon: '📁', tags: ['work'] }) + `# Projects
- [[Ink website]]
- [[Windows build]]
- [[Docs]]
- [[Release checklist]]
Back to the [[Roadmap]]. Work in progress lives on the [[Sprint board]].
`);
w(root, 'Projects/Ink website.md', fm({ icon: '🌐', tags: ['work', 'ink'] }) + 'Static site, calm and dark-first. See [[Typography]] and [[Colour notes]].\n');
// Linking demo page.
w(root, 'Reading list.md', fm({ icon: '📚', tags: ['reading'] }) + `# Reading list
Books and articles I want to get to. I keep notes on each in [[The Pragmatic Programmer]] and [[A Pattern Language]], and the monthly [[Book club]] picks the next one.
## Currently
- [x] [[The Pragmatic Programmer]]
- [ ] [[A Pattern Language]]
- [ ] [[Designing Data-Intensive Applications]]
Highlights from the weekly sync are in [[Roadmap#Q4 goals]].
![[Meeting notes]]
`);
w(root, 'Meeting notes.md', fm({ icon: '🗓️', tags: ['work'] }) + `# Meeting notes
Weekly sync, 7 October.
- Ship Linux first, announce Windows once it is tested
- Keep the docs short and honest
- Review the website copy on Friday
Follow-ups go into [[Roadmap]], [[Release checklist]] and the [[Sprint board]].
`);
for (const [title, icon, tags, links, body] of STUBS) {
if (['Meeting notes', 'Reading list'].includes(title)) continue;
const rel = ['Ink website'].includes(title) ? null : `${title}.md`;
if (!rel) continue;
const refs = links.filter((l) => stubTitles.has(l) || ['Roadmap', 'Projects', 'Project map'].includes(l)).map((l) => `[[${l}]]`);
w(root, rel, fm({ icon, tags }) + `${body}\n\nSee also ${refs.join(', ')}.\n`);
}
// Daily notes.
w(root, 'Daily/2026-10-10.md', fm({ icon: '📅', tags: ['personal'] }) + '- Morning run, easy pace\n- Reviewed the [[Roadmap]]\n- Sourdough went well: see [[Sourdough]]\n');
w(root, 'Daily/2026-10-11.md', fm({ icon: '📅', tags: ['personal'] }) + '- [ ] Write the release notes\n- [ ] Pack for the weekend\n- [x] Call about the [[Trip to Lisbon]] flights\n');
// Board.
w(root, 'Sprint board.md', `---
kanban-plugin: basic
icon: 🗂️
tags: [work, ink]
---
## To do
- [ ] macOS dmg build
- [ ] Docs: where notes are stored
- [ ] Verify checksum instructions
## Doing
- [ ] Website screenshots
- [ ] Changelog for 1.0.0-beta
- [ ] Spellcheck language picker polish
## Done
- [x] AppImage and deb builds
- [x] Windows installer
- [x] Canvas export (PNG, SVG, PDF)
- [x] Version history for canvases
- [x] Graph view filters
%% kanban:settings
\`\`\`
{"kanban-plugin":"basic"}
\`\`\`
%%
`);
// Canvas: cards, a live note preview, a link card, a group, and arrows.
const id = (n) => `n${String(n).padStart(15, '0')}`;
const canvas = {
nodes: [
{ id: id(1), type: 'group', label: 'Q4', x: -60, y: -60, width: 760, height: 520, color: '6' },
{ id: id(2), type: 'text', text: '## Research\n\n- user interviews\n- survey of 40 people\n- what makes notes calm?', x: 0, y: 20, width: 300, height: 190 },
{ id: id(3), type: 'text', text: '## Principles\n\n1. Plain files\n2. Offline first\n3. No lock-in', x: 0, y: 270, width: 300, height: 160, color: '4' },
{ id: id(4), type: 'file', file: 'Roadmap.md', subpath: '#Q4 goals', x: 400, y: 20, width: 270, height: 330 },
{ id: id(5), type: 'link', url: 'https://jsoncanvas.org', x: 860, y: 40, width: 300, height: 150 },
{ id: id(6), type: 'text', text: '## Ship it\n\nAppImage first. Windows and macOS when tested.', x: 860, y: 300, width: 300, height: 140, color: '1' },
],
edges: [
{ id: 'e0000000000000a1', fromNode: id(2), fromSide: 'right', toNode: id(4), toSide: 'left', label: 'informs' },
{ id: 'e0000000000000a2', fromNode: id(3), fromSide: 'right', toNode: id(4), toSide: 'bottom' },
{ id: 'e0000000000000a3', fromNode: id(4), fromSide: 'right', toNode: id(5), toSide: 'left', label: 'format' },
{ id: 'e0000000000000a4', fromNode: id(4), fromSide: 'bottom', toNode: id(6), toSide: 'left' },
],
};
w(root, 'Project map.canvas', JSON.stringify(canvas, null, '\t'));
// A short page for the slash-menu shot, with room under the last line.
w(root, 'Launch plan.md', fm({ icon: '🚀', tags: ['work', 'ink'] }) + `Everything we need before the first public build goes out.
The release is a single AppImage, so the checklist is short: bump the version, write the changelog and publish the checksum next to the download.
Once that is done, the website can go live with a plain download button and nothing else.
`);
// A database: the page holds the schema, every record is a sub-page whose properties are front matter.
const dbSchema = {
properties: [
{ key: 'status', name: 'Status', type: 'select', options: ['Backlog', 'In progress', 'Review', 'Done'], colors: { Backlog: 'gray', 'In progress': 'blue', Review: 'yellow', Done: 'green' } },
{ key: 'area', name: 'Area', type: 'select', options: ['Editor', 'Canvas', 'Databases', 'Themes', 'Site'], colors: { Editor: 'pink', Canvas: 'purple', Databases: 'teal', Themes: 'orange', Site: 'blue' } },
{ key: 'owner', name: 'Owner', type: 'people' },
{ key: 'start', name: 'Start', type: 'date' },
{ key: 'due', name: 'Due', type: 'date' },
{ key: 'progress', name: 'Progress', type: 'number', format: 'bar', max: 100 },
{ key: 'shipped', name: 'Shipped', type: 'checkbox' },
],
views: [
{ id: 'vtable', name: 'Table', type: 'table', sort: { key: 'due', dir: 'asc' }, filters: [], hidden: [], widths: {}, calc: {} },
{ id: 'vboard', name: 'Board', type: 'board', groupBy: 'status', sort: null, filters: [], hidden: [], widths: {}, calc: {} },
{ id: 'vcal', name: 'Calendar', type: 'calendar', dateProp: 'due', sort: null, filters: [], hidden: [], widths: {}, calc: {} },
{ id: 'vtime', name: 'Timeline', type: 'timeline', dateProp: 'start', endProp: 'due', sort: null, filters: [], hidden: [], widths: {}, calc: {} },
],
};
w(root, 'Feature tracker.md', fm({ icon: '🧭', tags: ['work', 'ink'], 'ink-database': 'basic' }) + `Everything planned for the next release, one record per feature.\n\n%% ink-database\n${JSON.stringify(dbSchema, null, 2)}\n%%\n`);
const recs = [
['Color themes', 'Themes', 'Done', ['Mara'], '2026-09-14', '2026-09-26', 100, true],
['Accent colors', 'Themes', 'Done', ['Mara'], '2026-09-20', '2026-10-01', 100, true],
['Table view', 'Databases', 'Done', ['Jo'], '2026-09-08', '2026-10-02', 100, true],
['Board view', 'Databases', 'Review', ['Jo', 'Ana'], '2026-09-24', '2026-10-12', 80, false],
['Calendar view', 'Databases', 'In progress', ['Ana'], '2026-10-01', '2026-10-16', 60, false],
['Timeline view', 'Databases', 'In progress', ['Jo'], '2026-10-05', '2026-10-22', 40, false],
['Formula properties', 'Databases', 'Backlog', ['Jo'], '2026-10-14', '2026-10-30', 10, false],
['Canvas alignment guides', 'Canvas', 'Done', ['Ana'], '2026-09-01', '2026-09-18', 100, true],
['Quick capture', 'Editor', 'Review', ['Mara'], '2026-10-03', '2026-10-14', 90, false],
['Website refresh', 'Site', 'In progress', ['Mara', 'Jo'], '2026-10-08', '2026-10-20', 55, false],
];
for (const [t, area, status, owner, start, due, progress, shipped] of recs) {
w(root, `Feature tracker/${t}.md`, fm({ area, status, owner, start, due, progress, shipped }) + `${t}: notes and decisions live here, as an ordinary page.\n`);
}
// Earlier versions of the Roadmap page, so version history has something to show.
const now = Date.now();
const hist = [
[20 * 60e3, 'Where Ink is heading this quarter.\n\n## Q4 goals\n\n- [x] Ship the first public build as an AppImage\n- [x] Canvas export to PNG, SVG and PDF\n- [x] Version history for pages and canvases\n- [ ] Test the Windows installer on a clean machine\n'],
[3 * 3600e3, 'Where Ink is heading this quarter.\n\n## Q4 goals\n\n- [x] Ship the first public build as an AppImage\n- [x] Canvas export to PNG, SVG and PDF\n- [ ] Version history for pages and canvases\n- [ ] Test the Windows installer on a clean machine\n'],
[26 * 3600e3, 'Where Ink is heading this quarter.\n\n## Q4 goals\n\n- [x] Ship the first public build as an AppImage\n- [ ] Canvas export to PNG, SVG and PDF\n- [ ] Windows installer\n'],
[49 * 3600e3, 'Roadmap for the quarter.\n\n- [ ] AppImage\n- [ ] Canvas export\n- [ ] Windows installer\n'],
[4 * 86400e3, 'Roadmap\n\n- first build\n- canvas\n'],
[9 * 86400e3, 'Ideas for later.\n'],
];
for (const [ago, body] of hist) {
w(root, `.notes/history/Roadmap/${now - ago}.md`, fm({ icon: '🗺️', tags: ['work', 'ink'], created: '2026-09-02' }) + body);
}
// Sidebar niceties and tag colours are settings, returned for the caller.
return {
lastPage: 'Roadmap',
vaultSettings: {
pins: ['Roadmap', 'Feature tracker', 'Sprint board', 'Project map.canvas'],
recent: ['Roadmap', 'Reading list', 'Sprint board'],
tagColors: { work: 'blue', ink: 'pink', reading: 'yellow', learning: 'teal', personal: 'green', ideas: 'purple' },
},
};
}
module.exports = { seedVault };