@tfw.in/structura-lib 0.2.0 → 0.2.1
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 +9 -361
- package/dist/cjs/EditableContent.js +45 -18
- package/dist/cjs/HtmlViewer.js +217 -83
- 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 +20 -66
- package/dist/cjs/Table.js +73 -8
- package/dist/cjs/TableCell.js +33 -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 +49 -19
- package/dist/esm/HtmlViewer.js +259 -100
- 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 +21 -69
- package/dist/esm/Table.js +82 -8
- package/dist/esm/TableCell.js +32 -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 +7 -1
- package/dist/esm/types/HtmlViewer.d.ts +1 -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 +1 -2
- package/dist/esm/types/Table.d.ts +3 -1
- package/dist/esm/types/TableCell.d.ts +6 -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 +78 -4
- 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,121 @@ 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
|
-
onNodeClick
|
|
153
|
-
onSave
|
|
252
|
+
onNodeClick
|
|
154
253
|
}) {
|
|
155
254
|
const [editedData, setEditedData] = React.useState(jsonData);
|
|
156
255
|
const [hasChanges, setHasChanges] = React.useState(false);
|
|
157
256
|
const [isModalOpen, setIsModalOpen] = React.useState(false);
|
|
158
257
|
const [modalData, setModalData] = React.useState(null);
|
|
159
|
-
const [
|
|
160
|
-
const [activeFormat, setActiveFormat] = React.useState("Show JSON");
|
|
258
|
+
const [viewMode, setViewMode] = React.useState('read');
|
|
161
259
|
const [accuracyMetrics$1, setAccuracyMetrics] = React.useState({
|
|
162
260
|
totalChanges: 0,
|
|
163
261
|
totalCharactersEdited: 0,
|
|
@@ -167,6 +265,9 @@ function HtmlViewer({
|
|
|
167
265
|
blockTypeStats: {}
|
|
168
266
|
});
|
|
169
267
|
const [isAnalyticsOpen, setIsAnalyticsOpen] = React.useState(false);
|
|
268
|
+
const [isTagModalOpen, setIsTagModalOpen] = React.useState(false);
|
|
269
|
+
const [selectedTag, setSelectedTag] = React.useState(null);
|
|
270
|
+
const [showSemanticTags, setShowSemanticTags] = React.useState(true);
|
|
170
271
|
React.useEffect(() => {
|
|
171
272
|
// Reset state when jsonData changes
|
|
172
273
|
// console.log("HtmlViewer received new jsonData");
|
|
@@ -185,6 +286,10 @@ function HtmlViewer({
|
|
|
185
286
|
setModalData(node);
|
|
186
287
|
setIsModalOpen(true);
|
|
187
288
|
};
|
|
289
|
+
const handleSemanticTagClick = tag => {
|
|
290
|
+
setSelectedTag(tag);
|
|
291
|
+
setIsTagModalOpen(true);
|
|
292
|
+
};
|
|
188
293
|
const updateAccuracyMetrics = React.useCallback((nodeId, blockType, originalContent, newContent) => {
|
|
189
294
|
const differences = accuracyMetrics.calculateDifferences(originalContent, newContent);
|
|
190
295
|
const change = {
|
|
@@ -268,10 +373,7 @@ function HtmlViewer({
|
|
|
268
373
|
};
|
|
269
374
|
// Function to get the appropriate HTML content based on settings
|
|
270
375
|
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 || '');
|
|
376
|
+
return cleanHtml(node.html || "");
|
|
275
377
|
};
|
|
276
378
|
const mergedTablesMap = React.useMemo(() => {
|
|
277
379
|
const map = new Map();
|
|
@@ -413,10 +515,13 @@ function HtmlViewer({
|
|
|
413
515
|
const hasChildren = node.children && node.children.length > 0;
|
|
414
516
|
const isTable = ((_a = node.block_type) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === "table";
|
|
415
517
|
const isTableOfContents = node.block_type === "TableOfContents";
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
518
|
+
node.block_type === "Text";
|
|
519
|
+
node.block_type === "Handwriting";
|
|
520
|
+
node.block_type === "SectionHeader";
|
|
419
521
|
const isPage = node.block_type === "Page";
|
|
522
|
+
const isFigure = node.block_type === "Figure" || node.block_type === "Picture";
|
|
523
|
+
const isPageHeader = node.block_type === "PageHeader";
|
|
524
|
+
const isPageFooter = node.block_type === "PageFooter";
|
|
420
525
|
// Get the appropriate HTML content
|
|
421
526
|
const htmlContent = getHtmlContent(node);
|
|
422
527
|
const isHeading = htmlContent && (htmlContent.startsWith("<h1") || htmlContent.startsWith("<h2") || htmlContent.startsWith("<h3") || htmlContent.startsWith("<h4"));
|
|
@@ -437,46 +542,64 @@ function HtmlViewer({
|
|
|
437
542
|
onNodeClick(node.id);
|
|
438
543
|
}
|
|
439
544
|
};
|
|
440
|
-
return jsxRuntime.
|
|
545
|
+
return jsxRuntime.jsx("div", {
|
|
441
546
|
id: node.id,
|
|
442
|
-
className: `
|
|
547
|
+
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
548
|
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, {
|
|
549
|
+
children: isTable || isTableOfContents ? jsxRuntime.jsx(Table.default, {
|
|
455
550
|
node: node,
|
|
456
551
|
selectedBboxId: selectedBboxId,
|
|
457
552
|
onJsonClick: handleJsonClick,
|
|
458
553
|
onContentChange: handleContentChange,
|
|
459
554
|
mergedTables: node.merged_table_id ? (mergedTablesMap.get(node.merged_table_id) || []).slice(1) : [],
|
|
460
555
|
hasLlmHtml: !!node.llm_table_html,
|
|
461
|
-
showJsonIcons:
|
|
462
|
-
onNodeClick: onNodeClick
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
className: "
|
|
556
|
+
showJsonIcons: false,
|
|
557
|
+
onNodeClick: onNodeClick,
|
|
558
|
+
isEditMode: viewMode === 'edit',
|
|
559
|
+
isJsonMode: viewMode === 'json'
|
|
560
|
+
}) : isFigure && node.images ? jsxRuntime.jsx("div", {
|
|
561
|
+
className: `my-2 ${viewMode === 'json' ? 'cursor-pointer hover:opacity-80' : ''}`,
|
|
562
|
+
onClick: viewMode === 'json' ? () => handleJsonClick(node) : undefined,
|
|
563
|
+
children: Object.entries(node.images).map(([key, imageData]) => jsxRuntime.jsx("img", {
|
|
564
|
+
src: typeof imageData === 'string' && imageData.startsWith('data:') ? imageData : `data:image/png;base64,${imageData}`,
|
|
565
|
+
alt: `Figure ${node.id}`,
|
|
566
|
+
className: "max-w-full h-auto rounded border border-gray-200"
|
|
567
|
+
}, key))
|
|
568
|
+
}) : isPageHeader || isPageFooter ? jsxRuntime.jsxs("div", {
|
|
569
|
+
className: `my-1 px-3 py-1.5 rounded-md border text-xs flex items-center gap-2
|
|
570
|
+
${isPageHeader ? 'bg-amber-50 border-amber-200 text-amber-800' : 'bg-slate-50 border-slate-200 text-slate-600'}
|
|
571
|
+
${viewMode === 'json' ? 'cursor-pointer hover:opacity-80' : ''}`,
|
|
572
|
+
onClick: viewMode === 'json' ? () => handleJsonClick(node) : undefined,
|
|
573
|
+
children: [jsxRuntime.jsx("span", {
|
|
574
|
+
className: `font-semibold uppercase tracking-wide ${isPageHeader ? 'text-amber-600' : 'text-slate-500'}`,
|
|
575
|
+
children: isPageHeader ? '⬆ Header' : '⬇ Footer'
|
|
576
|
+
}), htmlContent && jsxRuntime.jsx("span", {
|
|
577
|
+
className: "text-gray-600",
|
|
472
578
|
dangerouslySetInnerHTML: {
|
|
473
579
|
__html: htmlContent
|
|
474
580
|
}
|
|
581
|
+
}), node.images && 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: `${isPageHeader ? 'Header' : 'Footer'} image`,
|
|
584
|
+
className: "h-6 w-auto"
|
|
585
|
+
}, key))]
|
|
586
|
+
}) : jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
|
587
|
+
children: [jsxRuntime.jsx(EditableContent.default, {
|
|
588
|
+
id: node.id,
|
|
589
|
+
content: htmlContent,
|
|
590
|
+
onContentChange: handleContentChange,
|
|
591
|
+
isHeading: !!isHeading,
|
|
592
|
+
isEditMode: viewMode === 'edit',
|
|
593
|
+
isJsonMode: viewMode === 'json',
|
|
594
|
+
onJsonClick: () => handleJsonClick(node),
|
|
595
|
+
onNodeClick: onNodeClick && !isPage ? () => onNodeClick(node.id) : undefined,
|
|
596
|
+
enableSemanticTags: showSemanticTags,
|
|
597
|
+
onSemanticTagClick: handleSemanticTagClick
|
|
475
598
|
}), hasChildren && jsxRuntime.jsx("div", {
|
|
476
|
-
className: "ml-
|
|
599
|
+
className: "ml-2 mt-1 border-l border-gray-200 pl-2 max-w-full overflow-hidden",
|
|
477
600
|
children: node.children.map(child => renderHtmlContent(child))
|
|
478
601
|
})]
|
|
479
|
-
})
|
|
602
|
+
})
|
|
480
603
|
}, node.id);
|
|
481
604
|
};
|
|
482
605
|
const getAllNodes = data => {
|
|
@@ -494,63 +617,70 @@ function HtmlViewer({
|
|
|
494
617
|
});
|
|
495
618
|
};
|
|
496
619
|
const allNodes = getAllNodes(editedData);
|
|
497
|
-
// Update the header section
|
|
620
|
+
// Update the header section with toggle buttons
|
|
498
621
|
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
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
622
|
+
className: "sticky top-0 z-20 bg-white border-b border-gray-200 p-3 flex justify-between items-center flex-shrink-0",
|
|
623
|
+
children: [jsxRuntime.jsxs("div", {
|
|
624
|
+
className: "flex gap-1",
|
|
625
|
+
children: [jsxRuntime.jsxs("div", {
|
|
626
|
+
className: "flex bg-gray-100 rounded-lg p-1",
|
|
627
|
+
children: [jsxRuntime.jsxs("button", {
|
|
628
|
+
onClick: () => setViewMode(viewMode === 'json' ? 'read' : 'json'),
|
|
629
|
+
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"}`,
|
|
630
|
+
title: "JSON Mode - Click any element to view its JSON",
|
|
631
|
+
children: [jsxRuntime.jsx(VscJsonIcon, {
|
|
632
|
+
size: 16
|
|
633
|
+
}), jsxRuntime.jsx("span", {
|
|
634
|
+
className: "text-sm font-medium",
|
|
635
|
+
children: "JSON"
|
|
636
|
+
})]
|
|
510
637
|
}), jsxRuntime.jsxs("button", {
|
|
511
|
-
onClick:
|
|
512
|
-
className:
|
|
513
|
-
title: "
|
|
514
|
-
children: [jsxRuntime.jsx(
|
|
515
|
-
size:
|
|
516
|
-
}), "
|
|
638
|
+
onClick: () => setViewMode(viewMode === 'edit' ? 'read' : 'edit'),
|
|
639
|
+
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"}`,
|
|
640
|
+
title: "Edit Mode - Click any element to edit it",
|
|
641
|
+
children: [jsxRuntime.jsx(FaEditIcon, {
|
|
642
|
+
size: 16
|
|
643
|
+
}), jsxRuntime.jsx("span", {
|
|
644
|
+
className: "text-sm font-medium",
|
|
645
|
+
children: "Edit"
|
|
646
|
+
})]
|
|
517
647
|
})]
|
|
518
|
-
})
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
})
|
|
528
|
-
}), jsxRuntime.jsxs("
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
className:
|
|
536
|
-
children: "
|
|
537
|
-
}), jsxRuntime.jsx("button", {
|
|
538
|
-
onClick: () => {
|
|
539
|
-
setShowJsonIcons(false);
|
|
540
|
-
setActiveFormat("Hide JSON");
|
|
541
|
-
},
|
|
542
|
-
className: `py-1.5 px-3 text-sm font-medium rounded-md transition-colors whitespace-nowrap ${activeFormat === "Hide JSON" ? "bg-white text-gray-900 shadow-sm" : "text-gray-600 hover:text-gray-900"}`,
|
|
543
|
-
children: "Hide JSON"
|
|
648
|
+
}), jsxRuntime.jsxs("button", {
|
|
649
|
+
onClick: () => setShowSemanticTags(!showSemanticTags),
|
|
650
|
+
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"}`,
|
|
651
|
+
title: showSemanticTags ? "Hide Semantic Tags" : "Show Semantic Tags",
|
|
652
|
+
children: [jsxRuntime.jsx(FaTagsIcon, {
|
|
653
|
+
size: 16
|
|
654
|
+
}), jsxRuntime.jsx("span", {
|
|
655
|
+
className: "text-sm font-medium",
|
|
656
|
+
children: "Tags"
|
|
657
|
+
})]
|
|
658
|
+
}), hasChanges && jsxRuntime.jsxs("button", {
|
|
659
|
+
onClick: handleDownload,
|
|
660
|
+
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",
|
|
661
|
+
title: "Download Updated JSON",
|
|
662
|
+
children: [jsxRuntime.jsx(FaFileDownloadIcon, {
|
|
663
|
+
size: 16
|
|
664
|
+
}), jsxRuntime.jsx("span", {
|
|
665
|
+
className: "text-sm font-medium",
|
|
666
|
+
children: "Save"
|
|
544
667
|
})]
|
|
545
668
|
})]
|
|
669
|
+
}), jsxRuntime.jsx("button", {
|
|
670
|
+
onClick: () => setIsAnalyticsOpen(true),
|
|
671
|
+
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",
|
|
672
|
+
title: "View Analytics",
|
|
673
|
+
children: jsxRuntime.jsx(FaChartBarIcon, {
|
|
674
|
+
size: 16
|
|
675
|
+
})
|
|
546
676
|
})]
|
|
547
677
|
});
|
|
548
678
|
return jsxRuntime.jsxs("div", {
|
|
549
679
|
className: "w-full h-full max-w-full flex flex-col overflow-hidden",
|
|
550
680
|
children: [renderHeader(), jsxRuntime.jsx("div", {
|
|
551
|
-
className:
|
|
681
|
+
className: `flex-1 overflow-auto min-h-0 max-w-full ${viewMode === 'edit' ? 'bg-green-50/50' : viewMode === 'json' ? 'bg-blue-50/50' : ''}`,
|
|
552
682
|
children: jsxRuntime.jsx("div", {
|
|
553
|
-
className: "p-
|
|
683
|
+
className: "p-2 max-w-full",
|
|
554
684
|
children: allNodes.map(node => jsxRuntime.jsx("div", {
|
|
555
685
|
className: "w-full max-w-full overflow-hidden break-words",
|
|
556
686
|
children: renderHtmlContent(node)
|
|
@@ -564,6 +694,10 @@ function HtmlViewer({
|
|
|
564
694
|
isOpen: isModalOpen,
|
|
565
695
|
onClose: () => setIsModalOpen(false),
|
|
566
696
|
data: modalData
|
|
697
|
+
}), jsxRuntime.jsx(TagModal, {
|
|
698
|
+
isOpen: isTagModalOpen,
|
|
699
|
+
onClose: () => setIsTagModalOpen(false),
|
|
700
|
+
tag: selectedTag
|
|
567
701
|
})]
|
|
568
702
|
});
|
|
569
703
|
}
|
|
@@ -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]);
|