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/LICENSE +21 -0
- package/README.md +103 -0
- package/dist/bibtex-BV8HliUE.d.cts +13 -0
- package/dist/bibtex-BV8HliUE.d.ts +13 -0
- package/dist/chunk-5AWY4EOD.js +125 -0
- package/dist/chunk-5AWY4EOD.js.map +1 -0
- package/dist/index.cjs +400 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +62 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.js +252 -0
- package/dist/index.js.map +1 -0
- package/dist/react.cjs +298 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +37 -0
- package/dist/react.d.ts +37 -0
- package/dist/react.js +158 -0
- package/dist/react.js.map +1 -0
- package/package.json +76 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import {
|
|
2
|
+
citationCompletionSource,
|
|
3
|
+
latexCommandCompletionSource,
|
|
4
|
+
latexHighlightPlugin,
|
|
5
|
+
latexHighlightTheme
|
|
6
|
+
} from "./chunk-5AWY4EOD.js";
|
|
7
|
+
|
|
8
|
+
// src/bibtex.ts
|
|
9
|
+
function parseBibtex(source) {
|
|
10
|
+
const entries = [];
|
|
11
|
+
const n = source.length;
|
|
12
|
+
let i = 0;
|
|
13
|
+
const isSpace = (ch) => ch === " " || ch === " " || ch === "\n" || ch === "\r";
|
|
14
|
+
const skipWhitespace = () => {
|
|
15
|
+
while (i < n && isSpace(source[i])) i++;
|
|
16
|
+
};
|
|
17
|
+
while (i < n) {
|
|
18
|
+
if (source[i] !== "@") {
|
|
19
|
+
i++;
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
i++;
|
|
23
|
+
const typeStart = i;
|
|
24
|
+
while (i < n && /[a-zA-Z]/.test(source[i])) i++;
|
|
25
|
+
const type = source.slice(typeStart, i).toLowerCase();
|
|
26
|
+
skipWhitespace();
|
|
27
|
+
if (source[i] !== "{" && source[i] !== "(") continue;
|
|
28
|
+
const closer = source[i] === "{" ? "}" : ")";
|
|
29
|
+
i++;
|
|
30
|
+
skipWhitespace();
|
|
31
|
+
const keyStart = i;
|
|
32
|
+
while (i < n && source[i] !== "," && source[i] !== closer) i++;
|
|
33
|
+
const key = source.slice(keyStart, i).trim();
|
|
34
|
+
if (source[i] === closer) {
|
|
35
|
+
entries.push({ key, type, fields: {} });
|
|
36
|
+
i++;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
i++;
|
|
40
|
+
const fields = {};
|
|
41
|
+
while (i < n) {
|
|
42
|
+
skipWhitespace();
|
|
43
|
+
if (source[i] === closer) {
|
|
44
|
+
i++;
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
const nameStart = i;
|
|
48
|
+
while (i < n && /[a-zA-Z0-9_-]/.test(source[i])) i++;
|
|
49
|
+
const name = source.slice(nameStart, i).toLowerCase();
|
|
50
|
+
if (!name) {
|
|
51
|
+
i++;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
skipWhitespace();
|
|
55
|
+
if (source[i] === "=") i++;
|
|
56
|
+
skipWhitespace();
|
|
57
|
+
let value = "";
|
|
58
|
+
if (source[i] === "{") {
|
|
59
|
+
let depth = 0;
|
|
60
|
+
const valStart = i;
|
|
61
|
+
do {
|
|
62
|
+
if (source[i] === "{") depth++;
|
|
63
|
+
else if (source[i] === "}") depth--;
|
|
64
|
+
i++;
|
|
65
|
+
} while (i < n && depth > 0);
|
|
66
|
+
value = source.slice(valStart + 1, i - 1);
|
|
67
|
+
} else if (source[i] === '"') {
|
|
68
|
+
i++;
|
|
69
|
+
const valStart = i;
|
|
70
|
+
while (i < n && source[i] !== '"') i++;
|
|
71
|
+
value = source.slice(valStart, i);
|
|
72
|
+
i++;
|
|
73
|
+
} else {
|
|
74
|
+
const valStart = i;
|
|
75
|
+
while (i < n && source[i] !== "," && source[i] !== closer) i++;
|
|
76
|
+
value = source.slice(valStart, i).trim();
|
|
77
|
+
}
|
|
78
|
+
fields[name] = value.replace(/\s+/g, " ").trim();
|
|
79
|
+
skipWhitespace();
|
|
80
|
+
if (source[i] === ",") {
|
|
81
|
+
i++;
|
|
82
|
+
} else if (source[i] === closer) {
|
|
83
|
+
i++;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
entries.push({ key, type, fields });
|
|
88
|
+
}
|
|
89
|
+
return entries.filter((e) => e.type !== "comment" && e.type !== "string" && e.type !== "preamble" && e.key);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// src/latexText.ts
|
|
93
|
+
var SYMBOL_ACCENTS = {
|
|
94
|
+
"`": "\u0300",
|
|
95
|
+
// grave
|
|
96
|
+
"'": "\u0301",
|
|
97
|
+
// acute
|
|
98
|
+
"^": "\u0302",
|
|
99
|
+
// circumflex
|
|
100
|
+
'"': "\u0308",
|
|
101
|
+
// diaeresis / umlaut
|
|
102
|
+
"~": "\u0303",
|
|
103
|
+
// tilde
|
|
104
|
+
"=": "\u0304",
|
|
105
|
+
// macron
|
|
106
|
+
".": "\u0307"
|
|
107
|
+
// dot above
|
|
108
|
+
};
|
|
109
|
+
var LETTER_ACCENTS = {
|
|
110
|
+
u: "\u0306",
|
|
111
|
+
// breve
|
|
112
|
+
v: "\u030C",
|
|
113
|
+
// caron / háček
|
|
114
|
+
H: "\u030B",
|
|
115
|
+
// double acute
|
|
116
|
+
c: "\u0327",
|
|
117
|
+
// cedilla
|
|
118
|
+
k: "\u0328",
|
|
119
|
+
// ogonek
|
|
120
|
+
r: "\u030A",
|
|
121
|
+
// ring above
|
|
122
|
+
b: "\u0331",
|
|
123
|
+
// macron below
|
|
124
|
+
d: "\u0323"
|
|
125
|
+
// dot below
|
|
126
|
+
};
|
|
127
|
+
var LIGATURES = {
|
|
128
|
+
ss: "\xDF",
|
|
129
|
+
aa: "\xE5",
|
|
130
|
+
AA: "\xC5",
|
|
131
|
+
oe: "\u0153",
|
|
132
|
+
OE: "\u0152",
|
|
133
|
+
ae: "\xE6",
|
|
134
|
+
AE: "\xC6",
|
|
135
|
+
o: "\xF8",
|
|
136
|
+
O: "\xD8",
|
|
137
|
+
l: "\u0142",
|
|
138
|
+
L: "\u0141"
|
|
139
|
+
};
|
|
140
|
+
var ESCAPED_SYMBOLS = {
|
|
141
|
+
"&": "&",
|
|
142
|
+
"%": "%",
|
|
143
|
+
_: "_",
|
|
144
|
+
"#": "#",
|
|
145
|
+
$: "$",
|
|
146
|
+
"{": "{",
|
|
147
|
+
"}": "}"
|
|
148
|
+
};
|
|
149
|
+
var SYMBOL_ACCENT_RE = /\\([`'^"~=.])\{?([a-zA-Z])\}?/g;
|
|
150
|
+
var LETTER_ACCENT_RE = /\\(u|v|H|c|k|r|b|d)(?:\{([a-zA-Z])\}|\s+([a-zA-Z])\b)/g;
|
|
151
|
+
var LIGATURE_RE = /\\(ss|aa|AA|oe|OE|ae|AE|o|O|l|L)(?![a-zA-Z]) ?/g;
|
|
152
|
+
var ESCAPED_SYMBOL_RE = /\\([&%_#$}{])/g;
|
|
153
|
+
function cleanLatexText(value) {
|
|
154
|
+
if (!value) return value;
|
|
155
|
+
let text = value;
|
|
156
|
+
text = text.replace(SYMBOL_ACCENT_RE, (match, cmd, letter) => {
|
|
157
|
+
const mark = SYMBOL_ACCENTS[cmd];
|
|
158
|
+
if (!mark || !letter) return match;
|
|
159
|
+
return (letter + mark).normalize("NFC");
|
|
160
|
+
});
|
|
161
|
+
text = text.replace(LETTER_ACCENT_RE, (match, cmd, braced, spaced) => {
|
|
162
|
+
const mark = LETTER_ACCENTS[cmd];
|
|
163
|
+
const letter = braced ?? spaced;
|
|
164
|
+
if (!mark || !letter) return match;
|
|
165
|
+
return (letter + mark).normalize("NFC");
|
|
166
|
+
});
|
|
167
|
+
text = text.replace(LIGATURE_RE, (match, cmd) => LIGATURES[cmd] ?? match);
|
|
168
|
+
text = text.replace(ESCAPED_SYMBOL_RE, (match, sym) => ESCAPED_SYMBOLS[sym] ?? match);
|
|
169
|
+
return text.replace(/[{}]/g, "");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// src/citations.ts
|
|
173
|
+
var CITE_COMMAND_RE = /\\cite\{([^}]*)\}/g;
|
|
174
|
+
function escapeHtml(value) {
|
|
175
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
176
|
+
}
|
|
177
|
+
function extractCiteKeys(text) {
|
|
178
|
+
const keys = [];
|
|
179
|
+
const seen = /* @__PURE__ */ new Set();
|
|
180
|
+
const re = new RegExp(CITE_COMMAND_RE);
|
|
181
|
+
let match;
|
|
182
|
+
while (match = re.exec(text)) {
|
|
183
|
+
for (const key of match[1].split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
184
|
+
if (!seen.has(key)) {
|
|
185
|
+
seen.add(key);
|
|
186
|
+
keys.push(key);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return keys;
|
|
191
|
+
}
|
|
192
|
+
function formatEntry(entry) {
|
|
193
|
+
const f = entry.fields;
|
|
194
|
+
const author = f.author ? cleanLatexText(f.author.replace(/\s+and\s+/g, ", ")) : "Autor desconhecido";
|
|
195
|
+
const year = f.year ? ` (${f.year})` : "";
|
|
196
|
+
const title = f.title ? ` ${cleanLatexText(f.title)}.` : "";
|
|
197
|
+
const venue = f.journal || f.booktitle || f.publisher || "";
|
|
198
|
+
let text = `${author}${year}.${title}`;
|
|
199
|
+
if (venue) text += ` ${cleanLatexText(venue)}.`;
|
|
200
|
+
return text.trim();
|
|
201
|
+
}
|
|
202
|
+
function resolveEntryUrl(entry) {
|
|
203
|
+
const f = entry.fields;
|
|
204
|
+
return f.url || f.link || (f.doi ? `https://doi.org/${f.doi}` : void 0);
|
|
205
|
+
}
|
|
206
|
+
function resolveCitations(text, entries) {
|
|
207
|
+
const byKey = new Map(entries.map((e) => [e.key, e]));
|
|
208
|
+
const order = extractCiteKeys(text);
|
|
209
|
+
const indexOf = new Map(order.map((key, idx) => [key, idx + 1]));
|
|
210
|
+
const occurrenceCount = /* @__PURE__ */ new Map();
|
|
211
|
+
const resolvedText = text.replace(CITE_COMMAND_RE, (_match, rawKeys) => {
|
|
212
|
+
const keys = rawKeys.split(",").map((s) => s.trim()).filter(Boolean);
|
|
213
|
+
return keys.map((key) => {
|
|
214
|
+
const idx = indexOf.get(key);
|
|
215
|
+
if (!idx) return `<sup class="citation-ref citation-missing">[?]</sup>`;
|
|
216
|
+
const occurrence = (occurrenceCount.get(idx) ?? 0) + 1;
|
|
217
|
+
occurrenceCount.set(idx, occurrence);
|
|
218
|
+
return `<sup class="citation-ref"><a href="#cite-${idx}" id="cite-ref-${idx}-${occurrence}">[${idx}]</a></sup>`;
|
|
219
|
+
}).join("");
|
|
220
|
+
});
|
|
221
|
+
const bibliography = order.map((key) => {
|
|
222
|
+
const entry = byKey.get(key);
|
|
223
|
+
const idx = indexOf.get(key);
|
|
224
|
+
return {
|
|
225
|
+
key,
|
|
226
|
+
index: idx,
|
|
227
|
+
entry,
|
|
228
|
+
text: entry ? formatEntry(entry) : `Refer\xEAncia n\xE3o encontrada no .bib: ${key}`,
|
|
229
|
+
url: entry ? resolveEntryUrl(entry) : void 0
|
|
230
|
+
};
|
|
231
|
+
});
|
|
232
|
+
const bibliographyHtml = bibliography.length ? `<div class="bibliography"><h2>Refer\xEAncias</h2><ol>${bibliography.map((item) => {
|
|
233
|
+
const escapedText = escapeHtml(item.text);
|
|
234
|
+
const body = item.url ? `<a href="${escapeHtml(item.url)}" target="_blank" rel="noopener noreferrer" class="citation-link">${escapedText}</a>` : escapedText;
|
|
235
|
+
return `<li id="cite-${item.index}">${body} <a href="#cite-ref-${item.index}-1" class="citation-backref">\u21A9</a></li>`;
|
|
236
|
+
}).join("")}</ol></div>` : "";
|
|
237
|
+
return { text: resolvedText, bibliography, bibliographyHtml };
|
|
238
|
+
}
|
|
239
|
+
export {
|
|
240
|
+
CITE_COMMAND_RE,
|
|
241
|
+
citationCompletionSource,
|
|
242
|
+
cleanLatexText,
|
|
243
|
+
extractCiteKeys,
|
|
244
|
+
formatEntry,
|
|
245
|
+
latexCommandCompletionSource,
|
|
246
|
+
latexHighlightPlugin,
|
|
247
|
+
latexHighlightTheme,
|
|
248
|
+
parseBibtex,
|
|
249
|
+
resolveCitations,
|
|
250
|
+
resolveEntryUrl
|
|
251
|
+
};
|
|
252
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/bibtex.ts","../src/latexText.ts","../src/citations.ts"],"sourcesContent":["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, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\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"],"mappings":";;;;;;;;AAWO,SAAS,YAAY,QAA4B;AACtD,QAAM,UAAsB,CAAC;AAC7B,QAAM,IAAI,OAAO;AACjB,MAAI,IAAI;AAER,QAAM,UAAU,CAAC,OAAe,OAAO,OAAO,OAAO,OAAQ,OAAO,QAAQ,OAAO;AACnF,QAAM,iBAAiB,MAAM;AAC3B,WAAO,IAAI,KAAK,QAAQ,OAAO,CAAC,CAAC,EAAG;AAAA,EACtC;AAEA,SAAO,IAAI,GAAG;AACZ,QAAI,OAAO,CAAC,MAAM,KAAK;AACrB;AACA;AAAA,IACF;AACA;AACA,UAAM,YAAY;AAClB,WAAO,IAAI,KAAK,WAAW,KAAK,OAAO,CAAC,CAAC,EAAG;AAC5C,UAAM,OAAO,OAAO,MAAM,WAAW,CAAC,EAAE,YAAY;AACpD,mBAAe;AAEf,QAAI,OAAO,CAAC,MAAM,OAAO,OAAO,CAAC,MAAM,IAAK;AAC5C,UAAM,SAAS,OAAO,CAAC,MAAM,MAAM,MAAM;AACzC;AACA,mBAAe;AAEf,UAAM,WAAW;AACjB,WAAO,IAAI,KAAK,OAAO,CAAC,MAAM,OAAO,OAAO,CAAC,MAAM,OAAQ;AAC3D,UAAM,MAAM,OAAO,MAAM,UAAU,CAAC,EAAE,KAAK;AAE3C,QAAI,OAAO,CAAC,MAAM,QAAQ;AACxB,cAAQ,KAAK,EAAE,KAAK,MAAM,QAAQ,CAAC,EAAE,CAAC;AACtC;AACA;AAAA,IACF;AACA;AAEA,UAAM,SAAiC,CAAC;AACxC,WAAO,IAAI,GAAG;AACZ,qBAAe;AACf,UAAI,OAAO,CAAC,MAAM,QAAQ;AACxB;AACA;AAAA,MACF;AAEA,YAAM,YAAY;AAClB,aAAO,IAAI,KAAK,gBAAgB,KAAK,OAAO,CAAC,CAAC,EAAG;AACjD,YAAM,OAAO,OAAO,MAAM,WAAW,CAAC,EAAE,YAAY;AACpD,UAAI,CAAC,MAAM;AAET;AACA;AAAA,MACF;AACA,qBAAe;AACf,UAAI,OAAO,CAAC,MAAM,IAAK;AACvB,qBAAe;AAEf,UAAI,QAAQ;AACZ,UAAI,OAAO,CAAC,MAAM,KAAK;AACrB,YAAI,QAAQ;AACZ,cAAM,WAAW;AACjB,WAAG;AACD,cAAI,OAAO,CAAC,MAAM,IAAK;AAAA,mBACd,OAAO,CAAC,MAAM,IAAK;AAC5B;AAAA,QACF,SAAS,IAAI,KAAK,QAAQ;AAC1B,gBAAQ,OAAO,MAAM,WAAW,GAAG,IAAI,CAAC;AAAA,MAC1C,WAAW,OAAO,CAAC,MAAM,KAAK;AAC5B;AACA,cAAM,WAAW;AACjB,eAAO,IAAI,KAAK,OAAO,CAAC,MAAM,IAAK;AACnC,gBAAQ,OAAO,MAAM,UAAU,CAAC;AAChC;AAAA,MACF,OAAO;AACL,cAAM,WAAW;AACjB,eAAO,IAAI,KAAK,OAAO,CAAC,MAAM,OAAO,OAAO,CAAC,MAAM,OAAQ;AAC3D,gBAAQ,OAAO,MAAM,UAAU,CAAC,EAAE,KAAK;AAAA,MACzC;AAEA,aAAO,IAAI,IAAI,MAAM,QAAQ,QAAQ,GAAG,EAAE,KAAK;AAC/C,qBAAe;AACf,UAAI,OAAO,CAAC,MAAM,KAAK;AACrB;AAAA,MACF,WAAW,OAAO,CAAC,MAAM,QAAQ;AAC/B;AACA;AAAA,MACF;AAAA,IACF;AAEA,YAAQ,KAAK,EAAE,KAAK,MAAM,OAAO,CAAC;AAAA,EACpC;AAEA,SAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,SAAS,YAAY,EAAE,SAAS,cAAc,EAAE,GAAG;AAC5G;;;AC9FA,IAAM,iBAAyC;AAAA,EAC7C,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AAAA,EACL,KAAK;AAAA;AACP;AAIA,IAAM,iBAAyC;AAAA,EAC7C,GAAG;AAAA;AAAA,EACH,GAAG;AAAA;AAAA,EACH,GAAG;AAAA;AAAA,EACH,GAAG;AAAA;AAAA,EACH,GAAG;AAAA;AAAA,EACH,GAAG;AAAA;AAAA,EACH,GAAG;AAAA;AAAA,EACH,GAAG;AAAA;AACL;AAGA,IAAM,YAAoC;AAAA,EACxC,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AACL;AAGA,IAAM,kBAA0C;AAAA,EAC9C,KAAK;AAAA,EACL,KAAK;AAAA,EACL,GAAG;AAAA,EACH,KAAK;AAAA,EACL,GAAG;AAAA,EACH,KAAK;AAAA,EACL,KAAK;AACP;AAEA,IAAM,mBAAmB;AACzB,IAAM,mBAAmB;AAGzB,IAAM,cAAc;AACpB,IAAM,oBAAoB;AAOnB,SAAS,eAAe,OAAuB;AACpD,MAAI,CAAC,MAAO,QAAO;AAEnB,MAAI,OAAO;AAEX,SAAO,KAAK,QAAQ,kBAAkB,CAAC,OAAO,KAAa,WAAmB;AAC5E,UAAM,OAAO,eAAe,GAAG;AAC/B,QAAI,CAAC,QAAQ,CAAC,OAAQ,QAAO;AAC7B,YAAQ,SAAS,MAAM,UAAU,KAAK;AAAA,EACxC,CAAC;AAED,SAAO,KAAK,QAAQ,kBAAkB,CAAC,OAAO,KAAa,QAAiB,WAAoB;AAC9F,UAAM,OAAO,eAAe,GAAG;AAC/B,UAAM,SAAS,UAAU;AACzB,QAAI,CAAC,QAAQ,CAAC,OAAQ,QAAO;AAC7B,YAAQ,SAAS,MAAM,UAAU,KAAK;AAAA,EACxC,CAAC;AAED,SAAO,KAAK,QAAQ,aAAa,CAAC,OAAO,QAAgB,UAAU,GAAG,KAAK,KAAK;AAChF,SAAO,KAAK,QAAQ,mBAAmB,CAAC,OAAO,QAAgB,gBAAgB,GAAG,KAAK,KAAK;AAE5F,SAAO,KAAK,QAAQ,SAAS,EAAE;AACjC;;;AC1FO,IAAM,kBAAkB;AAE/B,SAAS,WAAW,OAAuB;AACzC,SAAO,MACJ,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ;AAC3B;AAGO,SAAS,gBAAgB,MAAwB;AACtD,QAAM,OAAiB,CAAC;AACxB,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,KAAK,IAAI,OAAO,eAAe;AACrC,MAAI;AACJ,SAAQ,QAAQ,GAAG,KAAK,IAAI,GAAI;AAC9B,eAAW,OAAO,MAAM,CAAC,EAAE,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO,GAAG;AAC1E,UAAI,CAAC,KAAK,IAAI,GAAG,GAAG;AAClB,aAAK,IAAI,GAAG;AACZ,aAAK,KAAK,GAAG;AAAA,MACf;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,YAAY,OAAyB;AACnD,QAAM,IAAI,MAAM;AAChB,QAAM,SAAS,EAAE,SAAS,eAAe,EAAE,OAAO,QAAQ,cAAc,IAAI,CAAC,IAAI;AACjF,QAAM,OAAO,EAAE,OAAO,KAAK,EAAE,IAAI,MAAM;AACvC,QAAM,QAAQ,EAAE,QAAQ,IAAI,eAAe,EAAE,KAAK,CAAC,MAAM;AACzD,QAAM,QAAQ,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa;AACzD,MAAI,OAAO,GAAG,MAAM,GAAG,IAAI,IAAI,KAAK;AACpC,MAAI,MAAO,SAAQ,IAAI,eAAe,KAAK,CAAC;AAC5C,SAAO,KAAK,KAAK;AACnB;AAGO,SAAS,gBAAgB,OAAqC;AACnE,QAAM,IAAI,MAAM;AAChB,SAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,EAAE,GAAG,KAAK;AAClE;AAuBO,SAAS,iBAAiB,MAAc,SAAwC;AACrF,QAAM,QAAQ,IAAI,IAAI,QAAQ,IAAI,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;AACpD,QAAM,QAAQ,gBAAgB,IAAI;AAClC,QAAM,UAAU,IAAI,IAAI,MAAM,IAAI,CAAC,KAAK,QAAQ,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC;AAE/D,QAAM,kBAAkB,oBAAI,IAAoB;AAChD,QAAM,eAAe,KAAK,QAAQ,iBAAiB,CAAC,QAAQ,YAAoB;AAC9E,UAAM,OAAO,QAAQ,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO;AACnE,WAAO,KACJ,IAAI,CAAC,QAAQ;AACZ,YAAM,MAAM,QAAQ,IAAI,GAAG;AAC3B,UAAI,CAAC,IAAK,QAAO;AAGjB,YAAM,cAAc,gBAAgB,IAAI,GAAG,KAAK,KAAK;AACrD,sBAAgB,IAAI,KAAK,UAAU;AACnC,aAAO,4CAA4C,GAAG,kBAAkB,GAAG,IAAI,UAAU,MAAM,GAAG;AAAA,IACpG,CAAC,EACA,KAAK,EAAE;AAAA,EACZ,CAAC;AAED,QAAM,eAAmC,MAAM,IAAI,CAAC,QAAQ;AAC1D,UAAM,QAAQ,MAAM,IAAI,GAAG;AAC3B,UAAM,MAAM,QAAQ,IAAI,GAAG;AAC3B,WAAO;AAAA,MACL;AAAA,MACA,OAAO;AAAA,MACP;AAAA,MACA,MAAM,QAAQ,YAAY,KAAK,IAAI,4CAAsC,GAAG;AAAA,MAC5E,KAAK,QAAQ,gBAAgB,KAAK,IAAI;AAAA,IACxC;AAAA,EACF,CAAC;AAED,QAAM,mBAAmB,aAAa,SAClC,wDAAqD,aAClD,IAAI,CAAC,SAAS;AACb,UAAM,cAAc,WAAW,KAAK,IAAI;AACxC,UAAM,OAAO,KAAK,MACd,YAAY,WAAW,KAAK,GAAG,CAAC,qEAAqE,WAAW,SAChH;AACJ,WAAO,gBAAgB,KAAK,KAAK,KAAK,IAAI,uBAAuB,KAAK,KAAK;AAAA,EAC7E,CAAC,EACA,KAAK,EAAE,CAAC,gBACX;AAEJ,SAAO,EAAE,MAAM,cAAc,cAAc,iBAAiB;AAC9D;","names":[]}
|
package/dist/react.cjs
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/react.ts
|
|
22
|
+
var react_exports = {};
|
|
23
|
+
__export(react_exports, {
|
|
24
|
+
CiteEditor: () => CiteEditor_default
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(react_exports);
|
|
27
|
+
|
|
28
|
+
// src/CiteEditor.tsx
|
|
29
|
+
var import_react = require("react");
|
|
30
|
+
var import_state = require("@codemirror/state");
|
|
31
|
+
var import_view2 = require("@codemirror/view");
|
|
32
|
+
var import_commands = require("@codemirror/commands");
|
|
33
|
+
var import_lang_markdown = require("@codemirror/lang-markdown");
|
|
34
|
+
var import_language = require("@codemirror/language");
|
|
35
|
+
var import_autocomplete = require("@codemirror/autocomplete");
|
|
36
|
+
var import_search = require("@codemirror/search");
|
|
37
|
+
|
|
38
|
+
// src/latexDecorations.ts
|
|
39
|
+
var import_view = require("@codemirror/view");
|
|
40
|
+
var LATEX_TOKEN_RE = /\\cite\{[^}]*\}|\\[a-zA-Z]+(?:\{[^}]*\})?|\$\$[^$]*\$\$|\$[^$\n]+\$/g;
|
|
41
|
+
function classify(token) {
|
|
42
|
+
if (token.startsWith("\\cite{")) return "cm-latex-cite";
|
|
43
|
+
if (token.startsWith("$$")) return "cm-latex-math-block";
|
|
44
|
+
if (token.startsWith("$")) return "cm-latex-math-inline";
|
|
45
|
+
return "cm-latex-command";
|
|
46
|
+
}
|
|
47
|
+
var matcher = new import_view.MatchDecorator({
|
|
48
|
+
regexp: LATEX_TOKEN_RE,
|
|
49
|
+
decoration: (match) => import_view.Decoration.mark({ class: classify(match[0]) })
|
|
50
|
+
});
|
|
51
|
+
var latexHighlightPlugin = import_view.ViewPlugin.define(
|
|
52
|
+
(view) => ({
|
|
53
|
+
decorations: matcher.createDeco(view),
|
|
54
|
+
update(update) {
|
|
55
|
+
this.decorations = matcher.updateDeco(update, this.decorations);
|
|
56
|
+
}
|
|
57
|
+
}),
|
|
58
|
+
{
|
|
59
|
+
decorations: (instance) => instance.decorations
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
var latexHighlightTheme = import_view.EditorView.baseTheme({
|
|
63
|
+
".cm-latex-cite": {
|
|
64
|
+
color: "#0ea5e9",
|
|
65
|
+
fontWeight: "600"
|
|
66
|
+
},
|
|
67
|
+
".cm-latex-command": {
|
|
68
|
+
color: "#a855f7"
|
|
69
|
+
},
|
|
70
|
+
".cm-latex-math-inline, .cm-latex-math-block": {
|
|
71
|
+
color: "#f59e0b"
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// src/autocomplete.ts
|
|
76
|
+
var LATEX_COMMANDS = [
|
|
77
|
+
{ label: "\\cite{}", apply: "\\cite{}", type: "keyword", detail: "citar refer\xEAncia do .bib", boost: 2 },
|
|
78
|
+
{ label: "\\textbf{}", apply: "\\textbf{}", type: "keyword", detail: "negrito" },
|
|
79
|
+
{ label: "\\textit{}", apply: "\\textit{}", type: "keyword", detail: "it\xE1lico" },
|
|
80
|
+
{ label: "\\section{}", apply: "\\section{}", type: "keyword", detail: "se\xE7\xE3o" },
|
|
81
|
+
{ label: "\\subsection{}", apply: "\\subsection{}", type: "keyword", detail: "subse\xE7\xE3o" },
|
|
82
|
+
{ label: "\\label{}", apply: "\\label{}", type: "keyword", detail: "r\xF3tulo para refer\xEAncia cruzada" },
|
|
83
|
+
{ label: "\\ref{}", apply: "\\ref{}", type: "keyword", detail: "refer\xEAncia cruzada a um \\label" },
|
|
84
|
+
{ label: "\\footnote{}", apply: "\\footnote{}", type: "keyword", detail: "nota de rodap\xE9" }
|
|
85
|
+
];
|
|
86
|
+
var MATH_SYMBOLS = [
|
|
87
|
+
"\\alpha",
|
|
88
|
+
"\\beta",
|
|
89
|
+
"\\gamma",
|
|
90
|
+
"\\delta",
|
|
91
|
+
"\\theta",
|
|
92
|
+
"\\lambda",
|
|
93
|
+
"\\pi",
|
|
94
|
+
"\\sigma",
|
|
95
|
+
"\\omega",
|
|
96
|
+
"\\sum",
|
|
97
|
+
"\\int",
|
|
98
|
+
"\\prod",
|
|
99
|
+
"\\infty",
|
|
100
|
+
"\\partial",
|
|
101
|
+
"\\nabla",
|
|
102
|
+
"\\leq",
|
|
103
|
+
"\\geq",
|
|
104
|
+
"\\neq",
|
|
105
|
+
"\\approx",
|
|
106
|
+
"\\times",
|
|
107
|
+
"\\cdot"
|
|
108
|
+
].map((label) => ({ label, type: "constant" })).concat([
|
|
109
|
+
{ label: "\\sqrt{}", apply: "\\sqrt{}", type: "constant" },
|
|
110
|
+
{ label: "\\frac{}{}", apply: "\\frac{}{}", type: "constant" }
|
|
111
|
+
]);
|
|
112
|
+
function citationDetail(entry) {
|
|
113
|
+
const firstAuthor = entry.fields.author?.split(/\s+and\s+/)[0]?.split(",")[0]?.trim();
|
|
114
|
+
const year = entry.fields.year;
|
|
115
|
+
const who = firstAuthor ?? "autor desconhecido";
|
|
116
|
+
return year ? `${who} (${year})` : who;
|
|
117
|
+
}
|
|
118
|
+
function citationCompletionSource(getEntries) {
|
|
119
|
+
return (context) => {
|
|
120
|
+
const match = context.matchBefore(/\\cite\{[^}]*/);
|
|
121
|
+
if (!match) return null;
|
|
122
|
+
const braceIdx = match.text.indexOf("{");
|
|
123
|
+
const from = match.from + braceIdx + 1;
|
|
124
|
+
const entries = getEntries();
|
|
125
|
+
if (entries.length === 0) return null;
|
|
126
|
+
return {
|
|
127
|
+
from,
|
|
128
|
+
options: entries.map((entry) => ({
|
|
129
|
+
label: entry.key,
|
|
130
|
+
detail: citationDetail(entry),
|
|
131
|
+
info: entry.fields.title ? entry.fields.title.replace(/[{}]/g, "") : void 0,
|
|
132
|
+
type: "text"
|
|
133
|
+
})),
|
|
134
|
+
validFor: /^[^}]*$/
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function isInsideMath(textBeforeCursor) {
|
|
139
|
+
const lastParagraphBreak = textBeforeCursor.lastIndexOf("\n\n");
|
|
140
|
+
const scope = lastParagraphBreak === -1 ? textBeforeCursor : textBeforeCursor.slice(lastParagraphBreak);
|
|
141
|
+
const dollarCount = (scope.match(/\$/g) || []).length;
|
|
142
|
+
return dollarCount % 2 === 1;
|
|
143
|
+
}
|
|
144
|
+
function latexCommandCompletionSource(context) {
|
|
145
|
+
const word = context.matchBefore(/\\[a-zA-Z]*/);
|
|
146
|
+
if (!word || word.from === word.to && !context.explicit) return null;
|
|
147
|
+
const inMath = isInsideMath(context.state.sliceDoc(0, word.from));
|
|
148
|
+
const options = inMath ? [...MATH_SYMBOLS, ...LATEX_COMMANDS] : LATEX_COMMANDS;
|
|
149
|
+
return {
|
|
150
|
+
from: word.from,
|
|
151
|
+
options,
|
|
152
|
+
validFor: /^\\[a-zA-Z]*$/
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// src/CiteEditor.tsx
|
|
157
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
158
|
+
function fontSizeTheme(fontSize, minHeight) {
|
|
159
|
+
return import_view2.EditorView.theme({
|
|
160
|
+
"&": { fontSize: `${fontSize}px`, height: "100%" },
|
|
161
|
+
".cm-scroller": { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", minHeight },
|
|
162
|
+
".cm-content": { padding: "12px 0" }
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
var CiteEditor = (0, import_react.forwardRef)(function CiteEditor2({ value, onChange, bibEntries = [], fontSize = 14, className, placeholder, minHeight = "500px" }, ref) {
|
|
166
|
+
const containerRef = (0, import_react.useRef)(null);
|
|
167
|
+
const viewRef = (0, import_react.useRef)(null);
|
|
168
|
+
const bibEntriesRef = (0, import_react.useRef)(bibEntries);
|
|
169
|
+
bibEntriesRef.current = bibEntries;
|
|
170
|
+
const onChangeRef = (0, import_react.useRef)(onChange);
|
|
171
|
+
onChangeRef.current = onChange;
|
|
172
|
+
const [themeCompartment] = (0, import_react.useState)(() => new import_state.Compartment());
|
|
173
|
+
(0, import_react.useImperativeHandle)(
|
|
174
|
+
ref,
|
|
175
|
+
() => ({
|
|
176
|
+
get view() {
|
|
177
|
+
return viewRef.current;
|
|
178
|
+
},
|
|
179
|
+
focus() {
|
|
180
|
+
viewRef.current?.focus();
|
|
181
|
+
},
|
|
182
|
+
getSelection() {
|
|
183
|
+
const view = viewRef.current;
|
|
184
|
+
if (!view) return "";
|
|
185
|
+
return view.state.sliceDoc(view.state.selection.main.from, view.state.selection.main.to);
|
|
186
|
+
},
|
|
187
|
+
wrapSelection(before, after, placeholderText = "") {
|
|
188
|
+
const view = viewRef.current;
|
|
189
|
+
if (!view) return;
|
|
190
|
+
const { state } = view;
|
|
191
|
+
const changes = state.changeByRange((range) => {
|
|
192
|
+
const selected = state.sliceDoc(range.from, range.to) || placeholderText;
|
|
193
|
+
const insert = `${before}${selected}${after}`;
|
|
194
|
+
return {
|
|
195
|
+
changes: { from: range.from, to: range.to, insert },
|
|
196
|
+
range: import_state.EditorSelection.range(range.from + before.length, range.from + before.length + selected.length)
|
|
197
|
+
};
|
|
198
|
+
});
|
|
199
|
+
view.dispatch(state.update(changes, { scrollIntoView: true }));
|
|
200
|
+
view.focus();
|
|
201
|
+
},
|
|
202
|
+
insertAtLineStart(prefix) {
|
|
203
|
+
const view = viewRef.current;
|
|
204
|
+
if (!view) return;
|
|
205
|
+
const { state } = view;
|
|
206
|
+
const changes = state.changeByRange((range) => {
|
|
207
|
+
const line = state.doc.lineAt(range.from);
|
|
208
|
+
return {
|
|
209
|
+
changes: { from: line.from, to: line.from, insert: prefix },
|
|
210
|
+
range: import_state.EditorSelection.range(range.from + prefix.length, range.to + prefix.length)
|
|
211
|
+
};
|
|
212
|
+
});
|
|
213
|
+
view.dispatch(state.update(changes, { scrollIntoView: true }));
|
|
214
|
+
view.focus();
|
|
215
|
+
},
|
|
216
|
+
insertText(text) {
|
|
217
|
+
const view = viewRef.current;
|
|
218
|
+
if (!view) return;
|
|
219
|
+
const { state } = view;
|
|
220
|
+
const changes = state.changeByRange((range) => ({
|
|
221
|
+
changes: { from: range.from, to: range.to, insert: text },
|
|
222
|
+
range: import_state.EditorSelection.cursor(range.from + text.length)
|
|
223
|
+
}));
|
|
224
|
+
view.dispatch(state.update(changes, { scrollIntoView: true }));
|
|
225
|
+
view.focus();
|
|
226
|
+
},
|
|
227
|
+
duplicateCurrentLine() {
|
|
228
|
+
const view = viewRef.current;
|
|
229
|
+
if (!view) return;
|
|
230
|
+
const line = view.state.doc.lineAt(view.state.selection.main.head);
|
|
231
|
+
view.dispatch({
|
|
232
|
+
changes: { from: line.to, to: line.to, insert: "\n" + line.text },
|
|
233
|
+
scrollIntoView: true
|
|
234
|
+
});
|
|
235
|
+
view.focus();
|
|
236
|
+
},
|
|
237
|
+
openSearch() {
|
|
238
|
+
const view = viewRef.current;
|
|
239
|
+
if (!view) return;
|
|
240
|
+
(0, import_search.openSearchPanel)(view);
|
|
241
|
+
}
|
|
242
|
+
}),
|
|
243
|
+
[]
|
|
244
|
+
);
|
|
245
|
+
(0, import_react.useEffect)(() => {
|
|
246
|
+
if (!containerRef.current) return;
|
|
247
|
+
const extensions = [
|
|
248
|
+
(0, import_view2.lineNumbers)(),
|
|
249
|
+
(0, import_commands.history)(),
|
|
250
|
+
(0, import_view2.highlightActiveLine)(),
|
|
251
|
+
(0, import_lang_markdown.markdown)(),
|
|
252
|
+
(0, import_language.syntaxHighlighting)(import_language.defaultHighlightStyle),
|
|
253
|
+
latexHighlightPlugin,
|
|
254
|
+
latexHighlightTheme,
|
|
255
|
+
(0, import_autocomplete.closeBrackets)(),
|
|
256
|
+
(0, import_search.search)({ top: true }),
|
|
257
|
+
(0, import_autocomplete.autocompletion)({
|
|
258
|
+
override: [citationCompletionSource(() => bibEntriesRef.current), latexCommandCompletionSource]
|
|
259
|
+
}),
|
|
260
|
+
import_view2.keymap.of([...import_autocomplete.closeBracketsKeymap, ...import_commands.defaultKeymap, ...import_commands.historyKeymap, ...import_autocomplete.completionKeymap, ...import_search.searchKeymap]),
|
|
261
|
+
import_view2.EditorView.lineWrapping,
|
|
262
|
+
import_view2.EditorView.updateListener.of((update) => {
|
|
263
|
+
if (update.docChanged) {
|
|
264
|
+
onChangeRef.current(update.state.doc.toString());
|
|
265
|
+
}
|
|
266
|
+
}),
|
|
267
|
+
themeCompartment.of(fontSizeTheme(fontSize, minHeight)),
|
|
268
|
+
...placeholder ? [(0, import_view2.placeholder)(placeholder)] : []
|
|
269
|
+
];
|
|
270
|
+
const state = import_state.EditorState.create({ doc: value, extensions });
|
|
271
|
+
const view = new import_view2.EditorView({ state, parent: containerRef.current });
|
|
272
|
+
viewRef.current = view;
|
|
273
|
+
return () => {
|
|
274
|
+
view.destroy();
|
|
275
|
+
viewRef.current = null;
|
|
276
|
+
};
|
|
277
|
+
}, []);
|
|
278
|
+
(0, import_react.useEffect)(() => {
|
|
279
|
+
const view = viewRef.current;
|
|
280
|
+
if (!view) return;
|
|
281
|
+
const current = view.state.doc.toString();
|
|
282
|
+
if (current !== value) {
|
|
283
|
+
view.dispatch({ changes: { from: 0, to: current.length, insert: value } });
|
|
284
|
+
}
|
|
285
|
+
}, [value]);
|
|
286
|
+
(0, import_react.useEffect)(() => {
|
|
287
|
+
const view = viewRef.current;
|
|
288
|
+
if (!view) return;
|
|
289
|
+
view.dispatch({ effects: themeCompartment.reconfigure(fontSizeTheme(fontSize, minHeight)) });
|
|
290
|
+
}, [fontSize, minHeight, themeCompartment]);
|
|
291
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: containerRef, className });
|
|
292
|
+
});
|
|
293
|
+
var CiteEditor_default = CiteEditor;
|
|
294
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
295
|
+
0 && (module.exports = {
|
|
296
|
+
CiteEditor
|
|
297
|
+
});
|
|
298
|
+
//# sourceMappingURL=react.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react.ts","../src/CiteEditor.tsx","../src/latexDecorations.ts","../src/autocomplete.ts"],"sourcesContent":["'use client';\n\nexport { default as CiteEditor } from './CiteEditor';\nexport type { CiteEditorProps, CiteEditorHandle } from './CiteEditor';\n","'use client';\n\nimport { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';\nimport { Compartment, EditorSelection, EditorState, type Extension } from '@codemirror/state';\nimport { EditorView, keymap, highlightActiveLine, lineNumbers, placeholder as placeholderExt } from '@codemirror/view';\nimport { defaultKeymap, history, historyKeymap } from '@codemirror/commands';\nimport { markdown } from '@codemirror/lang-markdown';\nimport { defaultHighlightStyle, syntaxHighlighting } from '@codemirror/language';\nimport { autocompletion, closeBrackets, closeBracketsKeymap, completionKeymap } from '@codemirror/autocomplete';\nimport { openSearchPanel, search, searchKeymap } from '@codemirror/search';\nimport type { BibEntry } from './bibtex';\nimport { latexHighlightPlugin, latexHighlightTheme } from './latexDecorations';\nimport { citationCompletionSource, latexCommandCompletionSource } from './autocomplete';\n\nexport interface CiteEditorProps {\n value: string;\n onChange: (value: string) => void;\n /** Parsed .bib entries used to power \\cite{} autocomplete; pass the output of parseBibtex(). */\n bibEntries?: BibEntry[];\n fontSize?: number;\n className?: string;\n placeholder?: string;\n minHeight?: string;\n}\n\n/**\n * Imperative API for host toolbars that need to manipulate the document the\n * way a `<textarea>` toolbar would (wrap selection, insert at line start, ...)\n * without reaching into CodeMirror internals themselves.\n */\nexport interface CiteEditorHandle {\n view: EditorView | null;\n focus(): void;\n getSelection(): string;\n /** Wraps every selection range with `before`/`after`; empty ranges use `placeholder` as the wrapped text. */\n wrapSelection(before: string, after: string, placeholder?: string): void;\n /** Inserts `prefix` at the start of the line containing the cursor. */\n insertAtLineStart(prefix: string): void;\n /** Inserts `text` at the cursor, replacing any selection. */\n insertText(text: string): void;\n /** Duplicates the line containing the cursor, matching the common editor Ctrl+D shortcut. */\n duplicateCurrentLine(): void;\n /** Opens CodeMirror's built-in search & replace panel. */\n openSearch(): void;\n}\n\nfunction fontSizeTheme(fontSize: number, minHeight: string): Extension {\n return EditorView.theme({\n '&': { fontSize: `${fontSize}px`, height: '100%' },\n '.cm-scroller': { fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace', minHeight },\n '.cm-content': { padding: '12px 0' },\n });\n}\n\nconst CiteEditor = forwardRef<CiteEditorHandle, CiteEditorProps>(function CiteEditor(\n { value, onChange, bibEntries = [], fontSize = 14, className, placeholder, minHeight = '500px' },\n ref\n) {\n const containerRef = useRef<HTMLDivElement | null>(null);\n const viewRef = useRef<EditorView | null>(null);\n const bibEntriesRef = useRef(bibEntries);\n bibEntriesRef.current = bibEntries;\n const onChangeRef = useRef(onChange);\n onChangeRef.current = onChange;\n const [themeCompartment] = useState(() => new Compartment());\n\n useImperativeHandle(\n ref,\n () => ({\n get view() {\n return viewRef.current;\n },\n focus() {\n viewRef.current?.focus();\n },\n getSelection() {\n const view = viewRef.current;\n if (!view) return '';\n return view.state.sliceDoc(view.state.selection.main.from, view.state.selection.main.to);\n },\n wrapSelection(before, after, placeholderText = '') {\n const view = viewRef.current;\n if (!view) return;\n const { state } = view;\n const changes = state.changeByRange((range) => {\n const selected = state.sliceDoc(range.from, range.to) || placeholderText;\n const insert = `${before}${selected}${after}`;\n return {\n changes: { from: range.from, to: range.to, insert },\n range: EditorSelection.range(range.from + before.length, range.from + before.length + selected.length),\n };\n });\n view.dispatch(state.update(changes, { scrollIntoView: true }));\n view.focus();\n },\n insertAtLineStart(prefix) {\n const view = viewRef.current;\n if (!view) return;\n const { state } = view;\n const changes = state.changeByRange((range) => {\n const line = state.doc.lineAt(range.from);\n return {\n changes: { from: line.from, to: line.from, insert: prefix },\n range: EditorSelection.range(range.from + prefix.length, range.to + prefix.length),\n };\n });\n view.dispatch(state.update(changes, { scrollIntoView: true }));\n view.focus();\n },\n insertText(text) {\n const view = viewRef.current;\n if (!view) return;\n const { state } = view;\n const changes = state.changeByRange((range) => ({\n changes: { from: range.from, to: range.to, insert: text },\n range: EditorSelection.cursor(range.from + text.length),\n }));\n view.dispatch(state.update(changes, { scrollIntoView: true }));\n view.focus();\n },\n duplicateCurrentLine() {\n const view = viewRef.current;\n if (!view) return;\n const line = view.state.doc.lineAt(view.state.selection.main.head);\n view.dispatch({\n changes: { from: line.to, to: line.to, insert: '\\n' + line.text },\n scrollIntoView: true,\n });\n view.focus();\n },\n openSearch() {\n const view = viewRef.current;\n if (!view) return;\n openSearchPanel(view);\n },\n }),\n []\n );\n\n useEffect(() => {\n if (!containerRef.current) return;\n\n const extensions: Extension[] = [\n lineNumbers(),\n history(),\n highlightActiveLine(),\n markdown(),\n syntaxHighlighting(defaultHighlightStyle),\n latexHighlightPlugin,\n latexHighlightTheme,\n closeBrackets(),\n search({ top: true }),\n autocompletion({\n override: [citationCompletionSource(() => bibEntriesRef.current), latexCommandCompletionSource],\n }),\n keymap.of([...closeBracketsKeymap, ...defaultKeymap, ...historyKeymap, ...completionKeymap, ...searchKeymap]),\n EditorView.lineWrapping,\n EditorView.updateListener.of((update) => {\n if (update.docChanged) {\n onChangeRef.current(update.state.doc.toString());\n }\n }),\n themeCompartment.of(fontSizeTheme(fontSize, minHeight)),\n ...(placeholder ? [placeholderExt(placeholder)] : []),\n ];\n\n const state = EditorState.create({ doc: value, extensions });\n const view = new EditorView({ state, parent: containerRef.current });\n viewRef.current = view;\n\n return () => {\n view.destroy();\n viewRef.current = null;\n };\n // Mount once; external `value` changes are synced via the effect below instead of\n // recreating the view (which would drop cursor position/undo history on every keystroke).\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n const view = viewRef.current;\n if (!view) return;\n const current = view.state.doc.toString();\n if (current !== value) {\n view.dispatch({ changes: { from: 0, to: current.length, insert: value } });\n }\n }, [value]);\n\n useEffect(() => {\n const view = viewRef.current;\n if (!view) return;\n view.dispatch({ effects: themeCompartment.reconfigure(fontSizeTheme(fontSize, minHeight)) });\n }, [fontSize, minHeight, themeCompartment]);\n\n return <div ref={containerRef} className={className} />;\n});\n\nexport default CiteEditor;\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","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"],"mappings":"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","names":["import_view","CiteEditor","placeholderExt"]}
|
package/dist/react.d.cts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { EditorView } from '@codemirror/view';
|
|
3
|
+
import { B as BibEntry } from './bibtex-BV8HliUE.cjs';
|
|
4
|
+
|
|
5
|
+
interface CiteEditorProps {
|
|
6
|
+
value: string;
|
|
7
|
+
onChange: (value: string) => void;
|
|
8
|
+
/** Parsed .bib entries used to power \cite{} autocomplete; pass the output of parseBibtex(). */
|
|
9
|
+
bibEntries?: BibEntry[];
|
|
10
|
+
fontSize?: number;
|
|
11
|
+
className?: string;
|
|
12
|
+
placeholder?: string;
|
|
13
|
+
minHeight?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Imperative API for host toolbars that need to manipulate the document the
|
|
17
|
+
* way a `<textarea>` toolbar would (wrap selection, insert at line start, ...)
|
|
18
|
+
* without reaching into CodeMirror internals themselves.
|
|
19
|
+
*/
|
|
20
|
+
interface CiteEditorHandle {
|
|
21
|
+
view: EditorView | null;
|
|
22
|
+
focus(): void;
|
|
23
|
+
getSelection(): string;
|
|
24
|
+
/** Wraps every selection range with `before`/`after`; empty ranges use `placeholder` as the wrapped text. */
|
|
25
|
+
wrapSelection(before: string, after: string, placeholder?: string): void;
|
|
26
|
+
/** Inserts `prefix` at the start of the line containing the cursor. */
|
|
27
|
+
insertAtLineStart(prefix: string): void;
|
|
28
|
+
/** Inserts `text` at the cursor, replacing any selection. */
|
|
29
|
+
insertText(text: string): void;
|
|
30
|
+
/** Duplicates the line containing the cursor, matching the common editor Ctrl+D shortcut. */
|
|
31
|
+
duplicateCurrentLine(): void;
|
|
32
|
+
/** Opens CodeMirror's built-in search & replace panel. */
|
|
33
|
+
openSearch(): void;
|
|
34
|
+
}
|
|
35
|
+
declare const CiteEditor: react.ForwardRefExoticComponent<CiteEditorProps & react.RefAttributes<CiteEditorHandle>>;
|
|
36
|
+
|
|
37
|
+
export { CiteEditor, type CiteEditorHandle, type CiteEditorProps };
|