@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
|
@@ -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 };
|
package/dist/esm/Structura.js
CHANGED
|
@@ -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,
|
|
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
|
-
//
|
|
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
|
|
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("
|
|
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:
|
|
742
|
+
children: jsx(Card, {
|
|
770
743
|
className: "h-full border-0 rounded-none",
|
|
771
|
-
children:
|
|
772
|
-
className: "
|
|
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:
|
|
847
|
+
children: jsx(Card, {
|
|
887
848
|
className: "h-full border-0 rounded-none",
|
|
888
|
-
children:
|
|
889
|
-
className: "
|
|
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
|
|
91
|
-
var childNode = (_a = node.children) === null || _a === void 0 ? void 0 : _a[
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
171
|
-
} : undefined
|
|
242
|
+
return onNodeClick(node.id);
|
|
243
|
+
} : undefined,
|
|
244
|
+
isEditMode: isEditMode,
|
|
245
|
+
isJsonMode: isJsonMode
|
|
172
246
|
}, cell.id);
|
|
173
247
|
})
|
|
174
248
|
}, rowIndex);
|