@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.
Files changed (49) hide show
  1. package/README.md +9 -361
  2. package/dist/cjs/EditableContent.js +45 -18
  3. package/dist/cjs/HtmlViewer.js +217 -83
  4. package/dist/cjs/MathRenderer.js +88 -0
  5. package/dist/cjs/PdfDocumentViewer.js +1 -1
  6. package/dist/cjs/SemanticTagParser.js +189 -0
  7. package/dist/cjs/SemanticTagRenderer.js +135 -0
  8. package/dist/cjs/Structura.js +20 -66
  9. package/dist/cjs/Table.js +73 -8
  10. package/dist/cjs/TableCell.js +33 -10
  11. package/dist/cjs/index.js +12 -0
  12. package/dist/cjs/node_modules/react-icons/fa/index.esm.js +6 -0
  13. package/dist/cjs/styles.css +2 -4
  14. package/dist/cjs/styles.css.map +1 -1
  15. package/dist/esm/EditableContent.js +49 -19
  16. package/dist/esm/HtmlViewer.js +259 -100
  17. package/dist/esm/MathRenderer.js +85 -0
  18. package/dist/esm/PdfDocumentViewer.js +1 -1
  19. package/dist/esm/SemanticTagParser.js +187 -0
  20. package/dist/esm/SemanticTagRenderer.js +140 -0
  21. package/dist/esm/Structura.js +21 -69
  22. package/dist/esm/Table.js +82 -8
  23. package/dist/esm/TableCell.js +32 -6
  24. package/dist/esm/index.js +3 -0
  25. package/dist/esm/node_modules/react-icons/fa/index.esm.js +5 -1
  26. package/dist/esm/styles.css +2 -4
  27. package/dist/esm/styles.css.map +1 -1
  28. package/dist/esm/types/DocumentOutline.d.ts +7 -0
  29. package/dist/esm/types/EditableContent.d.ts +7 -1
  30. package/dist/esm/types/HtmlViewer.d.ts +1 -2
  31. package/dist/esm/types/MathRenderer.d.ts +25 -0
  32. package/dist/esm/types/SemanticTagParser.d.ts +33 -0
  33. package/dist/esm/types/SemanticTagRenderer.d.ts +17 -0
  34. package/dist/esm/types/Structura.d.ts +1 -2
  35. package/dist/esm/types/Table.d.ts +3 -1
  36. package/dist/esm/types/TableCell.d.ts +6 -1
  37. package/dist/esm/types/helpers/index.d.ts +0 -1
  38. package/dist/esm/types/index.d.ts +3 -0
  39. package/dist/esm/types/test-app/src/App.d.ts +1 -2
  40. package/dist/index.d.ts +78 -4
  41. package/package.json +9 -16
  42. package/PRODUCTION_ARCHITECTURE.md +0 -511
  43. package/SAVE_FUNCTIONALITY_COMPLETE.md +0 -448
  44. package/dist/cjs/ui/badge.js +0 -34
  45. package/dist/esm/types/helpers/jsonToHtml.d.ts +0 -40
  46. package/dist/esm/ui/badge.js +0 -31
  47. package/server/README.md +0 -203
  48. package/server/db.js +0 -142
  49. package/server/server.js +0 -165
@@ -2,10 +2,11 @@ import { slicedToArray as _slicedToArray, objectSpread2 as _objectSpread2, defin
2
2
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
3
  import { useState, useEffect, useCallback, useMemo } from 'react';
4
4
  import { VscJson } from 'react-icons/vsc';
5
- import { FaFileDownload, FaChartBar } from './node_modules/react-icons/fa/index.esm.js';
5
+ import { FaEdit, FaTags, FaFileDownload, FaChartBar } from './node_modules/react-icons/fa/index.esm.js';
6
6
  import Table from './Table.js';
7
7
  import EditableContent from './EditableContent.js';
8
8
  import { calculateDifferences } from './accuracyMetrics.js';
9
+ import { getTagTypeInfo } from './SemanticTagParser.js';
9
10
 
10
11
  // Utility function to clean HTML content
11
12
  var cleanHtml = function cleanHtml(html) {
@@ -140,16 +141,113 @@ function AnalyticsModal(_ref2) {
140
141
  })
141
142
  });
142
143
  }
144
+ function TagModal(_ref5) {
145
+ var isOpen = _ref5.isOpen,
146
+ onClose = _ref5.onClose,
147
+ tag = _ref5.tag;
148
+ if (!isOpen || !tag) return null;
149
+ var typeInfo = getTagTypeInfo(tag.type);
150
+ return jsx("div", {
151
+ className: "fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50",
152
+ children: jsxs("div", {
153
+ className: "bg-white rounded-lg p-6 max-w-md w-full shadow-xl",
154
+ children: [jsxs("div", {
155
+ className: "flex justify-between items-center mb-4",
156
+ children: [jsxs("div", {
157
+ className: "flex items-center gap-2",
158
+ children: [jsx("span", {
159
+ className: "text-2xl",
160
+ children: typeInfo.icon
161
+ }), jsxs("h3", {
162
+ className: "text-lg font-semibold",
163
+ children: [typeInfo.label, " Tag"]
164
+ })]
165
+ }), jsx("button", {
166
+ onClick: onClose,
167
+ className: "text-gray-500 hover:text-gray-700",
168
+ children: "\u2715"
169
+ })]
170
+ }), jsx("div", {
171
+ className: "p-4 rounded-lg border ".concat(typeInfo.bgColor, " mb-4"),
172
+ children: jsxs("div", {
173
+ className: "text-2xl font-bold ".concat(typeInfo.color),
174
+ children: [tag.value, tag.attributes.uom && jsx("span", {
175
+ className: "text-lg ml-2 opacity-75",
176
+ children: tag.attributes.uom
177
+ })]
178
+ })
179
+ }), jsxs("div", {
180
+ className: "space-y-3",
181
+ children: [jsxs("div", {
182
+ className: "flex justify-between py-2 border-b",
183
+ children: [jsx("span", {
184
+ className: "text-gray-600",
185
+ children: "Type"
186
+ }), jsx("span", {
187
+ className: "font-medium ".concat(typeInfo.color),
188
+ children: tag.type
189
+ })]
190
+ }), tag.attributes.uom && jsxs("div", {
191
+ className: "flex justify-between py-2 border-b",
192
+ children: [jsx("span", {
193
+ className: "text-gray-600",
194
+ children: "Unit of Measure"
195
+ }), jsx("span", {
196
+ className: "font-medium",
197
+ children: tag.attributes.uom
198
+ })]
199
+ }), tag.attributes.id && jsxs("div", {
200
+ className: "py-2 border-b",
201
+ children: [jsx("span", {
202
+ className: "text-gray-600 block mb-1",
203
+ children: "Tag ID"
204
+ }), jsx("code", {
205
+ className: "text-xs bg-gray-100 p-2 rounded block break-all",
206
+ children: tag.attributes.id
207
+ })]
208
+ }), tag.attributes.format && jsxs("div", {
209
+ className: "flex justify-between py-2 border-b",
210
+ children: [jsx("span", {
211
+ className: "text-gray-600",
212
+ children: "Format"
213
+ }), jsx("span", {
214
+ className: "font-mono text-sm",
215
+ children: tag.attributes.format
216
+ })]
217
+ }), tag.attributes.filter && jsxs("div", {
218
+ className: "flex justify-between py-2 border-b",
219
+ children: [jsx("span", {
220
+ className: "text-gray-600",
221
+ children: "File Filter"
222
+ }), jsx("span", {
223
+ className: "font-mono text-sm",
224
+ children: tag.attributes.filter
225
+ })]
226
+ }), jsxs("div", {
227
+ className: "py-2",
228
+ children: [jsx("span", {
229
+ className: "text-gray-600 block mb-1",
230
+ children: "Raw Tag"
231
+ }), jsx("code", {
232
+ className: "text-xs bg-gray-100 p-2 rounded block break-all text-gray-700",
233
+ children: tag.rawMatch
234
+ })]
235
+ })]
236
+ })]
237
+ })
238
+ });
239
+ }
143
240
  // Update icon usage in the JSX
144
241
  var VscJsonIcon = VscJson;
145
242
  var FaFileDownloadIcon = FaFileDownload;
146
243
  var FaChartBarIcon = FaChartBar;
147
- function HtmlViewer(_ref5) {
148
- var jsonData = _ref5.jsonData,
149
- selectedBboxId = _ref5.selectedBboxId,
150
- isLoading = _ref5.isLoading,
151
- onNodeClick = _ref5.onNodeClick,
152
- onSave = _ref5.onSave;
244
+ var FaEditIcon = FaEdit;
245
+ var FaTagsIcon = FaTags;
246
+ function HtmlViewer(_ref6) {
247
+ var jsonData = _ref6.jsonData,
248
+ selectedBboxId = _ref6.selectedBboxId,
249
+ isLoading = _ref6.isLoading,
250
+ onNodeClick = _ref6.onNodeClick;
153
251
  var _useState = useState(jsonData),
154
252
  _useState2 = _slicedToArray(_useState, 2),
155
253
  editedData = _useState2[0],
@@ -166,15 +264,11 @@ function HtmlViewer(_ref5) {
166
264
  _useState8 = _slicedToArray(_useState7, 2),
167
265
  modalData = _useState8[0],
168
266
  setModalData = _useState8[1];
169
- var _useState9 = useState(true),
267
+ var _useState9 = useState('read'),
170
268
  _useState0 = _slicedToArray(_useState9, 2),
171
- showJsonIcons = _useState0[0],
172
- setShowJsonIcons = _useState0[1];
173
- var _useState1 = useState("Show JSON"),
174
- _useState10 = _slicedToArray(_useState1, 2),
175
- activeFormat = _useState10[0],
176
- setActiveFormat = _useState10[1];
177
- var _useState11 = useState({
269
+ viewMode = _useState0[0],
270
+ setViewMode = _useState0[1];
271
+ var _useState1 = useState({
178
272
  totalChanges: 0,
179
273
  totalCharactersEdited: 0,
180
274
  totalWordsEdited: 0,
@@ -182,13 +276,25 @@ function HtmlViewer(_ref5) {
182
276
  changeHistory: [],
183
277
  blockTypeStats: {}
184
278
  }),
279
+ _useState10 = _slicedToArray(_useState1, 2),
280
+ accuracyMetrics = _useState10[0],
281
+ setAccuracyMetrics = _useState10[1];
282
+ var _useState11 = useState(false),
185
283
  _useState12 = _slicedToArray(_useState11, 2),
186
- accuracyMetrics = _useState12[0],
187
- setAccuracyMetrics = _useState12[1];
284
+ isAnalyticsOpen = _useState12[0],
285
+ setIsAnalyticsOpen = _useState12[1];
188
286
  var _useState13 = useState(false),
189
287
  _useState14 = _slicedToArray(_useState13, 2),
190
- isAnalyticsOpen = _useState14[0],
191
- setIsAnalyticsOpen = _useState14[1];
288
+ isTagModalOpen = _useState14[0],
289
+ setIsTagModalOpen = _useState14[1];
290
+ var _useState15 = useState(null),
291
+ _useState16 = _slicedToArray(_useState15, 2),
292
+ selectedTag = _useState16[0],
293
+ setSelectedTag = _useState16[1];
294
+ var _useState17 = useState(true),
295
+ _useState18 = _slicedToArray(_useState17, 2),
296
+ showSemanticTags = _useState18[0],
297
+ setShowSemanticTags = _useState18[1];
192
298
  useEffect(function () {
193
299
  // Reset state when jsonData changes
194
300
  // console.log("HtmlViewer received new jsonData");
@@ -207,6 +313,10 @@ function HtmlViewer(_ref5) {
207
313
  setModalData(node);
208
314
  setIsModalOpen(true);
209
315
  };
316
+ var handleSemanticTagClick = function handleSemanticTagClick(tag) {
317
+ setSelectedTag(tag);
318
+ setIsTagModalOpen(true);
319
+ };
210
320
  var updateAccuracyMetrics = useCallback(function (nodeId, blockType, originalContent, newContent) {
211
321
  var differences = calculateDifferences(originalContent, newContent);
212
322
  var change = {
@@ -296,10 +406,7 @@ function HtmlViewer(_ref5) {
296
406
  };
297
407
  // Function to get the appropriate HTML content based on settings
298
408
  var getHtmlContent = function getHtmlContent(node) {
299
- // For individual nodes in the tree view, just return their HTML directly
300
- // This preserves the node structure and click interactivity
301
- // getBlockHtml/jsonToHtml should only be used for full page rendering/export
302
- return cleanHtml(node.html || '');
409
+ return cleanHtml(node.html || "");
303
410
  };
304
411
  var mergedTablesMap = useMemo(function () {
305
412
  var map = new Map();
@@ -450,10 +557,13 @@ function HtmlViewer(_ref5) {
450
557
  var hasChildren = node.children && node.children.length > 0;
451
558
  var isTable = ((_a = node.block_type) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === "table";
452
559
  var isTableOfContents = node.block_type === "TableOfContents";
453
- var isText = node.block_type === "Text";
454
- var isHandwritten = node.block_type === "Handwriting";
455
- var isSectionHeader = node.block_type === "SectionHeader";
560
+ node.block_type === "Text";
561
+ node.block_type === "Handwriting";
562
+ node.block_type === "SectionHeader";
456
563
  var isPage = node.block_type === "Page";
564
+ var isFigure = node.block_type === "Figure" || node.block_type === "Picture";
565
+ var isPageHeader = node.block_type === "PageHeader";
566
+ var isPageFooter = node.block_type === "PageFooter";
457
567
  // Get the appropriate HTML content
458
568
  var htmlContent = getHtmlContent(node);
459
569
  var isHeading = htmlContent && (htmlContent.startsWith("<h1") || htmlContent.startsWith("<h2") || htmlContent.startsWith("<h3") || htmlContent.startsWith("<h4"));
@@ -474,50 +584,82 @@ function HtmlViewer(_ref5) {
474
584
  onNodeClick(node.id);
475
585
  }
476
586
  };
477
- return jsxs("div", {
587
+ return jsx("div", {
478
588
  id: node.id,
479
- className: "p-2 my-1 rounded transition-colors relative group max-w-full overflow-hidden ".concat(isSelected ? "bg-blue-100 border-2 border-blue-500" : "", " ").concat(onNodeClick && !isPage ? "cursor-pointer hover:bg-gray-50" : ""),
589
+ className: "px-1 py-0.5 rounded transition-colors relative group max-w-full overflow-hidden ".concat(isSelected ? "bg-blue-100 border-2 border-blue-500" : "", " ").concat(onNodeClick && !isPage ? "cursor-pointer hover:bg-gray-50" : ""),
480
590
  onClick: isPage ? undefined : handleContentClick,
481
- children: [!isPage && showJsonIcons && jsx("button", {
482
- onClick: function onClick(e) {
483
- e.stopPropagation(); // Prevent triggering parent onClick
484
- handleJsonClick(node);
485
- },
486
- className: "absolute right-2 top-2 z-10 ".concat(showJsonIcons ? "opacity-0 group-hover:opacity-100 transition-opacity" : "hidden", " text-gray-500 hover:text-gray-700"),
487
- title: "View JSON",
488
- children: jsx(VscJsonIcon, {
489
- size: 18
490
- })
491
- }), isTable || isTableOfContents ? jsx(Table, {
591
+ children: isTable || isTableOfContents ? jsx(Table, {
492
592
  node: node,
493
593
  selectedBboxId: selectedBboxId,
494
594
  onJsonClick: handleJsonClick,
495
595
  onContentChange: handleContentChange,
496
596
  mergedTables: node.merged_table_id ? (mergedTablesMap.get(node.merged_table_id) || []).slice(1) : [],
497
597
  hasLlmHtml: !!node.llm_table_html,
498
- showJsonIcons: showJsonIcons,
499
- onNodeClick: onNodeClick
500
- }) : isText || isHandwritten || isSectionHeader ? jsx(EditableContent, {
501
- id: node.id,
502
- content: htmlContent,
503
- onContentChange: handleContentChange,
504
- isHeading: !!isHeading,
505
- onNodeClick: onNodeClick && !isPage ? function () {
506
- return onNodeClick(node.id);
507
- } : undefined
508
- }) : jsxs(Fragment, {
509
- children: [jsx("div", {
510
- className: "prose max-w-none w-full overflow-hidden break-words",
598
+ showJsonIcons: false,
599
+ onNodeClick: onNodeClick,
600
+ isEditMode: viewMode === 'edit',
601
+ isJsonMode: viewMode === 'json'
602
+ }) : isFigure && node.images ? jsx("div", {
603
+ className: "my-2 ".concat(viewMode === 'json' ? 'cursor-pointer hover:opacity-80' : ''),
604
+ onClick: viewMode === 'json' ? function () {
605
+ return handleJsonClick(node);
606
+ } : undefined,
607
+ children: Object.entries(node.images).map(function (_ref7) {
608
+ var _ref8 = _slicedToArray(_ref7, 2),
609
+ key = _ref8[0],
610
+ imageData = _ref8[1];
611
+ return jsx("img", {
612
+ src: typeof imageData === 'string' && imageData.startsWith('data:') ? imageData : "data:image/png;base64,".concat(imageData),
613
+ alt: "Figure ".concat(node.id),
614
+ className: "max-w-full h-auto rounded border border-gray-200"
615
+ }, key);
616
+ })
617
+ }) : isPageHeader || isPageFooter ? jsxs("div", {
618
+ className: "my-1 px-3 py-1.5 rounded-md border text-xs flex items-center gap-2\n\t\t\t\t\t\t\t".concat(isPageHeader ? 'bg-amber-50 border-amber-200 text-amber-800' : 'bg-slate-50 border-slate-200 text-slate-600', "\n\t\t\t\t\t\t\t").concat(viewMode === 'json' ? 'cursor-pointer hover:opacity-80' : ''),
619
+ onClick: viewMode === 'json' ? function () {
620
+ return handleJsonClick(node);
621
+ } : undefined,
622
+ children: [jsx("span", {
623
+ className: "font-semibold uppercase tracking-wide ".concat(isPageHeader ? 'text-amber-600' : 'text-slate-500'),
624
+ children: isPageHeader ? '⬆ Header' : '⬇ Footer'
625
+ }), htmlContent && jsx("span", {
626
+ className: "text-gray-600",
511
627
  dangerouslySetInnerHTML: {
512
628
  __html: htmlContent
513
629
  }
630
+ }), node.images && Object.entries(node.images).map(function (_ref9) {
631
+ var _ref0 = _slicedToArray(_ref9, 2),
632
+ key = _ref0[0],
633
+ imageData = _ref0[1];
634
+ return jsx("img", {
635
+ src: typeof imageData === 'string' && imageData.startsWith('data:') ? imageData : "data:image/png;base64,".concat(imageData),
636
+ alt: "".concat(isPageHeader ? 'Header' : 'Footer', " image"),
637
+ className: "h-6 w-auto"
638
+ }, key);
639
+ })]
640
+ }) : jsxs(Fragment, {
641
+ children: [jsx(EditableContent, {
642
+ id: node.id,
643
+ content: htmlContent,
644
+ onContentChange: handleContentChange,
645
+ isHeading: !!isHeading,
646
+ isEditMode: viewMode === 'edit',
647
+ isJsonMode: viewMode === 'json',
648
+ onJsonClick: function onJsonClick() {
649
+ return handleJsonClick(node);
650
+ },
651
+ onNodeClick: onNodeClick && !isPage ? function () {
652
+ return onNodeClick(node.id);
653
+ } : undefined,
654
+ enableSemanticTags: showSemanticTags,
655
+ onSemanticTagClick: handleSemanticTagClick
514
656
  }), hasChildren && jsx("div", {
515
- className: "ml-4 mt-2 border-l-2 border-gray-200 pl-4 max-w-full overflow-hidden",
657
+ className: "ml-2 mt-1 border-l border-gray-200 pl-2 max-w-full overflow-hidden",
516
658
  children: node.children.map(function (child) {
517
659
  return _renderHtmlContent(child);
518
660
  })
519
661
  })]
520
- })]
662
+ })
521
663
  }, node.id);
522
664
  };
523
665
  var getAllNodes = function getAllNodes(data) {
@@ -535,69 +677,80 @@ function HtmlViewer(_ref5) {
535
677
  });
536
678
  };
537
679
  var allNodes = getAllNodes(editedData);
538
- // Update the header section to include both buttons
680
+ // Update the header section with toggle buttons
539
681
  var renderHeader = function renderHeader() {
540
682
  return jsxs("div", {
541
- className: "sticky top-0 z-20 bg-white border-b border-gray-200 p-4 flex justify-between items-center flex-shrink-0",
542
- children: [jsx("div", {
543
- className: "flex gap-2",
544
- children: hasChanges && jsxs(Fragment, {
545
- children: [onSave && jsxs("button", {
683
+ className: "sticky top-0 z-20 bg-white border-b border-gray-200 p-3 flex justify-between items-center flex-shrink-0",
684
+ children: [jsxs("div", {
685
+ className: "flex gap-1",
686
+ children: [jsxs("div", {
687
+ className: "flex bg-gray-100 rounded-lg p-1",
688
+ children: [jsxs("button", {
546
689
  onClick: function onClick() {
547
- return onSave(editedData);
690
+ return setViewMode(viewMode === 'json' ? 'read' : 'json');
548
691
  },
549
- 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",
550
- title: "Save Changes",
551
- children: [jsx(FaFileDownloadIcon, {
552
- size: 18
553
- }), "Save"]
692
+ className: "inline-flex items-center gap-2 px-3 py-1.5 rounded-md transition-colors ".concat(viewMode === 'json' ? "bg-blue-600 text-white shadow-sm" : "text-gray-600 hover:text-gray-900 hover:bg-gray-200"),
693
+ title: "JSON Mode - Click any element to view its JSON",
694
+ children: [jsx(VscJsonIcon, {
695
+ size: 16
696
+ }), jsx("span", {
697
+ className: "text-sm font-medium",
698
+ children: "JSON"
699
+ })]
554
700
  }), jsxs("button", {
555
- onClick: handleDownload,
556
- 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",
557
- title: "Download Updated JSON",
558
- children: [jsx(FaFileDownloadIcon, {
559
- size: 18
560
- }), "Download"]
701
+ onClick: function onClick() {
702
+ return setViewMode(viewMode === 'edit' ? 'read' : 'edit');
703
+ },
704
+ className: "inline-flex items-center gap-2 px-3 py-1.5 rounded-md transition-colors ".concat(viewMode === 'edit' ? "bg-green-600 text-white shadow-sm" : "text-gray-600 hover:text-gray-900 hover:bg-gray-200"),
705
+ title: "Edit Mode - Click any element to edit it",
706
+ children: [jsx(FaEditIcon, {
707
+ size: 16
708
+ }), jsx("span", {
709
+ className: "text-sm font-medium",
710
+ children: "Edit"
711
+ })]
561
712
  })]
562
- })
563
- }), jsxs("div", {
564
- className: "flex gap-2 items-center",
565
- children: [jsx("button", {
713
+ }), jsxs("button", {
566
714
  onClick: function onClick() {
567
- return setIsAnalyticsOpen(true);
715
+ return setShowSemanticTags(!showSemanticTags);
568
716
  },
569
- 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",
570
- title: "View Analytics",
571
- children: jsx(FaChartBarIcon, {
572
- size: 18
573
- })
574
- }), jsxs("div", {
575
- className: "flex bg-gray-100 rounded-lg p-1",
576
- children: [jsx("button", {
577
- onClick: function onClick() {
578
- setShowJsonIcons(true);
579
- setActiveFormat("Show JSON");
580
- },
581
- className: "py-1.5 px-3 text-sm font-medium rounded-md transition-colors whitespace-nowrap ".concat(activeFormat === "Show JSON" ? "bg-white text-gray-900 shadow-sm" : "text-gray-600 hover:text-gray-900"),
582
- children: "Show JSON"
583
- }), jsx("button", {
584
- onClick: function onClick() {
585
- setShowJsonIcons(false);
586
- setActiveFormat("Hide JSON");
587
- },
588
- className: "py-1.5 px-3 text-sm font-medium rounded-md transition-colors whitespace-nowrap ".concat(activeFormat === "Hide JSON" ? "bg-white text-gray-900 shadow-sm" : "text-gray-600 hover:text-gray-900"),
589
- children: "Hide JSON"
717
+ className: "inline-flex items-center gap-2 px-3 py-1.5 rounded-md transition-colors ml-2 ".concat(showSemanticTags ? "bg-amber-100 text-amber-700 border border-amber-300" : "bg-gray-100 text-gray-500 border border-gray-200"),
718
+ title: showSemanticTags ? "Hide Semantic Tags" : "Show Semantic Tags",
719
+ children: [jsx(FaTagsIcon, {
720
+ size: 16
721
+ }), jsx("span", {
722
+ className: "text-sm font-medium",
723
+ children: "Tags"
724
+ })]
725
+ }), hasChanges && jsxs("button", {
726
+ onClick: handleDownload,
727
+ 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",
728
+ title: "Download Updated JSON",
729
+ children: [jsx(FaFileDownloadIcon, {
730
+ size: 16
731
+ }), jsx("span", {
732
+ className: "text-sm font-medium",
733
+ children: "Save"
590
734
  })]
591
735
  })]
736
+ }), jsx("button", {
737
+ onClick: function onClick() {
738
+ return setIsAnalyticsOpen(true);
739
+ },
740
+ 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",
741
+ title: "View Analytics",
742
+ children: jsx(FaChartBarIcon, {
743
+ size: 16
744
+ })
592
745
  })]
593
746
  });
594
747
  };
595
748
  return jsxs("div", {
596
749
  className: "w-full h-full max-w-full flex flex-col overflow-hidden",
597
750
  children: [renderHeader(), jsx("div", {
598
- className: "flex-1 overflow-auto min-h-0 max-w-full",
751
+ className: "flex-1 overflow-auto min-h-0 max-w-full ".concat(viewMode === 'edit' ? 'bg-green-50/50' : viewMode === 'json' ? 'bg-blue-50/50' : ''),
599
752
  children: jsx("div", {
600
- className: "p-4 max-w-full",
753
+ className: "p-2 max-w-full",
601
754
  children: allNodes.map(function (node) {
602
755
  return jsx("div", {
603
756
  className: "w-full max-w-full overflow-hidden break-words",
@@ -617,6 +770,12 @@ function HtmlViewer(_ref5) {
617
770
  return setIsModalOpen(false);
618
771
  },
619
772
  data: modalData
773
+ }), jsx(TagModal, {
774
+ isOpen: isTagModalOpen,
775
+ onClose: function onClose() {
776
+ return setIsTagModalOpen(false);
777
+ },
778
+ tag: selectedTag
620
779
  })]
621
780
  });
622
781
  }
@@ -0,0 +1,85 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useMemo } from 'react';
3
+ import katex from 'katex';
4
+
5
+ /**
6
+ * Renders LaTeX math expressions in HTML content using KaTeX.
7
+ * Supports both inline ($...$) and display ($$...$$) math.
8
+ */
9
+ // Regex patterns for math delimiters
10
+ var DISPLAY_MATH_REGEX = /\$\$([\s\S]*?)\$\$/g;
11
+ var INLINE_MATH_REGEX = /\$([^\$\n]+?)\$/g;
12
+ /**
13
+ * Render a single LaTeX expression to HTML using KaTeX
14
+ */
15
+ function renderLatex(latex, displayMode) {
16
+ try {
17
+ return katex.renderToString(latex, {
18
+ displayMode: displayMode,
19
+ throwOnError: false,
20
+ errorColor: "#cc0000",
21
+ strict: false,
22
+ trust: true,
23
+ macros: {
24
+ "\\leq": "\\le",
25
+ "\\geq": "\\ge"
26
+ }
27
+ });
28
+ } catch (error) {
29
+ console.warn("KaTeX render error:", error);
30
+ // Return the original with error styling
31
+ return "<span class=\"katex-error\" style=\"color: #cc0000;\">".concat(latex, "</span>");
32
+ }
33
+ }
34
+ /**
35
+ * Process HTML content and render all math expressions
36
+ */
37
+ function renderMathInHtml(html) {
38
+ if (!html) return "";
39
+ // First, handle display math ($$...$$)
40
+ var result = html.replace(DISPLAY_MATH_REGEX, function (match, latex) {
41
+ return renderLatex(latex.trim(), true);
42
+ });
43
+ // Then, handle inline math ($...$)
44
+ // The $...$ pattern with closing $ is always math, not currency
45
+ result = result.replace(INLINE_MATH_REGEX, function (match, latex) {
46
+ return renderLatex(latex.trim(), false);
47
+ });
48
+ return result;
49
+ }
50
+ /**
51
+ * Check if content contains any math expressions
52
+ */
53
+ function containsMath(html) {
54
+ if (!html) return false;
55
+ return DISPLAY_MATH_REGEX.test(html) || INLINE_MATH_REGEX.test(html);
56
+ }
57
+ /**
58
+ * React component that renders HTML with math expressions
59
+ */
60
+ function MathContent(_ref) {
61
+ var html = _ref.html,
62
+ _ref$className = _ref.className,
63
+ className = _ref$className === void 0 ? "" : _ref$className,
64
+ _ref$as = _ref.as,
65
+ Component = _ref$as === void 0 ? "span" : _ref$as;
66
+ var renderedHtml = useMemo(function () {
67
+ return renderMathInHtml(html);
68
+ }, [html]);
69
+ return jsx(Component, {
70
+ className: className,
71
+ dangerouslySetInnerHTML: {
72
+ __html: renderedHtml
73
+ }
74
+ });
75
+ }
76
+ /**
77
+ * Hook to get rendered math HTML
78
+ */
79
+ function useMathHtml(html) {
80
+ return useMemo(function () {
81
+ return renderMathInHtml(html);
82
+ }, [html]);
83
+ }
84
+
85
+ export { MathContent, containsMath, MathContent as default, renderMathInHtml, useMathHtml };
@@ -177,7 +177,7 @@ function PdfDocumentViewer(_ref2) {
177
177
  var currentPath = "".concat(path, "/").concat(block.block_type, "[").concat(index, "](").concat(block.id, ")");
178
178
  // Assuming block.polygon is number[][] (array of points)
179
179
  var points = block.polygon;
180
- if (points && Array.isArray(points) && points.length >= 2 && block.block_type !== "Page") {
180
+ if (points && Array.isArray(points) && points.length >= 2 && block.block_type !== "Page" && block.block_type !== "TableCell") {
181
181
  // Calculate bbox from points
182
182
  var x_coords = points.map(function (p) {
183
183
  return p[0];