Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@wipcomputer/markdown-viewer

Package Overview
Dependencies
Maintainers
1
Versions
20
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@wipcomputer/markdown-viewer - npm Package Compare versions

Comparing version
1.1.0
to
1.1.1
+13
-10
markdown-viewer.html

@@ -1041,12 +1041,15 @@ <!DOCTYPE html>

// Render math equations with KaTeX
renderMathInElement(document.getElementById('markdown-content'), {
delimiters: [
{left: '$$', right: '$$', display: true},
{left: '$', right: '$', display: false},
{left: '\\[', right: '\\]', display: true},
{left: '\\(', right: '\\)', display: false}
],
throwOnError: false
});
// Render math equations with KaTeX (only if math delimiters are present)
const contentText = document.getElementById('markdown-content').textContent;
const hasMath = /\$\$/.test(contentText) || /\\\[/.test(contentText) || /\\\(/.test(contentText);
if (hasMath) {
renderMathInElement(document.getElementById('markdown-content'), {
delimiters: [
{left: '$$', right: '$$', display: true},
{left: '\\[', right: '\\]', display: true},
{left: '\\(', right: '\\)', display: false}
],
throwOnError: false
});
}

@@ -1053,0 +1056,0 @@ // Generate Table of Contents and show it

{
"name": "@wipcomputer/markdown-viewer",
"version": "1.1.0",
"version": "1.1.1",
"description": "Live markdown viewer for AI pair-editing. When you collaborate, the updates render instantly. Works with any AI agent and web browser.",

@@ -5,0 +5,0 @@ "type": "module",

+40
-15

@@ -13,3 +13,3 @@ #!/usr/bin/env node

import { createServer } from "node:http";
import { readFileSync, watchFile, unwatchFile, existsSync, statSync } from "node:fs";
import { readFileSync, watch, existsSync, statSync } from "node:fs";
import { resolve, basename, dirname, join, extname } from "node:path";

@@ -66,5 +66,15 @@ import { exec } from "node:child_process";

// Map<absolutePath, { clients: Set<res>, lastMtime: number }>
// Map<absolutePath, { clients: Set<res>, watcher: FSWatcher, lastMtime: number }>
const watchers = new Map();
// SSE keepalive: ping all clients every 30s so connections don't go stale
setInterval(() => {
for (const [, entry] of watchers) {
for (const client of entry.clients) {
try { client.write(`:keepalive\n\n`); }
catch { entry.clients.delete(client); }
}
}
}, 30_000);
function startWatching(filePath) {

@@ -76,15 +86,28 @@ if (watchers.has(filePath)) return;

const entry = { clients: new Set(), lastMtime };
const entry = { clients: new Set(), watcher: null, lastMtime };
watchers.set(filePath, entry);
watchFile(filePath, { interval: 500 }, (curr) => {
if (curr.mtimeMs > entry.lastMtime) {
entry.lastMtime = curr.mtimeMs;
console.log(`File changed: ${basename(filePath)}`);
for (const client of entry.clients) {
try { client.write(`data: reload\n\n`); }
catch { entry.clients.delete(client); }
}
}
});
// Use fs.watch (native OS events) instead of fs.watchFile (polling).
// Debounce to avoid duplicate events (common on macOS).
let debounce = null;
try {
entry.watcher = watch(filePath, () => {
if (debounce) return;
debounce = setTimeout(() => {
debounce = null;
let currMtime = 0;
try { currMtime = statSync(filePath).mtimeMs; } catch {}
if (currMtime > entry.lastMtime) {
entry.lastMtime = currMtime;
console.log(`File changed: ${basename(filePath)}`);
for (const client of entry.clients) {
try { client.write(`data: reload\n\n`); }
catch { entry.clients.delete(client); }
}
}
}, 200);
});
} catch (err) {
console.error(`Watch failed for ${filePath}: ${err.message}`);
}
}

@@ -95,3 +118,3 @@

if (entry && entry.clients.size === 0) {
unwatchFile(filePath);
if (entry.watcher) entry.watcher.close();
watchers.delete(filePath);

@@ -357,5 +380,7 @@ }

process.on("SIGINT", () => {
for (const [path] of watchers) { unwatchFile(path); }
for (const [, entry] of watchers) {
if (entry.watcher) entry.watcher.close();
}
server.close();
process.exit(0);
});