Skip to content

Commit f5604e8

Browse files
committed
implement hot-reload
1 parent 7b6659b commit f5604e8

File tree

2 files changed

+89
-15
lines changed

2 files changed

+89
-15
lines changed

app.js

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,46 @@ function getStartPage() {
156156
return url;
157157
}
158158

159+
// --- SSE Hot Reload ---
160+
const clients = [];
161+
app.get('/hot-reload', (req, res) => {
162+
res.set({
163+
'Content-Type': 'text/event-stream',
164+
'Cache-Control': 'no-cache',
165+
'Connection': 'keep-alive',
166+
});
167+
res.flushHeaders();
168+
res.write('\n');
169+
clients.push(res);
170+
171+
req.on('close', () => {
172+
const idx = clients.indexOf(res);
173+
if (idx !== -1) clients.splice(idx, 1);
174+
});
175+
});
176+
177+
function broadcastReloadSSE() {
178+
console.log("Broadcasting reload to", clients.length, "clients");
179+
for (const client of clients) {
180+
client.write('event: reload\ndata: now\n\n');
181+
}
182+
}
183+
184+
const basePath = process.env.NEXT_PUBLIC_IS_APP_FOLDER ? '/app/' : '.';
185+
186+
// Watch md/ folder and trigger scanFiles on changes if NEXT_AUTOSCAN is true
187+
const isAutoScan = process.env.NEXT_AUTOSCAN === "true";
188+
console.log(`AutoScan is set to ${isAutoScan}`);
189+
if (isAutoScan) {
190+
const mdPath = path.join(basePath, "md");
191+
const watcher = chokidar.watch(mdPath, { ignoreInitial: true, persistent: true, depth: 99 });
192+
watcher.on("all", (event, pathChanged) => {
193+
console.log(`[Watcher] Detected ${event} in ${pathChanged}. Triggering scanFiles...`);
194+
scanFiles("md/", mdPath);
195+
broadcastReloadSSE();
196+
});
197+
}
198+
159199
app.use(express.urlencoded({ extended: true }));
160200
app.use(express.json());
161201

@@ -316,27 +356,13 @@ initKeycloak(app).then(() => {
316356
}
317357
});
318358

319-
const basePath = process.env.NEXT_PUBLIC_IS_APP_FOLDER ? '/app/' : '.';
320-
321359
// Initial scan
322360
scanFiles("md/", path.join(basePath, "md")).then(() => {
323361
scanFonts(path.join(basePath, "assets")).then(() => {
324362
app.listen(process.env.NEXT_PUBLIC_PORT, "0.0.0.0");
325363
});
326364
});
327365

328-
// Watch md/ folder and trigger scanFiles on changes if NEXT_AUTOSCAN is true
329-
const isAutoScan = process.env.NEXT_AUTOSCAN === "true";
330-
console.log(`AutoScan is set to ${isAutoScan}`);
331-
if (isAutoScan) {
332-
const mdPath = path.join(basePath, "md");
333-
const watcher = chokidar.watch(mdPath, { ignoreInitial: true, persistent: true, depth: 99 });
334-
watcher.on("all", (event, pathChanged) => {
335-
console.log(`[Watcher] Detected ${event} in ${pathChanged}. Triggering scanFiles...`);
336-
scanFiles("md/", mdPath);
337-
});
338-
}
339-
340366
// If file is not found, redirect to the start page.
341367
app.use((_, res) => res.redirect(getStartPage()));
342368
});

obsidian.js

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1339,7 +1339,7 @@ function getMermaidScriptEntry() {
13391339
return txt.value;
13401340
}
13411341
1342-
// wenn Reveal „ready“ ist, oder einfach bei DOMContentLoaded
1342+
// If reveal is ready, or simply on DOMContentLoaded
13431343
document.addEventListener("DOMContentLoaded", () => {
13441344
document.querySelectorAll("pre.mermaid").forEach(async (el, i) => {
13451345
const raw = el.textContent;
@@ -1355,6 +1355,51 @@ function getMermaidScriptEntry() {
13551355
</script>`;
13561356
}
13571357

1358+
function getAutoReloadScript() {
1359+
return `<script>
1360+
(function() {
1361+
let scrollY = 0;
1362+
let slideIndices = null;
1363+
const es = new EventSource('/hot-reload');
1364+
es.addEventListener('reload', function() {
1365+
if (window.Reveal && Reveal.getIndices) {
1366+
slideIndices = Reveal.getIndices();
1367+
sessionStorage.setItem("revealSlide", JSON.stringify(slideIndices));
1368+
} else {
1369+
scrollY = window.scrollY;
1370+
sessionStorage.setItem("scrollY", scrollY);
1371+
}
1372+
location.reload();
1373+
});
1374+
1375+
window.addEventListener("DOMContentLoaded", function() {
1376+
// Hide body until slide/scroll is restored
1377+
document.body.style.display = "none";
1378+
if (window.Reveal && Reveal.slide) {
1379+
const saved = sessionStorage.getItem("revealSlide");
1380+
if (saved) {
1381+
const idx = JSON.parse(saved);
1382+
Reveal.slide(idx.h, idx.v || 0);
1383+
sessionStorage.removeItem("revealSlide");
1384+
setTimeout(function() {
1385+
document.body.style.display = "";
1386+
}, 0); // Reveal.slide is synchronous, but just in case
1387+
} else {
1388+
document.body.style.display = "";
1389+
}
1390+
} else {
1391+
const savedY = sessionStorage.getItem("scrollY");
1392+
if (savedY) {
1393+
window.scrollTo(0, parseInt(savedY, 10));
1394+
sessionStorage.removeItem("scrollY");
1395+
}
1396+
document.body.style.display = "";
1397+
}
1398+
});
1399+
})();
1400+
</script>`
1401+
}
1402+
13581403
export async function wrapInPage(html, startPage, req) {
13591404
const pre = `
13601405
<!DOCTYPE html>
@@ -1385,6 +1430,7 @@ export async function wrapInPage(html, startPage, req) {
13851430
</div>
13861431
<script src="/obsidian-page.js"></script>
13871432
${getMermaidScriptEntry()}
1433+
${getAutoReloadScript()}
13881434
<script lang="javascript">
13891435
initFonts('${JSON.stringify(mainFontsArray)}', '${JSON.stringify(
13901436
navFontsArray
@@ -1420,6 +1466,7 @@ export async function wrapAsDocument(html, req) {
14201466
</div>
14211467
<script src="/obsidian-page.js"></script>
14221468
${getMermaidScriptEntry()}
1469+
${getAutoReloadScript()}
14231470
<script lang="javascript">
14241471
initFonts('${JSON.stringify(mainFontsArray)}', '${JSON.stringify(
14251472
navFontsArray
@@ -1503,6 +1550,7 @@ export async function wrapInReveal(reveal, req) {
15031550
<!-- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.13.1/styles/tomorrow.min.css"> -->
15041551
15051552
${getMermaidScriptEntry()}
1553+
${getAutoReloadScript()}
15061554
15071555
<!-- Printing and PDF exports -->
15081556
<script>

0 commit comments

Comments
 (0)