@larkup/tool-doc-editor 0.2.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/LICENSE +176 -0
- package/dist/editor.d.ts +22 -0
- package/dist/editor.js +696 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +21 -0
- package/dist/parsers.d.ts +15 -0
- package/dist/parsers.js +384 -0
- package/dist/sandbox-scripts.d.ts +19 -0
- package/dist/sandbox-scripts.js +359 -0
- package/dist/types.d.ts +158 -0
- package/dist/types.js +4 -0
- package/package.json +68 -0
- package/src/editor.ts +840 -0
- package/src/index.ts +53 -0
- package/src/parsers.ts +439 -0
- package/src/sandbox-scripts.ts +380 -0
- package/src/types.ts +170 -0
- package/tool.manifest.json +22 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @larkup/tool-doc-editor
|
|
3
|
+
*
|
|
4
|
+
* Document Editor / Form Filling tool for the Larkup platform.
|
|
5
|
+
* Enables AI-powered form filling and document editing in a Canvas-style UI.
|
|
6
|
+
*
|
|
7
|
+
* Supports: PDF, DOCX, PPTX, TXT
|
|
8
|
+
*
|
|
9
|
+
* Uses:
|
|
10
|
+
* - pdf-lib (MIT) for native PDF form field operations
|
|
11
|
+
* - mammoth (MIT) for DOCX reading
|
|
12
|
+
* - @larkup/sandbox for heavy editing (python-docx, python-pptx, pypdf)
|
|
13
|
+
*/
|
|
14
|
+
export declare const TOOL_META: {
|
|
15
|
+
readonly id: "doc-editor";
|
|
16
|
+
readonly name: "Document Editor";
|
|
17
|
+
readonly version: "0.1.0";
|
|
18
|
+
};
|
|
19
|
+
export { createSession, getSession, getAllSessions, deleteSession, restoreSession, applyFieldEdits, applyContentEdits, applySignature, exportDocument, } from './editor.js';
|
|
20
|
+
export { parseDocument, parsePDF, parseDOCX, parsePPTX, parseTXT, detectDocumentType, enrichPDFWithText, } from './parsers.js';
|
|
21
|
+
export type { DocumentType, DocumentField, DocumentPage, ParsedDocument, FieldEdit, ContentEdit, EditorSession, EditResult, } from './types.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @larkup/tool-doc-editor
|
|
3
|
+
*
|
|
4
|
+
* Document Editor / Form Filling tool for the Larkup platform.
|
|
5
|
+
* Enables AI-powered form filling and document editing in a Canvas-style UI.
|
|
6
|
+
*
|
|
7
|
+
* Supports: PDF, DOCX, PPTX, TXT
|
|
8
|
+
*
|
|
9
|
+
* Uses:
|
|
10
|
+
* - pdf-lib (MIT) for native PDF form field operations
|
|
11
|
+
* - mammoth (MIT) for DOCX reading
|
|
12
|
+
* - @larkup/sandbox for heavy editing (python-docx, python-pptx, pypdf)
|
|
13
|
+
*/
|
|
14
|
+
export const TOOL_META = {
|
|
15
|
+
id: 'doc-editor',
|
|
16
|
+
name: 'Document Editor',
|
|
17
|
+
version: '0.1.0',
|
|
18
|
+
};
|
|
19
|
+
// Public API
|
|
20
|
+
export { createSession, getSession, getAllSessions, deleteSession, restoreSession, applyFieldEdits, applyContentEdits, applySignature, exportDocument, } from './editor.js';
|
|
21
|
+
export { parseDocument, parsePDF, parseDOCX, parsePPTX, parseTXT, detectDocumentType, enrichPDFWithText, } from './parsers.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Document parsers for PDF, DOCX, PPTX, and TXT files.
|
|
3
|
+
*/
|
|
4
|
+
import type { DocumentType, ParsedDocument } from './types.js';
|
|
5
|
+
export declare function detectDocumentType(fileName: string, mimeType?: string): DocumentType | null;
|
|
6
|
+
export declare function parsePDF(buffer: Buffer, fileName: string): Promise<ParsedDocument>;
|
|
7
|
+
/**
|
|
8
|
+
* Extract raw text from PDF using pdf-parse (already available in web app).
|
|
9
|
+
* This is called separately since pdf-parse is in the web app, not this package.
|
|
10
|
+
*/
|
|
11
|
+
export declare function enrichPDFWithText(parsed: ParsedDocument, extractedText: string): ParsedDocument;
|
|
12
|
+
export declare function parseDOCX(buffer: Buffer, fileName: string): Promise<ParsedDocument>;
|
|
13
|
+
export declare function parsePPTX(buffer: Buffer, fileName: string): Promise<ParsedDocument>;
|
|
14
|
+
export declare function parseTXT(buffer: Buffer, fileName: string): Promise<ParsedDocument>;
|
|
15
|
+
export declare function parseDocument(buffer: Buffer, fileName: string, mimeType?: string): Promise<ParsedDocument>;
|
package/dist/parsers.js
ADDED
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Document parsers for PDF, DOCX, PPTX, and TXT files.
|
|
3
|
+
*/
|
|
4
|
+
import { PDFDocument, PDFTextField, PDFCheckBox, PDFDropdown, PDFRadioGroup } from 'pdf-lib';
|
|
5
|
+
import mammoth from 'mammoth';
|
|
6
|
+
import { randomUUID } from 'node:crypto';
|
|
7
|
+
/* ------------------------------------------------------------------ */
|
|
8
|
+
/* Detect document type from MIME / extension */
|
|
9
|
+
/* ------------------------------------------------------------------ */
|
|
10
|
+
const MIME_MAP = {
|
|
11
|
+
'application/pdf': 'pdf',
|
|
12
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx',
|
|
13
|
+
'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'pptx',
|
|
14
|
+
'text/plain': 'txt',
|
|
15
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx',
|
|
16
|
+
};
|
|
17
|
+
const EXT_MAP = {
|
|
18
|
+
'.pdf': 'pdf',
|
|
19
|
+
'.docx': 'docx',
|
|
20
|
+
'.pptx': 'pptx',
|
|
21
|
+
'.txt': 'txt',
|
|
22
|
+
'.xlsx': 'xlsx',
|
|
23
|
+
};
|
|
24
|
+
export function detectDocumentType(fileName, mimeType) {
|
|
25
|
+
if (mimeType && MIME_MAP[mimeType])
|
|
26
|
+
return MIME_MAP[mimeType];
|
|
27
|
+
const ext = fileName.substring(fileName.lastIndexOf('.')).toLowerCase();
|
|
28
|
+
return EXT_MAP[ext] ?? null;
|
|
29
|
+
}
|
|
30
|
+
/* ------------------------------------------------------------------ */
|
|
31
|
+
/* PDF Parser — using pdf-lib (MIT) */
|
|
32
|
+
/* ------------------------------------------------------------------ */
|
|
33
|
+
export async function parsePDF(buffer, fileName) {
|
|
34
|
+
const sessionId = randomUUID();
|
|
35
|
+
const pdfDoc = await PDFDocument.load(buffer, { ignoreEncryption: true });
|
|
36
|
+
const pageCount = pdfDoc.getPageCount();
|
|
37
|
+
const fields = [];
|
|
38
|
+
const pages = [];
|
|
39
|
+
// Extract form fields (AcroForm)
|
|
40
|
+
const form = pdfDoc.getForm();
|
|
41
|
+
const formFields = form.getFields();
|
|
42
|
+
for (const field of formFields) {
|
|
43
|
+
const name = field.getName();
|
|
44
|
+
const widgets = field.acroField.getWidgets();
|
|
45
|
+
let pageIndex = 0;
|
|
46
|
+
let bbox;
|
|
47
|
+
// Try to determine which page the field is on
|
|
48
|
+
if (widgets.length > 0) {
|
|
49
|
+
const widget = widgets[0];
|
|
50
|
+
const rect = widget.getRectangle();
|
|
51
|
+
for (let i = 0; i < pageCount; i++) {
|
|
52
|
+
const page = pdfDoc.getPage(i);
|
|
53
|
+
const annots = page.node.lookupMaybe(page.node.get(page.node.normalizedEntries().Annots),
|
|
54
|
+
// @ts-expect-error — accessing internal for page detection
|
|
55
|
+
page.node.context.lookupMaybe);
|
|
56
|
+
// Simplified page detection — place on page 0 if uncertain
|
|
57
|
+
pageIndex = 0;
|
|
58
|
+
}
|
|
59
|
+
if (rect) {
|
|
60
|
+
const page = pdfDoc.getPage(pageIndex);
|
|
61
|
+
const { width: pw, height: ph } = page.getSize();
|
|
62
|
+
bbox = {
|
|
63
|
+
x: (rect.x / pw) * 100,
|
|
64
|
+
y: (1 - (rect.y + rect.height) / ph) * 100,
|
|
65
|
+
width: (rect.width / pw) * 100,
|
|
66
|
+
height: (rect.height / ph) * 100,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
let fieldType = 'text';
|
|
71
|
+
let value = '';
|
|
72
|
+
let options;
|
|
73
|
+
if (field instanceof PDFTextField) {
|
|
74
|
+
fieldType = field.isMultiline() ? 'textarea' : 'text';
|
|
75
|
+
value = field.getText() ?? '';
|
|
76
|
+
}
|
|
77
|
+
else if (field instanceof PDFCheckBox) {
|
|
78
|
+
fieldType = 'checkbox';
|
|
79
|
+
value = field.isChecked() ? 'true' : 'false';
|
|
80
|
+
}
|
|
81
|
+
else if (field instanceof PDFDropdown) {
|
|
82
|
+
fieldType = 'select';
|
|
83
|
+
const selected = field.getSelected();
|
|
84
|
+
value = selected.length > 0 ? selected[0] : '';
|
|
85
|
+
options = field.getOptions();
|
|
86
|
+
}
|
|
87
|
+
else if (field instanceof PDFRadioGroup) {
|
|
88
|
+
fieldType = 'radio';
|
|
89
|
+
value = field.getSelected() ?? '';
|
|
90
|
+
options = field.getOptions();
|
|
91
|
+
}
|
|
92
|
+
fields.push({
|
|
93
|
+
id: `pdf_field_${fields.length}`,
|
|
94
|
+
name,
|
|
95
|
+
type: fieldType,
|
|
96
|
+
value,
|
|
97
|
+
pageIndex,
|
|
98
|
+
bbox,
|
|
99
|
+
options,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
// Extract text per page (basic extraction from PDF operators)
|
|
103
|
+
// pdf-lib doesn't have built-in text extraction, so we use the raw text
|
|
104
|
+
// For rich text extraction, the sandbox pypdf is used
|
|
105
|
+
for (let i = 0; i < pageCount; i++) {
|
|
106
|
+
const page = pdfDoc.getPage(i);
|
|
107
|
+
const { width, height } = page.getSize();
|
|
108
|
+
const pageFields = fields.filter((f) => f.pageIndex === i);
|
|
109
|
+
pages.push({
|
|
110
|
+
index: i,
|
|
111
|
+
text: '', // Will be populated by sandbox if needed
|
|
112
|
+
fields: pageFields,
|
|
113
|
+
dimensions: { width, height },
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
// Get metadata
|
|
117
|
+
const metadata = {};
|
|
118
|
+
const title = pdfDoc.getTitle();
|
|
119
|
+
const author = pdfDoc.getAuthor();
|
|
120
|
+
const subject = pdfDoc.getSubject();
|
|
121
|
+
const creator = pdfDoc.getCreator();
|
|
122
|
+
if (title)
|
|
123
|
+
metadata['title'] = title;
|
|
124
|
+
if (author)
|
|
125
|
+
metadata['author'] = author;
|
|
126
|
+
if (subject)
|
|
127
|
+
metadata['subject'] = subject;
|
|
128
|
+
if (creator)
|
|
129
|
+
metadata['creator'] = creator;
|
|
130
|
+
return {
|
|
131
|
+
sessionId,
|
|
132
|
+
fileName,
|
|
133
|
+
type: 'pdf',
|
|
134
|
+
mimeType: 'application/pdf',
|
|
135
|
+
fileSize: buffer.length,
|
|
136
|
+
pages,
|
|
137
|
+
fields,
|
|
138
|
+
rawText: '', // Populated separately via pdf-parse or sandbox
|
|
139
|
+
metadata,
|
|
140
|
+
totalPages: pageCount,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Extract raw text from PDF using pdf-parse (already available in web app).
|
|
145
|
+
* This is called separately since pdf-parse is in the web app, not this package.
|
|
146
|
+
*/
|
|
147
|
+
export function enrichPDFWithText(parsed, extractedText) {
|
|
148
|
+
// Split text by page breaks (pdf-parse uses \n\n\n between pages typically)
|
|
149
|
+
const pageTexts = extractedText.split(/\n{3,}/);
|
|
150
|
+
const updatedPages = parsed.pages.map((page, i) => ({
|
|
151
|
+
...page,
|
|
152
|
+
text: pageTexts[i]?.trim() ?? '',
|
|
153
|
+
}));
|
|
154
|
+
return {
|
|
155
|
+
...parsed,
|
|
156
|
+
pages: updatedPages,
|
|
157
|
+
rawText: extractedText,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/* ------------------------------------------------------------------ */
|
|
161
|
+
/* DOCX Parser — using mammoth (MIT) */
|
|
162
|
+
/* ------------------------------------------------------------------ */
|
|
163
|
+
export async function parseDOCX(buffer, fileName) {
|
|
164
|
+
const sessionId = randomUUID();
|
|
165
|
+
// Extract HTML (for preview rendering)
|
|
166
|
+
const htmlResult = await mammoth.convertToHtml({ buffer });
|
|
167
|
+
const html = htmlResult.value;
|
|
168
|
+
// Extract raw text
|
|
169
|
+
const textResult = await mammoth.extractRawText({ buffer });
|
|
170
|
+
const rawText = textResult.value;
|
|
171
|
+
// Detect form-like fields from the text (common patterns)
|
|
172
|
+
const fields = detectFormFieldsFromText(rawText, 0);
|
|
173
|
+
// DOCX is treated as a single "page" for simplicity
|
|
174
|
+
const pages = [
|
|
175
|
+
{
|
|
176
|
+
index: 0,
|
|
177
|
+
text: rawText,
|
|
178
|
+
html,
|
|
179
|
+
fields,
|
|
180
|
+
},
|
|
181
|
+
];
|
|
182
|
+
return {
|
|
183
|
+
sessionId,
|
|
184
|
+
fileName,
|
|
185
|
+
type: 'docx',
|
|
186
|
+
mimeType: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
187
|
+
fileSize: buffer.length,
|
|
188
|
+
pages,
|
|
189
|
+
fields,
|
|
190
|
+
rawText,
|
|
191
|
+
metadata: {},
|
|
192
|
+
totalPages: 1,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
/* ------------------------------------------------------------------ */
|
|
196
|
+
/* PPTX Parser — uses sandbox python-pptx for real parsing */
|
|
197
|
+
/* Fallback: JSZip to extract basic slide text */
|
|
198
|
+
/* ------------------------------------------------------------------ */
|
|
199
|
+
export async function parsePPTX(buffer, fileName) {
|
|
200
|
+
const sessionId = randomUUID();
|
|
201
|
+
// Basic extraction using JSZip (already in web app deps)
|
|
202
|
+
// For richer parsing, the sandbox python-pptx script is used
|
|
203
|
+
try {
|
|
204
|
+
const JSZip = (await import('jszip')).default;
|
|
205
|
+
const zip = await JSZip.loadAsync(buffer);
|
|
206
|
+
const pages = [];
|
|
207
|
+
const slideFiles = Object.keys(zip.files)
|
|
208
|
+
.filter((f) => /^ppt\/slides\/slide\d+\.xml$/.test(f))
|
|
209
|
+
.sort();
|
|
210
|
+
for (let i = 0; i < slideFiles.length; i++) {
|
|
211
|
+
const slideXml = await zip.files[slideFiles[i]].async('text');
|
|
212
|
+
// Extract text from XML (simplified)
|
|
213
|
+
const text = slideXml
|
|
214
|
+
.replace(/<[^>]+>/g, ' ')
|
|
215
|
+
.replace(/\s+/g, ' ')
|
|
216
|
+
.trim();
|
|
217
|
+
pages.push({
|
|
218
|
+
index: i,
|
|
219
|
+
text,
|
|
220
|
+
fields: [],
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
const fields = pages.flatMap((p) => detectFormFieldsFromText(p.text, p.index));
|
|
224
|
+
return {
|
|
225
|
+
sessionId,
|
|
226
|
+
fileName,
|
|
227
|
+
type: 'pptx',
|
|
228
|
+
mimeType: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
229
|
+
fileSize: buffer.length,
|
|
230
|
+
pages,
|
|
231
|
+
fields,
|
|
232
|
+
rawText: pages.map((p) => p.text).join('\n\n'),
|
|
233
|
+
metadata: {},
|
|
234
|
+
totalPages: pages.length,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
return {
|
|
239
|
+
sessionId,
|
|
240
|
+
fileName,
|
|
241
|
+
type: 'pptx',
|
|
242
|
+
mimeType: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
243
|
+
fileSize: buffer.length,
|
|
244
|
+
pages: [],
|
|
245
|
+
fields: [],
|
|
246
|
+
rawText: '',
|
|
247
|
+
metadata: {},
|
|
248
|
+
totalPages: 0,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/* ------------------------------------------------------------------ */
|
|
253
|
+
/* TXT Parser — native passthrough */
|
|
254
|
+
/* ------------------------------------------------------------------ */
|
|
255
|
+
export async function parseTXT(buffer, fileName) {
|
|
256
|
+
const sessionId = randomUUID();
|
|
257
|
+
const rawText = buffer.toString('utf-8');
|
|
258
|
+
const fields = detectFormFieldsFromText(rawText, 0);
|
|
259
|
+
return {
|
|
260
|
+
sessionId,
|
|
261
|
+
fileName,
|
|
262
|
+
type: 'txt',
|
|
263
|
+
mimeType: 'text/plain',
|
|
264
|
+
fileSize: buffer.length,
|
|
265
|
+
pages: [
|
|
266
|
+
{
|
|
267
|
+
index: 0,
|
|
268
|
+
text: rawText,
|
|
269
|
+
fields,
|
|
270
|
+
},
|
|
271
|
+
],
|
|
272
|
+
fields,
|
|
273
|
+
rawText,
|
|
274
|
+
metadata: {},
|
|
275
|
+
totalPages: 1,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
/* ------------------------------------------------------------------ */
|
|
279
|
+
/* Unified parser */
|
|
280
|
+
/* ------------------------------------------------------------------ */
|
|
281
|
+
export async function parseDocument(buffer, fileName, mimeType) {
|
|
282
|
+
const type = detectDocumentType(fileName, mimeType);
|
|
283
|
+
if (!type) {
|
|
284
|
+
throw new Error(`Unsupported file type: ${fileName}`);
|
|
285
|
+
}
|
|
286
|
+
switch (type) {
|
|
287
|
+
case 'pdf':
|
|
288
|
+
return parsePDF(buffer, fileName);
|
|
289
|
+
case 'docx':
|
|
290
|
+
return parseDOCX(buffer, fileName);
|
|
291
|
+
case 'pptx':
|
|
292
|
+
return parsePPTX(buffer, fileName);
|
|
293
|
+
case 'txt':
|
|
294
|
+
return parseTXT(buffer, fileName);
|
|
295
|
+
default:
|
|
296
|
+
// Return a basic parsed document for unsupported types so they can be "viewed" without crashing
|
|
297
|
+
return {
|
|
298
|
+
sessionId: randomUUID(),
|
|
299
|
+
fileName,
|
|
300
|
+
type: type || 'unknown',
|
|
301
|
+
mimeType: mimeType || 'application/octet-stream',
|
|
302
|
+
fileSize: buffer.length,
|
|
303
|
+
pages: [],
|
|
304
|
+
fields: [],
|
|
305
|
+
rawText: `Preview not available for this file type.\nFile: ${fileName}\nSize: ${(buffer.length / 1024).toFixed(1)} KB`,
|
|
306
|
+
metadata: {},
|
|
307
|
+
totalPages: 0,
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
/* ------------------------------------------------------------------ */
|
|
312
|
+
/* Heuristic form field detection from unstructured text */
|
|
313
|
+
/* ------------------------------------------------------------------ */
|
|
314
|
+
/**
|
|
315
|
+
* Detects form-like patterns in text:
|
|
316
|
+
* - "Name: ___________"
|
|
317
|
+
* - "Date: "
|
|
318
|
+
* - "Email: "
|
|
319
|
+
* - "[ ]" checkboxes
|
|
320
|
+
* - Lines ending with ":" followed by blank/underscores
|
|
321
|
+
*/
|
|
322
|
+
function detectFormFieldsFromText(text, pageIndex) {
|
|
323
|
+
const fields = [];
|
|
324
|
+
const lines = text.split('\n');
|
|
325
|
+
const labelPatterns = [
|
|
326
|
+
// "Label: ___" or "Label: " (blank after colon)
|
|
327
|
+
/^(.+?):\s*[_\s]{3,}$/,
|
|
328
|
+
// "Label: " at end of line
|
|
329
|
+
/^(.+?):\s*$/,
|
|
330
|
+
// "[ ] Label" — checkbox
|
|
331
|
+
/^\[[\sx]\]\s+(.+)$/i,
|
|
332
|
+
];
|
|
333
|
+
for (const line of lines) {
|
|
334
|
+
const trimmed = line.trim();
|
|
335
|
+
if (!trimmed || trimmed.length < 3)
|
|
336
|
+
continue;
|
|
337
|
+
// Check for checkbox pattern
|
|
338
|
+
const checkMatch = trimmed.match(/^\[([\sx])\]\s+(.+)$/i);
|
|
339
|
+
if (checkMatch) {
|
|
340
|
+
fields.push({
|
|
341
|
+
id: `detected_${fields.length}`,
|
|
342
|
+
name: checkMatch[2].trim(),
|
|
343
|
+
type: 'checkbox',
|
|
344
|
+
value: checkMatch[1].toLowerCase() === 'x' ? 'true' : 'false',
|
|
345
|
+
pageIndex,
|
|
346
|
+
});
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
// Check for label: value patterns
|
|
350
|
+
const labelMatch = trimmed.match(/^(.+?):\s*([_\s]*)$/);
|
|
351
|
+
if (labelMatch && labelMatch[1].length < 60) {
|
|
352
|
+
const label = labelMatch[1].trim();
|
|
353
|
+
// Detect field type from label name
|
|
354
|
+
const lowerLabel = label.toLowerCase();
|
|
355
|
+
let fieldType = 'text';
|
|
356
|
+
if (lowerLabel.includes('date') || lowerLabel.includes('dob')) {
|
|
357
|
+
fieldType = 'date';
|
|
358
|
+
}
|
|
359
|
+
else if (lowerLabel.includes('description') ||
|
|
360
|
+
lowerLabel.includes('comments') ||
|
|
361
|
+
lowerLabel.includes('notes') ||
|
|
362
|
+
lowerLabel.includes('address')) {
|
|
363
|
+
fieldType = 'textarea';
|
|
364
|
+
}
|
|
365
|
+
else if (lowerLabel.includes('email') ||
|
|
366
|
+
lowerLabel.includes('phone') ||
|
|
367
|
+
lowerLabel.includes('number') ||
|
|
368
|
+
lowerLabel.includes('amount') ||
|
|
369
|
+
lowerLabel.includes('zip') ||
|
|
370
|
+
lowerLabel.includes('code')) {
|
|
371
|
+
fieldType = lowerLabel.includes('email') ? 'text' : 'text';
|
|
372
|
+
}
|
|
373
|
+
fields.push({
|
|
374
|
+
id: `detected_${fields.length}`,
|
|
375
|
+
name: label,
|
|
376
|
+
type: fieldType,
|
|
377
|
+
value: '',
|
|
378
|
+
pageIndex,
|
|
379
|
+
placeholder: `Enter ${label.toLowerCase()}`,
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return fields;
|
|
384
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python code templates for sandbox-based document editing.
|
|
3
|
+
*
|
|
4
|
+
* These scripts run inside the Docker sandbox with:
|
|
5
|
+
* - pypdf: PDF form field filling
|
|
6
|
+
* - python-docx: Word document editing
|
|
7
|
+
* - python-pptx: PowerPoint editing
|
|
8
|
+
* - Pillow: Image processing
|
|
9
|
+
*
|
|
10
|
+
* All scripts expect input files in /sandbox/input/ and output to /sandbox/output/
|
|
11
|
+
*/
|
|
12
|
+
import type { FieldEdit, ContentEdit } from './types.js';
|
|
13
|
+
export declare function generateFillPDFScript(edits: FieldEdit[]): string;
|
|
14
|
+
export declare function generateExtractPDFTextScript(): string;
|
|
15
|
+
export declare function generateEditDOCXScript(edits: ContentEdit[]): string;
|
|
16
|
+
export declare function generateFillDOCXScript(edits: FieldEdit[]): string;
|
|
17
|
+
export declare function generateExtractPPTXScript(): string;
|
|
18
|
+
export declare function generateEditPPTXScript(edits: ContentEdit[]): string;
|
|
19
|
+
export declare function generateFillPPTXScript(edits: FieldEdit[]): string;
|