@tfw.in/structura-lib 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -323
- package/dist/cjs/EditableContent.js +46 -18
- package/dist/cjs/HtmlViewer.js +238 -85
- package/dist/cjs/MathRenderer.js +88 -0
- package/dist/cjs/PdfDocumentViewer.js +1 -1
- package/dist/cjs/SemanticTagParser.js +189 -0
- package/dist/cjs/SemanticTagRenderer.js +135 -0
- package/dist/cjs/Structura.js +49 -76
- package/dist/cjs/Table.js +75 -8
- package/dist/cjs/TableCell.js +34 -10
- package/dist/cjs/index.js +12 -0
- package/dist/cjs/node_modules/react-icons/fa/index.esm.js +6 -0
- package/dist/cjs/styles.css +2 -4
- package/dist/cjs/styles.css.map +1 -1
- package/dist/esm/EditableContent.js +51 -19
- package/dist/esm/HtmlViewer.js +287 -103
- package/dist/esm/MathRenderer.js +85 -0
- package/dist/esm/PdfDocumentViewer.js +1 -1
- package/dist/esm/SemanticTagParser.js +187 -0
- package/dist/esm/SemanticTagRenderer.js +140 -0
- package/dist/esm/Structura.js +57 -80
- package/dist/esm/Table.js +85 -8
- package/dist/esm/TableCell.js +34 -6
- package/dist/esm/index.js +3 -0
- package/dist/esm/node_modules/react-icons/fa/index.esm.js +5 -1
- package/dist/esm/styles.css +2 -4
- package/dist/esm/styles.css.map +1 -1
- package/dist/esm/types/DocumentOutline.d.ts +7 -0
- package/dist/esm/types/EditableContent.d.ts +8 -1
- package/dist/esm/types/HtmlViewer.d.ts +9 -2
- package/dist/esm/types/MathRenderer.d.ts +25 -0
- package/dist/esm/types/SemanticTagParser.d.ts +33 -0
- package/dist/esm/types/SemanticTagRenderer.d.ts +17 -0
- package/dist/esm/types/Structura.d.ts +13 -8
- package/dist/esm/types/Table.d.ts +4 -1
- package/dist/esm/types/TableCell.d.ts +7 -1
- package/dist/esm/types/helpers/index.d.ts +0 -1
- package/dist/esm/types/index.d.ts +3 -0
- package/dist/esm/types/test-app/src/App.d.ts +1 -2
- package/dist/index.d.ts +90 -10
- package/package.json +9 -16
- package/PRODUCTION_ARCHITECTURE.md +0 -511
- package/SAVE_FUNCTIONALITY_COMPLETE.md +0 -448
- package/dist/cjs/ui/badge.js +0 -34
- package/dist/esm/types/helpers/jsonToHtml.d.ts +0 -40
- package/dist/esm/ui/badge.js +0 -31
- package/server/README.md +0 -203
- package/server/db.js +0 -142
- package/server/server.js +0 -165
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Semantic Tag Parser for SOP/Batch Record domain tags
|
|
4
|
+
// Supports: MeasurementTag, HiddenMeasurementTag, ValueTag, DateTimeTag, FileTag, ImageTag
|
|
5
|
+
// Regular expression patterns for tag detection
|
|
6
|
+
// Standard tag: [[value][type:TYPE][attr:value]...]
|
|
7
|
+
// Hidden tag: {[value][type:TYPE][attr:value]...]}
|
|
8
|
+
const STANDARD_TAG_PATTERN = /\[\[([^\]]+)\](\[([^\]]+)\])*\]/g;
|
|
9
|
+
const HIDDEN_TAG_PATTERN = /\{\[([^\]]+)\](\[([^\]]+)\])*\]\}/g;
|
|
10
|
+
// Parse a single tag's attribute string like "[type:MEASUREMENT][uom:mL/min][id:abc-123]"
|
|
11
|
+
function parseTagAttributes(attributeStr) {
|
|
12
|
+
const attributes = {};
|
|
13
|
+
let type = 'VALUE'; // Default type
|
|
14
|
+
// Match all [key:value] pairs
|
|
15
|
+
const attrPattern = /\[([^:\]]+):([^\]]+)\]/g;
|
|
16
|
+
let match;
|
|
17
|
+
while ((match = attrPattern.exec(attributeStr)) !== null) {
|
|
18
|
+
const key = match[1].toLowerCase();
|
|
19
|
+
const value = match[2];
|
|
20
|
+
switch (key) {
|
|
21
|
+
case 'type':
|
|
22
|
+
type = value.toUpperCase();
|
|
23
|
+
break;
|
|
24
|
+
case 'uom':
|
|
25
|
+
attributes.uom = value;
|
|
26
|
+
break;
|
|
27
|
+
case 'id':
|
|
28
|
+
attributes.id = value;
|
|
29
|
+
break;
|
|
30
|
+
case 'format':
|
|
31
|
+
attributes.format = value;
|
|
32
|
+
break;
|
|
33
|
+
case 'filter':
|
|
34
|
+
attributes.filter = value;
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
type,
|
|
40
|
+
attributes
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// Parse a single tag match
|
|
44
|
+
function parseTag(match, isHidden) {
|
|
45
|
+
const rawMatch = match[0];
|
|
46
|
+
const startIndex = match.index;
|
|
47
|
+
const endIndex = startIndex + rawMatch.length;
|
|
48
|
+
// Extract the value (first bracket content) and attributes (remaining brackets)
|
|
49
|
+
let innerContent;
|
|
50
|
+
if (isHidden) {
|
|
51
|
+
// Remove {[ and ]} wrapper
|
|
52
|
+
innerContent = rawMatch.slice(2, -2);
|
|
53
|
+
} else {
|
|
54
|
+
// Remove [[ and ]] wrapper (but there might be more brackets for attributes)
|
|
55
|
+
innerContent = rawMatch.slice(2, -1);
|
|
56
|
+
}
|
|
57
|
+
// Split by ][ to get value and attributes
|
|
58
|
+
const parts = innerContent.split('][');
|
|
59
|
+
const value = parts[0];
|
|
60
|
+
const attributeStr = parts.length > 1 ? '[' + parts.slice(1).join('][') + ']' : '';
|
|
61
|
+
const {
|
|
62
|
+
type,
|
|
63
|
+
attributes
|
|
64
|
+
} = parseTagAttributes(attributeStr);
|
|
65
|
+
// Adjust type for hidden tags
|
|
66
|
+
const finalType = isHidden ? 'HIDDEN_MEASUREMENT' : type;
|
|
67
|
+
return {
|
|
68
|
+
type: finalType,
|
|
69
|
+
value,
|
|
70
|
+
rawMatch,
|
|
71
|
+
startIndex,
|
|
72
|
+
endIndex,
|
|
73
|
+
attributes,
|
|
74
|
+
isHidden
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
// Main parsing function
|
|
78
|
+
function parseSemanticTags(text) {
|
|
79
|
+
const tags = [];
|
|
80
|
+
// Find all standard tags
|
|
81
|
+
let match;
|
|
82
|
+
const standardPattern = new RegExp(STANDARD_TAG_PATTERN.source, 'g');
|
|
83
|
+
while ((match = standardPattern.exec(text)) !== null) {
|
|
84
|
+
tags.push(parseTag(match, false));
|
|
85
|
+
}
|
|
86
|
+
// Find all hidden tags
|
|
87
|
+
const hiddenPattern = new RegExp(HIDDEN_TAG_PATTERN.source, 'g');
|
|
88
|
+
while ((match = hiddenPattern.exec(text)) !== null) {
|
|
89
|
+
tags.push(parseTag(match, true));
|
|
90
|
+
}
|
|
91
|
+
// Sort tags by start index
|
|
92
|
+
tags.sort((a, b) => a.startIndex - b.startIndex);
|
|
93
|
+
// Build segments
|
|
94
|
+
const segments = [];
|
|
95
|
+
let currentIndex = 0;
|
|
96
|
+
for (const tag of tags) {
|
|
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
|
|
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,
|
|
124
|
+
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
|
+
exports.getTagTypeInfo = getTagTypeInfo;
|
|
188
|
+
exports.hasSemanticTags = hasSemanticTags;
|
|
189
|
+
exports.parseSemanticTags = parseSemanticTags;
|
|
@@ -0,0 +1,135 @@
|
|
|
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 SemanticTagParser = require('./SemanticTagParser.js');
|
|
8
|
+
|
|
9
|
+
// Individual tag badge component
|
|
10
|
+
function TagBadge({
|
|
11
|
+
tag,
|
|
12
|
+
showTooltip = true,
|
|
13
|
+
onClick
|
|
14
|
+
}) {
|
|
15
|
+
const [isHovered, setIsHovered] = React.useState(false);
|
|
16
|
+
const typeInfo = SemanticTagParser.getTagTypeInfo(tag.type);
|
|
17
|
+
return jsxRuntime.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
|
|
19
|
+
${typeInfo.bgColor} ${typeInfo.color}
|
|
20
|
+
${onClick ? 'cursor-pointer hover:shadow-md' : ''}
|
|
21
|
+
${isHovered ? 'ring-2 ring-offset-1 ring-blue-400' : ''}`,
|
|
22
|
+
onMouseEnter: () => setIsHovered(true),
|
|
23
|
+
onMouseLeave: () => setIsHovered(false),
|
|
24
|
+
onClick: onClick,
|
|
25
|
+
children: [jsxRuntime.jsx("span", {
|
|
26
|
+
className: "text-[10px]",
|
|
27
|
+
children: typeInfo.icon
|
|
28
|
+
}), jsxRuntime.jsx("span", {
|
|
29
|
+
children: tag.value
|
|
30
|
+
}), tag.attributes.uom && jsxRuntime.jsx("span", {
|
|
31
|
+
className: "text-[10px] opacity-75 ml-0.5",
|
|
32
|
+
children: tag.attributes.uom
|
|
33
|
+
}), showTooltip && isHovered && jsxRuntime.jsx("div", {
|
|
34
|
+
className: "absolute bottom-full left-1/2 -translate-x-1/2 mb-2 z-50 pointer-events-none",
|
|
35
|
+
children: jsxRuntime.jsxs("div", {
|
|
36
|
+
className: "bg-gray-900 text-white text-xs rounded-lg px-3 py-2 shadow-lg whitespace-nowrap",
|
|
37
|
+
children: [jsxRuntime.jsxs("div", {
|
|
38
|
+
className: "font-semibold mb-1",
|
|
39
|
+
children: [typeInfo.label, " Tag"]
|
|
40
|
+
}), jsxRuntime.jsxs("div", {
|
|
41
|
+
className: "space-y-0.5 text-gray-300",
|
|
42
|
+
children: [jsxRuntime.jsxs("div", {
|
|
43
|
+
children: ["Value: ", jsxRuntime.jsx("span", {
|
|
44
|
+
className: "text-white",
|
|
45
|
+
children: tag.value
|
|
46
|
+
})]
|
|
47
|
+
}), tag.attributes.uom && jsxRuntime.jsxs("div", {
|
|
48
|
+
children: ["Unit: ", jsxRuntime.jsx("span", {
|
|
49
|
+
className: "text-white",
|
|
50
|
+
children: tag.attributes.uom
|
|
51
|
+
})]
|
|
52
|
+
}), tag.attributes.id && jsxRuntime.jsxs("div", {
|
|
53
|
+
className: "text-[10px] text-gray-400 truncate max-w-[200px]",
|
|
54
|
+
children: ["ID: ", tag.attributes.id]
|
|
55
|
+
}), tag.attributes.format && jsxRuntime.jsxs("div", {
|
|
56
|
+
children: ["Format: ", jsxRuntime.jsx("span", {
|
|
57
|
+
className: "text-white",
|
|
58
|
+
children: tag.attributes.format
|
|
59
|
+
})]
|
|
60
|
+
})]
|
|
61
|
+
}), jsxRuntime.jsx("div", {
|
|
62
|
+
className: "absolute top-full left-1/2 -translate-x-1/2 -mt-px",
|
|
63
|
+
children: jsxRuntime.jsx("div", {
|
|
64
|
+
className: "border-4 border-transparent border-t-gray-900"
|
|
65
|
+
})
|
|
66
|
+
})]
|
|
67
|
+
})
|
|
68
|
+
})]
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
// Main renderer component
|
|
72
|
+
function SemanticTagRenderer({
|
|
73
|
+
content,
|
|
74
|
+
className = '',
|
|
75
|
+
showTooltips = true,
|
|
76
|
+
onTagClick
|
|
77
|
+
}) {
|
|
78
|
+
// If no semantic tags, just return the content as-is
|
|
79
|
+
if (!SemanticTagParser.hasSemanticTags(content)) {
|
|
80
|
+
return jsxRuntime.jsx("span", {
|
|
81
|
+
className: className,
|
|
82
|
+
dangerouslySetInnerHTML: {
|
|
83
|
+
__html: content
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
const parsed = SemanticTagParser.parseSemanticTags(content);
|
|
88
|
+
return jsxRuntime.jsx("span", {
|
|
89
|
+
className: className,
|
|
90
|
+
children: parsed.segments.map((segment, index) => {
|
|
91
|
+
if (segment.type === 'text') {
|
|
92
|
+
// Render plain text (may contain HTML)
|
|
93
|
+
return jsxRuntime.jsx("span", {
|
|
94
|
+
dangerouslySetInnerHTML: {
|
|
95
|
+
__html: segment.content
|
|
96
|
+
}
|
|
97
|
+
}, index);
|
|
98
|
+
} else if (segment.type === 'tag' && segment.tag) {
|
|
99
|
+
// Render semantic tag badge
|
|
100
|
+
return jsxRuntime.jsx(TagBadge, {
|
|
101
|
+
tag: segment.tag,
|
|
102
|
+
showTooltip: showTooltips,
|
|
103
|
+
onClick: onTagClick ? () => onTagClick(segment.tag) : undefined
|
|
104
|
+
}, index);
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
})
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function SmartContent({
|
|
111
|
+
content,
|
|
112
|
+
className = '',
|
|
113
|
+
enableSemanticTags = true,
|
|
114
|
+
showTooltips = true,
|
|
115
|
+
onTagClick
|
|
116
|
+
}) {
|
|
117
|
+
if (enableSemanticTags && SemanticTagParser.hasSemanticTags(content)) {
|
|
118
|
+
return jsxRuntime.jsx(SemanticTagRenderer, {
|
|
119
|
+
content: content,
|
|
120
|
+
className: className,
|
|
121
|
+
showTooltips: showTooltips,
|
|
122
|
+
onTagClick: onTagClick
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
// Regular content without semantic tags
|
|
126
|
+
return jsxRuntime.jsx("span", {
|
|
127
|
+
className: className,
|
|
128
|
+
dangerouslySetInnerHTML: {
|
|
129
|
+
__html: content
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
exports.SmartContent = SmartContent;
|
|
135
|
+
exports.default = SemanticTagRenderer;
|
package/dist/cjs/Structura.js
CHANGED
|
@@ -12,7 +12,6 @@ var PdfDocumentViewer = require('./PdfDocumentViewer.js');
|
|
|
12
12
|
var HtmlViewer = require('./HtmlViewer.js');
|
|
13
13
|
var preprocessData = require('./helpers/preprocessData.js');
|
|
14
14
|
var reactResizablePanels = require('react-resizable-panels');
|
|
15
|
-
var badge = require('./ui/badge.js');
|
|
16
15
|
var progress = require('./ui/progress.js');
|
|
17
16
|
var scrollArea = require('./ui/scroll-area.js');
|
|
18
17
|
var route = require('./route.js');
|
|
@@ -39,16 +38,19 @@ var LucideIcons__namespace = /*#__PURE__*/_interopNamespaceDefault(LucideIcons);
|
|
|
39
38
|
// Remove component aliases and use direct imports
|
|
40
39
|
// This will preserve the propser types
|
|
41
40
|
function Structura({
|
|
41
|
+
apiKey,
|
|
42
|
+
baseUrl,
|
|
42
43
|
initialPdfPath,
|
|
43
44
|
initialJsonData,
|
|
44
|
-
|
|
45
|
+
editMode = true,
|
|
46
|
+
jsonMode = true,
|
|
47
|
+
mathRendering = true,
|
|
48
|
+
semanticTags = true,
|
|
49
|
+
headerFooterBadges = true,
|
|
50
|
+
defaultViewMode = 'read',
|
|
51
|
+
onContentChange,
|
|
52
|
+
onExport
|
|
45
53
|
}) {
|
|
46
|
-
// Log the API key (for development only - remove in production)
|
|
47
|
-
// useEffect(() => {
|
|
48
|
-
// if (props?.APIKey) {
|
|
49
|
-
// console.log("API Key provided:", props.APIKey);
|
|
50
|
-
// }
|
|
51
|
-
// }, [props]);
|
|
52
54
|
// Log the imported SDK items to demonstrate usage
|
|
53
55
|
// console.log("Imported BlockSchema from SDK:", BlockSchema);
|
|
54
56
|
const [numPages, setNumPages] = React.useState(null);
|
|
@@ -56,7 +58,7 @@ function Structura({
|
|
|
56
58
|
const [selectedField, setSelectedField] = React.useState(null);
|
|
57
59
|
const [file, setFile] = React.useState(null);
|
|
58
60
|
const [fileUrl, setFileUrl] = React.useState(initialPdfPath || null);
|
|
59
|
-
const [jsonData, setJsonData] = React.useState(null);
|
|
61
|
+
const [jsonData, setJsonData] = React.useState(initialJsonData || null);
|
|
60
62
|
const [allowedPages, setAllowedPages] = React.useState(null);
|
|
61
63
|
const pdfWrapperRef = React.useRef(null);
|
|
62
64
|
const [flattenedFields, setFlattenedFields] = React.useState([]);
|
|
@@ -68,18 +70,18 @@ function Structura({
|
|
|
68
70
|
const [activeTab, setActiveTab] = React.useState("pdf");
|
|
69
71
|
const [backendStatus, setBackendStatus] = React.useState('checking');
|
|
70
72
|
const [isDraggingOver, setIsDraggingOver] = React.useState(false); //NOSONAR
|
|
71
|
-
//
|
|
73
|
+
// Sync initialJsonData prop with state when it changes
|
|
72
74
|
React.useEffect(() => {
|
|
73
|
-
var _a;
|
|
74
75
|
if (initialJsonData) {
|
|
75
|
-
console.log('[Structura] Loading initial JSON data from props');
|
|
76
76
|
setJsonData(initialJsonData);
|
|
77
|
-
const pageCount = ((_a = initialJsonData.children) === null || _a === void 0 ? void 0 : _a.length) || 0;
|
|
78
|
-
setAllowedPages(Array.from({
|
|
79
|
-
length: pageCount
|
|
80
|
-
}, (_, i) => i + 1));
|
|
81
77
|
}
|
|
82
78
|
}, [initialJsonData]);
|
|
79
|
+
// Sync initialPdfPath prop with state when it changes
|
|
80
|
+
React.useEffect(() => {
|
|
81
|
+
if (initialPdfPath) {
|
|
82
|
+
setFileUrl(initialPdfPath);
|
|
83
|
+
}
|
|
84
|
+
}, [initialPdfPath]);
|
|
83
85
|
React.useEffect(() => {
|
|
84
86
|
const handleDragOver = e => {
|
|
85
87
|
e.preventDefault();
|
|
@@ -273,12 +275,10 @@ function Structura({
|
|
|
273
275
|
// Add logging to the useEffect that watches isFileLoaded
|
|
274
276
|
React.useEffect(() => {
|
|
275
277
|
// console.log("!!! [Structura] isFileLoaded changed:", isFileLoaded);
|
|
276
|
-
// Skip
|
|
278
|
+
// Skip auto-generation if we already have JSON data from props
|
|
277
279
|
if (isFileLoaded && !initialJsonData) {
|
|
278
280
|
// console.log("!!! [Structura] Calling handleGenerateJSON due to isFileLoaded");
|
|
279
281
|
handleGenerateJSON();
|
|
280
|
-
} else if (isFileLoaded && initialJsonData) {
|
|
281
|
-
console.log('[Structura] Skipping API call - using cached JSON data');
|
|
282
282
|
}
|
|
283
283
|
}, [isFileLoaded, initialJsonData]);
|
|
284
284
|
// Handle removing the PDF and resetting state
|
|
@@ -304,12 +304,12 @@ function Structura({
|
|
|
304
304
|
const formData = new FormData();
|
|
305
305
|
formData.append("file", file);
|
|
306
306
|
// console.log("!!! [Structura] FormData:", formData);
|
|
307
|
-
if (!
|
|
307
|
+
if (!apiKey) {
|
|
308
308
|
throw new Error("API key is required but not provided");
|
|
309
309
|
}
|
|
310
310
|
const submitResponse = await route.POST(formData, {
|
|
311
|
-
apiKey:
|
|
312
|
-
baseUrl:
|
|
311
|
+
apiKey: apiKey,
|
|
312
|
+
baseUrl: baseUrl
|
|
313
313
|
});
|
|
314
314
|
console.log("API response status:", submitResponse.status);
|
|
315
315
|
if (submitResponse.status !== 200) {
|
|
@@ -330,8 +330,8 @@ function Structura({
|
|
|
330
330
|
let resultData = null;
|
|
331
331
|
while (true) {
|
|
332
332
|
const resultResponse = await route.GET(requestId, {
|
|
333
|
-
apiKey:
|
|
334
|
-
baseUrl:
|
|
333
|
+
apiKey: apiKey,
|
|
334
|
+
baseUrl: baseUrl
|
|
335
335
|
});
|
|
336
336
|
if (resultResponse.data.status === "completed") {
|
|
337
337
|
// Store the response data
|
|
@@ -455,28 +455,7 @@ function Structura({
|
|
|
455
455
|
}
|
|
456
456
|
return jsxRuntime.jsxs("div", {
|
|
457
457
|
className: "h-screen max-h-screen flex flex-col bg-background text-foreground",
|
|
458
|
-
children: [jsxRuntime.jsx("
|
|
459
|
-
className: "border-b border-border bg-card p-4",
|
|
460
|
-
children: jsxRuntime.jsxs("div", {
|
|
461
|
-
className: "container mx-auto flex justify-between items-center",
|
|
462
|
-
children: [jsxRuntime.jsxs("div", {
|
|
463
|
-
className: "flex items-center space-x-2",
|
|
464
|
-
children: [jsxRuntime.jsx(LucideIcons__namespace.FileText, {
|
|
465
|
-
className: "h-6 w-6 text-primary"
|
|
466
|
-
}), jsxRuntime.jsx("h1", {
|
|
467
|
-
className: "text-2xl mt-2.5 font-bold",
|
|
468
|
-
children: "PLAYGROUND"
|
|
469
|
-
})]
|
|
470
|
-
}), jsxRuntime.jsx("div", {
|
|
471
|
-
className: "flex items-center gap-2",
|
|
472
|
-
children: jsxRuntime.jsx(badge.Badge, {
|
|
473
|
-
variant: "outline",
|
|
474
|
-
className: "px-3 py-1 text-xs",
|
|
475
|
-
children: isFileLoaded ? "Document Loaded" : "No Document"
|
|
476
|
-
})
|
|
477
|
-
})]
|
|
478
|
-
})
|
|
479
|
-
}), jsxRuntime.jsx("div", {
|
|
458
|
+
children: [jsxRuntime.jsx("div", {
|
|
480
459
|
className: "md:hidden",
|
|
481
460
|
children: jsxRuntime.jsxs(tabs.Tabs, {
|
|
482
461
|
value: activeTab,
|
|
@@ -608,7 +587,14 @@ function Structura({
|
|
|
608
587
|
selectedBboxId: selectedBboxId,
|
|
609
588
|
isLoading: isLoading,
|
|
610
589
|
onNodeClick: handleHtmlNodeClick,
|
|
611
|
-
|
|
590
|
+
editMode: editMode,
|
|
591
|
+
jsonMode: jsonMode,
|
|
592
|
+
mathRendering: mathRendering,
|
|
593
|
+
semanticTags: semanticTags,
|
|
594
|
+
headerFooterBadges: headerFooterBadges,
|
|
595
|
+
defaultViewMode: defaultViewMode,
|
|
596
|
+
onContentChange: onContentChange,
|
|
597
|
+
onExport: onExport
|
|
612
598
|
})
|
|
613
599
|
})
|
|
614
600
|
})]
|
|
@@ -621,22 +607,10 @@ function Structura({
|
|
|
621
607
|
children: [jsxRuntime.jsx(reactResizablePanels.Panel, {
|
|
622
608
|
minSize: 20,
|
|
623
609
|
defaultSize: 50,
|
|
624
|
-
children: jsxRuntime.
|
|
610
|
+
children: jsxRuntime.jsx(card.Card, {
|
|
625
611
|
className: "h-full border-0 rounded-none",
|
|
626
|
-
children:
|
|
627
|
-
className: "
|
|
628
|
-
children: jsxRuntime.jsxs(card.CardTitle, {
|
|
629
|
-
className: "text-base flex items-center gap-2",
|
|
630
|
-
children: [jsxRuntime.jsx(LucideIcons__namespace.FileText, {
|
|
631
|
-
className: "h-4 w-4"
|
|
632
|
-
}), "PDF Document", file && jsxRuntime.jsx(badge.Badge, {
|
|
633
|
-
variant: "secondary",
|
|
634
|
-
className: "ml-2",
|
|
635
|
-
children: file.name
|
|
636
|
-
})]
|
|
637
|
-
})
|
|
638
|
-
}), jsxRuntime.jsx(card.CardContent, {
|
|
639
|
-
className: "p-0 flex-1 flex flex-col overflow-hidden",
|
|
612
|
+
children: jsxRuntime.jsx(card.CardContent, {
|
|
613
|
+
className: "p-0 flex-1 flex flex-col overflow-hidden h-full",
|
|
640
614
|
children: jsxRuntime.jsx(scrollArea.ScrollArea, {
|
|
641
615
|
className: "w-full h-[calc(100vh-220px)]",
|
|
642
616
|
children: fileUrl ? jsxRuntime.jsx(PdfDocumentViewer.default, {
|
|
@@ -731,25 +705,17 @@ function Structura({
|
|
|
731
705
|
})
|
|
732
706
|
})
|
|
733
707
|
})
|
|
734
|
-
})
|
|
708
|
+
})
|
|
735
709
|
})
|
|
736
710
|
}), jsxRuntime.jsx(reactResizablePanels.PanelResizeHandle, {
|
|
737
711
|
className: "w-1.5 bg-black hover:bg-primary/20 transition-colors cursor-col-resize"
|
|
738
712
|
}), jsxRuntime.jsx(reactResizablePanels.Panel, {
|
|
739
713
|
minSize: 20,
|
|
740
714
|
defaultSize: 50,
|
|
741
|
-
children: jsxRuntime.
|
|
715
|
+
children: jsxRuntime.jsx(card.Card, {
|
|
742
716
|
className: "h-full border-0 rounded-none",
|
|
743
|
-
children:
|
|
744
|
-
className: "
|
|
745
|
-
children: jsxRuntime.jsxs(card.CardTitle, {
|
|
746
|
-
className: "text-base flex items-center gap-2",
|
|
747
|
-
children: [jsxRuntime.jsx(LucideIcons__namespace.FileText, {
|
|
748
|
-
className: "h-4 w-4"
|
|
749
|
-
}), "HTML Structure"]
|
|
750
|
-
})
|
|
751
|
-
}), jsxRuntime.jsx(card.CardContent, {
|
|
752
|
-
className: "p-0 h-[calc(100%-56px)] relative overflow-hidden",
|
|
717
|
+
children: jsxRuntime.jsx(card.CardContent, {
|
|
718
|
+
className: "p-0 h-full relative overflow-hidden",
|
|
753
719
|
children: jsxRuntime.jsx("div", {
|
|
754
720
|
className: "absolute inset-0",
|
|
755
721
|
children: jsonData ? jsxRuntime.jsx(HtmlViewer.default, {
|
|
@@ -757,7 +723,14 @@ function Structura({
|
|
|
757
723
|
selectedBboxId: selectedBboxId,
|
|
758
724
|
isLoading: isLoading,
|
|
759
725
|
onNodeClick: handleHtmlNodeClick,
|
|
760
|
-
|
|
726
|
+
editMode: editMode,
|
|
727
|
+
jsonMode: jsonMode,
|
|
728
|
+
mathRendering: mathRendering,
|
|
729
|
+
semanticTags: semanticTags,
|
|
730
|
+
headerFooterBadges: headerFooterBadges,
|
|
731
|
+
defaultViewMode: defaultViewMode,
|
|
732
|
+
onContentChange: onContentChange,
|
|
733
|
+
onExport: onExport
|
|
761
734
|
}) : jsxRuntime.jsx("div", {
|
|
762
735
|
className: "flex flex-col items-center justify-center h-full p-8 text-center",
|
|
763
736
|
children: jsxRuntime.jsxs("div", {
|
|
@@ -774,7 +747,7 @@ function Structura({
|
|
|
774
747
|
})
|
|
775
748
|
})
|
|
776
749
|
})
|
|
777
|
-
})
|
|
750
|
+
})
|
|
778
751
|
})
|
|
779
752
|
})]
|
|
780
753
|
})
|