tformula 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/detect.js ADDED
@@ -0,0 +1,342 @@
1
+ import stringWidth from "string-width";
2
+ const COMMAND_RE = /\\(?:frac|dfrac|tfrac|sum|prod|int|iint|iiint|log|ln|exp|sqrt|lim|begin|end|left|right|mathrm|mathbf|mathbb|mathcal|operatorname|overline|underline|hat|bar|vec|partial|nabla|infty|alpha|beta|gamma|delta|epsilon|varepsilon|zeta|eta|theta|vartheta|iota|kappa|lambda|mu|nu|xi|omicron|pi|varpi|rho|varrho|sigma|varsigma|tau|upsilon|phi|varphi|chi|psi|omega)(?![A-Za-z])/gu;
3
+ function mathScore(value) {
4
+ let score = 0;
5
+ score += (value.match(COMMAND_RE)?.length ?? 0) * 3;
6
+ score += Math.min(3, value.match(/[\^_][{A-Za-z0-9(]/gu)?.length ?? 0);
7
+ score += Math.min(2, value.match(/\\[A-Za-z]+/gu)?.length ?? 0);
8
+ if (/\{[^{}]+\}/u.test(value))
9
+ score += 1;
10
+ if (/[=≈≠≤≥∑∫√∞]|\\(?:le|ge|neq|approx|cdot|times)/u.test(value))
11
+ score += 1;
12
+ if (/\b[A-Z]\s*\([^)]*\)/u.test(value))
13
+ score += 1;
14
+ return score;
15
+ }
16
+ function visualColumn(line, utf16Index) {
17
+ return stringWidth(line.slice(0, utf16Index));
18
+ }
19
+ function visualEnd(line, start, end) {
20
+ const startCol = visualColumn(line, start);
21
+ return [startCol, startCol + Math.max(1, stringWidth(line.slice(start, end)))];
22
+ }
23
+ function normalizeLatex(parts) {
24
+ return parts.map((part) => part.trim()).filter(Boolean).join("\n");
25
+ }
26
+ function parenthesizedSegments(line) {
27
+ const segments = [];
28
+ for (let start = 0; start < line.length; start += 1) {
29
+ if (line[start] !== "(" || line[start - 1] === "\\")
30
+ continue;
31
+ let depth = 1;
32
+ for (let end = start + 1; end < line.length; end += 1) {
33
+ if (line[end] === "(" && line[end - 1] !== "\\")
34
+ depth += 1;
35
+ if (line[end] === ")" && line[end - 1] !== "\\")
36
+ depth -= 1;
37
+ if (depth !== 0)
38
+ continue;
39
+ const body = line.slice(start + 1, end).trim();
40
+ if (body)
41
+ segments.push({ start, end: end + 1, body });
42
+ start = end;
43
+ break;
44
+ }
45
+ }
46
+ return segments;
47
+ }
48
+ function looksLikeAsciiMath(value) {
49
+ const compact = value.replace(/\\[ ,;:!]/gu, "").trim();
50
+ if (!/[A-Za-z0-9]/u.test(compact))
51
+ return false;
52
+ if (!/^[A-Za-z0-9\s.,+*/=<>^_{}()[\]|\\-]+$/u.test(compact))
53
+ return false;
54
+ if (/[=<>]/u.test(compact))
55
+ return true;
56
+ if (/^[A-Za-z](?:[_^](?:[A-Za-z0-9]|\{[A-Za-z0-9]+\}))+$/u.test(compact))
57
+ return true;
58
+ const operand = String.raw `(?:\d+(?:\.\d+)?|[A-Za-z])`;
59
+ return new RegExp(`^${operand}(?:[+*/-]${operand})+$`, "u").test(compact.replace(/\s+/gu, ""));
60
+ }
61
+ function isLikelyMath(value) {
62
+ return mathScore(value) >= 3 || looksLikeAsciiMath(value);
63
+ }
64
+ function inferredParenthesizedMath(line) {
65
+ return parenthesizedSegments(line).filter((segment) => isLikelyMath(segment.body));
66
+ }
67
+ function escapeTexText(value) {
68
+ const replacements = {
69
+ "\\": "\\backslash{}",
70
+ "{": "\\{",
71
+ "}": "\\}",
72
+ "$": "\\$",
73
+ "&": "\\&",
74
+ "#": "\\#",
75
+ "%": "\\%",
76
+ "_": "\\_",
77
+ "^": "\\^{}",
78
+ "~": "\\~{}"
79
+ };
80
+ return value.replace(/[\\{}$&#%_^~]/gu, (character) => replacements[character]);
81
+ }
82
+ function descriptionToLatex(value) {
83
+ const parts = [];
84
+ let cursor = 0;
85
+ for (const segment of parenthesizedSegments(value)) {
86
+ if (!isLikelyMath(segment.body))
87
+ continue;
88
+ const prose = value.slice(cursor, segment.start);
89
+ if (prose)
90
+ parts.push(`\\text{${escapeTexText(prose)}}`);
91
+ parts.push(segment.body);
92
+ cursor = segment.end;
93
+ }
94
+ const prose = value.slice(cursor);
95
+ if (prose)
96
+ parts.push(`\\text{${escapeTexText(prose)}}`);
97
+ return parts.join("");
98
+ }
99
+ function isDefinitionSymbol(value) {
100
+ return isLikelyMath(value) || /^[A-Za-z](?:[_^](?:[A-Za-z0-9]|\{[A-Za-z0-9]+\}))?$/u.test(value);
101
+ }
102
+ function definitionItem(line) {
103
+ for (const segment of parenthesizedSegments(line)) {
104
+ if (!isDefinitionSymbol(segment.body))
105
+ continue;
106
+ const prefix = line.slice(0, segment.start);
107
+ if (!/^\s*(?:[-*•]\s+|\d+[.)]\s+)$/u.test(prefix))
108
+ continue;
109
+ const suffix = line.slice(segment.end);
110
+ const description = suffix.match(/^\s*([::]\s*\S.*)$/u)?.[1];
111
+ if (!description)
112
+ continue;
113
+ return {
114
+ body: segment.body,
115
+ descriptionLatex: descriptionToLatex(description),
116
+ startCol: visualColumn(line, segment.start),
117
+ lineWidth: stringWidth(line)
118
+ };
119
+ }
120
+ return undefined;
121
+ }
122
+ function inferredDefinitionGroup(lines, startRow) {
123
+ const items = [];
124
+ for (let row = startRow; row < lines.length; row += 1) {
125
+ const item = definitionItem(lines[row] ?? "");
126
+ if (!item || (items[0] && item.startCol !== items[0].startCol))
127
+ break;
128
+ items.push(item);
129
+ }
130
+ if (items.length < 2)
131
+ return undefined;
132
+ const latexRows = items
133
+ .map((item) => `${item.body} & ${item.descriptionLatex}`)
134
+ .join("\\\\");
135
+ return {
136
+ startRow,
137
+ endRow: startRow + items.length - 1,
138
+ startCol: items[0].startCol,
139
+ endCol: Math.max(...items.map((item) => item.lineWidth)),
140
+ latex: `\\begin{array}{ll}${latexRows}\\end{array}`,
141
+ display: false,
142
+ confidence: "inferred",
143
+ compact: true
144
+ };
145
+ }
146
+ function isStandaloneDisplayLine(line) {
147
+ const trimmed = line.trim();
148
+ return /^(?:\\\[[\s\S]+\\\]|\$\$[^$]+\$\$)$/u.test(trimmed);
149
+ }
150
+ function expandStandaloneDisplayRegions(lines, regions) {
151
+ const claimedRows = new Set();
152
+ for (const region of regions) {
153
+ for (let row = region.startRow; row <= region.endRow; row += 1)
154
+ claimedRows.add(row);
155
+ }
156
+ return regions.map((region) => {
157
+ if (!region.display
158
+ || region.startRow !== region.endRow
159
+ || !isStandaloneDisplayLine(lines[region.startRow] ?? "")) {
160
+ return region;
161
+ }
162
+ let startRow = region.startRow;
163
+ let endRow = region.endRow;
164
+ const previousRow = startRow - 1;
165
+ const nextRow = endRow + 1;
166
+ if (previousRow >= 0
167
+ && !(lines[previousRow] ?? "").trim()
168
+ && !claimedRows.has(previousRow)) {
169
+ startRow = previousRow;
170
+ claimedRows.add(previousRow);
171
+ }
172
+ if (nextRow < lines.length
173
+ && !(lines[nextRow] ?? "").trim()
174
+ && !claimedRows.has(nextRow)) {
175
+ endRow = nextRow;
176
+ claimedRows.add(nextRow);
177
+ }
178
+ if (startRow === region.startRow && endRow === region.endRow)
179
+ return region;
180
+ return {
181
+ ...region,
182
+ startRow,
183
+ endRow,
184
+ startCol: 0,
185
+ endCol: Math.max(...lines.slice(startRow, endRow + 1).map((line) => stringWidth(line)), 1)
186
+ };
187
+ });
188
+ }
189
+ function trailingInlineRegion(lines, row, line, startIndex, endIndex, latex, confidence) {
190
+ const suffix = line.slice(endIndex);
191
+ const trailing = suffix.match(/^([.,;:!?,。;:!?]?)\s*$/u);
192
+ let regionEndIndex = endIndex;
193
+ let regionLatex = latex;
194
+ let endRow = row;
195
+ let compact = false;
196
+ if (trailing) {
197
+ const punctuation = trailing[1] ?? "";
198
+ if (punctuation) {
199
+ regionLatex += `\\text{${escapeTexText(punctuation)}}`;
200
+ regionEndIndex += punctuation.length;
201
+ }
202
+ compact = true;
203
+ if (row + 1 < lines.length && !(lines[row + 1] ?? "").trim())
204
+ endRow = row + 1;
205
+ }
206
+ const [startCol, endCol] = visualEnd(line, startIndex, regionEndIndex);
207
+ return {
208
+ startRow: row,
209
+ endRow,
210
+ startCol,
211
+ endCol,
212
+ latex: regionLatex,
213
+ display: false,
214
+ confidence,
215
+ ...(compact ? { compact: true } : {})
216
+ };
217
+ }
218
+ /**
219
+ * Detect formulas in the post-ANSI terminal screen. Explicit TeX delimiters are
220
+ * preferred. A conservative inferred form handles TUIs that turn `\[`/`\]`
221
+ * into bare bracket lines while leaving the TeX body visible.
222
+ */
223
+ export function detectFormulaRegions(lines) {
224
+ const regions = [];
225
+ let inCodeFence = false;
226
+ for (let row = 0; row < lines.length; row += 1) {
227
+ const line = lines[row] ?? "";
228
+ const trimmed = line.trim();
229
+ if (/^```/u.test(trimmed)) {
230
+ inCodeFence = !inCodeFence;
231
+ continue;
232
+ }
233
+ if (inCodeFence || !trimmed)
234
+ continue;
235
+ // Definition lists need group-level layout: the source TeX tokens have
236
+ // different character widths, so independently overlaying each token
237
+ // leaves the colons and descriptions staggered. A compact MathJax array
238
+ // aligns both columns without changing the child terminal's cell layout.
239
+ const definitionGroup = inferredDefinitionGroup(lines, row);
240
+ if (definitionGroup) {
241
+ regions.push(definitionGroup);
242
+ row = definitionGroup.endRow;
243
+ continue;
244
+ }
245
+ const blockStart = trimmed === "\\[" || trimmed === "$$" || trimmed === "[";
246
+ if (blockStart) {
247
+ const explicit = trimmed !== "[";
248
+ const closing = trimmed === "$$" ? "$$" : trimmed === "\\[" ? "\\]" : "]";
249
+ const body = [];
250
+ let endRow = -1;
251
+ for (let candidate = row + 1; candidate < Math.min(lines.length, row + 16); candidate += 1) {
252
+ const candidateTrimmed = (lines[candidate] ?? "").trim();
253
+ if (candidateTrimmed === closing || (closing === "\\]" && candidateTrimmed === "]")) {
254
+ endRow = candidate;
255
+ break;
256
+ }
257
+ body.push(lines[candidate] ?? "");
258
+ }
259
+ const latex = normalizeLatex(body);
260
+ if (endRow > row && latex && (explicit || isLikelyMath(latex))) {
261
+ regions.push({
262
+ startRow: row,
263
+ endRow,
264
+ startCol: 0,
265
+ endCol: Math.max(...lines.slice(row, endRow + 1).map((value) => stringWidth(value)), 1),
266
+ latex,
267
+ display: true,
268
+ confidence: explicit ? "explicit" : "inferred"
269
+ });
270
+ row = endRow;
271
+ continue;
272
+ }
273
+ }
274
+ const explicitPatterns = [
275
+ { regex: /\\\[([\s\S]+?)\\\]/gu, display: true },
276
+ { regex: /\$\$([^$]+?)\$\$/gu, display: true },
277
+ { regex: /\\\((.+?)\\\)/gu, display: false }
278
+ ];
279
+ for (const { regex, display } of explicitPatterns) {
280
+ for (const match of line.matchAll(regex)) {
281
+ if (match.index === undefined || !match[1]?.trim())
282
+ continue;
283
+ const matchEnd = match.index + match[0].length;
284
+ if (!display) {
285
+ regions.push(trailingInlineRegion(lines, row, line, match.index, matchEnd, match[1].trim(), "explicit"));
286
+ }
287
+ else {
288
+ const [startCol, endCol] = visualEnd(line, match.index, matchEnd);
289
+ regions.push({
290
+ startRow: row,
291
+ endRow: row,
292
+ startCol,
293
+ endCol,
294
+ latex: match[1].trim(),
295
+ display,
296
+ confidence: "explicit"
297
+ });
298
+ }
299
+ }
300
+ }
301
+ // Several terminal Markdown renderers consume the backslashes in \(...\)
302
+ // and leave forms such as `(\mathbf E)` or `(\rho)`. Only infer these
303
+ // when the body has strong TeX evidence, so ordinary prose parentheses
304
+ // remain untouched.
305
+ for (const segment of inferredParenthesizedMath(line)) {
306
+ const [startCol, endCol] = visualEnd(line, segment.start, segment.end);
307
+ regions.push({
308
+ startRow: row,
309
+ endRow: row,
310
+ startCol,
311
+ endCol,
312
+ latex: segment.body,
313
+ display: false,
314
+ confidence: "inferred"
315
+ });
316
+ }
317
+ // Single-dollar inline math is intentionally conservative to avoid prices.
318
+ const inlineDollar = /(?<!\$)\$([^$\n]+?)\$(?!\$)/gu;
319
+ for (const match of line.matchAll(inlineDollar)) {
320
+ if (match.index === undefined || !match[1] || mathScore(match[1]) < 2)
321
+ continue;
322
+ regions.push(trailingInlineRegion(lines, row, line, match.index, match.index + match[0].length, match[1].trim(), "explicit"));
323
+ }
324
+ }
325
+ return expandStandaloneDisplayRegions(lines, regions);
326
+ }
327
+ export const detectorInternals = {
328
+ descriptionToLatex,
329
+ definitionItem,
330
+ escapeTexText,
331
+ expandStandaloneDisplayRegions,
332
+ inferredDefinitionGroup,
333
+ inferredParenthesizedMath,
334
+ isLikelyMath,
335
+ isStandaloneDisplayLine,
336
+ looksLikeAsciiMath,
337
+ mathScore,
338
+ parenthesizedSegments,
339
+ trailingInlineRegion,
340
+ visualColumn
341
+ };
342
+ //# sourceMappingURL=detect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.js","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export interface FormulaCacheOptions {
2
+ root?: string;
3
+ memoryEntries?: number;
4
+ maxDiskBytes?: number;
5
+ }
6
+ export declare function formulaCacheKey(value: unknown): string;
7
+ export declare class FormulaCache {
8
+ #private;
9
+ readonly root: string;
10
+ constructor(options?: FormulaCacheOptions);
11
+ getSvg(key: string): Promise<string | undefined>;
12
+ getPng(key: string): Promise<Buffer | undefined>;
13
+ getOrCreateSvg(key: string, create: () => Promise<string>): Promise<string>;
14
+ getOrCreatePng(key: string, create: () => Promise<Uint8Array>): Promise<Buffer>;
15
+ clearMemory(): void;
16
+ clearDisk(): Promise<void>;
17
+ }
18
+ export declare const sharedFormulaCache: FormulaCache;
@@ -0,0 +1,238 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { constants } from "node:fs";
3
+ import { mkdir, open, readdir, readFile, rename, rm, stat, unlink, utimes, writeFile } from "node:fs/promises";
4
+ import { homedir, tmpdir } from "node:os";
5
+ import { dirname, join } from "node:path";
6
+ const CACHE_SCHEMA = "v1";
7
+ const DEFAULT_MAX_DISK_BYTES = 256 * 1024 * 1024;
8
+ const LOCK_STALE_MS = 30_000;
9
+ const LOCK_POLL_MS = 30;
10
+ const CLEANUP_WRITE_INTERVAL = 32;
11
+ function defaultCacheRoot() {
12
+ if (process.env.TFORMULA_CACHE_DIR)
13
+ return process.env.TFORMULA_CACHE_DIR;
14
+ // Test runs must never populate or depend on the user's real cache.
15
+ if (process.env.VITEST)
16
+ return join(tmpdir(), `tformula-vitest-${process.pid}`);
17
+ if (process.platform === "darwin")
18
+ return join(homedir(), "Library", "Caches", "TFormula");
19
+ return join(process.env.XDG_CACHE_HOME ?? join(homedir(), ".cache"), "tformula");
20
+ }
21
+ function configuredMaxDiskBytes() {
22
+ const megabytes = Number(process.env.TFORMULA_CACHE_MAX_MB);
23
+ return Number.isFinite(megabytes) && megabytes > 0
24
+ ? Math.floor(megabytes * 1024 * 1024)
25
+ : DEFAULT_MAX_DISK_BYTES;
26
+ }
27
+ export function formulaCacheKey(value) {
28
+ return createHash("sha256").update(JSON.stringify(value)).digest("hex");
29
+ }
30
+ function isValid(kind, data) {
31
+ if (kind === "svg")
32
+ return /<svg(?:\s|>)/u.test(data.subarray(0, 256).toString("utf8"));
33
+ return data.length >= 24
34
+ && data.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))
35
+ && data.subarray(12, 16).toString("ascii") === "IHDR";
36
+ }
37
+ function sleep(milliseconds) {
38
+ return new Promise((resolve) => setTimeout(resolve, milliseconds));
39
+ }
40
+ function isCode(error, code) {
41
+ return error instanceof Error && "code" in error && error.code === code;
42
+ }
43
+ export class FormulaCache {
44
+ root;
45
+ #memoryEntries;
46
+ #maxDiskBytes;
47
+ #memory = new Map();
48
+ #inFlight = new Map();
49
+ #cleanupRunning = false;
50
+ #hasCleaned = false;
51
+ #writesSinceCleanup = 0;
52
+ constructor(options = {}) {
53
+ this.root = join(options.root ?? defaultCacheRoot(), CACHE_SCHEMA);
54
+ this.#memoryEntries = Math.max(1, options.memoryEntries ?? 256);
55
+ this.#maxDiskBytes = Math.max(0, options.maxDiskBytes ?? configuredMaxDiskBytes());
56
+ }
57
+ async getSvg(key) {
58
+ const data = await this.#read("svg", key);
59
+ return data?.toString("utf8");
60
+ }
61
+ async getPng(key) {
62
+ return this.#read("png", key);
63
+ }
64
+ async getOrCreateSvg(key, create) {
65
+ const data = await this.#getOrCreate("svg", key, async () => Buffer.from(await create(), "utf8"));
66
+ return data.toString("utf8");
67
+ }
68
+ async getOrCreatePng(key, create) {
69
+ return this.#getOrCreate("png", key, async () => Buffer.from(await create()));
70
+ }
71
+ clearMemory() {
72
+ this.#memory.clear();
73
+ }
74
+ async clearDisk() {
75
+ this.#memory.clear();
76
+ await rm(this.root, { force: true, recursive: true });
77
+ }
78
+ #memoryKey(kind, key) {
79
+ return `${kind}:${key}`;
80
+ }
81
+ #path(kind, key) {
82
+ const extension = kind === "svg" ? "svg" : "png";
83
+ return join(this.root, kind, key.slice(0, 2), `${key}.${extension}`);
84
+ }
85
+ #remember(kind, key, data) {
86
+ const memoryKey = this.#memoryKey(kind, key);
87
+ this.#memory.delete(memoryKey);
88
+ this.#memory.set(memoryKey, data);
89
+ while (this.#memory.size > this.#memoryEntries) {
90
+ this.#memory.delete(this.#memory.keys().next().value);
91
+ }
92
+ return data;
93
+ }
94
+ async #read(kind, key) {
95
+ const memoryKey = this.#memoryKey(kind, key);
96
+ const memory = this.#memory.get(memoryKey);
97
+ if (memory)
98
+ return this.#remember(kind, key, memory);
99
+ const path = this.#path(kind, key);
100
+ try {
101
+ const data = await readFile(path);
102
+ if (!isValid(kind, data)) {
103
+ await rm(path, { force: true });
104
+ return undefined;
105
+ }
106
+ const now = new Date();
107
+ void utimes(path, now, now).catch(() => undefined);
108
+ return this.#remember(kind, key, data);
109
+ }
110
+ catch (error) {
111
+ if (isCode(error, "ENOENT"))
112
+ return undefined;
113
+ throw error;
114
+ }
115
+ }
116
+ async #getOrCreate(kind, key, create) {
117
+ const cached = await this.#read(kind, key);
118
+ if (cached)
119
+ return cached;
120
+ const inFlightKey = this.#memoryKey(kind, key);
121
+ const existing = this.#inFlight.get(inFlightKey);
122
+ if (existing)
123
+ return existing;
124
+ const promise = this.#createWithLock(kind, key, create).finally(() => {
125
+ this.#inFlight.delete(inFlightKey);
126
+ });
127
+ this.#inFlight.set(inFlightKey, promise);
128
+ return promise;
129
+ }
130
+ async #createWithLock(kind, key, create) {
131
+ const path = this.#path(kind, key);
132
+ const lockPath = `${path}.lock`;
133
+ await mkdir(dirname(path), { recursive: true });
134
+ let lock;
135
+ while (!lock) {
136
+ try {
137
+ lock = await open(lockPath, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY);
138
+ await lock.writeFile(`${process.pid}\n`);
139
+ }
140
+ catch (error) {
141
+ if (!isCode(error, "EEXIST"))
142
+ throw error;
143
+ const cached = await this.#read(kind, key);
144
+ if (cached)
145
+ return cached;
146
+ try {
147
+ const lockInfo = await stat(lockPath);
148
+ if (Date.now() - lockInfo.mtimeMs > LOCK_STALE_MS) {
149
+ await unlink(lockPath).catch(() => undefined);
150
+ continue;
151
+ }
152
+ }
153
+ catch (statError) {
154
+ if (!isCode(statError, "ENOENT"))
155
+ throw statError;
156
+ continue;
157
+ }
158
+ await sleep(LOCK_POLL_MS);
159
+ }
160
+ }
161
+ try {
162
+ const cached = await this.#read(kind, key);
163
+ if (cached)
164
+ return cached;
165
+ const data = await create();
166
+ if (!isValid(kind, data))
167
+ throw new Error(`refusing to cache invalid ${kind} data`);
168
+ const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
169
+ try {
170
+ await writeFile(temporary, data, { flag: "wx" });
171
+ await rename(temporary, path);
172
+ }
173
+ finally {
174
+ await rm(temporary, { force: true });
175
+ }
176
+ this.#startCleanup();
177
+ return this.#remember(kind, key, data);
178
+ }
179
+ finally {
180
+ await lock.close().catch(() => undefined);
181
+ await unlink(lockPath).catch(() => undefined);
182
+ }
183
+ }
184
+ #startCleanup() {
185
+ if (this.#maxDiskBytes === 0)
186
+ return;
187
+ this.#writesSinceCleanup += 1;
188
+ if (this.#cleanupRunning || (this.#hasCleaned
189
+ && this.#writesSinceCleanup < CLEANUP_WRITE_INTERVAL))
190
+ return;
191
+ this.#cleanupRunning = true;
192
+ this.#writesSinceCleanup = 0;
193
+ void this.#trimDisk()
194
+ .catch(() => undefined)
195
+ .finally(() => {
196
+ this.#cleanupRunning = false;
197
+ this.#hasCleaned = true;
198
+ if (this.#writesSinceCleanup >= CLEANUP_WRITE_INTERVAL)
199
+ this.#startCleanup();
200
+ });
201
+ }
202
+ async #trimDisk() {
203
+ const entries = [];
204
+ const visit = async (directory) => {
205
+ let children;
206
+ try {
207
+ children = await readdir(directory, { withFileTypes: true });
208
+ }
209
+ catch (error) {
210
+ if (isCode(error, "ENOENT"))
211
+ return;
212
+ throw error;
213
+ }
214
+ await Promise.all(children.map(async (child) => {
215
+ const path = join(directory, child.name);
216
+ if (child.isDirectory())
217
+ return visit(path);
218
+ if (child.name.endsWith(".lock") || child.name.endsWith(".tmp"))
219
+ return;
220
+ const info = await stat(path);
221
+ entries.push({ path, size: info.size, mtimeMs: info.mtimeMs });
222
+ }));
223
+ };
224
+ await visit(this.root);
225
+ let total = entries.reduce((sum, entry) => sum + entry.size, 0);
226
+ if (total <= this.#maxDiskBytes)
227
+ return;
228
+ entries.sort((left, right) => left.mtimeMs - right.mtimeMs);
229
+ for (const entry of entries) {
230
+ if (total <= this.#maxDiskBytes)
231
+ break;
232
+ await rm(entry.path, { force: true });
233
+ total -= entry.size;
234
+ }
235
+ }
236
+ }
237
+ export const sharedFormulaCache = new FormulaCache();
238
+ //# sourceMappingURL=formula-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formula-cache.js","sourceRoot":"","sources":["../src/formula-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { CellMetrics } from "./types.js";
2
+ export interface FormulaGeometryInput {
3
+ aspectRatio: number;
4
+ naturalHeightEx: number;
5
+ depthEx: number;
6
+ columns: number;
7
+ rows: number;
8
+ cell: CellMetrics;
9
+ scale: number;
10
+ display: boolean;
11
+ leftAlign?: boolean;
12
+ }
13
+ export interface FormulaGeometry {
14
+ canvasWidth: number;
15
+ canvasHeight: number;
16
+ formulaWidth: number;
17
+ formulaHeight: number;
18
+ offsetX: number;
19
+ offsetY: number;
20
+ }
21
+ /**
22
+ * MathJax dimensions are expressed in ex units. A terminal glyph's x-height is
23
+ * approximately 45% of its cell height. This keeps ordinary symbols aligned
24
+ * with terminal text while allowing fractions to use their natural extra height.
25
+ */
26
+ export declare function calculateFormulaGeometry(input: FormulaGeometryInput): FormulaGeometry;