Skip to content

Commit 8d4187c

Browse files
committed
Replace File System Access API save with dev server POST /save
⌘S now saves everything (slide content + notes) to disk via `server.js`, no file picker dialogs, works in any browser. Start with `node server.js`.
1 parent 3b2c2be commit 8d4187c

3 files changed

Lines changed: 162 additions & 77 deletions

File tree

server.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Dev server with save endpoint for the slide presentation.
2+
// Usage: node server.js [port]
3+
// Serves static files and accepts POST /save to write the HTML back to disk.
4+
5+
const http = require('http');
6+
const fs = require('fs');
7+
const path = require('path');
8+
9+
const PORT = parseInt(process.argv[2] || '8080', 10);
10+
const ROOT = __dirname;
11+
const SLIDE_FILE = path.join(ROOT, 'the-future-of-ruby-documentation.html');
12+
13+
const MIME = {
14+
'.html': 'text/html; charset=utf-8',
15+
'.css': 'text/css',
16+
'.js': 'application/javascript',
17+
'.json': 'application/json',
18+
'.png': 'image/png',
19+
'.jpg': 'image/jpeg',
20+
'.jpeg': 'image/jpeg',
21+
'.gif': 'image/gif',
22+
'.svg': 'image/svg+xml',
23+
'.ico': 'image/x-icon',
24+
'.woff2': 'font/woff2',
25+
'.pdf': 'application/pdf',
26+
};
27+
28+
const server = http.createServer((req, res) => {
29+
// Save endpoint — writes the POST body to the slide file
30+
if (req.method === 'POST' && req.url === '/save') {
31+
const chunks = [];
32+
req.on('data', chunk => chunks.push(chunk));
33+
req.on('end', () => {
34+
try {
35+
fs.writeFileSync(SLIDE_FILE, Buffer.concat(chunks));
36+
res.writeHead(200, {
37+
'Content-Type': 'text/plain',
38+
'Access-Control-Allow-Origin': '*',
39+
});
40+
res.end('saved');
41+
console.log(`[${new Date().toLocaleTimeString()}] Saved ${path.basename(SLIDE_FILE)}`);
42+
} catch (err) {
43+
res.writeHead(500, { 'Content-Type': 'text/plain' });
44+
res.end('Error: ' + err.message);
45+
console.error('Save failed:', err.message);
46+
}
47+
});
48+
return;
49+
}
50+
51+
// Static file serving
52+
const urlPath = decodeURIComponent(req.url.split('?')[0]);
53+
let filePath = path.join(ROOT, urlPath === '/' ? 'the-future-of-ruby-documentation.html' : urlPath);
54+
filePath = path.normalize(filePath);
55+
56+
// Prevent directory traversal
57+
if (!filePath.startsWith(ROOT)) {
58+
res.writeHead(403);
59+
res.end('Forbidden');
60+
return;
61+
}
62+
63+
fs.readFile(filePath, (err, data) => {
64+
if (err) {
65+
res.writeHead(404, { 'Content-Type': 'text/plain' });
66+
res.end('Not found: ' + urlPath);
67+
return;
68+
}
69+
const ext = path.extname(filePath).toLowerCase();
70+
res.writeHead(200, { 'Content-Type': MIME[ext] || 'application/octet-stream' });
71+
res.end(data);
72+
});
73+
});
74+
75+
server.listen(PORT, () => {
76+
console.log(`Dev server running at http://localhost:${PORT}`);
77+
console.log(`Serving: ${ROOT}`);
78+
console.log(`Save target: ${path.basename(SLIDE_FILE)}`);
79+
console.log(`Press Ctrl+C to stop\n`);
80+
});

slides_outline.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,9 +303,24 @@ All supporting research is in `research/`:
303303

304304
## Working with the Slides
305305

306+
### Dev Server
307+
308+
Start the dev server (serves files + enables saving):
309+
310+
```bash
311+
node server.js # http://localhost:8080
312+
node server.js 3000 # custom port
313+
```
314+
315+
Open `http://localhost:8080` in any browser. The slides are served as the default page.
316+
306317
### Editing
307318

308-
Open `the-future-of-ruby-documentation.html` in a browser. Press **E** or hover the top-left corner to enter edit mode. Click any text to edit. **Ctrl+S** saves directly back to the file (Chrome/Edge only — first save prompts for file location, subsequent saves are instant).
319+
Press **E** or hover the top-left corner to enter edit mode. Click any text to edit. Press **N** to open the speaker notes editor. **⌘S** saves everything (slide content + notes) back to the file via the dev server — works from anywhere, any browser, no dialogs.
320+
321+
### Presenter Mode
322+
323+
Press **S** to open the presenter window (timer, current/next slide, speaker notes). Press **B** to blackout the main window.
309324

310325
### Exporting to PDF
311326

0 commit comments

Comments
 (0)