latex-cite-editor 0.1.2

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/index.cjs ADDED
@@ -0,0 +1,400 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ CITE_COMMAND_RE: () => CITE_COMMAND_RE,
24
+ citationCompletionSource: () => citationCompletionSource,
25
+ cleanLatexText: () => cleanLatexText,
26
+ extractCiteKeys: () => extractCiteKeys,
27
+ formatEntry: () => formatEntry,
28
+ latexCommandCompletionSource: () => latexCommandCompletionSource,
29
+ latexHighlightPlugin: () => latexHighlightPlugin,
30
+ latexHighlightTheme: () => latexHighlightTheme,
31
+ parseBibtex: () => parseBibtex,
32
+ resolveCitations: () => resolveCitations,
33
+ resolveEntryUrl: () => resolveEntryUrl
34
+ });
35
+ module.exports = __toCommonJS(index_exports);
36
+
37
+ // src/bibtex.ts
38
+ function parseBibtex(source) {
39
+ const entries = [];
40
+ const n = source.length;
41
+ let i = 0;
42
+ const isSpace = (ch) => ch === " " || ch === " " || ch === "\n" || ch === "\r";
43
+ const skipWhitespace = () => {
44
+ while (i < n && isSpace(source[i])) i++;
45
+ };
46
+ while (i < n) {
47
+ if (source[i] !== "@") {
48
+ i++;
49
+ continue;
50
+ }
51
+ i++;
52
+ const typeStart = i;
53
+ while (i < n && /[a-zA-Z]/.test(source[i])) i++;
54
+ const type = source.slice(typeStart, i).toLowerCase();
55
+ skipWhitespace();
56
+ if (source[i] !== "{" && source[i] !== "(") continue;
57
+ const closer = source[i] === "{" ? "}" : ")";
58
+ i++;
59
+ skipWhitespace();
60
+ const keyStart = i;
61
+ while (i < n && source[i] !== "," && source[i] !== closer) i++;
62
+ const key = source.slice(keyStart, i).trim();
63
+ if (source[i] === closer) {
64
+ entries.push({ key, type, fields: {} });
65
+ i++;
66
+ continue;
67
+ }
68
+ i++;
69
+ const fields = {};
70
+ while (i < n) {
71
+ skipWhitespace();
72
+ if (source[i] === closer) {
73
+ i++;
74
+ break;
75
+ }
76
+ const nameStart = i;
77
+ while (i < n && /[a-zA-Z0-9_-]/.test(source[i])) i++;
78
+ const name = source.slice(nameStart, i).toLowerCase();
79
+ if (!name) {
80
+ i++;
81
+ continue;
82
+ }
83
+ skipWhitespace();
84
+ if (source[i] === "=") i++;
85
+ skipWhitespace();
86
+ let value = "";
87
+ if (source[i] === "{") {
88
+ let depth = 0;
89
+ const valStart = i;
90
+ do {
91
+ if (source[i] === "{") depth++;
92
+ else if (source[i] === "}") depth--;
93
+ i++;
94
+ } while (i < n && depth > 0);
95
+ value = source.slice(valStart + 1, i - 1);
96
+ } else if (source[i] === '"') {
97
+ i++;
98
+ const valStart = i;
99
+ while (i < n && source[i] !== '"') i++;
100
+ value = source.slice(valStart, i);
101
+ i++;
102
+ } else {
103
+ const valStart = i;
104
+ while (i < n && source[i] !== "," && source[i] !== closer) i++;
105
+ value = source.slice(valStart, i).trim();
106
+ }
107
+ fields[name] = value.replace(/\s+/g, " ").trim();
108
+ skipWhitespace();
109
+ if (source[i] === ",") {
110
+ i++;
111
+ } else if (source[i] === closer) {
112
+ i++;
113
+ break;
114
+ }
115
+ }
116
+ entries.push({ key, type, fields });
117
+ }
118
+ return entries.filter((e) => e.type !== "comment" && e.type !== "string" && e.type !== "preamble" && e.key);
119
+ }
120
+
121
+ // src/latexText.ts
122
+ var SYMBOL_ACCENTS = {
123
+ "`": "\u0300",
124
+ // grave
125
+ "'": "\u0301",
126
+ // acute
127
+ "^": "\u0302",
128
+ // circumflex
129
+ '"': "\u0308",
130
+ // diaeresis / umlaut
131
+ "~": "\u0303",
132
+ // tilde
133
+ "=": "\u0304",
134
+ // macron
135
+ ".": "\u0307"
136
+ // dot above
137
+ };
138
+ var LETTER_ACCENTS = {
139
+ u: "\u0306",
140
+ // breve
141
+ v: "\u030C",
142
+ // caron / háček
143
+ H: "\u030B",
144
+ // double acute
145
+ c: "\u0327",
146
+ // cedilla
147
+ k: "\u0328",
148
+ // ogonek
149
+ r: "\u030A",
150
+ // ring above
151
+ b: "\u0331",
152
+ // macron below
153
+ d: "\u0323"
154
+ // dot below
155
+ };
156
+ var LIGATURES = {
157
+ ss: "\xDF",
158
+ aa: "\xE5",
159
+ AA: "\xC5",
160
+ oe: "\u0153",
161
+ OE: "\u0152",
162
+ ae: "\xE6",
163
+ AE: "\xC6",
164
+ o: "\xF8",
165
+ O: "\xD8",
166
+ l: "\u0142",
167
+ L: "\u0141"
168
+ };
169
+ var ESCAPED_SYMBOLS = {
170
+ "&": "&",
171
+ "%": "%",
172
+ _: "_",
173
+ "#": "#",
174
+ $: "$",
175
+ "{": "{",
176
+ "}": "}"
177
+ };
178
+ var SYMBOL_ACCENT_RE = /\\([`'^"~=.])\{?([a-zA-Z])\}?/g;
179
+ var LETTER_ACCENT_RE = /\\(u|v|H|c|k|r|b|d)(?:\{([a-zA-Z])\}|\s+([a-zA-Z])\b)/g;
180
+ var LIGATURE_RE = /\\(ss|aa|AA|oe|OE|ae|AE|o|O|l|L)(?![a-zA-Z]) ?/g;
181
+ var ESCAPED_SYMBOL_RE = /\\([&%_#$}{])/g;
182
+ function cleanLatexText(value) {
183
+ if (!value) return value;
184
+ let text = value;
185
+ text = text.replace(SYMBOL_ACCENT_RE, (match, cmd, letter) => {
186
+ const mark = SYMBOL_ACCENTS[cmd];
187
+ if (!mark || !letter) return match;
188
+ return (letter + mark).normalize("NFC");
189
+ });
190
+ text = text.replace(LETTER_ACCENT_RE, (match, cmd, braced, spaced) => {
191
+ const mark = LETTER_ACCENTS[cmd];
192
+ const letter = braced ?? spaced;
193
+ if (!mark || !letter) return match;
194
+ return (letter + mark).normalize("NFC");
195
+ });
196
+ text = text.replace(LIGATURE_RE, (match, cmd) => LIGATURES[cmd] ?? match);
197
+ text = text.replace(ESCAPED_SYMBOL_RE, (match, sym) => ESCAPED_SYMBOLS[sym] ?? match);
198
+ return text.replace(/[{}]/g, "");
199
+ }
200
+
201
+ // src/citations.ts
202
+ var CITE_COMMAND_RE = /\\cite\{([^}]*)\}/g;
203
+ function escapeHtml(value) {
204
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
205
+ }
206
+ function extractCiteKeys(text) {
207
+ const keys = [];
208
+ const seen = /* @__PURE__ */ new Set();
209
+ const re = new RegExp(CITE_COMMAND_RE);
210
+ let match;
211
+ while (match = re.exec(text)) {
212
+ for (const key of match[1].split(",").map((s) => s.trim()).filter(Boolean)) {
213
+ if (!seen.has(key)) {
214
+ seen.add(key);
215
+ keys.push(key);
216
+ }
217
+ }
218
+ }
219
+ return keys;
220
+ }
221
+ function formatEntry(entry) {
222
+ const f = entry.fields;
223
+ const author = f.author ? cleanLatexText(f.author.replace(/\s+and\s+/g, ", ")) : "Autor desconhecido";
224
+ const year = f.year ? ` (${f.year})` : "";
225
+ const title = f.title ? ` ${cleanLatexText(f.title)}.` : "";
226
+ const venue = f.journal || f.booktitle || f.publisher || "";
227
+ let text = `${author}${year}.${title}`;
228
+ if (venue) text += ` ${cleanLatexText(venue)}.`;
229
+ return text.trim();
230
+ }
231
+ function resolveEntryUrl(entry) {
232
+ const f = entry.fields;
233
+ return f.url || f.link || (f.doi ? `https://doi.org/${f.doi}` : void 0);
234
+ }
235
+ function resolveCitations(text, entries) {
236
+ const byKey = new Map(entries.map((e) => [e.key, e]));
237
+ const order = extractCiteKeys(text);
238
+ const indexOf = new Map(order.map((key, idx) => [key, idx + 1]));
239
+ const occurrenceCount = /* @__PURE__ */ new Map();
240
+ const resolvedText = text.replace(CITE_COMMAND_RE, (_match, rawKeys) => {
241
+ const keys = rawKeys.split(",").map((s) => s.trim()).filter(Boolean);
242
+ return keys.map((key) => {
243
+ const idx = indexOf.get(key);
244
+ if (!idx) return `<sup class="citation-ref citation-missing">[?]</sup>`;
245
+ const occurrence = (occurrenceCount.get(idx) ?? 0) + 1;
246
+ occurrenceCount.set(idx, occurrence);
247
+ return `<sup class="citation-ref"><a href="#cite-${idx}" id="cite-ref-${idx}-${occurrence}">[${idx}]</a></sup>`;
248
+ }).join("");
249
+ });
250
+ const bibliography = order.map((key) => {
251
+ const entry = byKey.get(key);
252
+ const idx = indexOf.get(key);
253
+ return {
254
+ key,
255
+ index: idx,
256
+ entry,
257
+ text: entry ? formatEntry(entry) : `Refer\xEAncia n\xE3o encontrada no .bib: ${key}`,
258
+ url: entry ? resolveEntryUrl(entry) : void 0
259
+ };
260
+ });
261
+ const bibliographyHtml = bibliography.length ? `<div class="bibliography"><h2>Refer\xEAncias</h2><ol>${bibliography.map((item) => {
262
+ const escapedText = escapeHtml(item.text);
263
+ const body = item.url ? `<a href="${escapeHtml(item.url)}" target="_blank" rel="noopener noreferrer" class="citation-link">${escapedText}</a>` : escapedText;
264
+ return `<li id="cite-${item.index}">${body} <a href="#cite-ref-${item.index}-1" class="citation-backref">\u21A9</a></li>`;
265
+ }).join("")}</ol></div>` : "";
266
+ return { text: resolvedText, bibliography, bibliographyHtml };
267
+ }
268
+
269
+ // src/autocomplete.ts
270
+ var LATEX_COMMANDS = [
271
+ { label: "\\cite{}", apply: "\\cite{}", type: "keyword", detail: "citar refer\xEAncia do .bib", boost: 2 },
272
+ { label: "\\textbf{}", apply: "\\textbf{}", type: "keyword", detail: "negrito" },
273
+ { label: "\\textit{}", apply: "\\textit{}", type: "keyword", detail: "it\xE1lico" },
274
+ { label: "\\section{}", apply: "\\section{}", type: "keyword", detail: "se\xE7\xE3o" },
275
+ { label: "\\subsection{}", apply: "\\subsection{}", type: "keyword", detail: "subse\xE7\xE3o" },
276
+ { label: "\\label{}", apply: "\\label{}", type: "keyword", detail: "r\xF3tulo para refer\xEAncia cruzada" },
277
+ { label: "\\ref{}", apply: "\\ref{}", type: "keyword", detail: "refer\xEAncia cruzada a um \\label" },
278
+ { label: "\\footnote{}", apply: "\\footnote{}", type: "keyword", detail: "nota de rodap\xE9" }
279
+ ];
280
+ var MATH_SYMBOLS = [
281
+ "\\alpha",
282
+ "\\beta",
283
+ "\\gamma",
284
+ "\\delta",
285
+ "\\theta",
286
+ "\\lambda",
287
+ "\\pi",
288
+ "\\sigma",
289
+ "\\omega",
290
+ "\\sum",
291
+ "\\int",
292
+ "\\prod",
293
+ "\\infty",
294
+ "\\partial",
295
+ "\\nabla",
296
+ "\\leq",
297
+ "\\geq",
298
+ "\\neq",
299
+ "\\approx",
300
+ "\\times",
301
+ "\\cdot"
302
+ ].map((label) => ({ label, type: "constant" })).concat([
303
+ { label: "\\sqrt{}", apply: "\\sqrt{}", type: "constant" },
304
+ { label: "\\frac{}{}", apply: "\\frac{}{}", type: "constant" }
305
+ ]);
306
+ function citationDetail(entry) {
307
+ const firstAuthor = entry.fields.author?.split(/\s+and\s+/)[0]?.split(",")[0]?.trim();
308
+ const year = entry.fields.year;
309
+ const who = firstAuthor ?? "autor desconhecido";
310
+ return year ? `${who} (${year})` : who;
311
+ }
312
+ function citationCompletionSource(getEntries) {
313
+ return (context) => {
314
+ const match = context.matchBefore(/\\cite\{[^}]*/);
315
+ if (!match) return null;
316
+ const braceIdx = match.text.indexOf("{");
317
+ const from = match.from + braceIdx + 1;
318
+ const entries = getEntries();
319
+ if (entries.length === 0) return null;
320
+ return {
321
+ from,
322
+ options: entries.map((entry) => ({
323
+ label: entry.key,
324
+ detail: citationDetail(entry),
325
+ info: entry.fields.title ? entry.fields.title.replace(/[{}]/g, "") : void 0,
326
+ type: "text"
327
+ })),
328
+ validFor: /^[^}]*$/
329
+ };
330
+ };
331
+ }
332
+ function isInsideMath(textBeforeCursor) {
333
+ const lastParagraphBreak = textBeforeCursor.lastIndexOf("\n\n");
334
+ const scope = lastParagraphBreak === -1 ? textBeforeCursor : textBeforeCursor.slice(lastParagraphBreak);
335
+ const dollarCount = (scope.match(/\$/g) || []).length;
336
+ return dollarCount % 2 === 1;
337
+ }
338
+ function latexCommandCompletionSource(context) {
339
+ const word = context.matchBefore(/\\[a-zA-Z]*/);
340
+ if (!word || word.from === word.to && !context.explicit) return null;
341
+ const inMath = isInsideMath(context.state.sliceDoc(0, word.from));
342
+ const options = inMath ? [...MATH_SYMBOLS, ...LATEX_COMMANDS] : LATEX_COMMANDS;
343
+ return {
344
+ from: word.from,
345
+ options,
346
+ validFor: /^\\[a-zA-Z]*$/
347
+ };
348
+ }
349
+
350
+ // src/latexDecorations.ts
351
+ var import_view = require("@codemirror/view");
352
+ var LATEX_TOKEN_RE = /\\cite\{[^}]*\}|\\[a-zA-Z]+(?:\{[^}]*\})?|\$\$[^$]*\$\$|\$[^$\n]+\$/g;
353
+ function classify(token) {
354
+ if (token.startsWith("\\cite{")) return "cm-latex-cite";
355
+ if (token.startsWith("$$")) return "cm-latex-math-block";
356
+ if (token.startsWith("$")) return "cm-latex-math-inline";
357
+ return "cm-latex-command";
358
+ }
359
+ var matcher = new import_view.MatchDecorator({
360
+ regexp: LATEX_TOKEN_RE,
361
+ decoration: (match) => import_view.Decoration.mark({ class: classify(match[0]) })
362
+ });
363
+ var latexHighlightPlugin = import_view.ViewPlugin.define(
364
+ (view) => ({
365
+ decorations: matcher.createDeco(view),
366
+ update(update) {
367
+ this.decorations = matcher.updateDeco(update, this.decorations);
368
+ }
369
+ }),
370
+ {
371
+ decorations: (instance) => instance.decorations
372
+ }
373
+ );
374
+ var latexHighlightTheme = import_view.EditorView.baseTheme({
375
+ ".cm-latex-cite": {
376
+ color: "#0ea5e9",
377
+ fontWeight: "600"
378
+ },
379
+ ".cm-latex-command": {
380
+ color: "#a855f7"
381
+ },
382
+ ".cm-latex-math-inline, .cm-latex-math-block": {
383
+ color: "#f59e0b"
384
+ }
385
+ });
386
+ // Annotate the CommonJS export names for ESM import in node:
387
+ 0 && (module.exports = {
388
+ CITE_COMMAND_RE,
389
+ citationCompletionSource,
390
+ cleanLatexText,
391
+ extractCiteKeys,
392
+ formatEntry,
393
+ latexCommandCompletionSource,
394
+ latexHighlightPlugin,
395
+ latexHighlightTheme,
396
+ parseBibtex,
397
+ resolveCitations,
398
+ resolveEntryUrl
399
+ });
400
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/bibtex.ts","../src/latexText.ts","../src/citations.ts","../src/autocomplete.ts","../src/latexDecorations.ts"],"sourcesContent":["export { parseBibtex } from './bibtex';\nexport type { BibEntry } from './bibtex';\n\nexport { extractCiteKeys, formatEntry, resolveEntryUrl, resolveCitations, CITE_COMMAND_RE } from './citations';\nexport type { BibliographyItem, ResolvedCitations } from './citations';\n\nexport { cleanLatexText } from './latexText';\n\nexport { citationCompletionSource, latexCommandCompletionSource } from './autocomplete';\nexport { latexHighlightPlugin, latexHighlightTheme } from './latexDecorations';\n\n// The CiteEditor React component lives in a separate './react' entry (see\n// react.ts) so that server-safe consumers of this module (parseBibtex,\n// resolveCitations, etc.) never pull React/CodeMirror into a server bundle.\n","export interface BibEntry {\n key: string;\n type: string;\n fields: Record<string, string>;\n}\n\n/**\n * Hand-rolled parser instead of a regex split: BibTeX field values commonly\n * contain nested braces (e.g. `title = {The {Higgs} Boson}`), which a single\n * regex cannot balance correctly.\n */\nexport function parseBibtex(source: string): BibEntry[] {\n const entries: BibEntry[] = [];\n const n = source.length;\n let i = 0;\n\n const isSpace = (ch: string) => ch === ' ' || ch === '\\t' || ch === '\\n' || ch === '\\r';\n const skipWhitespace = () => {\n while (i < n && isSpace(source[i])) i++;\n };\n\n while (i < n) {\n if (source[i] !== '@') {\n i++;\n continue;\n }\n i++;\n const typeStart = i;\n while (i < n && /[a-zA-Z]/.test(source[i])) i++;\n const type = source.slice(typeStart, i).toLowerCase();\n skipWhitespace();\n\n if (source[i] !== '{' && source[i] !== '(') continue;\n const closer = source[i] === '{' ? '}' : ')';\n i++;\n skipWhitespace();\n\n const keyStart = i;\n while (i < n && source[i] !== ',' && source[i] !== closer) i++;\n const key = source.slice(keyStart, i).trim();\n\n if (source[i] === closer) {\n entries.push({ key, type, fields: {} });\n i++;\n continue;\n }\n i++; // skip comma after key\n\n const fields: Record<string, string> = {};\n while (i < n) {\n skipWhitespace();\n if (source[i] === closer) {\n i++;\n break;\n }\n\n const nameStart = i;\n while (i < n && /[a-zA-Z0-9_-]/.test(source[i])) i++;\n const name = source.slice(nameStart, i).toLowerCase();\n if (!name) {\n // malformed entry; bail out of this field loop to avoid an infinite loop\n i++;\n continue;\n }\n skipWhitespace();\n if (source[i] === '=') i++;\n skipWhitespace();\n\n let value = '';\n if (source[i] === '{') {\n let depth = 0;\n const valStart = i;\n do {\n if (source[i] === '{') depth++;\n else if (source[i] === '}') depth--;\n i++;\n } while (i < n && depth > 0);\n value = source.slice(valStart + 1, i - 1);\n } else if (source[i] === '\"') {\n i++;\n const valStart = i;\n while (i < n && source[i] !== '\"') i++;\n value = source.slice(valStart, i);\n i++;\n } else {\n const valStart = i;\n while (i < n && source[i] !== ',' && source[i] !== closer) i++;\n value = source.slice(valStart, i).trim();\n }\n\n fields[name] = value.replace(/\\s+/g, ' ').trim();\n skipWhitespace();\n if (source[i] === ',') {\n i++;\n } else if (source[i] === closer) {\n i++;\n break;\n }\n }\n\n entries.push({ key, type, fields });\n }\n\n return entries.filter((e) => e.type !== 'comment' && e.type !== 'string' && e.type !== 'preamble' && e.key);\n}\n","/**\n * BibTeX exported from Google Scholar (and most reference managers) escapes\n * accented letters as LaTeX accent macros — `{\\^o}`, `{\\'e}`, `{\\c c}` — instead\n * of the actual UTF-8 character. Left alone, \"dem{\\^o}nios\" renders literally\n * as \"dem\\^onios\" instead of \"demônios\". This converts the common macros back\n * to real Unicode text for display.\n */\n\n// Diacritics written as a single non-letter symbol right after the backslash\n// (a TeX \"control symbol\"), so no separator is required: \\^o, \\^{o}, {\\^o}.\nconst SYMBOL_ACCENTS: Record<string, string> = {\n '`': '̀', // grave\n \"'\": '́', // acute\n '^': '̂', // circumflex\n '\"': '̈', // diaeresis / umlaut\n '~': '̃', // tilde\n '=': '̄', // macron\n '.': '̇', // dot above\n};\n\n// Diacritics written as a letter-named macro (a TeX \"control word\"), which\n// requires a separator (space or braces) before the argument: \\v{c}, \\c c.\nconst LETTER_ACCENTS: Record<string, string> = {\n u: '̆', // breve\n v: '̌', // caron / háček\n H: '̋', // double acute\n c: '̧', // cedilla\n k: '̨', // ogonek\n r: '̊', // ring above\n b: '̱', // macron below\n d: '̣', // dot below\n};\n\n// Letters/ligatures with no accentable base character, so no combining mark applies.\nconst LIGATURES: Record<string, string> = {\n ss: 'ß',\n aa: 'å',\n AA: 'Å',\n oe: 'œ',\n OE: 'Œ',\n ae: 'æ',\n AE: 'Æ',\n o: 'ø',\n O: 'Ø',\n l: 'ł',\n L: 'Ł',\n};\n\n// Backslash-escaped punctuation that's only special because of LaTeX/BibTeX syntax.\nconst ESCAPED_SYMBOLS: Record<string, string> = {\n '&': '&',\n '%': '%',\n _: '_',\n '#': '#',\n $: '$',\n '{': '{',\n '}': '}',\n};\n\nconst SYMBOL_ACCENT_RE = /\\\\([`'^\"~=.])\\{?([a-zA-Z])\\}?/g;\nconst LETTER_ACCENT_RE = /\\\\(u|v|H|c|k|r|b|d)(?:\\{([a-zA-Z])\\}|\\s+([a-zA-Z])\\b)/g;\n// TeX control words swallow exactly one trailing space (their normal argument\n// separator), so `\\OE uvres` renders as one word, \"Œuvres\" — not \"Œ uvres\".\nconst LIGATURE_RE = /\\\\(ss|aa|AA|oe|OE|ae|AE|o|O|l|L)(?![a-zA-Z]) ?/g;\nconst ESCAPED_SYMBOL_RE = /\\\\([&%_#$}{])/g;\n\n/**\n * Converts LaTeX accent macros and escaped symbols in BibTeX field text\n * (author/title/journal/...) into plain Unicode, then strips any leftover\n * `{...}` grouping braces (commonly used to protect capitalization).\n */\nexport function cleanLatexText(value: string): string {\n if (!value) return value;\n\n let text = value;\n\n text = text.replace(SYMBOL_ACCENT_RE, (match, cmd: string, letter: string) => {\n const mark = SYMBOL_ACCENTS[cmd];\n if (!mark || !letter) return match;\n return (letter + mark).normalize('NFC');\n });\n\n text = text.replace(LETTER_ACCENT_RE, (match, cmd: string, braced?: string, spaced?: string) => {\n const mark = LETTER_ACCENTS[cmd];\n const letter = braced ?? spaced;\n if (!mark || !letter) return match;\n return (letter + mark).normalize('NFC');\n });\n\n text = text.replace(LIGATURE_RE, (match, cmd: string) => LIGATURES[cmd] ?? match);\n text = text.replace(ESCAPED_SYMBOL_RE, (match, sym: string) => ESCAPED_SYMBOLS[sym] ?? match);\n\n return text.replace(/[{}]/g, '');\n}\n","import type { BibEntry } from './bibtex';\nimport { cleanLatexText } from './latexText';\n\nexport const CITE_COMMAND_RE = /\\\\cite\\{([^}]*)\\}/g;\n\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n\n/** Order of first appearance, deduplicated, across every \\cite{...} in the text. */\nexport function extractCiteKeys(text: string): string[] {\n const keys: string[] = [];\n const seen = new Set<string>();\n const re = new RegExp(CITE_COMMAND_RE);\n let match: RegExpExecArray | null;\n while ((match = re.exec(text))) {\n for (const key of match[1].split(',').map((s) => s.trim()).filter(Boolean)) {\n if (!seen.has(key)) {\n seen.add(key);\n keys.push(key);\n }\n }\n }\n return keys;\n}\n\n/** Formats a single BibTeX entry as a plain reference-list line (numeric/IEEE-ish style). */\nexport function formatEntry(entry: BibEntry): string {\n const f = entry.fields;\n const author = f.author ? cleanLatexText(f.author.replace(/\\s+and\\s+/g, ', ')) : 'Autor desconhecido';\n const year = f.year ? ` (${f.year})` : '';\n const title = f.title ? ` ${cleanLatexText(f.title)}.` : '';\n const venue = f.journal || f.booktitle || f.publisher || '';\n let text = `${author}${year}.${title}`;\n if (venue) text += ` ${cleanLatexText(venue)}.`;\n return text.trim();\n}\n\n/** Resolves the URL a bibliography entry should link to, if any (`url`, `link`, then `doi`). */\nexport function resolveEntryUrl(entry: BibEntry): string | undefined {\n const f = entry.fields;\n return f.url || f.link || (f.doi ? `https://doi.org/${f.doi}` : undefined);\n}\n\nexport interface BibliographyItem {\n key: string;\n index: number;\n entry?: BibEntry;\n text: string;\n url?: string;\n}\n\nexport interface ResolvedCitations {\n /** Markdown/HTML-safe text with every \\cite{key[,key2]} replaced by numbered, linked markers. */\n text: string;\n bibliography: BibliographyItem[];\n /** Ready-to-inject HTML block listing the bibliography (or '' if there are no citations). */\n bibliographyHtml: string;\n}\n\n/**\n * Resolves \\cite{...} commands against parsed BibTeX entries, numbering them\n * by order of first appearance (like LaTeX + natbib's numeric style), and\n * builds the trailing bibliography section.\n */\nexport function resolveCitations(text: string, entries: BibEntry[]): ResolvedCitations {\n const byKey = new Map(entries.map((e) => [e.key, e]));\n const order = extractCiteKeys(text);\n const indexOf = new Map(order.map((key, idx) => [key, idx + 1]));\n\n const occurrenceCount = new Map<number, number>();\n const resolvedText = text.replace(CITE_COMMAND_RE, (_match, rawKeys: string) => {\n const keys = rawKeys.split(',').map((s) => s.trim()).filter(Boolean);\n return keys\n .map((key) => {\n const idx = indexOf.get(key);\n if (!idx) return `<sup class=\"citation-ref citation-missing\">[?]</sup>`;\n // Each citation marker needs a unique id even when the same source is\n // cited more than once, so bibliography backrefs always target one anchor.\n const occurrence = (occurrenceCount.get(idx) ?? 0) + 1;\n occurrenceCount.set(idx, occurrence);\n return `<sup class=\"citation-ref\"><a href=\"#cite-${idx}\" id=\"cite-ref-${idx}-${occurrence}\">[${idx}]</a></sup>`;\n })\n .join('');\n });\n\n const bibliography: BibliographyItem[] = order.map((key) => {\n const entry = byKey.get(key);\n const idx = indexOf.get(key)!;\n return {\n key,\n index: idx,\n entry,\n text: entry ? formatEntry(entry) : `Referência não encontrada no .bib: ${key}`,\n url: entry ? resolveEntryUrl(entry) : undefined,\n };\n });\n\n const bibliographyHtml = bibliography.length\n ? `<div class=\"bibliography\"><h2>Referências</h2><ol>${bibliography\n .map((item) => {\n const escapedText = escapeHtml(item.text);\n const body = item.url\n ? `<a href=\"${escapeHtml(item.url)}\" target=\"_blank\" rel=\"noopener noreferrer\" class=\"citation-link\">${escapedText}</a>`\n : escapedText;\n return `<li id=\"cite-${item.index}\">${body} <a href=\"#cite-ref-${item.index}-1\" class=\"citation-backref\">↩</a></li>`;\n })\n .join('')}</ol></div>`\n : '';\n\n return { text: resolvedText, bibliography, bibliographyHtml };\n}\n","import type { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete';\nimport type { BibEntry } from './bibtex';\n\nconst LATEX_COMMANDS: Completion[] = [\n { label: '\\\\cite{}', apply: '\\\\cite{}', type: 'keyword', detail: 'citar referência do .bib', boost: 2 },\n { label: '\\\\textbf{}', apply: '\\\\textbf{}', type: 'keyword', detail: 'negrito' },\n { label: '\\\\textit{}', apply: '\\\\textit{}', type: 'keyword', detail: 'itálico' },\n { label: '\\\\section{}', apply: '\\\\section{}', type: 'keyword', detail: 'seção' },\n { label: '\\\\subsection{}', apply: '\\\\subsection{}', type: 'keyword', detail: 'subseção' },\n { label: '\\\\label{}', apply: '\\\\label{}', type: 'keyword', detail: 'rótulo para referência cruzada' },\n { label: '\\\\ref{}', apply: '\\\\ref{}', type: 'keyword', detail: 'referência cruzada a um \\\\label' },\n { label: '\\\\footnote{}', apply: '\\\\footnote{}', type: 'keyword', detail: 'nota de rodapé' },\n];\n\nconst MATH_SYMBOLS: Completion[] = [\n '\\\\alpha', '\\\\beta', '\\\\gamma', '\\\\delta', '\\\\theta', '\\\\lambda', '\\\\pi', '\\\\sigma', '\\\\omega',\n '\\\\sum', '\\\\int', '\\\\prod', '\\\\infty', '\\\\partial', '\\\\nabla',\n '\\\\leq', '\\\\geq', '\\\\neq', '\\\\approx', '\\\\times', '\\\\cdot',\n].map((label): Completion => ({ label, type: 'constant' })).concat([\n { label: '\\\\sqrt{}', apply: '\\\\sqrt{}', type: 'constant' },\n { label: '\\\\frac{}{}', apply: '\\\\frac{}{}', type: 'constant' },\n]);\n\nfunction citationDetail(entry: BibEntry): string {\n const firstAuthor = entry.fields.author?.split(/\\s+and\\s+/)[0]?.split(',')[0]?.trim();\n const year = entry.fields.year;\n const who = firstAuthor ?? 'autor desconhecido';\n return year ? `${who} (${year})` : who;\n}\n\n/** Autocompletes bib keys right after `\\cite{`, sourced live from the current .bib entries. */\nexport function citationCompletionSource(getEntries: () => BibEntry[]) {\n return (context: CompletionContext): CompletionResult | null => {\n const match = context.matchBefore(/\\\\cite\\{[^}]*/);\n if (!match) return null;\n const braceIdx = match.text.indexOf('{');\n const from = match.from + braceIdx + 1;\n const entries = getEntries();\n if (entries.length === 0) return null;\n\n return {\n from,\n options: entries.map((entry) => ({\n label: entry.key,\n detail: citationDetail(entry),\n info: entry.fields.title ? entry.fields.title.replace(/[{}]/g, '') : undefined,\n type: 'text',\n })),\n validFor: /^[^}]*$/,\n };\n };\n}\n\nfunction isInsideMath(textBeforeCursor: string): boolean {\n const lastParagraphBreak = textBeforeCursor.lastIndexOf('\\n\\n');\n const scope = lastParagraphBreak === -1 ? textBeforeCursor : textBeforeCursor.slice(lastParagraphBreak);\n const dollarCount = (scope.match(/\\$/g) || []).length;\n return dollarCount % 2 === 1;\n}\n\n/** Autocompletes LaTeX-like commands (\\cite, \\textbf, ...) and, inside $...$, math symbols. */\nexport function latexCommandCompletionSource(context: CompletionContext): CompletionResult | null {\n const word = context.matchBefore(/\\\\[a-zA-Z]*/);\n if (!word || (word.from === word.to && !context.explicit)) return null;\n\n const inMath = isInsideMath(context.state.sliceDoc(0, word.from));\n const options = inMath ? [...MATH_SYMBOLS, ...LATEX_COMMANDS] : LATEX_COMMANDS;\n\n return {\n from: word.from,\n options,\n validFor: /^\\\\[a-zA-Z]*$/,\n };\n}\n","import { Decoration, EditorView, MatchDecorator, ViewPlugin, type DecorationSet } from '@codemirror/view';\n\nconst LATEX_TOKEN_RE = /\\\\cite\\{[^}]*\\}|\\\\[a-zA-Z]+(?:\\{[^}]*\\})?|\\$\\$[^$]*\\$\\$|\\$[^$\\n]+\\$/g;\n\nfunction classify(token: string): string {\n if (token.startsWith('\\\\cite{')) return 'cm-latex-cite';\n if (token.startsWith('$$')) return 'cm-latex-math-block';\n if (token.startsWith('$')) return 'cm-latex-math-inline';\n return 'cm-latex-command';\n}\n\nconst matcher = new MatchDecorator({\n regexp: LATEX_TOKEN_RE,\n decoration: (match) => Decoration.mark({ class: classify(match[0]) }),\n});\n\n/** Regex-based decorations that highlight \\cite{}, other \\commands, and $...$/$$...$$ math spans. */\nexport const latexHighlightPlugin = ViewPlugin.define(\n (view) => ({\n decorations: matcher.createDeco(view),\n update(update) {\n this.decorations = matcher.updateDeco(update, this.decorations);\n },\n }),\n {\n decorations: (instance) => instance.decorations as DecorationSet,\n }\n);\n\nexport const latexHighlightTheme = EditorView.baseTheme({\n '.cm-latex-cite': {\n color: '#0ea5e9',\n fontWeight: '600',\n },\n '.cm-latex-command': {\n color: '#a855f7',\n },\n '.cm-latex-math-inline, .cm-latex-math-block': {\n color: '#f59e0b',\n },\n});\n"],"mappings":"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","names":[]}
@@ -0,0 +1,62 @@
1
+ import { B as BibEntry } from './bibtex-BV8HliUE.cjs';
2
+ export { p as parseBibtex } from './bibtex-BV8HliUE.cjs';
3
+ import { CompletionContext, CompletionResult } from '@codemirror/autocomplete';
4
+ import * as _codemirror_view from '@codemirror/view';
5
+ import { ViewPlugin, Decoration } from '@codemirror/view';
6
+ import * as _codemirror_state from '@codemirror/state';
7
+
8
+ declare const CITE_COMMAND_RE: RegExp;
9
+ /** Order of first appearance, deduplicated, across every \cite{...} in the text. */
10
+ declare function extractCiteKeys(text: string): string[];
11
+ /** Formats a single BibTeX entry as a plain reference-list line (numeric/IEEE-ish style). */
12
+ declare function formatEntry(entry: BibEntry): string;
13
+ /** Resolves the URL a bibliography entry should link to, if any (`url`, `link`, then `doi`). */
14
+ declare function resolveEntryUrl(entry: BibEntry): string | undefined;
15
+ interface BibliographyItem {
16
+ key: string;
17
+ index: number;
18
+ entry?: BibEntry;
19
+ text: string;
20
+ url?: string;
21
+ }
22
+ interface ResolvedCitations {
23
+ /** Markdown/HTML-safe text with every \cite{key[,key2]} replaced by numbered, linked markers. */
24
+ text: string;
25
+ bibliography: BibliographyItem[];
26
+ /** Ready-to-inject HTML block listing the bibliography (or '' if there are no citations). */
27
+ bibliographyHtml: string;
28
+ }
29
+ /**
30
+ * Resolves \cite{...} commands against parsed BibTeX entries, numbering them
31
+ * by order of first appearance (like LaTeX + natbib's numeric style), and
32
+ * builds the trailing bibliography section.
33
+ */
34
+ declare function resolveCitations(text: string, entries: BibEntry[]): ResolvedCitations;
35
+
36
+ /**
37
+ * BibTeX exported from Google Scholar (and most reference managers) escapes
38
+ * accented letters as LaTeX accent macros — `{\^o}`, `{\'e}`, `{\c c}` — instead
39
+ * of the actual UTF-8 character. Left alone, "dem{\^o}nios" renders literally
40
+ * as "dem\^onios" instead of "demônios". This converts the common macros back
41
+ * to real Unicode text for display.
42
+ */
43
+ /**
44
+ * Converts LaTeX accent macros and escaped symbols in BibTeX field text
45
+ * (author/title/journal/...) into plain Unicode, then strips any leftover
46
+ * `{...}` grouping braces (commonly used to protect capitalization).
47
+ */
48
+ declare function cleanLatexText(value: string): string;
49
+
50
+ /** Autocompletes bib keys right after `\cite{`, sourced live from the current .bib entries. */
51
+ declare function citationCompletionSource(getEntries: () => BibEntry[]): (context: CompletionContext) => CompletionResult | null;
52
+ /** Autocompletes LaTeX-like commands (\cite, \textbf, ...) and, inside $...$, math symbols. */
53
+ declare function latexCommandCompletionSource(context: CompletionContext): CompletionResult | null;
54
+
55
+ /** Regex-based decorations that highlight \cite{}, other \commands, and $...$/$$...$$ math spans. */
56
+ declare const latexHighlightPlugin: ViewPlugin<{
57
+ decorations: _codemirror_state.RangeSet<Decoration>;
58
+ update(update: _codemirror_view.ViewUpdate): void;
59
+ }, undefined>;
60
+ declare const latexHighlightTheme: _codemirror_state.Extension;
61
+
62
+ export { BibEntry, type BibliographyItem, CITE_COMMAND_RE, type ResolvedCitations, citationCompletionSource, cleanLatexText, extractCiteKeys, formatEntry, latexCommandCompletionSource, latexHighlightPlugin, latexHighlightTheme, resolveCitations, resolveEntryUrl };
@@ -0,0 +1,62 @@
1
+ import { B as BibEntry } from './bibtex-BV8HliUE.js';
2
+ export { p as parseBibtex } from './bibtex-BV8HliUE.js';
3
+ import { CompletionContext, CompletionResult } from '@codemirror/autocomplete';
4
+ import * as _codemirror_view from '@codemirror/view';
5
+ import { ViewPlugin, Decoration } from '@codemirror/view';
6
+ import * as _codemirror_state from '@codemirror/state';
7
+
8
+ declare const CITE_COMMAND_RE: RegExp;
9
+ /** Order of first appearance, deduplicated, across every \cite{...} in the text. */
10
+ declare function extractCiteKeys(text: string): string[];
11
+ /** Formats a single BibTeX entry as a plain reference-list line (numeric/IEEE-ish style). */
12
+ declare function formatEntry(entry: BibEntry): string;
13
+ /** Resolves the URL a bibliography entry should link to, if any (`url`, `link`, then `doi`). */
14
+ declare function resolveEntryUrl(entry: BibEntry): string | undefined;
15
+ interface BibliographyItem {
16
+ key: string;
17
+ index: number;
18
+ entry?: BibEntry;
19
+ text: string;
20
+ url?: string;
21
+ }
22
+ interface ResolvedCitations {
23
+ /** Markdown/HTML-safe text with every \cite{key[,key2]} replaced by numbered, linked markers. */
24
+ text: string;
25
+ bibliography: BibliographyItem[];
26
+ /** Ready-to-inject HTML block listing the bibliography (or '' if there are no citations). */
27
+ bibliographyHtml: string;
28
+ }
29
+ /**
30
+ * Resolves \cite{...} commands against parsed BibTeX entries, numbering them
31
+ * by order of first appearance (like LaTeX + natbib's numeric style), and
32
+ * builds the trailing bibliography section.
33
+ */
34
+ declare function resolveCitations(text: string, entries: BibEntry[]): ResolvedCitations;
35
+
36
+ /**
37
+ * BibTeX exported from Google Scholar (and most reference managers) escapes
38
+ * accented letters as LaTeX accent macros — `{\^o}`, `{\'e}`, `{\c c}` — instead
39
+ * of the actual UTF-8 character. Left alone, "dem{\^o}nios" renders literally
40
+ * as "dem\^onios" instead of "demônios". This converts the common macros back
41
+ * to real Unicode text for display.
42
+ */
43
+ /**
44
+ * Converts LaTeX accent macros and escaped symbols in BibTeX field text
45
+ * (author/title/journal/...) into plain Unicode, then strips any leftover
46
+ * `{...}` grouping braces (commonly used to protect capitalization).
47
+ */
48
+ declare function cleanLatexText(value: string): string;
49
+
50
+ /** Autocompletes bib keys right after `\cite{`, sourced live from the current .bib entries. */
51
+ declare function citationCompletionSource(getEntries: () => BibEntry[]): (context: CompletionContext) => CompletionResult | null;
52
+ /** Autocompletes LaTeX-like commands (\cite, \textbf, ...) and, inside $...$, math symbols. */
53
+ declare function latexCommandCompletionSource(context: CompletionContext): CompletionResult | null;
54
+
55
+ /** Regex-based decorations that highlight \cite{}, other \commands, and $...$/$$...$$ math spans. */
56
+ declare const latexHighlightPlugin: ViewPlugin<{
57
+ decorations: _codemirror_state.RangeSet<Decoration>;
58
+ update(update: _codemirror_view.ViewUpdate): void;
59
+ }, undefined>;
60
+ declare const latexHighlightTheme: _codemirror_state.Extension;
61
+
62
+ export { BibEntry, type BibliographyItem, CITE_COMMAND_RE, type ResolvedCitations, citationCompletionSource, cleanLatexText, extractCiteKeys, formatEntry, latexCommandCompletionSource, latexHighlightPlugin, latexHighlightTheme, resolveCitations, resolveEntryUrl };