@pretextbook/import 0.0.1 → 0.3.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,90 +1,116 @@
1
- import { d as T, r as E, a as R, f as P, b as h, w as f, s as g, e as v, c as M, p as j } from "./import-mode-iBDd9oQY.js";
2
- import { L, M as U, g as z, h as O, i as N, j as W, k as B, l as X, m as q, n as G, o as H, q as J, t as Q, u as V, v as Y, x as Z, y as tt, z as et, A as nt } from "./import-mode-iBDd9oQY.js";
3
- function x() {
4
- const t = globalThis.crypto;
5
- return t?.randomUUID ? t.randomUUID() : "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (e) => {
6
- const s = Math.random() * 16 | 0;
7
- return (e === "x" ? s : s & 3 | 8).toString(16);
8
- });
1
+ import { d as $, r as j, a as k, f as P, b as h, w as g, s as b, e as A, c as C, p as v } from "./import-mode-DK83Lfue.js";
2
+ import { L as O, M as W, g as U, h as X, i as q, j as H, k as Y, l as G, m as J, n as Q, o as V, q as Z, t as tt, u as et, v as nt, x as ot, y as at, z as it, A as st, B as rt, C as ct } from "./import-mode-DK83Lfue.js";
3
+ const w = {
4
+ png: "image/png",
5
+ jpg: "image/jpeg",
6
+ jpeg: "image/jpeg",
7
+ gif: "image/gif",
8
+ pdf: "application/pdf",
9
+ eps: "application/postscript",
10
+ ps: "application/postscript",
11
+ bmp: "image/bmp",
12
+ tiff: "image/tiff",
13
+ tif: "image/tiff",
14
+ webp: "image/webp",
15
+ ico: "image/vnd.microsoft.icon"
16
+ };
17
+ function R(t) {
18
+ const e = t.split(".").pop()?.toLowerCase() ?? "";
19
+ return w[e] ?? "application/octet-stream";
20
+ }
21
+ const f = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
22
+ function M(t) {
23
+ const e = t.length, a = [];
24
+ for (let n = 0; n < e; n += 3) {
25
+ const o = t[n], s = n + 1 < e, c = n + 2 < e, r = s ? t[n + 1] : 0, l = c ? t[n + 2] : 0;
26
+ a.push(f[o >> 2]), a.push(f[(o & 3) << 4 | r >> 4]), a.push(s ? f[(r & 15) << 2 | l >> 6] : "="), a.push(c ? f[l & 63] : "=");
27
+ }
28
+ return a.join("");
29
+ }
30
+ function y(t) {
31
+ return {
32
+ ref: t.ref,
33
+ kind: "file",
34
+ title: t.fileName,
35
+ short_description: t.fileName,
36
+ file: {
37
+ filename: t.fileName,
38
+ content_type: R(t.fileName),
39
+ data: M(t.data)
40
+ }
41
+ };
9
42
  }
10
- function C(t) {
43
+ function B(t) {
11
44
  return {
12
45
  title: t.title,
13
46
  docinfo: t.docinfo,
14
- documentType: t.documentKind,
15
- divisions: t.divisions.map((e) => ({
16
- id: x(),
47
+ document_type: t.documentKind,
48
+ divisions_attributes: t.divisions.map((e) => ({
17
49
  ref: e.xmlId,
18
50
  source: e.content,
19
- sourceFormat: e.sourceFormat,
20
- isRoot: e.isRoot
51
+ source_format: e.sourceFormat,
52
+ is_root: e.isRoot
21
53
  })),
22
- assets: t.assets.map((e) => ({
23
- id: x(),
24
- ref: e.ref,
25
- kind: "file",
26
- fileName: e.fileName,
27
- data: e.data
28
- }))
54
+ assets_attributes: t.assets.map(y)
29
55
  };
30
56
  }
31
- const p = {
57
+ const d = {
32
58
  mainSourcePath: "source/main.ptx",
33
59
  publicationPath: "publication/publication.ptx",
34
60
  projectFilePath: "project.ptx",
35
61
  docinfoPath: "source/docinfo.ptx"
36
62
  };
37
- function b(t) {
63
+ function x(t) {
38
64
  return P(t, "pretext") ? t : `<pretext>
39
65
  ${t}
40
66
  </pretext>`;
41
67
  }
42
- function F(t, e, s) {
43
- const n = /* @__PURE__ */ new Map(), a = [], i = `${e}-`;
68
+ function F(t, e, a) {
69
+ const n = /* @__PURE__ */ new Map(), o = [], s = `${e}-`;
44
70
  return t.forEach((c, r) => {
45
- const u = c.attributes["xml:id"];
46
- let l;
47
- if (u) {
48
- const m = M(u);
49
- l = m.startsWith(i) ? m : `${i}${m}`;
71
+ const l = c.attributes["xml:id"];
72
+ let p;
73
+ if (l) {
74
+ const u = C(l);
75
+ p = u.startsWith(s) ? u : `${s}${u}`;
50
76
  } else
51
- l = `${i}${j(r + 1, t.length)}`, s.push({
77
+ p = `${s}${v(r + 1, t.length)}`, a.push({
52
78
  action: "anomaly",
53
79
  severity: "info",
54
80
  kind: "structure",
55
81
  category: "missing_xml_id",
56
82
  macro: c.name,
57
83
  occurrences: 1,
58
- message: `<${c.name}> at position ${r + 1} has no xml:id; using \`${l}\` as filename.`
84
+ message: `<${c.name}> at position ${r + 1} has no xml:id; using \`${p}\` as filename.`
59
85
  });
60
- const d = n.get(l) ?? 0;
61
- n.set(l, d + 1);
62
- const o = d === 0 ? l : `${l}-${d + 1}`;
63
- a.push({
86
+ const m = n.get(p) ?? 0;
87
+ n.set(p, m + 1);
88
+ const i = m === 0 ? p : `${p}-${m + 1}`;
89
+ o.push({
64
90
  span: c,
65
- slug: o,
66
- filename: `${o}.ptx`
91
+ slug: i,
92
+ filename: `${i}.ptx`
67
93
  });
68
- }), a;
94
+ }), o;
69
95
  }
70
- function I(t, e, s, n) {
71
- const a = h(t, "section");
72
- if (a.length === 0)
96
+ function I(t, e, a, n) {
97
+ const o = h(t, "section");
98
+ if (o.length === 0)
73
99
  return t;
74
- const c = F(a, "sec", n).map((r) => {
75
- const u = `source/${e}/${r.filename}`;
76
- return s[u] = f(r.span.outer), {
100
+ const c = F(o, "sec", n).map((r) => {
101
+ const l = `source/${e}/${r.filename}`;
102
+ return a[l] = g(r.span.outer), {
77
103
  start: r.span.start,
78
104
  end: r.span.end,
79
105
  replacement: `<xi:include href="${e}/${r.filename}"/>`
80
106
  };
81
107
  });
82
- return g(t, c);
108
+ return b(t, c);
83
109
  }
84
- function w(t, e, s) {
110
+ function N(t, e, a) {
85
111
  const n = P(t, "book");
86
112
  if (!n)
87
- return s.push({
113
+ return a.push({
88
114
  action: "anomaly",
89
115
  severity: "warning",
90
116
  kind: "structure",
@@ -92,73 +118,80 @@ function w(t, e, s) {
92
118
  macro: "book",
93
119
  occurrences: 1,
94
120
  message: "Document was treated as a book but no <book> element found; emitting as a single file."
95
- }), $(t, e);
96
- const a = {}, i = h(n.inner, "chapter");
121
+ }), S(t, e);
122
+ const o = {}, s = h(n.inner, "chapter");
97
123
  let c = n.inner;
98
- if (e.splitChapters && i.length > 0) {
99
- const d = F(i, "ch", s).map((o) => {
100
- let m = o.span.outer;
124
+ if (e.splitChapters && s.length > 0) {
125
+ const m = F(s, "ch", a).map((i) => {
126
+ let u = i.span.outer;
101
127
  if (e.splitSections) {
102
- const y = o.span.inner, S = I(y, o.slug, a, s);
103
- m = o.span.outer.slice(0, o.span.startTagEnd - o.span.start) + S + o.span.outer.slice(o.span.contentEnd - o.span.start);
128
+ const E = i.span.inner, _ = I(
129
+ E,
130
+ i.slug,
131
+ o,
132
+ a
133
+ );
134
+ u = i.span.outer.slice(0, i.span.startTagEnd - i.span.start) + _ + i.span.outer.slice(i.span.contentEnd - i.span.start);
104
135
  }
105
- const k = `source/${o.filename}`;
106
- return a[k] = f(m), {
107
- start: o.span.start,
108
- end: o.span.end,
109
- replacement: `<xi:include href="${o.filename}"/>`
136
+ const T = `source/${i.filename}`;
137
+ return o[T] = g(u), {
138
+ start: i.span.start,
139
+ end: i.span.end,
140
+ replacement: `<xi:include href="${i.filename}"/>`
110
141
  };
111
142
  });
112
- c = g(n.inner, d);
143
+ c = b(n.inner, m);
113
144
  }
114
- const r = t.slice(n.start, n.startTagEnd) + c + t.slice(n.contentEnd, n.end), u = t.slice(0, n.start) + r + t.slice(n.end);
115
- return a[e.mainSourcePath] = f(
116
- v(b(u.trim()))
117
- ), a;
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 o[e.mainSourcePath] = g(
147
+ A(x(l.trim()))
148
+ ), o;
118
149
  }
119
- function $(t, e, s) {
150
+ function S(t, e, a) {
120
151
  const n = {};
121
- return n[e.mainSourcePath] = f(
122
- b(t.trim())
152
+ return n[e.mainSourcePath] = g(
153
+ x(t.trim())
123
154
  ), n;
124
155
  }
125
156
  function D(t, e = {}) {
126
- const s = [], n = e.documentKind ?? T(t), a = {
157
+ const a = [], n = e.documentKind ?? $(t), o = {
127
158
  splitChapters: e.splitChapters ?? n === "book",
128
159
  splitSections: e.splitSections ?? !1,
129
- mainSourcePath: e.mainSourcePath ?? p.mainSourcePath,
130
- publicationPath: e.publicationPath ?? p.publicationPath,
131
- projectFilePath: e.projectFilePath ?? p.projectFilePath,
132
- docinfoPath: e.docinfoPath ?? p.docinfoPath
133
- }, i = n === "book" ? w(t, a, s) : $(t, a);
134
- return i[a.projectFilePath] = E({
135
- mainSource: a.mainSourcePath,
136
- publication: a.publicationPath
137
- }), i[a.publicationPath] = R(), { files: i, documentKind: n, warnings: s };
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
+ }, s = n === "book" ? N(t, o, a) : S(t, o);
165
+ return s[o.projectFilePath] = j({
166
+ mainSource: o.mainSourcePath,
167
+ publication: o.publicationPath
168
+ }), s[o.publicationPath] = k(), { files: s, documentKind: n, warnings: a };
138
169
  }
139
170
  export {
140
- L as LATEX_FORMAT_MARKERS,
141
- U as MARKDOWN_FORMAT_MARKERS,
142
- z as assetsForImportMode,
143
- O as buildDivisionPool,
171
+ O as LATEX_FORMAT_MARKERS,
172
+ W as MARKDOWN_FORMAT_MARKERS,
173
+ U as assetsForImportMode,
174
+ X as buildDivisionPool,
175
+ q as buildNativeDivisionPool,
144
176
  D as buildPretextProjectFiles,
145
- N as cleanLatex,
146
- W as convertLatexToPretext,
147
- B as convertMarkdownToPretext,
148
- X as convertSourceToPretext,
149
- T as detectDocumentKind,
150
- q as detectSourceFormat,
151
- G as divisionChildRefs,
152
- H as expandPretextIncludes,
153
- J as filesForImportMode,
154
- Q as formatWarningLine,
155
- V as getConversionErrorMessage,
156
- Y as handleImportUploadFile,
157
- Z as importProjectFromFiles,
158
- tt as normalizePretextSource,
159
- E as renderProjectPtx,
160
- R as renderPublicationPtx,
161
- et as sanitizeRef,
162
- nt as serializeProjectToFiles,
163
- C as serializeProjectToPlusPayload
177
+ H as cleanLatex,
178
+ Y as convertLatexToPretext,
179
+ G as convertMarkdownToPretext,
180
+ J as convertSourceToPretext,
181
+ $ as detectDocumentKind,
182
+ Q as detectSourceFormat,
183
+ V as divisionChildRefs,
184
+ Z as expandPretextIncludes,
185
+ tt as filesForImportMode,
186
+ et as formatWarningLine,
187
+ nt as getConversionErrorMessage,
188
+ ot as handleImportUploadFile,
189
+ at as importProjectFromFiles,
190
+ it as normalizePretextSource,
191
+ st as projectForImportMode,
192
+ j as renderProjectPtx,
193
+ k as renderPublicationPtx,
194
+ rt as sanitizeRef,
195
+ ct as serializeProjectToFiles,
196
+ B as serializeProjectToPlusPayload
164
197
  };
@@ -1,7 +1,15 @@
1
1
  import { CleaningWarning } from './clean/warnings';
2
- import { ImportedProjectSuccess } from './types';
2
+ import { ImportedProject, ImportedProjectSuccess } from './types';
3
3
  /** Which of the result's alternatives the user chose to import. */
4
4
  export type ImportMode = "converted" | "native";
5
+ /**
6
+ * The division pool the pretext-plus host should serialize for the chosen
7
+ * mode: the native (LaTeX/Markdown) pool when the user keeps the source
8
+ * format, otherwise the converted PreTeXt pool. Falls back to the converted
9
+ * pool when there is no native projection (e.g. PreTeXt input, or an import
10
+ * that produced no native source).
11
+ */
12
+ export declare function projectForImportMode(result: ImportedProjectSuccess, mode: ImportMode): ImportedProject;
5
13
  /** The text files a host should write for the chosen mode. */
6
14
  export declare function filesForImportMode(result: ImportedProjectSuccess, mode: ImportMode): Record<string, string>;
7
15
  /** The binary assets a host should write for the chosen mode. */
@@ -1,6 +1,7 @@
1
1
  import { CleaningWarning } from '../clean/warnings';
2
2
  import { DocumentKind } from '../layout/document-kind';
3
3
  import { ImportedProject } from '../types';
4
+ export { sanitizeRef } from './refs';
4
5
  export interface BuildDivisionPoolOptions {
5
6
  documentKind?: DocumentKind;
6
7
  splitChapters?: boolean;
@@ -12,12 +13,6 @@ export interface BuildDivisionPoolResult {
12
13
  project: ImportedProject;
13
14
  warnings: CleaningWarning[];
14
15
  }
15
- /**
16
- * Coerce a string into a REF_REGEX-safe ref: invalid characters collapse to
17
- * `-`, leading characters that can't start a ref are stripped. Returns `""`
18
- * when nothing valid remains.
19
- */
20
- export declare function sanitizeRef(raw: string): string;
21
16
  /**
22
17
  * Parse a converted PreTeXt document
23
18
  * (`<pretext><docinfo>…</docinfo><book|article>…`) into the division pool.
@@ -1,3 +1,4 @@
1
1
  export { buildDivisionPool, sanitizeRef, type BuildDivisionPoolOptions, type BuildDivisionPoolResult, } from './division-pool';
2
+ export { buildNativeDivisionPool, type BuildNativeDivisionPoolOptions, } from './native-pool';
2
3
  export { serializeProjectToFiles, divisionChildRefs, type SerializeProjectFilesOptions, type SerializedProjectFiles, } from './serialize-files';
3
4
  export { serializeProjectToPlusPayload } from './serialize-plus';
@@ -0,0 +1,20 @@
1
+ import { DocumentKind } from '../layout/document-kind';
2
+ import { BuildDivisionPoolResult } from './division-pool';
3
+ export interface BuildNativeDivisionPoolOptions {
4
+ documentKind?: DocumentKind;
5
+ splitChapters?: boolean;
6
+ splitSections?: boolean;
7
+ /** Project title, hoisted from the converted PreTeXt pool. */
8
+ title?: string;
9
+ /** Project `<docinfo>`, hoisted from the converted PreTeXt pool. */
10
+ docinfo?: string;
11
+ /** Binary assets keyed by their original (input) path. */
12
+ assets?: Record<string, Uint8Array>;
13
+ }
14
+ /**
15
+ * Build a native (LaTeX/Markdown) division pool from cleaned native source.
16
+ * The result has the same shape as `buildDivisionPool`, so the same
17
+ * serializers consume it — `serializeProjectToPlusPayload` in particular emits
18
+ * `source_format: "latex" | "markdown"` divisions unchanged.
19
+ */
20
+ export declare function buildNativeDivisionPool(nativeSource: string, format: "latex" | "markdown", options?: BuildNativeDivisionPoolOptions): BuildDivisionPoolResult;
@@ -0,0 +1,39 @@
1
+ import { CleaningWarning } from '../clean/warnings';
2
+ import { ImportedAsset } from '../types';
3
+ /**
4
+ * Coerce a string into a REF_REGEX-safe ref: invalid characters collapse to
5
+ * `-`, leading characters that can't start a ref are stripped. Returns `""`
6
+ * when nothing valid remains.
7
+ */
8
+ export declare function sanitizeRef(raw: string): string;
9
+ /** Hands out refs, deduplicating with `-2`, `-3`, … suffixes. */
10
+ export declare class RefPool {
11
+ private used;
12
+ claim(preferred: string): string;
13
+ has(ref: string): boolean;
14
+ }
15
+ export interface ClaimRefResult {
16
+ ref: string;
17
+ renamedFrom?: string;
18
+ generated: boolean;
19
+ }
20
+ /**
21
+ * Decide a division's ref from a raw id (an `xml:id`, a `\label`, or nothing):
22
+ * the existing id when REF_REGEX-safe and unused, a sanitized/deduplicated
23
+ * variant when not (reported as a rename), or a generated `fallback` when
24
+ * there was no id at all.
25
+ */
26
+ export declare function claimRefFromId(rawId: string | undefined, refs: RefPool, fallback: string): ClaimRefResult;
27
+ /**
28
+ * Record the structural warning implied by a ref claim: a rename when the
29
+ * original id was invalid or collided, an info note when an id was generated.
30
+ * `elementName` is the division's type (e.g. `chapter`); `position` is its
31
+ * 1-based index among its siblings, for the generated-id message.
32
+ */
33
+ export declare function pushRefWarnings(warnings: CleaningWarning[], claim: ClaimRefResult, elementName: string, position: number): void;
34
+ /**
35
+ * Turn a map of raw asset bytes (keyed by original input path) into
36
+ * `ImportedAsset` records, minting a ref from each basename's stem and
37
+ * deduplicating against everything already claimed (divisions included).
38
+ */
39
+ export declare function buildAssets(rawAssets: Record<string, Uint8Array>, refs: RefPool): ImportedAsset[];
@@ -1,10 +1,5 @@
1
1
  import { ImportedProject, PlusProjectPayload } from '../types';
2
2
  /**
3
- * Serialize the division pool to a pretext-plus project payload.
4
- *
5
- * The host page maps this onto the Rails endpoints (snake_case
6
- * `divisions_attributes` / `assets_attributes`, multipart for asset bytes);
7
- * note that `docinfo` currently requires a follow-up PATCH after create —
8
- * see SPEC §4.3 for the endpoint mapping and known gaps.
3
+ * Serialize the division pool to a pretext-plus import payload.
9
4
  */
10
5
  export declare function serializeProjectToPlusPayload(project: ImportedProject): PlusProjectPayload;
@@ -70,35 +70,48 @@ export interface ImportedProject {
70
70
  divisions: ImportedDivision[];
71
71
  assets: ImportedAsset[];
72
72
  }
73
- /** One division row of the pretext-plus create/update payload (SPEC §4.3). */
74
- export interface PlusDivisionRecord {
75
- /** Client-minted UUID; Rails inserts it as the record's primary key. */
76
- id: string;
73
+ /**
74
+ * One division row of the pretext-plus import payload (`divisions_attributes`
75
+ * on `POST /projects/import`). New rows only — `ProjectsController#import_params`
76
+ * permits no `id`/`_destroy` there (imports never edit or delete existing rows).
77
+ */
78
+ export interface PlusDivisionAttributes {
77
79
  /** The division's `xml:id` (Rails column `ref`). */
78
80
  ref: string;
79
81
  source: string;
80
- sourceFormat: SourceFormat;
81
- isRoot: boolean;
82
+ source_format: SourceFormat;
83
+ is_root: boolean;
84
+ }
85
+ /**
86
+ * An asset's file upload. Bytes travel base64-encoded, since the whole import
87
+ * posts as one JSON body — `import_params` decodes `data` back into an
88
+ * ActiveStorage attachable server-side.
89
+ */
90
+ export interface PlusAssetFile {
91
+ filename: string;
92
+ content_type: string;
93
+ data: string;
82
94
  }
83
- /** One asset row of the pretext-plus payload; bytes attach as `file`. */
84
- export interface PlusAssetRecord {
85
- id: string;
95
+ /** One asset row of the pretext-plus import payload (`assets_attributes`). */
96
+ export interface PlusAssetAttributes {
86
97
  ref: string;
87
98
  kind: "file";
88
- /** → `short_description` and the multipart upload filename. */
89
- fileName: string;
90
- data: Uint8Array;
99
+ title: string;
100
+ short_description: string;
101
+ file: PlusAssetFile;
91
102
  }
92
103
  /**
93
- * Direct camelCase mirror of what pretext-plus's `ProjectsController`
94
- * permits (`divisions_attributes` / `assets_attributes`) — see SPEC §4.3.
104
+ * Wire shape of `POST /projects/import`
105
+ * (`ProjectsController#create_from_import` / `import_params`) — a direct
106
+ * snake_case mirror, since the endpoint permits no `id` on either nested
107
+ * attribute.
95
108
  */
96
109
  export interface PlusProjectPayload {
97
110
  title: string;
98
111
  docinfo: string;
99
- documentType: DocumentKind;
100
- divisions: PlusDivisionRecord[];
101
- assets: PlusAssetRecord[];
112
+ document_type: DocumentKind;
113
+ divisions_attributes: PlusDivisionAttributes[];
114
+ assets_attributes: PlusAssetAttributes[];
102
115
  }
103
116
  export interface ImportedProjectSuccess extends ConversionContext {
104
117
  pretextSource: string;
@@ -108,6 +121,14 @@ export interface ImportedProjectSuccess extends ConversionContext {
108
121
  documentKind: DocumentKind;
109
122
  /** Intermediate model of the imported project (SPEC §4.1). */
110
123
  project: ImportedProject;
124
+ /**
125
+ * Native-format (LaTeX/Markdown) projection of the same import, split into
126
+ * divisions joined by `\plus{…}{…}` / `::…{ref="…"}` placeholders (SPEC
127
+ * §4.3). Present only when the source was LaTeX or Markdown; the pretext-plus
128
+ * host serializes this instead of `project` when the user keeps the native
129
+ * format. `undefined` for PreTeXt input.
130
+ */
131
+ nativeProject?: ImportedProject;
111
132
  files: Record<string, string>;
112
133
  assets: Record<string, Uint8Array>;
113
134
  outputFiles: Record<string, string>;
@@ -17,4 +17,4 @@ export interface ImportSourceFormProps {
17
17
  disabled?: boolean;
18
18
  onImport: (result: ConvertedPretextResult) => void;
19
19
  }
20
- export declare function ImportSourceForm({ initialSource, initialSourceFormat, labels, disabled, onImport, }: ImportSourceFormProps): import("react/jsx-runtime").JSX.Element;
20
+ export declare function ImportSourceForm({ initialSource, initialSourceFormat, labels, disabled, onImport, }: ImportSourceFormProps): import("react").JSX.Element;
@@ -18,4 +18,4 @@ export interface ImportUploadPanelProps {
18
18
  importOptions?: ImportProjectOptions;
19
19
  onImport: (result: ImportedProjectResult) => void;
20
20
  }
21
- export declare function ImportUploadPanel({ labels, disabled, defaultDocumentKind, importOptions, onImport, }: ImportUploadPanelProps): import("react/jsx-runtime").JSX.Element;
21
+ export declare function ImportUploadPanel({ labels, disabled, defaultDocumentKind, importOptions, onImport, }: ImportUploadPanelProps): import("react").JSX.Element;
@@ -1,8 +1,26 @@
1
1
  import { ImportProjectOptions } from '../lib/upload';
2
2
  import { ImportMode } from '../lib/import-mode';
3
3
  import { DocumentKind } from '../lib/layout/document-kind';
4
- import { ImportedProjectSuccess } from '../lib/types';
4
+ import { ImportedProjectResult, ImportedProjectSuccess } from '../lib/types';
5
5
  export type { ImportMode };
6
+ /**
7
+ * A pluggable conversion engine. The wizard owns the whole UI (upload, review,
8
+ * preview, confirm) and only delegates the source → result step to the selected
9
+ * engine, so hosts can inject their own converters (e.g. a VS Code-only pandoc
10
+ * engine that round-trips to the extension host) without touching this package.
11
+ */
12
+ export interface ImportEngine {
13
+ /** Stable identifier, used as the radio value. */
14
+ id: string;
15
+ /** Short name shown in the engine selector. */
16
+ label: string;
17
+ /** Optional one-line explanation shown under the label. */
18
+ description?: string;
19
+ /** Extensions this engine accepts (with leading dot). Defaults to the built-in set. */
20
+ acceptExtensions?: string[];
21
+ /** Convert an uploaded file into an import result. */
22
+ convertFile: (file: File, options: ImportProjectOptions) => Promise<ImportedProjectResult>;
23
+ }
6
24
  export interface ImportWizardProps {
7
25
  /** Called when the user confirms the import. */
8
26
  onConfirm: (result: ImportedProjectSuccess, mode: ImportMode) => void;
@@ -11,5 +29,10 @@ export interface ImportWizardProps {
11
29
  /** Pass fixed options to skip the document-kind / split-sections controls. */
12
30
  importOptions?: ImportProjectOptions;
13
31
  defaultDocumentKind?: DocumentKind | "auto";
32
+ /**
33
+ * Converters offered to the user. When more than one is supplied, an engine
34
+ * selector is shown on the upload step. Defaults to a single built-in engine.
35
+ */
36
+ engines?: ImportEngine[];
14
37
  }
15
- export declare function ImportWizard({ onConfirm, onCancel, importOptions, defaultDocumentKind, }: ImportWizardProps): import("react/jsx-runtime").JSX.Element;
38
+ export declare function ImportWizard({ onConfirm, onCancel, importOptions, defaultDocumentKind, engines, }: ImportWizardProps): import("react").JSX.Element;
@@ -1,5 +1,5 @@
1
1
  export { ImportWizard } from './import-wizard';
2
- export type { ImportWizardProps, ImportMode } from './import-wizard';
2
+ export type { ImportWizardProps, ImportMode, ImportEngine, } from './import-wizard';
3
3
  export { ImportSourceForm } from './import-source-form';
4
4
  export { ImportUploadPanel } from './import-upload-panel';
5
5
  export type { ImportSourceFormProps, ImportSourceFormLabels, } from './import-source-form';