@pretextbook/import 0.5.0 → 0.7.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/index.js CHANGED
@@ -1,6 +1,7 @@
1
- import { d as j, r as v, a as R, f as g, b as h, w as P, s as b, e as A, c as M, p as $ } from "./import-mode-Dc8qmZP6.js";
2
- import { L as B, M as W, P as X, g as G, h as U, i as H, j as q, k as J, l as Q, m as V, n as Y, o as Z, q as tt, t as et, u as nt, v as at, x as st, y as ot, z as it, A as rt, B as ct, C as lt, D as pt, E as ut, F as mt, G as ft, H as dt, I as Pt, J as gt, K as ht, N as bt, O as xt, Q as Tt, R as Ft, S as St, T as Et } from "./import-mode-Dc8qmZP6.js";
3
- const k = {
1
+ import { d as w, r as I, a as _, f as E, b as T, w as x, s as v, e as C, c as k, p as y, g as b, h as A } from "./upload-BBHV4rXJ.js";
2
+ import { C as q, L as V, i as Y, M as J, j as Q, P as Z, k as ee, l as te, m as se, n as ne, o as ae, q as oe, t as re, u as ie, v as ce, x as le, y as ue, z as pe, A as fe, B as me, D as de, E as ge, F as Pe, G as he, H as xe, I as be, J as Ee, K as Te, N as ve, O as Se, Q as Le, R as Fe, S as we, T as Ie, U as _e, V as Ce, W as ke, X as ye, Y as Ae, Z as Me, _ as je, $ as Re, a0 as $e, a1 as De, a2 as Ne, a3 as Oe, a4 as Be, a5 as We } from "./upload-BBHV4rXJ.js";
3
+ import { a as Ke, d as ze, b as Ue, c as Ge, f as He, e as qe, g as Ve, p as Ye } from "./import-mode-GqdypBfV.js";
4
+ const M = {
4
5
  png: "image/png",
5
6
  jpg: "image/jpeg",
6
7
  jpeg: "image/jpeg",
@@ -14,103 +15,103 @@ const k = {
14
15
  webp: "image/webp",
15
16
  ico: "image/vnd.microsoft.icon"
16
17
  };
17
- function I(t) {
18
- const e = t.split(".").pop()?.toLowerCase() ?? "";
19
- return k[e] ?? "application/octet-stream";
18
+ function j(e) {
19
+ const s = e.split(".").pop()?.toLowerCase() ?? "";
20
+ return M[s] ?? "application/octet-stream";
20
21
  }
21
- const f = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
22
- function w(t) {
23
- const e = t.length, s = [];
24
- for (let n = 0; n < e; n += 3) {
25
- const a = t[n], i = n + 1 < e, c = n + 2 < e, r = i ? t[n + 1] : 0, l = c ? t[n + 2] : 0;
26
- s.push(f[a >> 2]), s.push(f[(a & 3) << 4 | r >> 4]), s.push(i ? f[(r & 15) << 2 | l >> 6] : "="), s.push(c ? f[l & 63] : "=");
22
+ const P = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
23
+ function R(e) {
24
+ const s = e.length, o = [];
25
+ for (let t = 0; t < s; t += 3) {
26
+ const n = e[t], c = t + 1 < s, r = t + 2 < s, l = c ? e[t + 1] : 0, i = r ? e[t + 2] : 0;
27
+ o.push(P[n >> 2]), o.push(P[(n & 3) << 4 | l >> 4]), o.push(c ? P[(l & 15) << 2 | i >> 6] : "="), o.push(r ? P[i & 63] : "=");
27
28
  }
28
- return s.join("");
29
+ return o.join("");
29
30
  }
30
- function C(t) {
31
+ function $(e) {
31
32
  return {
32
- ref: t.ref,
33
+ ref: e.ref,
33
34
  kind: "file",
34
- title: t.fileName,
35
- short_description: t.fileName,
35
+ title: e.fileName,
36
+ short_description: e.fileName,
36
37
  file: {
37
- filename: t.fileName,
38
- content_type: I(t.fileName),
39
- data: w(t.data)
38
+ filename: e.fileName,
39
+ content_type: j(e.fileName),
40
+ data: R(e.data)
40
41
  }
41
42
  };
42
43
  }
43
- function N(t) {
44
+ function K(e) {
44
45
  return {
45
- title: t.title,
46
- docinfo: t.docinfo,
47
- document_type: t.documentKind,
48
- divisions_attributes: t.divisions.map((e) => ({
49
- ref: e.xmlId,
50
- source: e.content,
51
- source_format: e.sourceFormat,
52
- is_root: e.isRoot
46
+ title: e.title,
47
+ docinfo: e.docinfo,
48
+ document_type: e.documentKind,
49
+ divisions_attributes: e.divisions.map((s) => ({
50
+ ref: s.xmlId,
51
+ source: s.content,
52
+ source_format: s.sourceFormat,
53
+ is_root: s.isRoot
53
54
  })),
54
- assets_attributes: t.assets.map(C)
55
+ assets_attributes: e.assets.map($)
55
56
  };
56
57
  }
57
- const d = {
58
+ const h = {
58
59
  mainSourcePath: "source/main.ptx",
59
60
  publicationPath: "publication/publication.ptx",
60
61
  projectFilePath: "project.ptx",
61
62
  docinfoPath: "source/docinfo.ptx"
62
63
  };
63
- function x(t) {
64
- return g(t, "pretext") ? t : `<pretext>
65
- ${t}
64
+ function S(e) {
65
+ return E(e, "pretext") ? e : `<pretext>
66
+ ${e}
66
67
  </pretext>`;
67
68
  }
68
- function T(t, e, s) {
69
- const n = /* @__PURE__ */ new Map(), a = [], i = `${e}-`;
70
- return t.forEach((c, r) => {
71
- const l = c.attributes["xml:id"];
72
- let p;
73
- if (l) {
74
- const u = M(l);
75
- p = u.startsWith(i) ? u : `${i}${u}`;
69
+ function L(e, s, o) {
70
+ const t = /* @__PURE__ */ new Map(), n = [], c = `${s}-`;
71
+ return e.forEach((r, l) => {
72
+ const i = r.attributes["xml:id"];
73
+ let u;
74
+ if (i) {
75
+ const m = k(i);
76
+ u = m.startsWith(c) ? m : `${c}${m}`;
76
77
  } else
77
- p = `${i}${$(r + 1, t.length)}`, s.push({
78
+ u = `${c}${y(l + 1, e.length)}`, o.push({
78
79
  action: "anomaly",
79
80
  severity: "info",
80
81
  kind: "structure",
81
82
  category: "missing_xml_id",
82
- macro: c.name,
83
+ macro: r.name,
83
84
  occurrences: 1,
84
- message: `<${c.name}> at position ${r + 1} has no xml:id; using \`${p}\` as filename.`
85
+ message: `<${r.name}> at position ${l + 1} has no xml:id; using \`${u}\` as filename.`
85
86
  });
86
- const m = n.get(p) ?? 0;
87
- n.set(p, m + 1);
88
- const o = m === 0 ? p : `${p}-${m + 1}`;
89
- a.push({
90
- span: c,
91
- slug: o,
92
- filename: `${o}.ptx`
87
+ const p = t.get(u) ?? 0;
88
+ t.set(u, p + 1);
89
+ const a = p === 0 ? u : `${u}-${p + 1}`;
90
+ n.push({
91
+ span: r,
92
+ slug: a,
93
+ filename: `${a}.ptx`
93
94
  });
94
- }), a;
95
+ }), n;
95
96
  }
96
- function y(t, e, s, n) {
97
- const a = h(t, "section");
98
- if (a.length === 0)
99
- return t;
100
- const c = T(a, "sec", n).map((r) => {
101
- const l = `source/${e}/${r.filename}`;
102
- return s[l] = P(r.span.outer), {
103
- start: r.span.start,
104
- end: r.span.end,
105
- replacement: `<xi:include href="${e}/${r.filename}"/>`
97
+ function D(e, s, o, t) {
98
+ const n = T(e, "section");
99
+ if (n.length === 0)
100
+ return e;
101
+ const r = L(n, "sec", t).map((l) => {
102
+ const i = `source/${s}/${l.filename}`;
103
+ return o[i] = x(l.span.outer), {
104
+ start: l.span.start,
105
+ end: l.span.end,
106
+ replacement: `<xi:include href="${s}/${l.filename}"/>`
106
107
  };
107
108
  });
108
- return b(t, c);
109
+ return v(e, r);
109
110
  }
110
- function D(t, e, s) {
111
- const n = g(t, "book");
112
- if (!n)
113
- return s.push({
111
+ function N(e, s, o) {
112
+ const t = E(e, "book");
113
+ if (!t)
114
+ return o.push({
114
115
  action: "anomaly",
115
116
  severity: "warning",
116
117
  kind: "structure",
@@ -118,95 +119,200 @@ function D(t, e, s) {
118
119
  macro: "book",
119
120
  occurrences: 1,
120
121
  message: "Document was treated as a book but no <book> element found; emitting as a single file."
121
- }), F(t, e);
122
- const a = {}, i = h(n.inner, "chapter");
123
- let c = n.inner;
124
- if (e.splitChapters && i.length > 0) {
125
- const m = T(i, "ch", s).map((o) => {
126
- let u = o.span.outer;
127
- if (e.splitSections) {
128
- const E = o.span.inner, _ = y(
129
- E,
130
- o.slug,
131
- a,
132
- s
122
+ }), F(e, s);
123
+ const n = {}, c = T(t.inner, "chapter");
124
+ let r = t.inner;
125
+ if (s.splitChapters && c.length > 0) {
126
+ const p = L(c, "ch", o).map((a) => {
127
+ let m = a.span.outer;
128
+ if (s.splitSections) {
129
+ const f = a.span.inner, d = D(
130
+ f,
131
+ a.slug,
132
+ n,
133
+ o
133
134
  );
134
- u = o.span.outer.slice(0, o.span.startTagEnd - o.span.start) + _ + o.span.outer.slice(o.span.contentEnd - o.span.start);
135
+ m = a.span.outer.slice(0, a.span.startTagEnd - a.span.start) + d + a.span.outer.slice(a.span.contentEnd - a.span.start);
135
136
  }
136
- const S = `source/${o.filename}`;
137
- return a[S] = P(u), {
138
- start: o.span.start,
139
- end: o.span.end,
140
- replacement: `<xi:include href="${o.filename}"/>`
137
+ const g = `source/${a.filename}`;
138
+ return n[g] = x(m), {
139
+ start: a.span.start,
140
+ end: a.span.end,
141
+ replacement: `<xi:include href="${a.filename}"/>`
141
142
  };
142
143
  });
143
- c = b(n.inner, m);
144
+ r = v(t.inner, p);
144
145
  }
145
- const r = t.slice(n.start, n.startTagEnd) + c + t.slice(n.contentEnd, n.end), l = t.slice(0, n.start) + r + t.slice(n.end);
146
- return a[e.mainSourcePath] = P(
147
- A(x(l.trim()))
148
- ), a;
149
- }
150
- function F(t, e, s) {
151
- const n = {};
152
- return n[e.mainSourcePath] = P(
153
- x(t.trim())
146
+ const l = e.slice(t.start, t.startTagEnd) + r + e.slice(t.contentEnd, t.end), i = e.slice(0, t.start) + l + e.slice(t.end);
147
+ return n[s.mainSourcePath] = x(
148
+ C(S(i.trim()))
154
149
  ), n;
155
150
  }
156
- function O(t, e = {}) {
157
- const s = [], n = e.documentKind ?? j(t), a = {
158
- splitChapters: e.splitChapters ?? n === "book",
159
- splitSections: e.splitSections ?? !1,
160
- mainSourcePath: e.mainSourcePath ?? d.mainSourcePath,
161
- publicationPath: e.publicationPath ?? d.publicationPath,
162
- projectFilePath: e.projectFilePath ?? d.projectFilePath,
163
- docinfoPath: e.docinfoPath ?? d.docinfoPath
164
- }, i = n === "book" ? D(t, a, s) : F(t, a);
165
- return i[a.projectFilePath] = v({
166
- mainSource: a.mainSourcePath,
167
- publication: a.publicationPath
168
- }), i[a.publicationPath] = R(), { files: i, documentKind: n, warnings: s };
151
+ function F(e, s, o) {
152
+ const t = {};
153
+ return t[s.mainSourcePath] = x(
154
+ S(e.trim())
155
+ ), t;
156
+ }
157
+ function z(e, s = {}) {
158
+ const o = [], t = s.documentKind ?? w(e), n = {
159
+ splitChapters: s.splitChapters ?? t === "book",
160
+ splitSections: s.splitSections ?? !1,
161
+ mainSourcePath: s.mainSourcePath ?? h.mainSourcePath,
162
+ publicationPath: s.publicationPath ?? h.publicationPath,
163
+ projectFilePath: s.projectFilePath ?? h.projectFilePath,
164
+ docinfoPath: s.docinfoPath ?? h.docinfoPath
165
+ }, c = t === "book" ? N(e, n, o) : F(e, n);
166
+ return c[n.projectFilePath] = I({
167
+ mainSource: n.mainSourcePath,
168
+ publication: n.publicationPath
169
+ }), c[n.publicationPath] = _(), { files: c, documentKind: t, warnings: o };
170
+ }
171
+ class O extends Error {
172
+ constructor() {
173
+ super("Import cancelled."), this.name = "ConversionCancelledError";
174
+ }
175
+ }
176
+ let B = 0;
177
+ function W(e, s, o = {}) {
178
+ const t = `convert-${B++}`, n = e(), { assets: c = {}, ...r } = o;
179
+ let l;
180
+ const i = new Promise((p, a) => {
181
+ let m = !1;
182
+ const g = (f) => {
183
+ m || (m = !0, n.terminate(), f());
184
+ };
185
+ l = () => g(() => a(new O())), n.addEventListener("message", (f) => {
186
+ const d = f.data;
187
+ if (!(!d || d.requestId !== t)) {
188
+ if (d.type === "result") {
189
+ g(() => p(d.result));
190
+ return;
191
+ }
192
+ g(() => a(new Error(d.message)));
193
+ }
194
+ }), n.addEventListener("error", (f) => {
195
+ const d = typeof f == "object" && f && "message" in f ? String(f.message) : "The import worker failed to start.";
196
+ g(() => a(new Error(d)));
197
+ });
198
+ }), u = [
199
+ ...new Set(
200
+ Object.values(c).map((p) => p.buffer).filter(
201
+ (p) => p instanceof ArrayBuffer
202
+ )
203
+ )
204
+ ];
205
+ return n.postMessage(
206
+ { type: "convert", requestId: t, files: s, assets: c, options: r },
207
+ u
208
+ ), { result: i, cancel: () => l?.() };
209
+ }
210
+ function U(e) {
211
+ const {
212
+ createWorker: s,
213
+ id: o = "worker",
214
+ label: t = "Built-in converter",
215
+ description: n = "Create a new project starting with LaTeX, Markdown, or PreTeXt files.",
216
+ acceptExtensions: c
217
+ } = e;
218
+ let r = null;
219
+ const l = async (i, u) => {
220
+ r?.cancel(), r = W(s, i, u);
221
+ try {
222
+ return await r.result;
223
+ } finally {
224
+ r = null;
225
+ }
226
+ };
227
+ return {
228
+ id: o,
229
+ label: t,
230
+ description: n,
231
+ acceptExtensions: c,
232
+ prepare: async (i) => {
233
+ const { files: u, assets: p } = await b(i);
234
+ return {
235
+ fileName: i.name,
236
+ files: u,
237
+ assets: p,
238
+ analysis: A(u)
239
+ };
240
+ },
241
+ convertPrepared: (i, u) => l(i.files, { ...u, assets: i.assets }),
242
+ convertFile: async (i, u) => {
243
+ const { files: p, assets: a } = await b(i);
244
+ return l(p, { ...u, assets: a });
245
+ },
246
+ cancel: () => {
247
+ r?.cancel(), r = null;
248
+ }
249
+ };
169
250
  }
170
251
  export {
171
- B as LATEX_FORMAT_MARKERS,
172
- W as MARKDOWN_FORMAT_MARKERS,
173
- X as PRETEXT_DIVISION_TAGS,
174
- G as PRETEXT_ROOT_TAGS,
175
- U as analyzeImportSources,
176
- H as assetsForImportMode,
177
- q as attachLatexRoots,
178
- J as attachMarkdownRoots,
179
- Q as buildDivisionPool,
180
- V as buildNativeDivisionPool,
181
- O as buildPretextProjectFiles,
182
- Y as carryOverProjectFiles,
183
- Z as cleanLatex,
184
- tt as convertLatexToPretext,
185
- et as convertMarkdownToPretext,
186
- nt as convertSourceToPretext,
187
- at as defaultAttachLevel,
188
- st as defaultManifestTarget,
189
- j as detectDocumentKind,
190
- ot as detectSourceFormat,
191
- it as divisionChildRefs,
192
- rt as expandPretextIncludes,
193
- ct as extractUpload,
194
- lt as filePrefixForDivision,
195
- pt as filesForImportMode,
196
- ut as findProjectManifest,
197
- mt as formatWarningLine,
198
- ft as getConversionErrorMessage,
199
- dt as handleImportUploadFile,
200
- Pt as importProjectFromFiles,
201
- gt as isDivisionTag,
202
- ht as normalizePretextSource,
203
- bt as parseProjectManifest,
204
- xt as projectForImportMode,
205
- v as renderProjectPtx,
206
- Tt as renderProjectPtxFromManifest,
207
- R as renderPublicationPtx,
208
- Ft as resolveSplitLevel,
209
- St as sanitizeRef,
210
- Et as serializeProjectToFiles,
211
- N as serializeProjectToPlusPayload
252
+ q as CLEAN_RULES,
253
+ O as ConversionCancelledError,
254
+ V as LATEX_DIVISION_COMMANDS,
255
+ Y as LATEX_FORMAT_MARKERS,
256
+ J as MARKDOWN_FORMAT_MARKERS,
257
+ Q as MAX_SUGGESTED_SPLIT_LEVEL,
258
+ Z as PRETEXT_DIVISION_TAGS,
259
+ ee as PRETEXT_ROOT_TAGS,
260
+ A as analyzeImportSources,
261
+ te as applyLatexFixes,
262
+ Ke as assetsForImportMode,
263
+ se as attachLatexRoots,
264
+ ne as attachMarkdownRoots,
265
+ ae as buildDivisionPool,
266
+ oe as buildNativeDivisionPool,
267
+ z as buildPretextProjectFiles,
268
+ re as carryOverProjectFiles,
269
+ ie as cleanLatex,
270
+ ce as cleanLatexInChunks,
271
+ le as cleanLatexText,
272
+ ue as convertLatexToPretext,
273
+ pe as convertMarkdownToPretext,
274
+ fe as convertSourceToPretext,
275
+ U as createWorkerEngine,
276
+ me as defaultAttachLevel,
277
+ de as defaultManifestTarget,
278
+ w as detectDocumentKind,
279
+ ge as detectSourceFormat,
280
+ ze as diffHunks,
281
+ Ue as diffLines,
282
+ Ge as diffStats,
283
+ Pe as divisionChildRefs,
284
+ he as divisionsAtLevel,
285
+ xe as expandPretextIncludes,
286
+ b as extractUpload,
287
+ He as fileChangesForImport,
288
+ be as filePrefixForDivision,
289
+ qe as filesForImportMode,
290
+ Ee as findLatexFixes,
291
+ Te as findLatexHeaders,
292
+ ve as findProjectManifest,
293
+ Se as fixesToWarnings,
294
+ Ve as formatWarningLine,
295
+ Le as getConversionErrorMessage,
296
+ Fe as getLatexCleanDiagnostics,
297
+ we as handleImportUploadFile,
298
+ Ie as importProjectFromFiles,
299
+ _e as isDivisionTag,
300
+ Ce as latexDivisionHierarchy,
301
+ ke as latexFixesToCodeActions,
302
+ ye as mergeChunksAtLevel,
303
+ Ae as normalizePretextSource,
304
+ Me as parseLatexDivisions,
305
+ je as parseProjectManifest,
306
+ Ye as projectForImportMode,
307
+ Re as relayoutImport,
308
+ I as renderProjectPtx,
309
+ $e as renderProjectPtxFromManifest,
310
+ _ as renderPublicationPtx,
311
+ De as resolveImportSplitLevel,
312
+ Ne as resolveSplitLevel,
313
+ W as runConversionInWorker,
314
+ Oe as sanitizeRef,
315
+ Be as serializeProjectToFiles,
316
+ K as serializeProjectToPlusPayload,
317
+ We as suggestSplitLevel
212
318
  };
@@ -0,0 +1,47 @@
1
+ import { LatexFix } from '@pretextbook/latex-style-pretext';
2
+ import { CleaningWarning } from './warnings';
3
+ export interface CleanedChunk {
4
+ /**
5
+ * Heading path from the document root — `["Limits", "One-sided limits"]`.
6
+ * Empty for the preamble and for body text ahead of the first heading.
7
+ */
8
+ path: string[];
9
+ /** The chunk's own heading, or `""` when it has none. */
10
+ title: string;
11
+ /** `"preamble"`, a LaTeX sectioning command, or `null` for the lead-in text. */
12
+ kind: "preamble" | "lead" | "division";
13
+ /** Sectioning command that opened this chunk, when `kind` is `"division"`. */
14
+ command?: string;
15
+ /** Depth in the document's own hierarchy; 0 for preamble and lead-in. */
16
+ level: number;
17
+ before: string;
18
+ after: string;
19
+ fixes: LatexFix[];
20
+ }
21
+ export interface CleanLatexChunksResult {
22
+ /** The cleaned document, chunks rejoined in source order. */
23
+ output: string;
24
+ chunks: CleanedChunk[];
25
+ /** Aggregate over every chunk — the document-level summary. */
26
+ warnings: CleaningWarning[];
27
+ fixes: LatexFix[];
28
+ }
29
+ /**
30
+ * Clean `source`, cutting it at every division header so each piece carries its
31
+ * own before/after text and fix list.
32
+ *
33
+ * Chunks are cut at the *deepest* level the document has, not at a requested
34
+ * split depth. A shallower view is a matter of concatenating consecutive chunks
35
+ * (`mergeChunksAtLevel`), which lets the wizard change split depth without
36
+ * re-cleaning or re-converting anything.
37
+ */
38
+ export declare function cleanLatexInChunks(source: string): CleanLatexChunksResult;
39
+ /**
40
+ * Collapse chunks to the view a given split depth produces: one entry per file
41
+ * that depth would generate, with the chunks below it concatenated in.
42
+ *
43
+ * `level` 0 gives a single entry for the whole body; 1 one per top-level
44
+ * division; and so on. The preamble is always its own entry, since it never
45
+ * becomes a file of its own.
46
+ */
47
+ export declare function mergeChunksAtLevel(chunks: CleanedChunk[], level: number): CleanedChunk[];
@@ -1,6 +1,40 @@
1
+ import { CleanScope, LatexFix } from '@pretextbook/latex-style-pretext';
1
2
  import { CleaningWarning } from './warnings';
3
+ export interface CleanLatexOptions {
4
+ /**
5
+ * What the text is. Defaults to `"auto"`, which detects `\begin{document}`.
6
+ * Per-division cleaning (SPEC §3.8) passes `"body"`.
7
+ */
8
+ scope?: CleanScope;
9
+ /** Rule ids to skip. */
10
+ disable?: string[];
11
+ }
2
12
  export interface CleanLatexResult {
3
13
  output: string;
14
+ /** Aggregate, one row per rule — what the wizard's summary list renders. */
4
15
  warnings: CleaningWarning[];
16
+ /**
17
+ * Positioned fixes, in application order. The before/after diff and any
18
+ * per-file report read these; `warnings` is the rolled-up view of the same
19
+ * data.
20
+ */
21
+ fixes: LatexFix[];
5
22
  }
6
- export declare function cleanLatex(source: string): CleanLatexResult;
23
+ /**
24
+ * Clean a piece of LaTeX: strip comments and trailing junk, then apply every
25
+ * cleaning rule to a fixpoint.
26
+ *
27
+ * `trimJunk` runs first and stays on this side of the boundary deliberately.
28
+ * Deleting every comment is right for an import (they are authoring notes about
29
+ * LaTeX, not content) but wrong for an editor, where the author is looking at
30
+ * their own comments and would not expect a cleanup to eat them.
31
+ */
32
+ export declare function cleanLatex(source: string, options?: CleanLatexOptions): CleanLatexResult;
33
+ /**
34
+ * Roll positioned fixes up into one `CleaningWarning` per rule.
35
+ *
36
+ * A rule with `reportMatch` emits two rows, matching what the cleaner has always
37
+ * produced: a `save` carrying the matched text (so the author can reconstruct
38
+ * what a `\def` meant) and a `delete` recording that it went away.
39
+ */
40
+ export declare function fixesToWarnings(fixes: LatexFix[]): CleaningWarning[];
@@ -1,3 +1,4 @@
1
+ import { CleanedChunk } from './clean/clean-chunks';
1
2
  import { CleaningWarning } from './clean/warnings';
2
3
  import { ConvertedPretextResult, SourceFormat } from './types';
3
4
  export declare function normalizePretextSource(pretextSource: string): string;
@@ -5,6 +6,12 @@ export interface LatexConversionResult {
5
6
  pretext: string;
6
7
  cleanedLatex: string;
7
8
  warnings: CleaningWarning[];
9
+ /**
10
+ * The cleaned source cut at its division headers, each piece carrying its own
11
+ * before/after text. The wizard's per-file diff reads these; see
12
+ * `clean-chunks.ts` for why cleaning is partitioned rather than global.
13
+ */
14
+ cleanChunks: CleanedChunk[];
8
15
  }
9
16
  export declare function convertLatexToPretext(latexSource: string): LatexConversionResult;
10
17
  export interface MarkdownConversionResult {
@@ -0,0 +1,36 @@
1
+ export type DiffOp = "keep" | "add" | "remove";
2
+ export interface DiffLine {
3
+ op: DiffOp;
4
+ text: string;
5
+ /** 1-based line number in the "before" text; absent for added lines. */
6
+ beforeLine?: number;
7
+ /** 1-based line number in the "after" text; absent for removed lines. */
8
+ afterLine?: number;
9
+ }
10
+ export interface DiffHunk {
11
+ /** Lines in this hunk, including the surrounding context. */
12
+ lines: DiffLine[];
13
+ }
14
+ export interface DiffStats {
15
+ added: number;
16
+ removed: number;
17
+ }
18
+ /**
19
+ * Longest common subsequence of two line arrays, as a diff.
20
+ *
21
+ * The classic O(n·m) dynamic program. A division is at most a few thousand
22
+ * lines, so the table is small enough not to matter; anything larger is guarded
23
+ * by `MAX_DIFF_LINES` below, which falls back to a whole-file replacement
24
+ * rather than allocating a table nobody will read.
25
+ */
26
+ export declare function diffLines(before: string, after: string): DiffLine[];
27
+ /** Above this combined line count, the LCS table is not worth building. */
28
+ export declare const MAX_DIFF_LINES = 20000;
29
+ export declare function diffStats(lines: DiffLine[]): DiffStats;
30
+ /**
31
+ * Group a diff into hunks of changed lines with `context` unchanged lines
32
+ * around each, dropping the long runs of untouched text between them. A
33
+ * cleaning pass usually touches a handful of scattered lines, so showing the
34
+ * whole file would bury them.
35
+ */
36
+ export declare function diffHunks(lines: DiffLine[], context?: number): DiffHunk[];
@@ -0,0 +1,22 @@
1
+ import { DiffHunk, DiffStats } from './diff';
2
+ import { ImportedProjectSuccess } from './types';
3
+ export interface FileChangeRecord {
4
+ path: string;
5
+ /** Division title this file holds, or `""` for the main file. */
6
+ title: string;
7
+ before: string;
8
+ after: string;
9
+ stats: DiffStats;
10
+ hunks: DiffHunk[];
11
+ /** Number of cleaning fixes applied within this file's source. */
12
+ fixCount: number;
13
+ }
14
+ /**
15
+ * A before/after record for each generated source file at `splitLevel`.
16
+ *
17
+ * Files with no matching chunk are omitted rather than shown with an empty
18
+ * diff: a title that did not survive conversion means the join is unreliable
19
+ * for that file, and an empty diff would read as "nothing changed here" when
20
+ * the truth is "we do not know".
21
+ */
22
+ export declare function fileChangesForImport(result: ImportedProjectSuccess, splitLevel?: number): FileChangeRecord[];