@tfw.in/structura-lib 0.2.0 → 0.2.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/README.md +72 -323
- package/dist/cjs/EditableContent.js +46 -18
- package/dist/cjs/HtmlViewer.js +238 -85
- package/dist/cjs/MathRenderer.js +88 -0
- package/dist/cjs/PdfDocumentViewer.js +1 -1
- package/dist/cjs/SemanticTagParser.js +189 -0
- package/dist/cjs/SemanticTagRenderer.js +135 -0
- package/dist/cjs/Structura.js +49 -76
- package/dist/cjs/Table.js +75 -8
- package/dist/cjs/TableCell.js +34 -10
- package/dist/cjs/index.js +12 -0
- package/dist/cjs/node_modules/react-icons/fa/index.esm.js +6 -0
- package/dist/cjs/styles.css +2 -4
- package/dist/cjs/styles.css.map +1 -1
- package/dist/esm/EditableContent.js +51 -19
- package/dist/esm/HtmlViewer.js +287 -103
- package/dist/esm/MathRenderer.js +85 -0
- package/dist/esm/PdfDocumentViewer.js +1 -1
- package/dist/esm/SemanticTagParser.js +187 -0
- package/dist/esm/SemanticTagRenderer.js +140 -0
- package/dist/esm/Structura.js +57 -80
- package/dist/esm/Table.js +85 -8
- package/dist/esm/TableCell.js +34 -6
- package/dist/esm/index.js +3 -0
- package/dist/esm/node_modules/react-icons/fa/index.esm.js +5 -1
- package/dist/esm/styles.css +2 -4
- package/dist/esm/styles.css.map +1 -1
- package/dist/esm/types/DocumentOutline.d.ts +7 -0
- package/dist/esm/types/EditableContent.d.ts +8 -1
- package/dist/esm/types/HtmlViewer.d.ts +9 -2
- package/dist/esm/types/MathRenderer.d.ts +25 -0
- package/dist/esm/types/SemanticTagParser.d.ts +33 -0
- package/dist/esm/types/SemanticTagRenderer.d.ts +17 -0
- package/dist/esm/types/Structura.d.ts +13 -8
- package/dist/esm/types/Table.d.ts +4 -1
- package/dist/esm/types/TableCell.d.ts +7 -1
- package/dist/esm/types/helpers/index.d.ts +0 -1
- package/dist/esm/types/index.d.ts +3 -0
- package/dist/esm/types/test-app/src/App.d.ts +1 -2
- package/dist/index.d.ts +90 -10
- package/package.json +9 -16
- package/PRODUCTION_ARCHITECTURE.md +0 -511
- package/SAVE_FUNCTIONALITY_COMPLETE.md +0 -448
- package/dist/cjs/ui/badge.js +0 -34
- package/dist/esm/types/helpers/jsonToHtml.d.ts +0 -40
- package/dist/esm/ui/badge.js +0 -31
- package/server/README.md +0 -203
- package/server/db.js +0 -142
- package/server/server.js +0 -165
package/dist/cjs/HtmlViewer.js
CHANGED
|
@@ -9,6 +9,7 @@ var index_esm = require('./node_modules/react-icons/fa/index.esm.js');
|
|
|
9
9
|
var Table = require('./Table.js');
|
|
10
10
|
var EditableContent = require('./EditableContent.js');
|
|
11
11
|
var accuracyMetrics = require('./accuracyMetrics.js');
|
|
12
|
+
var SemanticTagParser = require('./SemanticTagParser.js');
|
|
12
13
|
|
|
13
14
|
// Utility function to clean HTML content
|
|
14
15
|
const cleanHtml = html => {
|
|
@@ -140,24 +141,129 @@ function AnalyticsModal({
|
|
|
140
141
|
})
|
|
141
142
|
});
|
|
142
143
|
}
|
|
144
|
+
function TagModal({
|
|
145
|
+
isOpen,
|
|
146
|
+
onClose,
|
|
147
|
+
tag
|
|
148
|
+
}) {
|
|
149
|
+
if (!isOpen || !tag) return null;
|
|
150
|
+
const typeInfo = SemanticTagParser.getTagTypeInfo(tag.type);
|
|
151
|
+
return jsxRuntime.jsx("div", {
|
|
152
|
+
className: "fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50",
|
|
153
|
+
children: jsxRuntime.jsxs("div", {
|
|
154
|
+
className: "bg-white rounded-lg p-6 max-w-md w-full shadow-xl",
|
|
155
|
+
children: [jsxRuntime.jsxs("div", {
|
|
156
|
+
className: "flex justify-between items-center mb-4",
|
|
157
|
+
children: [jsxRuntime.jsxs("div", {
|
|
158
|
+
className: "flex items-center gap-2",
|
|
159
|
+
children: [jsxRuntime.jsx("span", {
|
|
160
|
+
className: "text-2xl",
|
|
161
|
+
children: typeInfo.icon
|
|
162
|
+
}), jsxRuntime.jsxs("h3", {
|
|
163
|
+
className: "text-lg font-semibold",
|
|
164
|
+
children: [typeInfo.label, " Tag"]
|
|
165
|
+
})]
|
|
166
|
+
}), jsxRuntime.jsx("button", {
|
|
167
|
+
onClick: onClose,
|
|
168
|
+
className: "text-gray-500 hover:text-gray-700",
|
|
169
|
+
children: "\u2715"
|
|
170
|
+
})]
|
|
171
|
+
}), jsxRuntime.jsx("div", {
|
|
172
|
+
className: `p-4 rounded-lg border ${typeInfo.bgColor} mb-4`,
|
|
173
|
+
children: jsxRuntime.jsxs("div", {
|
|
174
|
+
className: `text-2xl font-bold ${typeInfo.color}`,
|
|
175
|
+
children: [tag.value, tag.attributes.uom && jsxRuntime.jsx("span", {
|
|
176
|
+
className: "text-lg ml-2 opacity-75",
|
|
177
|
+
children: tag.attributes.uom
|
|
178
|
+
})]
|
|
179
|
+
})
|
|
180
|
+
}), jsxRuntime.jsxs("div", {
|
|
181
|
+
className: "space-y-3",
|
|
182
|
+
children: [jsxRuntime.jsxs("div", {
|
|
183
|
+
className: "flex justify-between py-2 border-b",
|
|
184
|
+
children: [jsxRuntime.jsx("span", {
|
|
185
|
+
className: "text-gray-600",
|
|
186
|
+
children: "Type"
|
|
187
|
+
}), jsxRuntime.jsx("span", {
|
|
188
|
+
className: `font-medium ${typeInfo.color}`,
|
|
189
|
+
children: tag.type
|
|
190
|
+
})]
|
|
191
|
+
}), tag.attributes.uom && jsxRuntime.jsxs("div", {
|
|
192
|
+
className: "flex justify-between py-2 border-b",
|
|
193
|
+
children: [jsxRuntime.jsx("span", {
|
|
194
|
+
className: "text-gray-600",
|
|
195
|
+
children: "Unit of Measure"
|
|
196
|
+
}), jsxRuntime.jsx("span", {
|
|
197
|
+
className: "font-medium",
|
|
198
|
+
children: tag.attributes.uom
|
|
199
|
+
})]
|
|
200
|
+
}), tag.attributes.id && jsxRuntime.jsxs("div", {
|
|
201
|
+
className: "py-2 border-b",
|
|
202
|
+
children: [jsxRuntime.jsx("span", {
|
|
203
|
+
className: "text-gray-600 block mb-1",
|
|
204
|
+
children: "Tag ID"
|
|
205
|
+
}), jsxRuntime.jsx("code", {
|
|
206
|
+
className: "text-xs bg-gray-100 p-2 rounded block break-all",
|
|
207
|
+
children: tag.attributes.id
|
|
208
|
+
})]
|
|
209
|
+
}), tag.attributes.format && jsxRuntime.jsxs("div", {
|
|
210
|
+
className: "flex justify-between py-2 border-b",
|
|
211
|
+
children: [jsxRuntime.jsx("span", {
|
|
212
|
+
className: "text-gray-600",
|
|
213
|
+
children: "Format"
|
|
214
|
+
}), jsxRuntime.jsx("span", {
|
|
215
|
+
className: "font-mono text-sm",
|
|
216
|
+
children: tag.attributes.format
|
|
217
|
+
})]
|
|
218
|
+
}), tag.attributes.filter && jsxRuntime.jsxs("div", {
|
|
219
|
+
className: "flex justify-between py-2 border-b",
|
|
220
|
+
children: [jsxRuntime.jsx("span", {
|
|
221
|
+
className: "text-gray-600",
|
|
222
|
+
children: "File Filter"
|
|
223
|
+
}), jsxRuntime.jsx("span", {
|
|
224
|
+
className: "font-mono text-sm",
|
|
225
|
+
children: tag.attributes.filter
|
|
226
|
+
})]
|
|
227
|
+
}), jsxRuntime.jsxs("div", {
|
|
228
|
+
className: "py-2",
|
|
229
|
+
children: [jsxRuntime.jsx("span", {
|
|
230
|
+
className: "text-gray-600 block mb-1",
|
|
231
|
+
children: "Raw Tag"
|
|
232
|
+
}), jsxRuntime.jsx("code", {
|
|
233
|
+
className: "text-xs bg-gray-100 p-2 rounded block break-all text-gray-700",
|
|
234
|
+
children: tag.rawMatch
|
|
235
|
+
})]
|
|
236
|
+
})]
|
|
237
|
+
})]
|
|
238
|
+
})
|
|
239
|
+
});
|
|
240
|
+
}
|
|
143
241
|
// Update icon usage in the JSX
|
|
144
242
|
const VscJsonIcon = vsc.VscJson;
|
|
145
243
|
const FaFileDownloadIcon = index_esm.FaFileDownload;
|
|
146
244
|
const FaChartBarIcon = index_esm.FaChartBar;
|
|
245
|
+
const FaEditIcon = index_esm.FaEdit;
|
|
246
|
+
const FaTagsIcon = index_esm.FaTags;
|
|
147
247
|
function HtmlViewer({
|
|
148
248
|
//NOSONAR
|
|
149
249
|
jsonData,
|
|
150
250
|
selectedBboxId,
|
|
151
251
|
isLoading,
|
|
152
252
|
onNodeClick,
|
|
153
|
-
|
|
253
|
+
editMode: enableEditMode = true,
|
|
254
|
+
jsonMode: enableJsonMode = true,
|
|
255
|
+
mathRendering: enableMathRendering = true,
|
|
256
|
+
semanticTags: enableSemanticTags = true,
|
|
257
|
+
headerFooterBadges: enableHeaderFooterBadges = true,
|
|
258
|
+
defaultViewMode = 'read',
|
|
259
|
+
onContentChange: onContentChangeCallback,
|
|
260
|
+
onExport: onExportCallback
|
|
154
261
|
}) {
|
|
155
262
|
const [editedData, setEditedData] = React.useState(jsonData);
|
|
156
263
|
const [hasChanges, setHasChanges] = React.useState(false);
|
|
157
264
|
const [isModalOpen, setIsModalOpen] = React.useState(false);
|
|
158
265
|
const [modalData, setModalData] = React.useState(null);
|
|
159
|
-
const [
|
|
160
|
-
const [activeFormat, setActiveFormat] = React.useState("Show JSON");
|
|
266
|
+
const [viewMode, setViewMode] = React.useState(defaultViewMode);
|
|
161
267
|
const [accuracyMetrics$1, setAccuracyMetrics] = React.useState({
|
|
162
268
|
totalChanges: 0,
|
|
163
269
|
totalCharactersEdited: 0,
|
|
@@ -167,6 +273,9 @@ function HtmlViewer({
|
|
|
167
273
|
blockTypeStats: {}
|
|
168
274
|
});
|
|
169
275
|
const [isAnalyticsOpen, setIsAnalyticsOpen] = React.useState(false);
|
|
276
|
+
const [isTagModalOpen, setIsTagModalOpen] = React.useState(false);
|
|
277
|
+
const [selectedTag, setSelectedTag] = React.useState(null);
|
|
278
|
+
const [showSemanticTags, setShowSemanticTags] = React.useState(enableSemanticTags);
|
|
170
279
|
React.useEffect(() => {
|
|
171
280
|
// Reset state when jsonData changes
|
|
172
281
|
// console.log("HtmlViewer received new jsonData");
|
|
@@ -185,6 +294,10 @@ function HtmlViewer({
|
|
|
185
294
|
setModalData(node);
|
|
186
295
|
setIsModalOpen(true);
|
|
187
296
|
};
|
|
297
|
+
const handleSemanticTagClick = tag => {
|
|
298
|
+
setSelectedTag(tag);
|
|
299
|
+
setIsTagModalOpen(true);
|
|
300
|
+
};
|
|
188
301
|
const updateAccuracyMetrics = React.useCallback((nodeId, blockType, originalContent, newContent) => {
|
|
189
302
|
const differences = accuracyMetrics.calculateDifferences(originalContent, newContent);
|
|
190
303
|
const change = {
|
|
@@ -228,12 +341,13 @@ function HtmlViewer({
|
|
|
228
341
|
}, [jsonData]);
|
|
229
342
|
const handleContentChange = React.useCallback((nodeId, newContent) => {
|
|
230
343
|
setHasChanges(true);
|
|
344
|
+
let originalContent = "";
|
|
231
345
|
setEditedData(prevData => {
|
|
232
346
|
const newData = JSON.parse(JSON.stringify(prevData));
|
|
233
347
|
const updateNode = node => {
|
|
234
348
|
if (!node) return false;
|
|
235
349
|
if (node.id === nodeId) {
|
|
236
|
-
|
|
350
|
+
originalContent = node.html || "";
|
|
237
351
|
const blockType = node.block_type || "unknown";
|
|
238
352
|
// Update accuracy metrics before changing content
|
|
239
353
|
updateAccuracyMetrics(nodeId, blockType, originalContent, newContent);
|
|
@@ -250,9 +364,17 @@ function HtmlViewer({
|
|
|
250
364
|
updateNode(newData);
|
|
251
365
|
return newData;
|
|
252
366
|
});
|
|
253
|
-
|
|
367
|
+
// Call external callback if provided
|
|
368
|
+
if (onContentChangeCallback) {
|
|
369
|
+
onContentChangeCallback(nodeId, originalContent, newContent);
|
|
370
|
+
}
|
|
371
|
+
}, [updateAccuracyMetrics, onContentChangeCallback]);
|
|
254
372
|
const handleDownload = () => {
|
|
255
373
|
if (!editedData) return;
|
|
374
|
+
// Call external callback if provided
|
|
375
|
+
if (onExportCallback) {
|
|
376
|
+
onExportCallback(editedData);
|
|
377
|
+
}
|
|
256
378
|
const jsonString = JSON.stringify(editedData, null, 2);
|
|
257
379
|
const blob = new Blob([jsonString], {
|
|
258
380
|
type: "application/json"
|
|
@@ -268,10 +390,7 @@ function HtmlViewer({
|
|
|
268
390
|
};
|
|
269
391
|
// Function to get the appropriate HTML content based on settings
|
|
270
392
|
const getHtmlContent = node => {
|
|
271
|
-
|
|
272
|
-
// This preserves the node structure and click interactivity
|
|
273
|
-
// getBlockHtml/jsonToHtml should only be used for full page rendering/export
|
|
274
|
-
return cleanHtml(node.html || '');
|
|
393
|
+
return cleanHtml(node.html || "");
|
|
275
394
|
};
|
|
276
395
|
const mergedTablesMap = React.useMemo(() => {
|
|
277
396
|
const map = new Map();
|
|
@@ -413,10 +532,13 @@ function HtmlViewer({
|
|
|
413
532
|
const hasChildren = node.children && node.children.length > 0;
|
|
414
533
|
const isTable = ((_a = node.block_type) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === "table";
|
|
415
534
|
const isTableOfContents = node.block_type === "TableOfContents";
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
535
|
+
node.block_type === "Text";
|
|
536
|
+
node.block_type === "Handwriting";
|
|
537
|
+
node.block_type === "SectionHeader";
|
|
419
538
|
const isPage = node.block_type === "Page";
|
|
539
|
+
const isFigure = node.block_type === "Figure" || node.block_type === "Picture";
|
|
540
|
+
const isPageHeader = node.block_type === "PageHeader";
|
|
541
|
+
const isPageFooter = node.block_type === "PageFooter";
|
|
420
542
|
// Get the appropriate HTML content
|
|
421
543
|
const htmlContent = getHtmlContent(node);
|
|
422
544
|
const isHeading = htmlContent && (htmlContent.startsWith("<h1") || htmlContent.startsWith("<h2") || htmlContent.startsWith("<h3") || htmlContent.startsWith("<h4"));
|
|
@@ -437,46 +559,66 @@ function HtmlViewer({
|
|
|
437
559
|
onNodeClick(node.id);
|
|
438
560
|
}
|
|
439
561
|
};
|
|
440
|
-
return jsxRuntime.
|
|
562
|
+
return jsxRuntime.jsx("div", {
|
|
441
563
|
id: node.id,
|
|
442
|
-
className: `
|
|
564
|
+
className: `px-1 py-0.5 rounded transition-colors relative group max-w-full overflow-hidden ${isSelected ? "bg-blue-100 border-2 border-blue-500" : ""} ${onNodeClick && !isPage ? "cursor-pointer hover:bg-gray-50" : ""}`,
|
|
443
565
|
onClick: isPage ? undefined : handleContentClick,
|
|
444
|
-
children:
|
|
445
|
-
onClick: e => {
|
|
446
|
-
e.stopPropagation(); // Prevent triggering parent onClick
|
|
447
|
-
handleJsonClick(node);
|
|
448
|
-
},
|
|
449
|
-
className: `absolute right-2 top-2 z-10 ${showJsonIcons ? "opacity-0 group-hover:opacity-100 transition-opacity" : "hidden"} text-gray-500 hover:text-gray-700`,
|
|
450
|
-
title: "View JSON",
|
|
451
|
-
children: jsxRuntime.jsx(VscJsonIcon, {
|
|
452
|
-
size: 18
|
|
453
|
-
})
|
|
454
|
-
}), isTable || isTableOfContents ? jsxRuntime.jsx(Table.default, {
|
|
566
|
+
children: isTable || isTableOfContents ? jsxRuntime.jsx(Table.default, {
|
|
455
567
|
node: node,
|
|
456
568
|
selectedBboxId: selectedBboxId,
|
|
457
569
|
onJsonClick: handleJsonClick,
|
|
458
570
|
onContentChange: handleContentChange,
|
|
459
571
|
mergedTables: node.merged_table_id ? (mergedTablesMap.get(node.merged_table_id) || []).slice(1) : [],
|
|
460
572
|
hasLlmHtml: !!node.llm_table_html,
|
|
461
|
-
showJsonIcons:
|
|
462
|
-
onNodeClick: onNodeClick
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
573
|
+
showJsonIcons: false,
|
|
574
|
+
onNodeClick: onNodeClick,
|
|
575
|
+
isEditMode: viewMode === 'edit',
|
|
576
|
+
isJsonMode: viewMode === 'json',
|
|
577
|
+
enableMathRendering: enableMathRendering
|
|
578
|
+
}) : isFigure && node.images ? jsxRuntime.jsx("div", {
|
|
579
|
+
className: `my-2 ${viewMode === 'json' ? 'cursor-pointer hover:opacity-80' : ''}`,
|
|
580
|
+
onClick: viewMode === 'json' ? () => handleJsonClick(node) : undefined,
|
|
581
|
+
children: Object.entries(node.images).map(([key, imageData]) => jsxRuntime.jsx("img", {
|
|
582
|
+
src: typeof imageData === 'string' && imageData.startsWith('data:') ? imageData : `data:image/png;base64,${imageData}`,
|
|
583
|
+
alt: `Figure ${node.id}`,
|
|
584
|
+
className: "max-w-full h-auto rounded border border-gray-200"
|
|
585
|
+
}, key))
|
|
586
|
+
}) : (isPageHeader || isPageFooter) && enableHeaderFooterBadges ? jsxRuntime.jsxs("div", {
|
|
587
|
+
className: `my-1 px-3 py-1.5 rounded-md border text-xs flex items-center gap-2
|
|
588
|
+
${isPageHeader ? 'bg-amber-50 border-amber-200 text-amber-800' : 'bg-slate-50 border-slate-200 text-slate-600'}
|
|
589
|
+
${viewMode === 'json' ? 'cursor-pointer hover:opacity-80' : ''}`,
|
|
590
|
+
onClick: viewMode === 'json' ? () => handleJsonClick(node) : undefined,
|
|
591
|
+
children: [jsxRuntime.jsx("span", {
|
|
592
|
+
className: `font-semibold uppercase tracking-wide ${isPageHeader ? 'text-amber-600' : 'text-slate-500'}`,
|
|
593
|
+
children: isPageHeader ? '⬆ Header' : '⬇ Footer'
|
|
594
|
+
}), htmlContent && jsxRuntime.jsx("span", {
|
|
595
|
+
className: "text-gray-600",
|
|
472
596
|
dangerouslySetInnerHTML: {
|
|
473
597
|
__html: htmlContent
|
|
474
598
|
}
|
|
599
|
+
}), node.images && Object.entries(node.images).map(([key, imageData]) => jsxRuntime.jsx("img", {
|
|
600
|
+
src: typeof imageData === 'string' && imageData.startsWith('data:') ? imageData : `data:image/png;base64,${imageData}`,
|
|
601
|
+
alt: `${isPageHeader ? 'Header' : 'Footer'} image`,
|
|
602
|
+
className: "h-6 w-auto"
|
|
603
|
+
}, key))]
|
|
604
|
+
}) : jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
|
605
|
+
children: [jsxRuntime.jsx(EditableContent.default, {
|
|
606
|
+
id: node.id,
|
|
607
|
+
content: htmlContent,
|
|
608
|
+
onContentChange: handleContentChange,
|
|
609
|
+
isHeading: !!isHeading,
|
|
610
|
+
isEditMode: viewMode === 'edit',
|
|
611
|
+
isJsonMode: viewMode === 'json',
|
|
612
|
+
onJsonClick: () => handleJsonClick(node),
|
|
613
|
+
onNodeClick: onNodeClick && !isPage ? () => onNodeClick(node.id) : undefined,
|
|
614
|
+
enableSemanticTags: showSemanticTags,
|
|
615
|
+
onSemanticTagClick: handleSemanticTagClick,
|
|
616
|
+
enableMathRendering: enableMathRendering
|
|
475
617
|
}), hasChildren && jsxRuntime.jsx("div", {
|
|
476
|
-
className: "ml-
|
|
618
|
+
className: "ml-2 mt-1 border-l border-gray-200 pl-2 max-w-full overflow-hidden",
|
|
477
619
|
children: node.children.map(child => renderHtmlContent(child))
|
|
478
620
|
})]
|
|
479
|
-
})
|
|
621
|
+
})
|
|
480
622
|
}, node.id);
|
|
481
623
|
};
|
|
482
624
|
const getAllNodes = data => {
|
|
@@ -494,63 +636,70 @@ function HtmlViewer({
|
|
|
494
636
|
});
|
|
495
637
|
};
|
|
496
638
|
const allNodes = getAllNodes(editedData);
|
|
497
|
-
// Update the header section
|
|
639
|
+
// Update the header section with toggle buttons
|
|
498
640
|
const renderHeader = () => jsxRuntime.jsxs("div", {
|
|
499
|
-
className: "sticky top-0 z-20 bg-white border-b border-gray-200 p-
|
|
500
|
-
children: [jsxRuntime.
|
|
501
|
-
className: "flex gap-
|
|
502
|
-
children:
|
|
503
|
-
children: [onSave && jsxRuntime.jsxs("button", {
|
|
504
|
-
onClick: () => onSave(editedData),
|
|
505
|
-
className: "inline-flex items-center gap-2 px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2",
|
|
506
|
-
title: "Save Changes",
|
|
507
|
-
children: [jsxRuntime.jsx(FaFileDownloadIcon, {
|
|
508
|
-
size: 18
|
|
509
|
-
}), "Save"]
|
|
510
|
-
}), jsxRuntime.jsxs("button", {
|
|
511
|
-
onClick: handleDownload,
|
|
512
|
-
className: "inline-flex items-center gap-2 px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2",
|
|
513
|
-
title: "Download Updated JSON",
|
|
514
|
-
children: [jsxRuntime.jsx(FaFileDownloadIcon, {
|
|
515
|
-
size: 18
|
|
516
|
-
}), "Download"]
|
|
517
|
-
})]
|
|
518
|
-
})
|
|
519
|
-
}), jsxRuntime.jsxs("div", {
|
|
520
|
-
className: "flex gap-2 items-center",
|
|
521
|
-
children: [jsxRuntime.jsx("button", {
|
|
522
|
-
onClick: () => setIsAnalyticsOpen(true),
|
|
523
|
-
className: "inline-flex items-center gap-2 px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2",
|
|
524
|
-
title: "View Analytics",
|
|
525
|
-
children: jsxRuntime.jsx(FaChartBarIcon, {
|
|
526
|
-
size: 18
|
|
527
|
-
})
|
|
528
|
-
}), jsxRuntime.jsxs("div", {
|
|
641
|
+
className: "sticky top-0 z-20 bg-white border-b border-gray-200 p-3 flex justify-between items-center flex-shrink-0",
|
|
642
|
+
children: [jsxRuntime.jsxs("div", {
|
|
643
|
+
className: "flex gap-1",
|
|
644
|
+
children: [(enableJsonMode || enableEditMode) && jsxRuntime.jsxs("div", {
|
|
529
645
|
className: "flex bg-gray-100 rounded-lg p-1",
|
|
530
|
-
children: [jsxRuntime.
|
|
531
|
-
onClick: () =>
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
className: `
|
|
543
|
-
|
|
646
|
+
children: [enableJsonMode && jsxRuntime.jsxs("button", {
|
|
647
|
+
onClick: () => setViewMode(viewMode === 'json' ? 'read' : 'json'),
|
|
648
|
+
className: `inline-flex items-center gap-2 px-3 py-1.5 rounded-md transition-colors ${viewMode === 'json' ? "bg-blue-600 text-white shadow-sm" : "text-gray-600 hover:text-gray-900 hover:bg-gray-200"}`,
|
|
649
|
+
title: "JSON Mode - Click any element to view its JSON",
|
|
650
|
+
children: [jsxRuntime.jsx(VscJsonIcon, {
|
|
651
|
+
size: 16
|
|
652
|
+
}), jsxRuntime.jsx("span", {
|
|
653
|
+
className: "text-sm font-medium",
|
|
654
|
+
children: "JSON"
|
|
655
|
+
})]
|
|
656
|
+
}), enableEditMode && jsxRuntime.jsxs("button", {
|
|
657
|
+
onClick: () => setViewMode(viewMode === 'edit' ? 'read' : 'edit'),
|
|
658
|
+
className: `inline-flex items-center gap-2 px-3 py-1.5 rounded-md transition-colors ${viewMode === 'edit' ? "bg-green-600 text-white shadow-sm" : "text-gray-600 hover:text-gray-900 hover:bg-gray-200"}`,
|
|
659
|
+
title: "Edit Mode - Click any element to edit it",
|
|
660
|
+
children: [jsxRuntime.jsx(FaEditIcon, {
|
|
661
|
+
size: 16
|
|
662
|
+
}), jsxRuntime.jsx("span", {
|
|
663
|
+
className: "text-sm font-medium",
|
|
664
|
+
children: "Edit"
|
|
665
|
+
})]
|
|
666
|
+
})]
|
|
667
|
+
}), enableSemanticTags && jsxRuntime.jsxs("button", {
|
|
668
|
+
onClick: () => setShowSemanticTags(!showSemanticTags),
|
|
669
|
+
className: `inline-flex items-center gap-2 px-3 py-1.5 rounded-md transition-colors ml-2 ${showSemanticTags ? "bg-amber-100 text-amber-700 border border-amber-300" : "bg-gray-100 text-gray-500 border border-gray-200"}`,
|
|
670
|
+
title: showSemanticTags ? "Hide Semantic Tags" : "Show Semantic Tags",
|
|
671
|
+
children: [jsxRuntime.jsx(FaTagsIcon, {
|
|
672
|
+
size: 16
|
|
673
|
+
}), jsxRuntime.jsx("span", {
|
|
674
|
+
className: "text-sm font-medium",
|
|
675
|
+
children: "Tags"
|
|
676
|
+
})]
|
|
677
|
+
}), hasChanges && jsxRuntime.jsxs("button", {
|
|
678
|
+
onClick: handleDownload,
|
|
679
|
+
className: "inline-flex items-center gap-2 px-3 py-1.5 bg-purple-600 text-white rounded-md hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-2 ml-2",
|
|
680
|
+
title: "Download Updated JSON",
|
|
681
|
+
children: [jsxRuntime.jsx(FaFileDownloadIcon, {
|
|
682
|
+
size: 16
|
|
683
|
+
}), jsxRuntime.jsx("span", {
|
|
684
|
+
className: "text-sm font-medium",
|
|
685
|
+
children: "Save"
|
|
544
686
|
})]
|
|
545
687
|
})]
|
|
688
|
+
}), jsxRuntime.jsx("button", {
|
|
689
|
+
onClick: () => setIsAnalyticsOpen(true),
|
|
690
|
+
className: "inline-flex items-center gap-1 px-3 py-1.5 text-gray-600 hover:text-gray-900 hover:bg-gray-100 rounded-md",
|
|
691
|
+
title: "View Analytics",
|
|
692
|
+
children: jsxRuntime.jsx(FaChartBarIcon, {
|
|
693
|
+
size: 16
|
|
694
|
+
})
|
|
546
695
|
})]
|
|
547
696
|
});
|
|
548
697
|
return jsxRuntime.jsxs("div", {
|
|
549
698
|
className: "w-full h-full max-w-full flex flex-col overflow-hidden",
|
|
550
699
|
children: [renderHeader(), jsxRuntime.jsx("div", {
|
|
551
|
-
className:
|
|
700
|
+
className: `flex-1 overflow-auto min-h-0 max-w-full ${viewMode === 'edit' ? 'bg-green-50/50' : viewMode === 'json' ? 'bg-blue-50/50' : ''}`,
|
|
552
701
|
children: jsxRuntime.jsx("div", {
|
|
553
|
-
className: "p-
|
|
702
|
+
className: "p-2 max-w-full",
|
|
554
703
|
children: allNodes.map(node => jsxRuntime.jsx("div", {
|
|
555
704
|
className: "w-full max-w-full overflow-hidden break-words",
|
|
556
705
|
children: renderHtmlContent(node)
|
|
@@ -564,6 +713,10 @@ function HtmlViewer({
|
|
|
564
713
|
isOpen: isModalOpen,
|
|
565
714
|
onClose: () => setIsModalOpen(false),
|
|
566
715
|
data: modalData
|
|
716
|
+
}), jsxRuntime.jsx(TagModal, {
|
|
717
|
+
isOpen: isTagModalOpen,
|
|
718
|
+
onClose: () => setIsTagModalOpen(false),
|
|
719
|
+
tag: selectedTag
|
|
567
720
|
})]
|
|
568
721
|
});
|
|
569
722
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
+
var React = require('react');
|
|
7
|
+
var katex = require('katex');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Renders LaTeX math expressions in HTML content using KaTeX.
|
|
11
|
+
* Supports both inline ($...$) and display ($$...$$) math.
|
|
12
|
+
*/
|
|
13
|
+
// Regex patterns for math delimiters
|
|
14
|
+
const DISPLAY_MATH_REGEX = /\$\$([\s\S]*?)\$\$/g;
|
|
15
|
+
const INLINE_MATH_REGEX = /\$([^\$\n]+?)\$/g;
|
|
16
|
+
/**
|
|
17
|
+
* Render a single LaTeX expression to HTML using KaTeX
|
|
18
|
+
*/
|
|
19
|
+
function renderLatex(latex, displayMode) {
|
|
20
|
+
try {
|
|
21
|
+
return katex.renderToString(latex, {
|
|
22
|
+
displayMode,
|
|
23
|
+
throwOnError: false,
|
|
24
|
+
errorColor: "#cc0000",
|
|
25
|
+
strict: false,
|
|
26
|
+
trust: true,
|
|
27
|
+
macros: {
|
|
28
|
+
"\\leq": "\\le",
|
|
29
|
+
"\\geq": "\\ge"
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.warn("KaTeX render error:", error);
|
|
34
|
+
// Return the original with error styling
|
|
35
|
+
return `<span class="katex-error" style="color: #cc0000;">${latex}</span>`;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Process HTML content and render all math expressions
|
|
40
|
+
*/
|
|
41
|
+
function renderMathInHtml(html) {
|
|
42
|
+
if (!html) return "";
|
|
43
|
+
// First, handle display math ($$...$$)
|
|
44
|
+
let result = html.replace(DISPLAY_MATH_REGEX, (match, latex) => {
|
|
45
|
+
return renderLatex(latex.trim(), true);
|
|
46
|
+
});
|
|
47
|
+
// Then, handle inline math ($...$)
|
|
48
|
+
// The $...$ pattern with closing $ is always math, not currency
|
|
49
|
+
result = result.replace(INLINE_MATH_REGEX, (match, latex) => {
|
|
50
|
+
return renderLatex(latex.trim(), false);
|
|
51
|
+
});
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Check if content contains any math expressions
|
|
56
|
+
*/
|
|
57
|
+
function containsMath(html) {
|
|
58
|
+
if (!html) return false;
|
|
59
|
+
return DISPLAY_MATH_REGEX.test(html) || INLINE_MATH_REGEX.test(html);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* React component that renders HTML with math expressions
|
|
63
|
+
*/
|
|
64
|
+
function MathContent({
|
|
65
|
+
html,
|
|
66
|
+
className = "",
|
|
67
|
+
as: Component = "span"
|
|
68
|
+
}) {
|
|
69
|
+
const renderedHtml = React.useMemo(() => renderMathInHtml(html), [html]);
|
|
70
|
+
return jsxRuntime.jsx(Component, {
|
|
71
|
+
className: className,
|
|
72
|
+
dangerouslySetInnerHTML: {
|
|
73
|
+
__html: renderedHtml
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Hook to get rendered math HTML
|
|
79
|
+
*/
|
|
80
|
+
function useMathHtml(html) {
|
|
81
|
+
return React.useMemo(() => renderMathInHtml(html), [html]);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
exports.MathContent = MathContent;
|
|
85
|
+
exports.containsMath = containsMath;
|
|
86
|
+
exports.default = MathContent;
|
|
87
|
+
exports.renderMathInHtml = renderMathInHtml;
|
|
88
|
+
exports.useMathHtml = useMathHtml;
|
|
@@ -169,7 +169,7 @@ function PdfDocumentViewer({
|
|
|
169
169
|
const currentPath = `${path}/${block.block_type}[${index}](${block.id})`;
|
|
170
170
|
// Assuming block.polygon is number[][] (array of points)
|
|
171
171
|
const points = block.polygon;
|
|
172
|
-
if (points && Array.isArray(points) && points.length >= 2 && block.block_type !== "Page") {
|
|
172
|
+
if (points && Array.isArray(points) && points.length >= 2 && block.block_type !== "Page" && block.block_type !== "TableCell") {
|
|
173
173
|
// Calculate bbox from points
|
|
174
174
|
const x_coords = points.map(p => p[0]);
|
|
175
175
|
const y_coords = points.map(p => p[1]);
|