@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
@@ -0,0 +1,187 @@
1
+ // Semantic Tag Parser for SOP/Batch Record domain tags
2
+ // Supports: MeasurementTag, HiddenMeasurementTag, ValueTag, DateTimeTag, FileTag, ImageTag
3
+ // Regular expression patterns for tag detection
4
+ // Standard tag: [[value][type:TYPE][attr:value]...]
5
+ // Hidden tag: {[value][type:TYPE][attr:value]...]}
6
+ var STANDARD_TAG_PATTERN = /\[\[([^\]]+)\](\[([^\]]+)\])*\]/g;
7
+ var HIDDEN_TAG_PATTERN = /\{\[([^\]]+)\](\[([^\]]+)\])*\]\}/g;
8
+ // Parse a single tag's attribute string like "[type:MEASUREMENT][uom:mL/min][id:abc-123]"
9
+ function parseTagAttributes(attributeStr) {
10
+ var attributes = {};
11
+ var type = 'VALUE'; // Default type
12
+ // Match all [key:value] pairs
13
+ var attrPattern = /\[([^:\]]+):([^\]]+)\]/g;
14
+ var match;
15
+ while ((match = attrPattern.exec(attributeStr)) !== null) {
16
+ var key = match[1].toLowerCase();
17
+ var value = match[2];
18
+ switch (key) {
19
+ case 'type':
20
+ type = value.toUpperCase();
21
+ break;
22
+ case 'uom':
23
+ attributes.uom = value;
24
+ break;
25
+ case 'id':
26
+ attributes.id = value;
27
+ break;
28
+ case 'format':
29
+ attributes.format = value;
30
+ break;
31
+ case 'filter':
32
+ attributes.filter = value;
33
+ break;
34
+ }
35
+ }
36
+ return {
37
+ type: type,
38
+ attributes: attributes
39
+ };
40
+ }
41
+ // Parse a single tag match
42
+ function parseTag(match, isHidden) {
43
+ var rawMatch = match[0];
44
+ var startIndex = match.index;
45
+ var endIndex = startIndex + rawMatch.length;
46
+ // Extract the value (first bracket content) and attributes (remaining brackets)
47
+ var innerContent;
48
+ if (isHidden) {
49
+ // Remove {[ and ]} wrapper
50
+ innerContent = rawMatch.slice(2, -2);
51
+ } else {
52
+ // Remove [[ and ]] wrapper (but there might be more brackets for attributes)
53
+ innerContent = rawMatch.slice(2, -1);
54
+ }
55
+ // Split by ][ to get value and attributes
56
+ var parts = innerContent.split('][');
57
+ var value = parts[0];
58
+ var attributeStr = parts.length > 1 ? '[' + parts.slice(1).join('][') + ']' : '';
59
+ var _parseTagAttributes = parseTagAttributes(attributeStr),
60
+ type = _parseTagAttributes.type,
61
+ attributes = _parseTagAttributes.attributes;
62
+ // Adjust type for hidden tags
63
+ var finalType = isHidden ? 'HIDDEN_MEASUREMENT' : type;
64
+ return {
65
+ type: finalType,
66
+ value: value,
67
+ rawMatch: rawMatch,
68
+ startIndex: startIndex,
69
+ endIndex: endIndex,
70
+ attributes: attributes,
71
+ isHidden: isHidden
72
+ };
73
+ }
74
+ // Main parsing function
75
+ function parseSemanticTags(text) {
76
+ var tags = [];
77
+ // Find all standard tags
78
+ var match;
79
+ var standardPattern = new RegExp(STANDARD_TAG_PATTERN.source, 'g');
80
+ while ((match = standardPattern.exec(text)) !== null) {
81
+ tags.push(parseTag(match, false));
82
+ }
83
+ // Find all hidden tags
84
+ var hiddenPattern = new RegExp(HIDDEN_TAG_PATTERN.source, 'g');
85
+ while ((match = hiddenPattern.exec(text)) !== null) {
86
+ tags.push(parseTag(match, true));
87
+ }
88
+ // Sort tags by start index
89
+ tags.sort(function (a, b) {
90
+ return a.startIndex - b.startIndex;
91
+ });
92
+ // Build segments
93
+ var segments = [];
94
+ var currentIndex = 0;
95
+ for (var _i = 0, _tags = tags; _i < _tags.length; _i++) {
96
+ var tag = _tags[_i];
97
+ // Add text before this tag
98
+ if (tag.startIndex > currentIndex) {
99
+ segments.push({
100
+ type: 'text',
101
+ content: text.slice(currentIndex, tag.startIndex)
102
+ });
103
+ }
104
+ // Add the tag segment (skip hidden tags in output)
105
+ if (!tag.isHidden) {
106
+ segments.push({
107
+ type: 'tag',
108
+ content: tag.value,
109
+ tag: tag
110
+ });
111
+ }
112
+ currentIndex = tag.endIndex;
113
+ }
114
+ // Add remaining text after last tag
115
+ if (currentIndex < text.length) {
116
+ segments.push({
117
+ type: 'text',
118
+ content: text.slice(currentIndex)
119
+ });
120
+ }
121
+ return {
122
+ originalText: text,
123
+ tags: tags,
124
+ segments: segments
125
+ };
126
+ }
127
+ // Check if text contains any semantic tags
128
+ function hasSemanticTags(text) {
129
+ if (!text) return false;
130
+ return STANDARD_TAG_PATTERN.test(text) || HIDDEN_TAG_PATTERN.test(text);
131
+ }
132
+ // Get tag type display info (for styling)
133
+ function getTagTypeInfo(type) {
134
+ switch (type) {
135
+ case 'MEASUREMENT':
136
+ return {
137
+ color: 'text-blue-700',
138
+ bgColor: 'bg-blue-100 border-blue-300',
139
+ icon: '📏',
140
+ label: 'Measurement'
141
+ };
142
+ case 'VALUE':
143
+ return {
144
+ color: 'text-emerald-700',
145
+ bgColor: 'bg-emerald-100 border-emerald-300',
146
+ icon: '📝',
147
+ label: 'Value'
148
+ };
149
+ case 'FILE':
150
+ return {
151
+ color: 'text-purple-700',
152
+ bgColor: 'bg-purple-100 border-purple-300',
153
+ icon: '📄',
154
+ label: 'File'
155
+ };
156
+ case 'IMAGE':
157
+ return {
158
+ color: 'text-pink-700',
159
+ bgColor: 'bg-pink-100 border-pink-300',
160
+ icon: '🖼️',
161
+ label: 'Image'
162
+ };
163
+ case 'DATETIME':
164
+ return {
165
+ color: 'text-orange-700',
166
+ bgColor: 'bg-orange-100 border-orange-300',
167
+ icon: '📅',
168
+ label: 'DateTime'
169
+ };
170
+ case 'HIDDEN_MEASUREMENT':
171
+ return {
172
+ color: 'text-gray-500',
173
+ bgColor: 'bg-gray-100 border-gray-300',
174
+ icon: '👁️‍🗨️',
175
+ label: 'Hidden'
176
+ };
177
+ default:
178
+ return {
179
+ color: 'text-gray-700',
180
+ bgColor: 'bg-gray-100 border-gray-300',
181
+ icon: '🏷️',
182
+ label: 'Tag'
183
+ };
184
+ }
185
+ }
186
+
187
+ export { getTagTypeInfo, hasSemanticTags, parseSemanticTags };
@@ -0,0 +1,140 @@
1
+ import { slicedToArray as _slicedToArray } from './_virtual/_rollupPluginBabelHelpers.js';
2
+ import { jsx, jsxs } from 'react/jsx-runtime';
3
+ import { useState } from 'react';
4
+ import { hasSemanticTags, parseSemanticTags, getTagTypeInfo } from './SemanticTagParser.js';
5
+
6
+ // Individual tag badge component
7
+ function TagBadge(_ref) {
8
+ var tag = _ref.tag,
9
+ _ref$showTooltip = _ref.showTooltip,
10
+ showTooltip = _ref$showTooltip === void 0 ? true : _ref$showTooltip,
11
+ onClick = _ref.onClick;
12
+ var _useState = useState(false),
13
+ _useState2 = _slicedToArray(_useState, 2),
14
+ isHovered = _useState2[0],
15
+ setIsHovered = _useState2[1];
16
+ var typeInfo = getTagTypeInfo(tag.type);
17
+ return jsxs("span", {
18
+ className: "relative inline-flex items-center gap-1 px-1.5 py-0.5 mx-0.5 rounded border text-xs font-medium cursor-default transition-all\n ".concat(typeInfo.bgColor, " ").concat(typeInfo.color, "\n ").concat(onClick ? 'cursor-pointer hover:shadow-md' : '', "\n ").concat(isHovered ? 'ring-2 ring-offset-1 ring-blue-400' : ''),
19
+ onMouseEnter: function onMouseEnter() {
20
+ return setIsHovered(true);
21
+ },
22
+ onMouseLeave: function onMouseLeave() {
23
+ return setIsHovered(false);
24
+ },
25
+ onClick: onClick,
26
+ children: [jsx("span", {
27
+ className: "text-[10px]",
28
+ children: typeInfo.icon
29
+ }), jsx("span", {
30
+ children: tag.value
31
+ }), tag.attributes.uom && jsx("span", {
32
+ className: "text-[10px] opacity-75 ml-0.5",
33
+ children: tag.attributes.uom
34
+ }), showTooltip && isHovered && jsx("div", {
35
+ className: "absolute bottom-full left-1/2 -translate-x-1/2 mb-2 z-50 pointer-events-none",
36
+ children: jsxs("div", {
37
+ className: "bg-gray-900 text-white text-xs rounded-lg px-3 py-2 shadow-lg whitespace-nowrap",
38
+ children: [jsxs("div", {
39
+ className: "font-semibold mb-1",
40
+ children: [typeInfo.label, " Tag"]
41
+ }), jsxs("div", {
42
+ className: "space-y-0.5 text-gray-300",
43
+ children: [jsxs("div", {
44
+ children: ["Value: ", jsx("span", {
45
+ className: "text-white",
46
+ children: tag.value
47
+ })]
48
+ }), tag.attributes.uom && jsxs("div", {
49
+ children: ["Unit: ", jsx("span", {
50
+ className: "text-white",
51
+ children: tag.attributes.uom
52
+ })]
53
+ }), tag.attributes.id && jsxs("div", {
54
+ className: "text-[10px] text-gray-400 truncate max-w-[200px]",
55
+ children: ["ID: ", tag.attributes.id]
56
+ }), tag.attributes.format && jsxs("div", {
57
+ children: ["Format: ", jsx("span", {
58
+ className: "text-white",
59
+ children: tag.attributes.format
60
+ })]
61
+ })]
62
+ }), jsx("div", {
63
+ className: "absolute top-full left-1/2 -translate-x-1/2 -mt-px",
64
+ children: jsx("div", {
65
+ className: "border-4 border-transparent border-t-gray-900"
66
+ })
67
+ })]
68
+ })
69
+ })]
70
+ });
71
+ }
72
+ // Main renderer component
73
+ function SemanticTagRenderer(_ref2) {
74
+ var content = _ref2.content,
75
+ _ref2$className = _ref2.className,
76
+ className = _ref2$className === void 0 ? '' : _ref2$className,
77
+ _ref2$showTooltips = _ref2.showTooltips,
78
+ showTooltips = _ref2$showTooltips === void 0 ? true : _ref2$showTooltips,
79
+ onTagClick = _ref2.onTagClick;
80
+ // If no semantic tags, just return the content as-is
81
+ if (!hasSemanticTags(content)) {
82
+ return jsx("span", {
83
+ className: className,
84
+ dangerouslySetInnerHTML: {
85
+ __html: content
86
+ }
87
+ });
88
+ }
89
+ var parsed = parseSemanticTags(content);
90
+ return jsx("span", {
91
+ className: className,
92
+ children: parsed.segments.map(function (segment, index) {
93
+ if (segment.type === 'text') {
94
+ // Render plain text (may contain HTML)
95
+ return jsx("span", {
96
+ dangerouslySetInnerHTML: {
97
+ __html: segment.content
98
+ }
99
+ }, index);
100
+ } else if (segment.type === 'tag' && segment.tag) {
101
+ // Render semantic tag badge
102
+ return jsx(TagBadge, {
103
+ tag: segment.tag,
104
+ showTooltip: showTooltips,
105
+ onClick: onTagClick ? function () {
106
+ return onTagClick(segment.tag);
107
+ } : undefined
108
+ }, index);
109
+ }
110
+ return null;
111
+ })
112
+ });
113
+ }
114
+ function SmartContent(_ref3) {
115
+ var content = _ref3.content,
116
+ _ref3$className = _ref3.className,
117
+ className = _ref3$className === void 0 ? '' : _ref3$className,
118
+ _ref3$enableSemanticT = _ref3.enableSemanticTags,
119
+ enableSemanticTags = _ref3$enableSemanticT === void 0 ? true : _ref3$enableSemanticT,
120
+ _ref3$showTooltips = _ref3.showTooltips,
121
+ showTooltips = _ref3$showTooltips === void 0 ? true : _ref3$showTooltips,
122
+ onTagClick = _ref3.onTagClick;
123
+ if (enableSemanticTags && hasSemanticTags(content)) {
124
+ return jsx(SemanticTagRenderer, {
125
+ content: content,
126
+ className: className,
127
+ showTooltips: showTooltips,
128
+ onTagClick: onTagClick
129
+ });
130
+ }
131
+ // Regular content without semantic tags
132
+ return jsx("span", {
133
+ className: className,
134
+ dangerouslySetInnerHTML: {
135
+ __html: content
136
+ }
137
+ });
138
+ }
139
+
140
+ export { SmartContent, SemanticTagRenderer as default };
@@ -2,14 +2,13 @@ import { slicedToArray as _slicedToArray, asyncToGenerator as _asyncToGenerator,
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
3
  import { useState, useRef, useEffect } from 'react';
4
4
  import { Button } from './ui/button.js';
5
- import { Card, CardHeader, CardTitle, CardContent } from './ui/card.js';
5
+ import { Card, CardContent } from './ui/card.js';
6
6
  import { Tabs, TabsList, TabsTrigger, TabsContent } from './ui/tabs.js';
7
7
  import * as LucideIcons from 'lucide-react';
8
8
  import PdfDocumentViewer from './PdfDocumentViewer.js';
9
9
  import HtmlViewer from './HtmlViewer.js';
10
10
  import { preprocessData } from './helpers/preprocessData.js';
11
11
  import { PanelGroup, Panel, PanelResizeHandle } from 'react-resizable-panels';
12
- import { Badge } from './ui/badge.js';
13
12
  import { Progress } from './ui/progress.js';
14
13
  import { ScrollArea } from './ui/scroll-area.js';
15
14
  import { GET, POST } from './route.js';
@@ -48,7 +47,7 @@ function Structura(_ref) {
48
47
  _useState0 = _slicedToArray(_useState9, 2),
49
48
  fileUrl = _useState0[0],
50
49
  setFileUrl = _useState0[1];
51
- var _useState1 = useState(null),
50
+ var _useState1 = useState(initialJsonData || null),
52
51
  _useState10 = _slicedToArray(_useState1, 2),
53
52
  jsonData = _useState10[0],
54
53
  setJsonData = _useState10[1];
@@ -93,20 +92,18 @@ function Structura(_ref) {
93
92
  _useState30 = _slicedToArray(_useState29, 2),
94
93
  isDraggingOver = _useState30[0],
95
94
  setIsDraggingOver = _useState30[1]; //NOSONAR
96
- // Load initial JSON data if provided (for faster iteration without API calls)
95
+ // Sync initialJsonData prop with state when it changes
97
96
  useEffect(function () {
98
- var _a;
99
97
  if (initialJsonData) {
100
- console.log('[Structura] Loading initial JSON data from props');
101
98
  setJsonData(initialJsonData);
102
- var pageCount = ((_a = initialJsonData.children) === null || _a === void 0 ? void 0 : _a.length) || 0;
103
- setAllowedPages(Array.from({
104
- length: pageCount
105
- }, function (_, i) {
106
- return i + 1;
107
- }));
108
99
  }
109
100
  }, [initialJsonData]);
101
+ // Sync initialPdfPath prop with state when it changes
102
+ useEffect(function () {
103
+ if (initialPdfPath) {
104
+ setFileUrl(initialPdfPath);
105
+ }
106
+ }, [initialPdfPath]);
110
107
  useEffect(function () {
111
108
  var handleDragOver = function handleDragOver(e) {
112
109
  e.preventDefault();
@@ -350,12 +347,10 @@ function Structura(_ref) {
350
347
  // Add logging to the useEffect that watches isFileLoaded
351
348
  useEffect(function () {
352
349
  // console.log("!!! [Structura] isFileLoaded changed:", isFileLoaded);
353
- // Skip API call if we already have initialJsonData (cached mode)
350
+ // Skip auto-generation if we already have JSON data from props
354
351
  if (isFileLoaded && !initialJsonData) {
355
352
  // console.log("!!! [Structura] Calling handleGenerateJSON due to isFileLoaded");
356
353
  handleGenerateJSON();
357
- } else if (isFileLoaded && initialJsonData) {
358
- console.log('[Structura] Skipping API call - using cached JSON data');
359
354
  }
360
355
  }, [isFileLoaded, initialJsonData]);
361
356
  // Handle removing the PDF and resetting state
@@ -600,28 +595,7 @@ function Structura(_ref) {
600
595
  }
601
596
  return jsxs("div", {
602
597
  className: "h-screen max-h-screen flex flex-col bg-background text-foreground",
603
- children: [jsx("header", {
604
- className: "border-b border-border bg-card p-4",
605
- children: jsxs("div", {
606
- className: "container mx-auto flex justify-between items-center",
607
- children: [jsxs("div", {
608
- className: "flex items-center space-x-2",
609
- children: [jsx(LucideIcons.FileText, {
610
- className: "h-6 w-6 text-primary"
611
- }), jsx("h1", {
612
- className: "text-2xl mt-2.5 font-bold",
613
- children: "PLAYGROUND"
614
- })]
615
- }), jsx("div", {
616
- className: "flex items-center gap-2",
617
- children: jsx(Badge, {
618
- variant: "outline",
619
- className: "px-3 py-1 text-xs",
620
- children: isFileLoaded ? "Document Loaded" : "No Document"
621
- })
622
- })]
623
- })
624
- }), jsx("div", {
598
+ children: [jsx("div", {
625
599
  className: "md:hidden",
626
600
  children: jsxs(Tabs, {
627
601
  value: activeTab,
@@ -752,8 +726,7 @@ function Structura(_ref) {
752
726
  jsonData: jsonData,
753
727
  selectedBboxId: selectedBboxId,
754
728
  isLoading: isLoading,
755
- onNodeClick: handleHtmlNodeClick,
756
- onSave: props.onSave
729
+ onNodeClick: handleHtmlNodeClick
757
730
  })
758
731
  })
759
732
  })]
@@ -766,22 +739,10 @@ function Structura(_ref) {
766
739
  children: [jsx(Panel, {
767
740
  minSize: 20,
768
741
  defaultSize: 50,
769
- children: jsxs(Card, {
742
+ children: jsx(Card, {
770
743
  className: "h-full border-0 rounded-none",
771
- children: [jsx(CardHeader, {
772
- className: "py-3 px-4 border-b bg-muted/30",
773
- children: jsxs(CardTitle, {
774
- className: "text-base flex items-center gap-2",
775
- children: [jsx(LucideIcons.FileText, {
776
- className: "h-4 w-4"
777
- }), "PDF Document", file && jsx(Badge, {
778
- variant: "secondary",
779
- className: "ml-2",
780
- children: file.name
781
- })]
782
- })
783
- }), jsx(CardContent, {
784
- className: "p-0 flex-1 flex flex-col overflow-hidden",
744
+ children: jsx(CardContent, {
745
+ className: "p-0 flex-1 flex flex-col overflow-hidden h-full",
785
746
  children: jsx(ScrollArea, {
786
747
  className: "w-full h-[calc(100vh-220px)]",
787
748
  children: fileUrl ? jsx(PdfDocumentViewer, {
@@ -876,33 +837,24 @@ function Structura(_ref) {
876
837
  })
877
838
  })
878
839
  })
879
- })]
840
+ })
880
841
  })
881
842
  }), jsx(PanelResizeHandle, {
882
843
  className: "w-1.5 bg-black hover:bg-primary/20 transition-colors cursor-col-resize"
883
844
  }), jsx(Panel, {
884
845
  minSize: 20,
885
846
  defaultSize: 50,
886
- children: jsxs(Card, {
847
+ children: jsx(Card, {
887
848
  className: "h-full border-0 rounded-none",
888
- children: [jsx(CardHeader, {
889
- className: "py-3 px-4 border-b bg-muted/30",
890
- children: jsxs(CardTitle, {
891
- className: "text-base flex items-center gap-2",
892
- children: [jsx(LucideIcons.FileText, {
893
- className: "h-4 w-4"
894
- }), "HTML Structure"]
895
- })
896
- }), jsx(CardContent, {
897
- className: "p-0 h-[calc(100%-56px)] relative overflow-hidden",
849
+ children: jsx(CardContent, {
850
+ className: "p-0 h-full relative overflow-hidden",
898
851
  children: jsx("div", {
899
852
  className: "absolute inset-0",
900
853
  children: jsonData ? jsx(HtmlViewer, {
901
854
  jsonData: jsonData,
902
855
  selectedBboxId: selectedBboxId,
903
856
  isLoading: isLoading,
904
- onNodeClick: handleHtmlNodeClick,
905
- onSave: props.onSave
857
+ onNodeClick: handleHtmlNodeClick
906
858
  }) : jsx("div", {
907
859
  className: "flex flex-col items-center justify-center h-full p-8 text-center",
908
860
  children: jsxs("div", {
@@ -919,7 +871,7 @@ function Structura(_ref) {
919
871
  })
920
872
  })
921
873
  })
922
- })]
874
+ })
923
875
  })
924
876
  })]
925
877
  })
package/dist/esm/Table.js CHANGED
@@ -35,7 +35,11 @@ function Table(_ref) {
35
35
  hasLlmHtml = _ref$hasLlmHtml === void 0 ? false : _ref$hasLlmHtml,
36
36
  _ref$showJsonIcons = _ref.showJsonIcons,
37
37
  showJsonIcons = _ref$showJsonIcons === void 0 ? true : _ref$showJsonIcons,
38
- onNodeClick = _ref.onNodeClick;
38
+ onNodeClick = _ref.onNodeClick,
39
+ _ref$isEditMode = _ref.isEditMode,
40
+ isEditMode = _ref$isEditMode === void 0 ? false : _ref$isEditMode,
41
+ _ref$isJsonMode = _ref.isJsonMode,
42
+ isJsonMode = _ref$isJsonMode === void 0 ? false : _ref$isJsonMode;
39
43
  var _useState = useState(false),
40
44
  _useState2 = _slicedToArray(_useState, 2),
41
45
  useLlmHtml = _useState2[0],
@@ -61,6 +65,39 @@ function Table(_ref) {
61
65
  var tempDiv = document.createElement("div");
62
66
  tempDiv.innerHTML = getHtmlContent(tableNode);
63
67
  var tableElement = tempDiv.querySelector("table");
68
+ // If no table element found but we have children, reconstruct from children
69
+ if (!tableElement && tableNode.children && tableNode.children.length > 0) {
70
+ var table = document.createElement("table");
71
+ table.className = "w-full border-collapse";
72
+ var tbody = document.createElement("tbody");
73
+ // Determine columns: assume 4 columns for typical signature tables
74
+ var colCount = 4;
75
+ var currentRow = null;
76
+ var cellsInRow = 0;
77
+ tableNode.children.forEach(function (child, index) {
78
+ var _a, _b;
79
+ if (child.block_type === "TableCell") {
80
+ if (cellsInRow === 0 || cellsInRow >= colCount) {
81
+ currentRow = document.createElement("tr");
82
+ tbody.appendChild(currentRow);
83
+ cellsInRow = 0;
84
+ }
85
+ var isHeader = ((_a = child.html) === null || _a === void 0 ? void 0 : _a.includes("<th")) || index < colCount;
86
+ var cell = document.createElement(isHeader ? "th" : "td");
87
+ // Strip wrapper th/td tags from child HTML
88
+ var cellHtml = ((_b = child.html) === null || _b === void 0 ? void 0 : _b.replace(/<\/?t[hd][^>]*>/gi, "")) || "";
89
+ cell.innerHTML = cellHtml;
90
+ cell.className = "px-4 py-2 border border-gray-200 text-left";
91
+ cell.id = child.id;
92
+ currentRow.appendChild(cell);
93
+ cellsInRow++;
94
+ }
95
+ });
96
+ table.appendChild(tbody);
97
+ tempDiv.innerHTML = "";
98
+ tempDiv.appendChild(table);
99
+ tableElement = table;
100
+ }
64
101
  if (!tableElement) return null;
65
102
  // Get all rows
66
103
  var rows = Array.from(tableElement.querySelectorAll("tr"));
@@ -83,14 +120,41 @@ function Table(_ref) {
83
120
  // Combine rows from main table and merged tables
84
121
  var allRows = [].concat(_toConsumableArray(mainTable.rows), mergedRows);
85
122
  // Map cells to children
123
+ var globalCellCounter = 0;
86
124
  var processedRows = allRows.map(function (row, rowIndex) {
87
125
  var cells = Array.from(row.querySelectorAll("th, td"));
88
126
  return cells.map(function (cell, colIndex) {
89
127
  var _a;
90
- var globalCellIndex = rowIndex * cells.length + colIndex;
91
- var childNode = (_a = node.children) === null || _a === void 0 ? void 0 : _a[globalCellIndex];
128
+ var cellElement = cell;
129
+ var childNode = (_a = node.children) === null || _a === void 0 ? void 0 : _a[globalCellCounter];
130
+ globalCellCounter++;
92
131
  var cellContent = cleanHtml(cell.innerHTML);
93
132
  var isHeaderCell = cell.tagName.toLowerCase() === "th"; // Check if this is a header cell
133
+ // Extract rowspan and colspan attributes
134
+ var rowSpan = cellElement.rowSpan > 1 ? cellElement.rowSpan : undefined;
135
+ var colSpan = cellElement.colSpan > 1 ? cellElement.colSpan : undefined;
136
+ // Extract style attribute
137
+ var styleAttr = cellElement.getAttribute("style");
138
+ var cellStyle;
139
+ if (styleAttr) {
140
+ // Parse inline style string to style object
141
+ cellStyle = {};
142
+ styleAttr.split(";").forEach(function (rule) {
143
+ var _rule$split$map = rule.split(":").map(function (s) {
144
+ return s.trim();
145
+ }),
146
+ _rule$split$map2 = _slicedToArray(_rule$split$map, 2),
147
+ property = _rule$split$map2[0],
148
+ value = _rule$split$map2[1];
149
+ if (property && value) {
150
+ // Convert kebab-case to camelCase
151
+ var camelProperty = property.replace(/-([a-z])/g, function (_, letter) {
152
+ return letter.toUpperCase();
153
+ });
154
+ cellStyle[camelProperty] = value;
155
+ }
156
+ });
157
+ }
94
158
  // If we should use TableCell content and we have a childNode with llm_table_html
95
159
  if (shouldUseTableCellContents() && childNode && childNode.llm_table_html !== undefined) {
96
160
  cellContent = useLlmHtml && childNode.llm_table_html ? cleanHtml(childNode.llm_table_html) : cleanHtml(childNode.html || "");
@@ -98,12 +162,15 @@ function Table(_ref) {
98
162
  // TODO: to be fixed later - only apply dubious highlighting if the cell has llm_table_html
99
163
  var isDubious = (childNode === null || childNode === void 0 ? void 0 : childNode.dubious) && (childNode === null || childNode === void 0 ? void 0 : childNode.llm_table_html) !== undefined ? true : false;
100
164
  return {
101
- id: (childNode === null || childNode === void 0 ? void 0 : childNode.id) || "".concat(node.id, "-cell-").concat(globalCellIndex),
165
+ id: (childNode === null || childNode === void 0 ? void 0 : childNode.id) || "".concat(node.id, "-cell-").concat(globalCellCounter),
102
166
  content: cellContent,
103
167
  isHeader: isHeaderCell,
104
168
  isDubious: isDubious,
169
+ rowSpan: rowSpan,
170
+ colSpan: colSpan,
171
+ style: cellStyle,
105
172
  nodeData: childNode || {
106
- id: "".concat(node.id, "-cell-").concat(globalCellIndex),
173
+ id: "".concat(node.id, "-cell-").concat(globalCellCounter),
107
174
  html: cellContent,
108
175
  block_type: "TableCell"
109
176
  }
@@ -148,10 +215,12 @@ function Table(_ref) {
148
215
  }), jsx("div", {
149
216
  className: "w-full overflow-x-auto",
150
217
  children: jsx("table", {
151
- className: "min-w-full divide-y divide-gray-200",
218
+ className: "min-w-full structura-bg-white",
152
219
  children: jsx("tbody", {
220
+ className: "structura-bg-white",
153
221
  children: processedRows.map(function (row, rowIndex) {
154
222
  return jsx("tr", {
223
+ className: "structura-bg-white",
155
224
  children: row.map(function (cell) {
156
225
  return jsx(TableCell, {
157
226
  id: cell.id,
@@ -162,13 +231,18 @@ function Table(_ref) {
162
231
  isSelected: cell.id === selectedBboxId,
163
232
  isHeader: cell.isHeader,
164
233
  isDubious: cell.isDubious,
234
+ rowSpan: cell.rowSpan,
235
+ colSpan: cell.colSpan,
236
+ style: cell.style,
165
237
  onContentChange: onContentChange ? function (newContent) {
166
238
  return handleCellContentChange(cell.id, newContent);
167
239
  } : undefined,
168
240
  showJsonIcons: showJsonIcons,
169
241
  onNodeClick: onNodeClick ? function () {
170
- return onNodeClick(cell.id);
171
- } : undefined
242
+ return onNodeClick(node.id);
243
+ } : undefined,
244
+ isEditMode: isEditMode,
245
+ isJsonMode: isJsonMode
172
246
  }, cell.id);
173
247
  })
174
248
  }, rowIndex);