pandoc-glance 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/styles.js ADDED
@@ -0,0 +1,323 @@
1
+ // Adapted from pi-markdown-preview's MIT-licensed browser palettes.
2
+ export const DARK_PALETTE = {
3
+ bg: "#0f1117",
4
+ card: "#171b24",
5
+ panel: "#11161f",
6
+ border: "#2d3748",
7
+ borderMuted: "#242b38",
8
+ text: "#e6edf3",
9
+ muted: "#9aa5b1",
10
+ accent: "#5ea1ff",
11
+ warning: "#f9c74f",
12
+ error: "#ff6b6b",
13
+ success: "#73d13d",
14
+ heading: "#f0c674",
15
+ link: "#81a2be",
16
+ linkUrl: "#8a93a1",
17
+ inlineCode: "#8abeb7",
18
+ codeText: "#d8e2cf",
19
+ codeBorder: "#505866",
20
+ quote: "#a6afbb",
21
+ quoteBorder: "#667080",
22
+ rule: "#46505f",
23
+ listBullet: "#8abeb7",
24
+ syntaxComment: "#6a9955",
25
+ syntaxKeyword: "#569cd6",
26
+ syntaxFunction: "#dcdcaa",
27
+ syntaxVariable: "#9cdcfe",
28
+ syntaxString: "#ce9178",
29
+ syntaxNumber: "#b5cea8",
30
+ syntaxType: "#4ec9b0",
31
+ syntaxOperator: "#d4d4d4",
32
+ syntaxPunctuation: "#d4d4d4",
33
+ };
34
+ export const LIGHT_PALETTE = {
35
+ bg: "#f5f7fb",
36
+ card: "#ffffff",
37
+ panel: "#f8fafc",
38
+ border: "#d0d7de",
39
+ borderMuted: "#e0e6ee",
40
+ text: "#1f2328",
41
+ muted: "#57606a",
42
+ accent: "#0969da",
43
+ warning: "#9a6700",
44
+ error: "#cf222e",
45
+ success: "#1a7f37",
46
+ heading: "#8b661f",
47
+ link: "#3f6f9f",
48
+ linkUrl: "#68717c",
49
+ inlineCode: "#436f70",
50
+ codeText: "#344f34",
51
+ codeBorder: "#afb8c1",
52
+ quote: "#57606a",
53
+ quoteBorder: "#8c959f",
54
+ rule: "#afb8c1",
55
+ listBullet: "#467146",
56
+ syntaxComment: "#008000",
57
+ syntaxKeyword: "#0000ff",
58
+ syntaxFunction: "#795e26",
59
+ syntaxVariable: "#001080",
60
+ syntaxString: "#a31515",
61
+ syntaxNumber: "#098658",
62
+ syntaxType: "#267f99",
63
+ syntaxOperator: "#000000",
64
+ syntaxPunctuation: "#000000",
65
+ };
66
+ function cssVariables(palette, fontSizePx, colorScheme) {
67
+ return `
68
+ color-scheme: ${colorScheme};
69
+ --preview-font-size: ${fontSizePx}px;
70
+ --bg: ${palette.bg};
71
+ --card: ${palette.card};
72
+ --panel: ${palette.panel};
73
+ --border: ${palette.border};
74
+ --border-muted: ${palette.borderMuted};
75
+ --text: ${palette.text};
76
+ --muted: ${palette.muted};
77
+ --accent: ${palette.accent};
78
+ --warning: ${palette.warning};
79
+ --error: ${palette.error};
80
+ --success: ${palette.success};
81
+ --heading: ${palette.heading};
82
+ --link: ${palette.link};
83
+ --link-url: ${palette.linkUrl};
84
+ --inline-code: ${palette.inlineCode};
85
+ --code-text: ${palette.codeText};
86
+ --code-border: ${palette.codeBorder};
87
+ --quote: ${palette.quote};
88
+ --quote-border: ${palette.quoteBorder};
89
+ --rule: ${palette.rule};
90
+ --list-bullet: ${palette.listBullet};
91
+ --syntax-comment: ${palette.syntaxComment};
92
+ --syntax-keyword: ${palette.syntaxKeyword};
93
+ --syntax-function: ${palette.syntaxFunction};
94
+ --syntax-variable: ${palette.syntaxVariable};
95
+ --syntax-string: ${palette.syntaxString};
96
+ --syntax-number: ${palette.syntaxNumber};
97
+ --syntax-type: ${palette.syntaxType};
98
+ --syntax-operator: ${palette.syntaxOperator};
99
+ --syntax-punctuation: ${palette.syntaxPunctuation};
100
+ `;
101
+ }
102
+ export function buildPreviewCss(theme, fontSizePx) {
103
+ const initial = theme === "dark" ? DARK_PALETTE : LIGHT_PALETTE;
104
+ const initialMode = theme === "dark" ? "dark" : "light";
105
+ const automaticDark = theme === "auto"
106
+ ? `\n@media (prefers-color-scheme: dark) {\n :root {${cssVariables(DARK_PALETTE, fontSizePx, "dark")} }\n}\n`
107
+ : "";
108
+ return `
109
+ :root {${cssVariables(initial, fontSizePx, initialMode)}
110
+ }
111
+ ${automaticDark}
112
+ * { box-sizing: border-box; }
113
+ html {
114
+ min-height: 100%;
115
+ scroll-padding-top: 24px;
116
+ background: var(--bg);
117
+ }
118
+ body {
119
+ min-height: 100vh;
120
+ margin: 0;
121
+ padding: 28px;
122
+ background: var(--bg);
123
+ color: var(--text);
124
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
125
+ transition: background-color 120ms ease, color 120ms ease;
126
+ }
127
+ #preview-root {
128
+ width: min(1100px, 100%);
129
+ min-height: calc(100vh - 56px);
130
+ margin: 0 auto;
131
+ padding: 28px 32px 44px;
132
+ overflow-wrap: anywhere;
133
+ border: 1px solid var(--border-muted);
134
+ border-radius: 12px;
135
+ background: var(--card);
136
+ box-shadow: 0 8px 30px color-mix(in srgb, var(--bg) 70%, transparent);
137
+ font-size: var(--preview-font-size);
138
+ line-height: 1.62;
139
+ }
140
+ #preview-root > :first-child { margin-top: 0; }
141
+ #preview-root > :last-child { margin-bottom: 0; }
142
+ #preview-root h1,
143
+ #preview-root h2,
144
+ #preview-root h3,
145
+ #preview-root h4,
146
+ #preview-root h5,
147
+ #preview-root h6 {
148
+ margin: 1.35em 0 0.55em;
149
+ color: var(--heading);
150
+ line-height: 1.25;
151
+ letter-spacing: -0.012em;
152
+ scroll-margin-top: 24px;
153
+ }
154
+ #preview-root h1 { font-size: 1.75em; }
155
+ #preview-root h2 { font-size: 1.38em; }
156
+ #preview-root h3 { font-size: 1.16em; }
157
+ #preview-root p,
158
+ #preview-root ul,
159
+ #preview-root ol,
160
+ #preview-root blockquote,
161
+ #preview-root table,
162
+ #preview-root figure { margin-top: 0; margin-bottom: 1em; }
163
+ #preview-root li + li { margin-top: 0.22em; }
164
+ #preview-root li::marker { color: var(--list-bullet); }
165
+ #preview-root a { color: var(--link); text-decoration: none; }
166
+ #preview-root a:hover { text-decoration: underline; }
167
+ #preview-root a.uri,
168
+ #preview-root .uri { color: var(--link-url); }
169
+ #preview-root blockquote {
170
+ margin-left: 0;
171
+ padding: 0.35em 1em;
172
+ border-left: 0.25em solid var(--quote-border);
173
+ border-radius: 0 8px 8px 0;
174
+ background: color-mix(in srgb, var(--quote-border) 9%, transparent);
175
+ color: var(--quote);
176
+ }
177
+ #preview-root pre {
178
+ margin: 1em 0;
179
+ padding: 13px 15px;
180
+ overflow: auto;
181
+ border: 1px solid var(--code-border);
182
+ border-radius: 9px;
183
+ background: var(--panel);
184
+ tab-size: 2;
185
+ }
186
+ #preview-root code {
187
+ color: var(--inline-code);
188
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
189
+ font-size: 0.9em;
190
+ }
191
+ #preview-root pre code { color: var(--code-text); }
192
+ #preview-root :not(pre) > code {
193
+ padding: 0.12em 0.36em;
194
+ border: 1px solid color-mix(in srgb, var(--code-border) 72%, transparent);
195
+ border-radius: 6px;
196
+ background: color-mix(in srgb, var(--code-border) 12%, transparent);
197
+ }
198
+ #preview-root code span.kw,
199
+ #preview-root code span.cf,
200
+ #preview-root code span.im { color: var(--syntax-keyword); font-weight: 600; }
201
+ #preview-root code span.dt { color: var(--syntax-type); font-weight: 600; }
202
+ #preview-root code span.fu,
203
+ #preview-root code span.bu { color: var(--syntax-function); }
204
+ #preview-root code span.va,
205
+ #preview-root code span.ot { color: var(--syntax-variable); }
206
+ #preview-root code span.st,
207
+ #preview-root code span.ss,
208
+ #preview-root code span.sc,
209
+ #preview-root code span.ch { color: var(--syntax-string); }
210
+ #preview-root code span.dv,
211
+ #preview-root code span.bn,
212
+ #preview-root code span.fl { color: var(--syntax-number); }
213
+ #preview-root code span.co { color: var(--syntax-comment); font-style: italic; }
214
+ #preview-root code span.op { color: var(--syntax-operator); }
215
+ #preview-root code span.pp,
216
+ #preview-root code span.pu { color: var(--syntax-punctuation); }
217
+ #preview-root code span.er,
218
+ #preview-root code span.al { color: var(--error); font-weight: 600; }
219
+ #preview-root table {
220
+ display: block;
221
+ max-width: 100%;
222
+ overflow: auto;
223
+ border-collapse: collapse;
224
+ }
225
+ #preview-root th,
226
+ #preview-root td { padding: 7px 12px; border: 1px solid var(--border-muted); }
227
+ #preview-root thead th { background: var(--panel); }
228
+ #preview-root tbody tr:nth-child(even) { background: color-mix(in srgb, var(--panel) 68%, transparent); }
229
+ #preview-root hr { margin: 1.5em 0; border: 0; border-top: 1px solid var(--rule); }
230
+ #preview-root img,
231
+ #preview-root video,
232
+ #preview-root svg { max-width: 100%; height: auto; }
233
+ #preview-root figure { text-align: center; }
234
+ #preview-root figcaption { margin-top: 0.45em; color: var(--muted); font-size: 0.9em; }
235
+ #preview-root math[display="block"],
236
+ #preview-root mjx-container[display="true"] {
237
+ display: block;
238
+ margin: 1em 0;
239
+ overflow-x: auto;
240
+ overflow-y: hidden;
241
+ }
242
+ #preview-root .mermaid-container { margin: 1.1em 0; overflow-x: auto; text-align: center; }
243
+ #preview-root .mermaid-container svg { max-width: 100%; height: auto; }
244
+ #preview-root .mermaid-error {
245
+ padding: 12px 14px;
246
+ border: 1px solid var(--error);
247
+ border-radius: 8px;
248
+ background: var(--panel);
249
+ text-align: left;
250
+ }
251
+ #preview-root .mermaid-error-message {
252
+ margin-bottom: 0.75em;
253
+ color: var(--error);
254
+ font-weight: 600;
255
+ overflow-wrap: anywhere;
256
+ }
257
+ #preview-root .mermaid-error .mermaid-source { margin: 0; }
258
+ .preview-warning {
259
+ margin: 1em 0;
260
+ padding: 10px 12px;
261
+ border: 1px solid color-mix(in srgb, var(--warning) 52%, transparent);
262
+ border-radius: 8px;
263
+ background: color-mix(in srgb, var(--warning) 11%, var(--card));
264
+ color: var(--text);
265
+ font-size: 0.9em;
266
+ }
267
+ .initial-render-error {
268
+ max-width: 760px;
269
+ margin: 8vh auto;
270
+ padding: 22px;
271
+ border: 1px solid color-mix(in srgb, var(--error) 55%, transparent);
272
+ border-radius: 10px;
273
+ background: color-mix(in srgb, var(--error) 8%, var(--card));
274
+ }
275
+ .initial-render-error h1 { color: var(--error) !important; }
276
+ .initial-render-error pre { white-space: pre-wrap; }
277
+ #preview-status {
278
+ position: fixed;
279
+ z-index: 1000;
280
+ right: 18px;
281
+ bottom: 18px;
282
+ max-width: min(560px, calc(100vw - 36px));
283
+ padding: 11px 14px;
284
+ border: 1px solid var(--border);
285
+ border-radius: 9px;
286
+ background: color-mix(in srgb, var(--card) 94%, transparent);
287
+ box-shadow: 0 10px 34px color-mix(in srgb, var(--bg) 62%, transparent);
288
+ color: var(--text);
289
+ font-size: 13px;
290
+ line-height: 1.4;
291
+ white-space: pre-wrap;
292
+ }
293
+ #preview-status[data-level="error"] { border-color: var(--error); }
294
+ #preview-status[data-level="warning"] { border-color: var(--warning); }
295
+ #preview-status[hidden] { display: none; }
296
+ @media (max-width: 720px) {
297
+ body { padding: 0; }
298
+ #preview-root {
299
+ min-height: 100vh;
300
+ padding: 22px 18px 36px;
301
+ border: 0;
302
+ border-radius: 0;
303
+ box-shadow: none;
304
+ }
305
+ }
306
+ @media print {
307
+ html, body { background: #fff; }
308
+ body { padding: 0; }
309
+ #preview-root {
310
+ width: 100%;
311
+ min-height: 0;
312
+ padding: 0;
313
+ border: 0;
314
+ box-shadow: none;
315
+ }
316
+ #preview-status { display: none !important; }
317
+ }
318
+ `;
319
+ }
320
+ export function palettesForClient() {
321
+ return { light: LIGHT_PALETTE, dark: DARK_PALETTE };
322
+ }
323
+ //# sourceMappingURL=styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.js","sourceRoot":"","sources":["../src/styles.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ import { type PreviewFormat } from "./render.js";
2
+ import { PreviewServer, type PreviewServerState } from "./server.js";
3
+ import type { PreviewTheme } from "./styles.js";
4
+ import { DebouncedFileWatcher } from "./watcher.js";
5
+ export interface WatchRenderContext {
6
+ revision: number;
7
+ sourcePath: string;
8
+ resourceRoot: string;
9
+ format: PreviewFormat;
10
+ theme: PreviewTheme;
11
+ fontSizePx: number;
12
+ eventsPath: string;
13
+ resourcePath: string;
14
+ assetPath: string;
15
+ storageKey: string;
16
+ }
17
+ export interface WatchRenderResult {
18
+ html: string;
19
+ assets?: Map<string, string>;
20
+ warnings?: string[];
21
+ }
22
+ export type WatchRenderer = (source: string, context: WatchRenderContext) => Promise<WatchRenderResult>;
23
+ export interface WatchPreviewStatus {
24
+ revision: number;
25
+ status: "success" | "error";
26
+ error?: string;
27
+ }
28
+ export interface StartWatchPreviewOptions {
29
+ inputPath: string;
30
+ format: PreviewFormat;
31
+ theme: PreviewTheme;
32
+ fontSizePx: number;
33
+ port?: number;
34
+ debounceMs?: number;
35
+ renderer?: WatchRenderer;
36
+ onLog?: (message: string, level: "info" | "warning" | "error") => void;
37
+ onStatus?: (status: WatchPreviewStatus) => void;
38
+ }
39
+ export declare class WatchPreviewSession {
40
+ #private;
41
+ readonly inputPath: string;
42
+ readonly server: PreviewServer;
43
+ readonly initialError: string | null;
44
+ constructor(options: {
45
+ inputPath: string;
46
+ server: PreviewServer;
47
+ watcher: DebouncedFileWatcher;
48
+ initialError: string | null;
49
+ getRenderChain: () => Promise<void>;
50
+ onBeforeClose: () => void;
51
+ });
52
+ get url(): string;
53
+ get state(): PreviewServerState;
54
+ waitForIdle(): Promise<void>;
55
+ close(): Promise<void>;
56
+ get closeRequested(): boolean;
57
+ }
58
+ export declare function startWatchPreview(options: StartWatchPreviewOptions): Promise<WatchPreviewSession>;
59
+ //# sourceMappingURL=watch-preview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watch-preview.d.ts","sourceRoot":"","sources":["../src/watch-preview.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,aAAa,EACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,aAAa,CAAC;IACtB,KAAK,EAAE,YAAY,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAExG,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,aAAa,CAAC;IACtB,KAAK,EAAE,YAAY,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,KAAK,IAAI,CAAC;IACvE,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;CACjD;AAMD,qBAAa,mBAAmB;;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;gBAQzB,OAAO,EAAE;QACnB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,aAAa,CAAC;QACtB,OAAO,EAAE,oBAAoB,CAAC;QAC9B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;QACpC,aAAa,EAAE,MAAM,IAAI,CAAC;KAC3B;IASD,IAAI,GAAG,IAAI,MAAM,CAEhB;IAED,IAAI,KAAK,IAAI,kBAAkB,CAE9B;IAEK,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAY5B,IAAI,cAAc,IAAI,OAAO,CAE5B;CACF;AA6BD,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA8FvG"}
@@ -0,0 +1,175 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { basename, dirname, resolve } from "node:path";
3
+ import { buildInitialErrorHtml, renderDocument, } from "./render.js";
4
+ import { PreviewServer } from "./server.js";
5
+ import { DebouncedFileWatcher } from "./watcher.js";
6
+ function errorMessage(error) {
7
+ return error instanceof Error ? error.message : String(error);
8
+ }
9
+ export class WatchPreviewSession {
10
+ inputPath;
11
+ server;
12
+ initialError;
13
+ #watcher;
14
+ #closeRequested = false;
15
+ #closePromise = null;
16
+ #getRenderChain;
17
+ #onBeforeClose;
18
+ constructor(options) {
19
+ this.inputPath = options.inputPath;
20
+ this.server = options.server;
21
+ this.#watcher = options.watcher;
22
+ this.initialError = options.initialError;
23
+ this.#getRenderChain = options.getRenderChain;
24
+ this.#onBeforeClose = options.onBeforeClose;
25
+ }
26
+ get url() {
27
+ return this.server.url;
28
+ }
29
+ get state() {
30
+ return this.server.state;
31
+ }
32
+ async waitForIdle() {
33
+ await this.#getRenderChain();
34
+ }
35
+ async close() {
36
+ if (this.#closePromise)
37
+ return await this.#closePromise;
38
+ this.#closeRequested = true;
39
+ this.#onBeforeClose();
40
+ this.#closePromise = (async () => {
41
+ await this.#watcher.close();
42
+ await this.#getRenderChain();
43
+ await this.server.close();
44
+ })();
45
+ return await this.#closePromise;
46
+ }
47
+ get closeRequested() {
48
+ return this.#closeRequested;
49
+ }
50
+ }
51
+ async function defaultRenderer(source, context) {
52
+ const rendered = await renderDocument({
53
+ source,
54
+ sourcePath: context.sourcePath,
55
+ resourceRoot: context.resourceRoot,
56
+ format: context.format,
57
+ theme: context.theme,
58
+ fontSizePx: context.fontSizePx,
59
+ title: `${basename(context.sourcePath)} — pandoc-glance`,
60
+ liveReload: {
61
+ eventsPath: context.eventsPath,
62
+ revision: context.revision,
63
+ storageKey: context.storageKey,
64
+ },
65
+ serverResources: {
66
+ resourcePath: context.resourcePath,
67
+ assetPath: context.assetPath,
68
+ revision: context.revision,
69
+ },
70
+ });
71
+ return {
72
+ html: rendered.html,
73
+ assets: rendered.assets,
74
+ warnings: rendered.pandocWarnings,
75
+ };
76
+ }
77
+ export async function startWatchPreview(options) {
78
+ const inputPath = resolve(options.inputPath);
79
+ const resourceRoot = dirname(inputPath);
80
+ const serverOptions = { resourceRoot };
81
+ if (options.port !== undefined)
82
+ serverOptions.port = options.port;
83
+ const server = await PreviewServer.create(serverOptions);
84
+ await server.start();
85
+ const renderer = options.renderer ?? defaultRenderer;
86
+ const log = options.onLog ?? (() => undefined);
87
+ let renderChain = Promise.resolve();
88
+ let closed = false;
89
+ let lastStatus = "starting";
90
+ const renderOnce = async () => {
91
+ if (closed)
92
+ return;
93
+ const revision = server.state.revision + 1;
94
+ try {
95
+ const source = await readFile(inputPath, "utf8");
96
+ const rendered = await renderer(source, {
97
+ revision,
98
+ sourcePath: inputPath,
99
+ resourceRoot,
100
+ format: options.format,
101
+ theme: options.theme,
102
+ fontSizePx: options.fontSizePx,
103
+ eventsPath: server.paths.events,
104
+ resourcePath: server.paths.resource,
105
+ assetPath: server.paths.asset,
106
+ storageKey: `pandoc-glance:${server.token}`,
107
+ });
108
+ if (closed)
109
+ return;
110
+ server.publishSuccess(revision, rendered.html, rendered.assets ?? new Map());
111
+ for (const warning of rendered.warnings ?? [])
112
+ log(`Pandoc: ${warning}`, "warning");
113
+ if (lastStatus === "error")
114
+ log(`Render recovered (revision ${revision}).`, "info");
115
+ else
116
+ log(`Rendered revision ${revision}.`, "info");
117
+ lastStatus = "success";
118
+ options.onStatus?.({ revision, status: "success" });
119
+ }
120
+ catch (error) {
121
+ if (closed)
122
+ return;
123
+ const message = errorMessage(error);
124
+ const initialErrorHtml = server.state.successfulRevision === 0
125
+ ? buildInitialErrorHtml({
126
+ title: `${basename(inputPath)} — pandoc-glance`,
127
+ error: message,
128
+ theme: options.theme,
129
+ fontSizePx: options.fontSizePx,
130
+ liveReload: {
131
+ eventsPath: server.paths.events,
132
+ revision,
133
+ storageKey: `pandoc-glance:${server.token}`,
134
+ },
135
+ })
136
+ : undefined;
137
+ server.publishError(revision, message, initialErrorHtml);
138
+ log(`Render failed (revision ${revision}): ${message}`, "error");
139
+ lastStatus = "error";
140
+ options.onStatus?.({ revision, status: "error", error: message });
141
+ }
142
+ };
143
+ const enqueueRender = () => {
144
+ renderChain = renderChain.then(renderOnce, renderOnce);
145
+ return renderChain;
146
+ };
147
+ const watcherOptions = {
148
+ ...(options.debounceMs === undefined ? {} : { debounceMs: options.debounceMs }),
149
+ onError: (error) => log(`File watcher error: ${errorMessage(error)}`, "error"),
150
+ };
151
+ const watcher = new DebouncedFileWatcher(inputPath, enqueueRender, watcherOptions);
152
+ try {
153
+ await watcher.ready();
154
+ await enqueueRender();
155
+ }
156
+ catch (error) {
157
+ closed = true;
158
+ await watcher.close().catch(() => undefined);
159
+ await server.close().catch(() => undefined);
160
+ throw error;
161
+ }
162
+ const initialError = server.state.status === "error" ? server.state.error : null;
163
+ const session = new WatchPreviewSession({
164
+ inputPath,
165
+ server,
166
+ watcher,
167
+ initialError,
168
+ getRenderChain: () => renderChain,
169
+ onBeforeClose: () => {
170
+ closed = true;
171
+ },
172
+ });
173
+ return session;
174
+ }
175
+ //# sourceMappingURL=watch-preview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watch-preview.js","sourceRoot":"","sources":["../src/watch-preview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EACL,qBAAqB,EACrB,cAAc,GAEf,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,aAAa,EAA2B,MAAM,aAAa,CAAC;AAErE,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAyCpD,SAAS,YAAY,CAAC,KAAc;IAClC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC;AAED,MAAM,OAAO,mBAAmB;IACrB,SAAS,CAAS;IAClB,MAAM,CAAgB;IACtB,YAAY,CAAgB;IAErC,QAAQ,CAAuB;IAC/B,eAAe,GAAG,KAAK,CAAC;IACxB,aAAa,GAAyB,IAAI,CAAC;IAC3C,eAAe,CAAsB;IACrC,cAAc,CAAa;IAE3B,YAAY,OAOX;QACC,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QACnC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC;QAChC,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;QACzC,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC;QAC9C,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAC9C,CAAC;IAED,IAAI,GAAG;QACL,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC;IACzB,CAAC;IAED,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,WAAW;QACf,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,aAAa;YAAE,OAAO,MAAM,IAAI,CAAC,aAAa,CAAC;QACxD,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC5B,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,IAAI,CAAC,aAAa,GAAG,CAAC,KAAK,IAAI,EAAE;YAC/B,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;YAC5B,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;YAC7B,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAC5B,CAAC,CAAC,EAAE,CAAC;QACL,OAAO,MAAM,IAAI,CAAC,aAAa,CAAC;IAClC,CAAC;IAED,IAAI,cAAc;QAChB,OAAO,IAAI,CAAC,eAAe,CAAC;IAC9B,CAAC;CACF;AAED,KAAK,UAAU,eAAe,CAAC,MAAc,EAAE,OAA2B;IACxE,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC;QACpC,MAAM;QACN,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,KAAK,EAAE,GAAG,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC,kBAAkB;QACxD,UAAU,EAAE;YACV,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,UAAU,EAAE,OAAO,CAAC,UAAU;SAC/B;QACD,eAAe,EAAE;YACf,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,QAAQ,EAAE,OAAO,CAAC,QAAQ;SAC3B;KACF,CAAC,CAAC;IACH,OAAO;QACL,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,QAAQ,EAAE,QAAQ,CAAC,cAAc;KAClC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OAAiC;IACvE,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC7C,MAAM,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IACxC,MAAM,aAAa,GAA4C,EAAE,YAAY,EAAE,CAAC;IAChF,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS;QAAE,aAAa,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAClE,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;IACzD,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IAErB,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,eAAe,CAAC;IACrD,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAC/C,IAAI,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IACpC,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,UAAU,GAAqC,UAAU,CAAC;IAE9D,MAAM,UAAU,GAAG,KAAK,IAAmB,EAAE;QAC3C,IAAI,MAAM;YAAE,OAAO;QACnB,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC;QAC3C,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;YACjD,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE;gBACtC,QAAQ;gBACR,UAAU,EAAE,SAAS;gBACrB,YAAY;gBACZ,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM;gBAC/B,YAAY,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ;gBACnC,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK;gBAC7B,UAAU,EAAE,iBAAiB,MAAM,CAAC,KAAK,EAAE;aAC5C,CAAC,CAAC;YACH,IAAI,MAAM;gBAAE,OAAO;YACnB,MAAM,CAAC,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,IAAI,IAAI,GAAG,EAAE,CAAC,CAAC;YAC7E,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,QAAQ,IAAI,EAAE;gBAAE,GAAG,CAAC,WAAW,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;YACpF,IAAI,UAAU,KAAK,OAAO;gBAAE,GAAG,CAAC,8BAA8B,QAAQ,IAAI,EAAE,MAAM,CAAC,CAAC;;gBAC/E,GAAG,CAAC,qBAAqB,QAAQ,GAAG,EAAE,MAAM,CAAC,CAAC;YACnD,UAAU,GAAG,SAAS,CAAC;YACvB,OAAO,CAAC,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;QACtD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,MAAM;gBAAE,OAAO;YACnB,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;YACpC,MAAM,gBAAgB,GAAG,MAAM,CAAC,KAAK,CAAC,kBAAkB,KAAK,CAAC;gBAC5D,CAAC,CAAC,qBAAqB,CAAC;oBACpB,KAAK,EAAE,GAAG,QAAQ,CAAC,SAAS,CAAC,kBAAkB;oBAC/C,KAAK,EAAE,OAAO;oBACd,KAAK,EAAE,OAAO,CAAC,KAAK;oBACpB,UAAU,EAAE,OAAO,CAAC,UAAU;oBAC9B,UAAU,EAAE;wBACV,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM;wBAC/B,QAAQ;wBACR,UAAU,EAAE,iBAAiB,MAAM,CAAC,KAAK,EAAE;qBAC5C;iBACF,CAAC;gBACJ,CAAC,CAAC,SAAS,CAAC;YACd,MAAM,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC;YACzD,GAAG,CAAC,2BAA2B,QAAQ,MAAM,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;YACjE,UAAU,GAAG,OAAO,CAAC;YACrB,OAAO,CAAC,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;QACpE,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,aAAa,GAAG,GAAkB,EAAE;QACxC,WAAW,GAAG,WAAW,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QACvD,OAAO,WAAW,CAAC;IACrB,CAAC,CAAC;IAEF,MAAM,cAAc,GAAG;QACrB,GAAG,CAAC,OAAO,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC;QAC/E,OAAO,EAAE,CAAC,KAAc,EAAE,EAAE,CAAC,GAAG,CAAC,uBAAuB,YAAY,CAAC,KAAK,CAAC,EAAE,EAAE,OAAO,CAAC;KACxF,CAAC;IACF,MAAM,OAAO,GAAG,IAAI,oBAAoB,CAAC,SAAS,EAAE,aAAa,EAAE,cAAc,CAAC,CAAC;IAEnF,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACtB,MAAM,aAAa,EAAE,CAAC;IACxB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,GAAG,IAAI,CAAC;QACd,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC7C,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC5C,MAAM,KAAK,CAAC;IACd,CAAC;IAED,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IACjF,MAAM,OAAO,GAAG,IAAI,mBAAmB,CAAC;QACtC,SAAS;QACT,MAAM;QACN,OAAO;QACP,YAAY;QACZ,cAAc,EAAE,GAAG,EAAE,CAAC,WAAW;QACjC,aAAa,EAAE,GAAG,EAAE;YAClB,MAAM,GAAG,IAAI,CAAC;QAChB,CAAC;KACF,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,16 @@
1
+ export interface DebouncedFileWatcherOptions {
2
+ debounceMs?: number;
3
+ onError?: (error: unknown) => void;
4
+ }
5
+ /**
6
+ * Watches the containing directory rather than only the inode, so editor
7
+ * atomic-save rename/replacement patterns continue to trigger updates.
8
+ */
9
+ export declare class DebouncedFileWatcher {
10
+ #private;
11
+ readonly filePath: string;
12
+ constructor(filePath: string, callback: () => void | Promise<void>, options?: DebouncedFileWatcherOptions);
13
+ ready(): Promise<void>;
14
+ close(): Promise<void>;
15
+ }
16
+ //# sourceMappingURL=watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,2BAA2B;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACpC;AAED;;;GAGG;AACH,qBAAa,oBAAoB;;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBASd,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,GAAE,2BAAgC;IAwCvG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAsB7B"}
@@ -0,0 +1,81 @@
1
+ import chokidar, {} from "chokidar";
2
+ import { dirname, resolve } from "node:path";
3
+ /**
4
+ * Watches the containing directory rather than only the inode, so editor
5
+ * atomic-save rename/replacement patterns continue to trigger updates.
6
+ */
7
+ export class DebouncedFileWatcher {
8
+ filePath;
9
+ #watcher;
10
+ #callback;
11
+ #debounceMs;
12
+ #timer = null;
13
+ #closed = false;
14
+ #ready;
15
+ constructor(filePath, callback, options = {}) {
16
+ this.filePath = resolve(filePath);
17
+ this.#callback = callback;
18
+ this.#debounceMs = options.debounceMs ?? 140;
19
+ const parentDirectory = dirname(this.filePath);
20
+ this.#watcher = chokidar.watch(parentDirectory, {
21
+ persistent: true,
22
+ ignoreInitial: true,
23
+ depth: 0,
24
+ atomic: true,
25
+ awaitWriteFinish: {
26
+ stabilityThreshold: 70,
27
+ pollInterval: 10,
28
+ },
29
+ });
30
+ this.#ready = new Promise((resolvePromise, rejectPromise) => {
31
+ const onReady = () => {
32
+ this.#watcher.off("error", onInitialError);
33
+ resolvePromise();
34
+ };
35
+ const onInitialError = (error) => {
36
+ this.#watcher.off("ready", onReady);
37
+ rejectPromise(error);
38
+ };
39
+ this.#watcher.once("ready", onReady);
40
+ this.#watcher.once("error", onInitialError);
41
+ });
42
+ this.#watcher.on("error", (error) => options.onError?.(error));
43
+ const handlePath = (changedPath) => {
44
+ if (resolve(changedPath) !== this.filePath)
45
+ return;
46
+ this.#schedule();
47
+ };
48
+ this.#watcher.on("add", handlePath);
49
+ this.#watcher.on("change", handlePath);
50
+ this.#watcher.on("unlink", handlePath);
51
+ }
52
+ async ready() {
53
+ await this.#ready;
54
+ }
55
+ async close() {
56
+ if (this.#closed)
57
+ return;
58
+ this.#closed = true;
59
+ if (this.#timer) {
60
+ clearTimeout(this.#timer);
61
+ this.#timer = null;
62
+ }
63
+ await this.#watcher.close();
64
+ }
65
+ #schedule() {
66
+ if (this.#closed)
67
+ return;
68
+ if (this.#timer)
69
+ clearTimeout(this.#timer);
70
+ this.#timer = setTimeout(() => {
71
+ this.#timer = null;
72
+ if (this.#closed)
73
+ return;
74
+ void Promise.resolve(this.#callback()).catch(() => {
75
+ // The owner reports callback errors; never let a watcher callback create
76
+ // an unhandled rejection that terminates watch mode.
77
+ });
78
+ }, this.#debounceMs);
79
+ }
80
+ }
81
+ //# sourceMappingURL=watcher.js.map