'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 - [ ] Test the Windows installer on a clean machine - [ ] 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: '0.1.0', platforms: ['linux'], notes: 'plain .md files, offline, no account', }; console.log(\`Shipping Ink \${release.version}\`); \`\`\`
Why Linux first? The AppImage is the only target built and tested so far. Windows and macOS are configured but still need a proper pass.
`); 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 - [ ] Windows installer test pass - [ ] macOS dmg build - [ ] Docs: where notes are stored - [ ] Verify checksum instructions ## Doing - [ ] Website screenshots - [ ] Changelog for 0.1.0 - [ ] Spellcheck language picker polish ## Done - [x] AppImage build - [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. `); // 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', 'Reading list', '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 };