@tfw.in/structura-lib 0.2.1 → 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 CHANGED
@@ -1,27 +1,128 @@
1
- # Structura Library
1
+ # @tfw.in/structura-lib
2
2
 
3
- A React component library for document viewing and processing.
3
+ A React component library for PDF document viewing with structured data extraction and rendering.
4
+
5
+ ## Features
6
+
7
+ - **PDF & JSON Side-by-Side Viewing** - View original PDF alongside extracted structured content
8
+ - **Edit Mode** - Inline editing of extracted content
9
+ - **Math Rendering** - LaTeX math expressions rendered via KaTeX (`$...$` inline, `$$...$$` display)
10
+ - **Semantic Tags** - Visual highlighting for corrections, additions, and deletions
11
+ - **Header/Footer Detection** - Automatic badges for header and footer content
12
+ - **Table Support** - Rich table rendering with cell-level editing
4
13
 
5
14
  ## Installation
6
15
 
7
16
  ```bash
8
- npm install structura_lib
17
+ npm install @tfw.in/structura-lib
9
18
  ```
10
19
 
11
20
  ## Usage
12
21
 
13
22
  ```jsx
14
- import { Structura } from 'structura-lib';
15
- import 'structura_lib/dist/styles.css'
23
+ import { Structura } from '@tfw.in/structura-lib';
24
+ import '@tfw.in/structura-lib/dist/esm/styles.css';
25
+
26
+ function App() {
27
+ return (
28
+ <Structura
29
+ apiKey="your-api-key"
30
+ baseUrl="https://api.example.com"
31
+ />
32
+ );
33
+ }
34
+ ```
35
+
36
+ ## Props
37
+
38
+ | Prop | Type | Default | Description |
39
+ |------|------|---------|-------------|
40
+ | `apiKey` | `string` | **required** | API key for authentication |
41
+ | `baseUrl` | `string` | `undefined` | Optional API base URL |
42
+ | `initialPdfPath` | `string \| null` | `null` | Initial PDF file path to load |
43
+ | `initialJsonData` | `any` | `null` | Initial JSON data to display |
44
+ | `editMode` | `boolean` | `true` | Enable/disable edit mode toggle |
45
+ | `jsonMode` | `boolean` | `true` | Enable/disable JSON view mode toggle |
46
+ | `mathRendering` | `boolean` | `true` | Enable LaTeX math rendering |
47
+ | `semanticTags` | `boolean` | `true` | Enable/disable semantic tags toggle |
48
+ | `headerFooterBadges` | `boolean` | `true` | Show header/footer badges |
49
+ | `defaultViewMode` | `'read' \| 'edit' \| 'json'` | `'read'` | Initial view mode |
50
+ | `onContentChange` | `function` | `undefined` | Callback when content is edited |
51
+ | `onExport` | `function` | `undefined` | Callback when data is exported |
52
+
53
+ ## Full Example
54
+
55
+ ```jsx
56
+ import { Structura } from '@tfw.in/structura-lib';
57
+ import '@tfw.in/structura-lib/dist/esm/styles.css';
16
58
 
17
59
  function App() {
60
+ const handleContentChange = (blockId, oldContent, newContent) => {
61
+ console.log(`Block ${blockId} changed`);
62
+ console.log('Old:', oldContent);
63
+ console.log('New:', newContent);
64
+ };
65
+
66
+ const handleExport = (data) => {
67
+ console.log('Exported data:', data);
68
+ // Save to your backend, etc.
69
+ };
70
+
18
71
  return (
19
- <Structura
20
- props={{
21
- APIKey: "your-api-key"
22
- baseURL: "optional-api-base-url"
23
- }}
72
+ <Structura
73
+ apiKey="your-api-key"
74
+ baseUrl="https://api.example.com"
75
+ editMode={true}
76
+ jsonMode={true}
77
+ mathRendering={true}
78
+ semanticTags={true}
79
+ headerFooterBadges={true}
80
+ defaultViewMode="read"
81
+ onContentChange={handleContentChange}
82
+ onExport={handleExport}
24
83
  />
25
84
  );
26
85
  }
27
86
  ```
87
+
88
+ ## Math Rendering
89
+
90
+ The library automatically renders LaTeX math expressions in content:
91
+
92
+ - Inline math: `$x^2 + y^2 = z^2$`
93
+ - Display math: `$$\sum_{i=1}^{n} x_i$$`
94
+
95
+ You can also use the math utilities directly:
96
+
97
+ ```jsx
98
+ import { MathContent, renderMathInHtml, containsMath } from '@tfw.in/structura-lib';
99
+
100
+ // React component
101
+ <MathContent html="The formula is $E = mc^2$" />
102
+
103
+ // Utility function
104
+ const rendered = renderMathInHtml("Price is $20$ dollars");
105
+
106
+ // Check for math content
107
+ if (containsMath(text)) {
108
+ // handle math
109
+ }
110
+ ```
111
+
112
+ ## Semantic Tags
113
+
114
+ Parse and render semantic tags for document corrections:
115
+
116
+ ```jsx
117
+ import { SemanticTagRenderer, parseSemanticTags } from '@tfw.in/structura-lib';
118
+
119
+ // Render with visual highlighting
120
+ <SemanticTagRenderer content="Text with <add>additions</add> and <del>deletions</del>" />
121
+
122
+ // Parse tags programmatically
123
+ const parsed = parseSemanticTags(content);
124
+ ```
125
+
126
+ ## License
127
+
128
+ MIT
@@ -33,7 +33,8 @@ function EditableContent({
33
33
  onNodeClick,
34
34
  onJsonClick,
35
35
  enableSemanticTags = true,
36
- onSemanticTagClick
36
+ onSemanticTagClick,
37
+ enableMathRendering = true
37
38
  }) {
38
39
  const [isEditing, setIsEditing] = React.useState(false);
39
40
  const [editedContent, setEditedContent] = React.useState(content);
@@ -142,10 +143,10 @@ function EditableContent({
142
143
  handleBlur();
143
144
  }
144
145
  };
145
- // Render math expressions in content
146
+ // Render math expressions in content (conditionally)
146
147
  const renderedContent = React.useMemo(() => {
147
- return MathRenderer.renderMathInHtml(editedContent);
148
- }, [editedContent]);
148
+ return enableMathRendering ? MathRenderer.renderMathInHtml(editedContent) : editedContent;
149
+ }, [editedContent, enableMathRendering]);
149
150
  return jsxRuntime.jsx("div", {
150
151
  className: `w-full ${isEdited ? "bg-yellow-100" : ""} ${isJsonMode ? "cursor-pointer hover:bg-purple-50 border border-transparent hover:border-purple-300 rounded" : ""} ${isEditMode && !isEditing ? "cursor-pointer hover:bg-blue-50 border border-transparent hover:border-blue-300 rounded" : ""} ${onNodeClick && !isEditing && !isEditMode && !isJsonMode ? "cursor-pointer" : ""}`,
151
152
  onClick: handleClick,
@@ -249,13 +249,21 @@ function HtmlViewer({
249
249
  jsonData,
250
250
  selectedBboxId,
251
251
  isLoading,
252
- onNodeClick
252
+ onNodeClick,
253
+ editMode: enableEditMode = true,
254
+ jsonMode: enableJsonMode = true,
255
+ mathRendering: enableMathRendering = true,
256
+ semanticTags: enableSemanticTags = true,
257
+ headerFooterBadges: enableHeaderFooterBadges = true,
258
+ defaultViewMode = 'read',
259
+ onContentChange: onContentChangeCallback,
260
+ onExport: onExportCallback
253
261
  }) {
254
262
  const [editedData, setEditedData] = React.useState(jsonData);
255
263
  const [hasChanges, setHasChanges] = React.useState(false);
256
264
  const [isModalOpen, setIsModalOpen] = React.useState(false);
257
265
  const [modalData, setModalData] = React.useState(null);
258
- const [viewMode, setViewMode] = React.useState('read');
266
+ const [viewMode, setViewMode] = React.useState(defaultViewMode);
259
267
  const [accuracyMetrics$1, setAccuracyMetrics] = React.useState({
260
268
  totalChanges: 0,
261
269
  totalCharactersEdited: 0,
@@ -267,7 +275,7 @@ function HtmlViewer({
267
275
  const [isAnalyticsOpen, setIsAnalyticsOpen] = React.useState(false);
268
276
  const [isTagModalOpen, setIsTagModalOpen] = React.useState(false);
269
277
  const [selectedTag, setSelectedTag] = React.useState(null);
270
- const [showSemanticTags, setShowSemanticTags] = React.useState(true);
278
+ const [showSemanticTags, setShowSemanticTags] = React.useState(enableSemanticTags);
271
279
  React.useEffect(() => {
272
280
  // Reset state when jsonData changes
273
281
  // console.log("HtmlViewer received new jsonData");
@@ -333,12 +341,13 @@ function HtmlViewer({
333
341
  }, [jsonData]);
334
342
  const handleContentChange = React.useCallback((nodeId, newContent) => {
335
343
  setHasChanges(true);
344
+ let originalContent = "";
336
345
  setEditedData(prevData => {
337
346
  const newData = JSON.parse(JSON.stringify(prevData));
338
347
  const updateNode = node => {
339
348
  if (!node) return false;
340
349
  if (node.id === nodeId) {
341
- const originalContent = node.html || "";
350
+ originalContent = node.html || "";
342
351
  const blockType = node.block_type || "unknown";
343
352
  // Update accuracy metrics before changing content
344
353
  updateAccuracyMetrics(nodeId, blockType, originalContent, newContent);
@@ -355,9 +364,17 @@ function HtmlViewer({
355
364
  updateNode(newData);
356
365
  return newData;
357
366
  });
358
- }, [updateAccuracyMetrics]);
367
+ // Call external callback if provided
368
+ if (onContentChangeCallback) {
369
+ onContentChangeCallback(nodeId, originalContent, newContent);
370
+ }
371
+ }, [updateAccuracyMetrics, onContentChangeCallback]);
359
372
  const handleDownload = () => {
360
373
  if (!editedData) return;
374
+ // Call external callback if provided
375
+ if (onExportCallback) {
376
+ onExportCallback(editedData);
377
+ }
361
378
  const jsonString = JSON.stringify(editedData, null, 2);
362
379
  const blob = new Blob([jsonString], {
363
380
  type: "application/json"
@@ -556,7 +573,8 @@ function HtmlViewer({
556
573
  showJsonIcons: false,
557
574
  onNodeClick: onNodeClick,
558
575
  isEditMode: viewMode === 'edit',
559
- isJsonMode: viewMode === 'json'
576
+ isJsonMode: viewMode === 'json',
577
+ enableMathRendering: enableMathRendering
560
578
  }) : isFigure && node.images ? jsxRuntime.jsx("div", {
561
579
  className: `my-2 ${viewMode === 'json' ? 'cursor-pointer hover:opacity-80' : ''}`,
562
580
  onClick: viewMode === 'json' ? () => handleJsonClick(node) : undefined,
@@ -565,7 +583,7 @@ function HtmlViewer({
565
583
  alt: `Figure ${node.id}`,
566
584
  className: "max-w-full h-auto rounded border border-gray-200"
567
585
  }, key))
568
- }) : isPageHeader || isPageFooter ? jsxRuntime.jsxs("div", {
586
+ }) : (isPageHeader || isPageFooter) && enableHeaderFooterBadges ? jsxRuntime.jsxs("div", {
569
587
  className: `my-1 px-3 py-1.5 rounded-md border text-xs flex items-center gap-2
570
588
  ${isPageHeader ? 'bg-amber-50 border-amber-200 text-amber-800' : 'bg-slate-50 border-slate-200 text-slate-600'}
571
589
  ${viewMode === 'json' ? 'cursor-pointer hover:opacity-80' : ''}`,
@@ -594,7 +612,8 @@ function HtmlViewer({
594
612
  onJsonClick: () => handleJsonClick(node),
595
613
  onNodeClick: onNodeClick && !isPage ? () => onNodeClick(node.id) : undefined,
596
614
  enableSemanticTags: showSemanticTags,
597
- onSemanticTagClick: handleSemanticTagClick
615
+ onSemanticTagClick: handleSemanticTagClick,
616
+ enableMathRendering: enableMathRendering
598
617
  }), hasChildren && jsxRuntime.jsx("div", {
599
618
  className: "ml-2 mt-1 border-l border-gray-200 pl-2 max-w-full overflow-hidden",
600
619
  children: node.children.map(child => renderHtmlContent(child))
@@ -622,9 +641,9 @@ function HtmlViewer({
622
641
  className: "sticky top-0 z-20 bg-white border-b border-gray-200 p-3 flex justify-between items-center flex-shrink-0",
623
642
  children: [jsxRuntime.jsxs("div", {
624
643
  className: "flex gap-1",
625
- children: [jsxRuntime.jsxs("div", {
644
+ children: [(enableJsonMode || enableEditMode) && jsxRuntime.jsxs("div", {
626
645
  className: "flex bg-gray-100 rounded-lg p-1",
627
- children: [jsxRuntime.jsxs("button", {
646
+ children: [enableJsonMode && jsxRuntime.jsxs("button", {
628
647
  onClick: () => setViewMode(viewMode === 'json' ? 'read' : 'json'),
629
648
  className: `inline-flex items-center gap-2 px-3 py-1.5 rounded-md transition-colors ${viewMode === 'json' ? "bg-blue-600 text-white shadow-sm" : "text-gray-600 hover:text-gray-900 hover:bg-gray-200"}`,
630
649
  title: "JSON Mode - Click any element to view its JSON",
@@ -634,7 +653,7 @@ function HtmlViewer({
634
653
  className: "text-sm font-medium",
635
654
  children: "JSON"
636
655
  })]
637
- }), jsxRuntime.jsxs("button", {
656
+ }), enableEditMode && jsxRuntime.jsxs("button", {
638
657
  onClick: () => setViewMode(viewMode === 'edit' ? 'read' : 'edit'),
639
658
  className: `inline-flex items-center gap-2 px-3 py-1.5 rounded-md transition-colors ${viewMode === 'edit' ? "bg-green-600 text-white shadow-sm" : "text-gray-600 hover:text-gray-900 hover:bg-gray-200"}`,
640
659
  title: "Edit Mode - Click any element to edit it",
@@ -645,7 +664,7 @@ function HtmlViewer({
645
664
  children: "Edit"
646
665
  })]
647
666
  })]
648
- }), jsxRuntime.jsxs("button", {
667
+ }), enableSemanticTags && jsxRuntime.jsxs("button", {
649
668
  onClick: () => setShowSemanticTags(!showSemanticTags),
650
669
  className: `inline-flex items-center gap-2 px-3 py-1.5 rounded-md transition-colors ml-2 ${showSemanticTags ? "bg-amber-100 text-amber-700 border border-amber-300" : "bg-gray-100 text-gray-500 border border-gray-200"}`,
651
670
  title: showSemanticTags ? "Hide Semantic Tags" : "Show Semantic Tags",
@@ -38,16 +38,19 @@ var LucideIcons__namespace = /*#__PURE__*/_interopNamespaceDefault(LucideIcons);
38
38
  // Remove component aliases and use direct imports
39
39
  // This will preserve the propser types
40
40
  function Structura({
41
+ apiKey,
42
+ baseUrl,
41
43
  initialPdfPath,
42
44
  initialJsonData,
43
- props
45
+ editMode = true,
46
+ jsonMode = true,
47
+ mathRendering = true,
48
+ semanticTags = true,
49
+ headerFooterBadges = true,
50
+ defaultViewMode = 'read',
51
+ onContentChange,
52
+ onExport
44
53
  }) {
45
- // Log the API key (for development only - remove in production)
46
- // useEffect(() => {
47
- // if (props?.APIKey) {
48
- // console.log("API Key provided:", props.APIKey);
49
- // }
50
- // }, [props]);
51
54
  // Log the imported SDK items to demonstrate usage
52
55
  // console.log("Imported BlockSchema from SDK:", BlockSchema);
53
56
  const [numPages, setNumPages] = React.useState(null);
@@ -301,12 +304,12 @@ function Structura({
301
304
  const formData = new FormData();
302
305
  formData.append("file", file);
303
306
  // console.log("!!! [Structura] FormData:", formData);
304
- if (!(props === null || props === void 0 ? void 0 : props.APIKey)) {
307
+ if (!apiKey) {
305
308
  throw new Error("API key is required but not provided");
306
309
  }
307
310
  const submitResponse = await route.POST(formData, {
308
- apiKey: props.APIKey,
309
- baseUrl: props.baseUrl
311
+ apiKey: apiKey,
312
+ baseUrl: baseUrl
310
313
  });
311
314
  console.log("API response status:", submitResponse.status);
312
315
  if (submitResponse.status !== 200) {
@@ -327,8 +330,8 @@ function Structura({
327
330
  let resultData = null;
328
331
  while (true) {
329
332
  const resultResponse = await route.GET(requestId, {
330
- apiKey: props.APIKey,
331
- baseUrl: props.baseUrl
333
+ apiKey: apiKey,
334
+ baseUrl: baseUrl
332
335
  });
333
336
  if (resultResponse.data.status === "completed") {
334
337
  // Store the response data
@@ -583,7 +586,15 @@ function Structura({
583
586
  jsonData: jsonData,
584
587
  selectedBboxId: selectedBboxId,
585
588
  isLoading: isLoading,
586
- onNodeClick: handleHtmlNodeClick
589
+ onNodeClick: handleHtmlNodeClick,
590
+ editMode: editMode,
591
+ jsonMode: jsonMode,
592
+ mathRendering: mathRendering,
593
+ semanticTags: semanticTags,
594
+ headerFooterBadges: headerFooterBadges,
595
+ defaultViewMode: defaultViewMode,
596
+ onContentChange: onContentChange,
597
+ onExport: onExport
587
598
  })
588
599
  })
589
600
  })]
@@ -711,7 +722,15 @@ function Structura({
711
722
  jsonData: jsonData,
712
723
  selectedBboxId: selectedBboxId,
713
724
  isLoading: isLoading,
714
- onNodeClick: handleHtmlNodeClick
725
+ onNodeClick: handleHtmlNodeClick,
726
+ editMode: editMode,
727
+ jsonMode: jsonMode,
728
+ mathRendering: mathRendering,
729
+ semanticTags: semanticTags,
730
+ headerFooterBadges: headerFooterBadges,
731
+ defaultViewMode: defaultViewMode,
732
+ onContentChange: onContentChange,
733
+ onExport: onExport
715
734
  }) : jsxRuntime.jsx("div", {
716
735
  className: "flex flex-col items-center justify-center h-full p-8 text-center",
717
736
  children: jsxRuntime.jsxs("div", {
package/dist/cjs/Table.js CHANGED
@@ -37,7 +37,8 @@ function Table({
37
37
  showJsonIcons = true,
38
38
  onNodeClick,
39
39
  isEditMode = false,
40
- isJsonMode = false
40
+ isJsonMode = false,
41
+ enableMathRendering = true
41
42
  }) {
42
43
  const [useLlmHtml, setUseLlmHtml] = React.useState(false);
43
44
  // Get the appropriate HTML content
@@ -217,7 +218,8 @@ function Table({
217
218
  showJsonIcons: showJsonIcons,
218
219
  onNodeClick: onNodeClick ? () => onNodeClick(node.id) : undefined,
219
220
  isEditMode: isEditMode,
220
- isJsonMode: isJsonMode
221
+ isJsonMode: isJsonMode,
222
+ enableMathRendering: enableMathRendering
221
223
  }, cell.id))
222
224
  }, rowIndex))
223
225
  })
@@ -42,7 +42,8 @@ function TableCell({
42
42
  colSpan,
43
43
  style,
44
44
  isEditMode = false,
45
- isJsonMode = false
45
+ isJsonMode = false,
46
+ enableMathRendering = true
46
47
  }) {
47
48
  const [isEditing, setIsEditing] = React.useState(false);
48
49
  const [editedContent, setEditedContent] = React.useState(cleanHtml(content));
@@ -91,10 +92,10 @@ function TableCell({
91
92
  onNodeClick();
92
93
  }
93
94
  };
94
- // Render math expressions in content
95
+ // Render math expressions in content (conditionally)
95
96
  const renderedContent = React.useMemo(() => {
96
- return MathRenderer.renderMathInHtml(editedContent);
97
- }, [editedContent]);
97
+ return enableMathRendering ? MathRenderer.renderMathInHtml(editedContent) : editedContent;
98
+ }, [editedContent, enableMathRendering]);
98
99
  return jsxRuntime.jsx(CellComponent, {
99
100
  id: id,
100
101
  rowSpan: rowSpan,
@@ -34,7 +34,9 @@ function EditableContent(_ref) {
34
34
  onJsonClick = _ref.onJsonClick,
35
35
  _ref$enableSemanticTa = _ref.enableSemanticTags,
36
36
  enableSemanticTags = _ref$enableSemanticTa === void 0 ? true : _ref$enableSemanticTa,
37
- onSemanticTagClick = _ref.onSemanticTagClick;
37
+ onSemanticTagClick = _ref.onSemanticTagClick,
38
+ _ref$enableMathRender = _ref.enableMathRendering,
39
+ enableMathRendering = _ref$enableMathRender === void 0 ? true : _ref$enableMathRender;
38
40
  var _useState = useState(false),
39
41
  _useState2 = _slicedToArray(_useState, 2),
40
42
  isEditing = _useState2[0],
@@ -154,10 +156,10 @@ function EditableContent(_ref) {
154
156
  handleBlur();
155
157
  }
156
158
  };
157
- // Render math expressions in content
159
+ // Render math expressions in content (conditionally)
158
160
  var renderedContent = useMemo(function () {
159
- return renderMathInHtml(editedContent);
160
- }, [editedContent]);
161
+ return enableMathRendering ? renderMathInHtml(editedContent) : editedContent;
162
+ }, [editedContent, enableMathRendering]);
161
163
  return jsx("div", {
162
164
  className: "w-full ".concat(isEdited ? "bg-yellow-100" : "", " ").concat(isJsonMode ? "cursor-pointer hover:bg-purple-50 border border-transparent hover:border-purple-300 rounded" : "", " ").concat(isEditMode && !isEditing ? "cursor-pointer hover:bg-blue-50 border border-transparent hover:border-blue-300 rounded" : "", " ").concat(onNodeClick && !isEditing && !isEditMode && !isJsonMode ? "cursor-pointer" : ""),
163
165
  onClick: handleClick,
@@ -247,7 +247,21 @@ function HtmlViewer(_ref6) {
247
247
  var jsonData = _ref6.jsonData,
248
248
  selectedBboxId = _ref6.selectedBboxId,
249
249
  isLoading = _ref6.isLoading,
250
- onNodeClick = _ref6.onNodeClick;
250
+ onNodeClick = _ref6.onNodeClick,
251
+ _ref6$editMode = _ref6.editMode,
252
+ enableEditMode = _ref6$editMode === void 0 ? true : _ref6$editMode,
253
+ _ref6$jsonMode = _ref6.jsonMode,
254
+ enableJsonMode = _ref6$jsonMode === void 0 ? true : _ref6$jsonMode,
255
+ _ref6$mathRendering = _ref6.mathRendering,
256
+ enableMathRendering = _ref6$mathRendering === void 0 ? true : _ref6$mathRendering,
257
+ _ref6$semanticTags = _ref6.semanticTags,
258
+ enableSemanticTags = _ref6$semanticTags === void 0 ? true : _ref6$semanticTags,
259
+ _ref6$headerFooterBad = _ref6.headerFooterBadges,
260
+ enableHeaderFooterBadges = _ref6$headerFooterBad === void 0 ? true : _ref6$headerFooterBad,
261
+ _ref6$defaultViewMode = _ref6.defaultViewMode,
262
+ defaultViewMode = _ref6$defaultViewMode === void 0 ? 'read' : _ref6$defaultViewMode,
263
+ onContentChangeCallback = _ref6.onContentChange,
264
+ onExportCallback = _ref6.onExport;
251
265
  var _useState = useState(jsonData),
252
266
  _useState2 = _slicedToArray(_useState, 2),
253
267
  editedData = _useState2[0],
@@ -264,7 +278,7 @@ function HtmlViewer(_ref6) {
264
278
  _useState8 = _slicedToArray(_useState7, 2),
265
279
  modalData = _useState8[0],
266
280
  setModalData = _useState8[1];
267
- var _useState9 = useState('read'),
281
+ var _useState9 = useState(defaultViewMode),
268
282
  _useState0 = _slicedToArray(_useState9, 2),
269
283
  viewMode = _useState0[0],
270
284
  setViewMode = _useState0[1];
@@ -291,7 +305,7 @@ function HtmlViewer(_ref6) {
291
305
  _useState16 = _slicedToArray(_useState15, 2),
292
306
  selectedTag = _useState16[0],
293
307
  setSelectedTag = _useState16[1];
294
- var _useState17 = useState(true),
308
+ var _useState17 = useState(enableSemanticTags),
295
309
  _useState18 = _slicedToArray(_useState17, 2),
296
310
  showSemanticTags = _useState18[0],
297
311
  setShowSemanticTags = _useState18[1];
@@ -357,12 +371,13 @@ function HtmlViewer(_ref6) {
357
371
  }, [jsonData]);
358
372
  var handleContentChange = useCallback(function (nodeId, newContent) {
359
373
  setHasChanges(true);
374
+ var originalContent = "";
360
375
  setEditedData(function (prevData) {
361
376
  var newData = JSON.parse(JSON.stringify(prevData));
362
377
  var _updateNode = function updateNode(node) {
363
378
  if (!node) return false;
364
379
  if (node.id === nodeId) {
365
- var originalContent = node.html || "";
380
+ originalContent = node.html || "";
366
381
  var blockType = node.block_type || "unknown";
367
382
  // Update accuracy metrics before changing content
368
383
  updateAccuracyMetrics(nodeId, blockType, originalContent, newContent);
@@ -388,9 +403,17 @@ function HtmlViewer(_ref6) {
388
403
  _updateNode(newData);
389
404
  return newData;
390
405
  });
391
- }, [updateAccuracyMetrics]);
406
+ // Call external callback if provided
407
+ if (onContentChangeCallback) {
408
+ onContentChangeCallback(nodeId, originalContent, newContent);
409
+ }
410
+ }, [updateAccuracyMetrics, onContentChangeCallback]);
392
411
  var handleDownload = function handleDownload() {
393
412
  if (!editedData) return;
413
+ // Call external callback if provided
414
+ if (onExportCallback) {
415
+ onExportCallback(editedData);
416
+ }
394
417
  var jsonString = JSON.stringify(editedData, null, 2);
395
418
  var blob = new Blob([jsonString], {
396
419
  type: "application/json"
@@ -598,7 +621,8 @@ function HtmlViewer(_ref6) {
598
621
  showJsonIcons: false,
599
622
  onNodeClick: onNodeClick,
600
623
  isEditMode: viewMode === 'edit',
601
- isJsonMode: viewMode === 'json'
624
+ isJsonMode: viewMode === 'json',
625
+ enableMathRendering: enableMathRendering
602
626
  }) : isFigure && node.images ? jsx("div", {
603
627
  className: "my-2 ".concat(viewMode === 'json' ? 'cursor-pointer hover:opacity-80' : ''),
604
628
  onClick: viewMode === 'json' ? function () {
@@ -614,7 +638,7 @@ function HtmlViewer(_ref6) {
614
638
  className: "max-w-full h-auto rounded border border-gray-200"
615
639
  }, key);
616
640
  })
617
- }) : isPageHeader || isPageFooter ? jsxs("div", {
641
+ }) : (isPageHeader || isPageFooter) && enableHeaderFooterBadges ? jsxs("div", {
618
642
  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
643
  onClick: viewMode === 'json' ? function () {
620
644
  return handleJsonClick(node);
@@ -652,7 +676,8 @@ function HtmlViewer(_ref6) {
652
676
  return onNodeClick(node.id);
653
677
  } : undefined,
654
678
  enableSemanticTags: showSemanticTags,
655
- onSemanticTagClick: handleSemanticTagClick
679
+ onSemanticTagClick: handleSemanticTagClick,
680
+ enableMathRendering: enableMathRendering
656
681
  }), hasChildren && jsx("div", {
657
682
  className: "ml-2 mt-1 border-l border-gray-200 pl-2 max-w-full overflow-hidden",
658
683
  children: node.children.map(function (child) {
@@ -683,9 +708,9 @@ function HtmlViewer(_ref6) {
683
708
  className: "sticky top-0 z-20 bg-white border-b border-gray-200 p-3 flex justify-between items-center flex-shrink-0",
684
709
  children: [jsxs("div", {
685
710
  className: "flex gap-1",
686
- children: [jsxs("div", {
711
+ children: [(enableJsonMode || enableEditMode) && jsxs("div", {
687
712
  className: "flex bg-gray-100 rounded-lg p-1",
688
- children: [jsxs("button", {
713
+ children: [enableJsonMode && jsxs("button", {
689
714
  onClick: function onClick() {
690
715
  return setViewMode(viewMode === 'json' ? 'read' : 'json');
691
716
  },
@@ -697,7 +722,7 @@ function HtmlViewer(_ref6) {
697
722
  className: "text-sm font-medium",
698
723
  children: "JSON"
699
724
  })]
700
- }), jsxs("button", {
725
+ }), enableEditMode && jsxs("button", {
701
726
  onClick: function onClick() {
702
727
  return setViewMode(viewMode === 'edit' ? 'read' : 'edit');
703
728
  },
@@ -710,7 +735,7 @@ function HtmlViewer(_ref6) {
710
735
  children: "Edit"
711
736
  })]
712
737
  })]
713
- }), jsxs("button", {
738
+ }), enableSemanticTags && jsxs("button", {
714
739
  onClick: function onClick() {
715
740
  return setShowSemanticTags(!showSemanticTags);
716
741
  },
@@ -16,15 +16,24 @@ import { GET, POST } from './route.js';
16
16
  // Remove component aliases and use direct imports
17
17
  // This will preserve the propser types
18
18
  function Structura(_ref) {
19
- var initialPdfPath = _ref.initialPdfPath,
19
+ var apiKey = _ref.apiKey,
20
+ baseUrl = _ref.baseUrl,
21
+ initialPdfPath = _ref.initialPdfPath,
20
22
  initialJsonData = _ref.initialJsonData,
21
- props = _ref.props;
22
- // Log the API key (for development only - remove in production)
23
- // useEffect(() => {
24
- // if (props?.APIKey) {
25
- // console.log("API Key provided:", props.APIKey);
26
- // }
27
- // }, [props]);
23
+ _ref$editMode = _ref.editMode,
24
+ editMode = _ref$editMode === void 0 ? true : _ref$editMode,
25
+ _ref$jsonMode = _ref.jsonMode,
26
+ jsonMode = _ref$jsonMode === void 0 ? true : _ref$jsonMode,
27
+ _ref$mathRendering = _ref.mathRendering,
28
+ mathRendering = _ref$mathRendering === void 0 ? true : _ref$mathRendering,
29
+ _ref$semanticTags = _ref.semanticTags,
30
+ semanticTags = _ref$semanticTags === void 0 ? true : _ref$semanticTags,
31
+ _ref$headerFooterBadg = _ref.headerFooterBadges,
32
+ headerFooterBadges = _ref$headerFooterBadg === void 0 ? true : _ref$headerFooterBadg,
33
+ _ref$defaultViewMode = _ref.defaultViewMode,
34
+ defaultViewMode = _ref$defaultViewMode === void 0 ? 'read' : _ref$defaultViewMode,
35
+ onContentChange = _ref.onContentChange,
36
+ onExport = _ref.onExport;
28
37
  // Log the imported SDK items to demonstrate usage
29
38
  // console.log("Imported BlockSchema from SDK:", BlockSchema);
30
39
  var _useState = useState(null),
@@ -386,7 +395,7 @@ function Structura(_ref) {
386
395
  formData = new FormData();
387
396
  formData.append("file", file);
388
397
  // console.log("!!! [Structura] FormData:", formData);
389
- if (props === null || props === void 0 ? void 0 : props.APIKey) {
398
+ if (apiKey) {
390
399
  _context2.n = 3;
391
400
  break;
392
401
  }
@@ -394,8 +403,8 @@ function Structura(_ref) {
394
403
  case 3:
395
404
  _context2.n = 4;
396
405
  return POST(formData, {
397
- apiKey: props.APIKey,
398
- baseUrl: props.baseUrl
406
+ apiKey: apiKey,
407
+ baseUrl: baseUrl
399
408
  });
400
409
  case 4:
401
410
  submitResponse = _context2.v;
@@ -426,8 +435,8 @@ function Structura(_ref) {
426
435
  case 8:
427
436
  _context2.n = 9;
428
437
  return GET(requestId, {
429
- apiKey: props.APIKey,
430
- baseUrl: props.baseUrl
438
+ apiKey: apiKey,
439
+ baseUrl: baseUrl
431
440
  });
432
441
  case 9:
433
442
  resultResponse = _context2.v;
@@ -726,7 +735,15 @@ function Structura(_ref) {
726
735
  jsonData: jsonData,
727
736
  selectedBboxId: selectedBboxId,
728
737
  isLoading: isLoading,
729
- onNodeClick: handleHtmlNodeClick
738
+ onNodeClick: handleHtmlNodeClick,
739
+ editMode: editMode,
740
+ jsonMode: jsonMode,
741
+ mathRendering: mathRendering,
742
+ semanticTags: semanticTags,
743
+ headerFooterBadges: headerFooterBadges,
744
+ defaultViewMode: defaultViewMode,
745
+ onContentChange: onContentChange,
746
+ onExport: onExport
730
747
  })
731
748
  })
732
749
  })]
@@ -854,7 +871,15 @@ function Structura(_ref) {
854
871
  jsonData: jsonData,
855
872
  selectedBboxId: selectedBboxId,
856
873
  isLoading: isLoading,
857
- onNodeClick: handleHtmlNodeClick
874
+ onNodeClick: handleHtmlNodeClick,
875
+ editMode: editMode,
876
+ jsonMode: jsonMode,
877
+ mathRendering: mathRendering,
878
+ semanticTags: semanticTags,
879
+ headerFooterBadges: headerFooterBadges,
880
+ defaultViewMode: defaultViewMode,
881
+ onContentChange: onContentChange,
882
+ onExport: onExport
858
883
  }) : jsx("div", {
859
884
  className: "flex flex-col items-center justify-center h-full p-8 text-center",
860
885
  children: jsxs("div", {
package/dist/esm/Table.js CHANGED
@@ -39,7 +39,9 @@ function Table(_ref) {
39
39
  _ref$isEditMode = _ref.isEditMode,
40
40
  isEditMode = _ref$isEditMode === void 0 ? false : _ref$isEditMode,
41
41
  _ref$isJsonMode = _ref.isJsonMode,
42
- isJsonMode = _ref$isJsonMode === void 0 ? false : _ref$isJsonMode;
42
+ isJsonMode = _ref$isJsonMode === void 0 ? false : _ref$isJsonMode,
43
+ _ref$enableMathRender = _ref.enableMathRendering,
44
+ enableMathRendering = _ref$enableMathRender === void 0 ? true : _ref$enableMathRender;
43
45
  var _useState = useState(false),
44
46
  _useState2 = _slicedToArray(_useState, 2),
45
47
  useLlmHtml = _useState2[0],
@@ -242,7 +244,8 @@ function Table(_ref) {
242
244
  return onNodeClick(node.id);
243
245
  } : undefined,
244
246
  isEditMode: isEditMode,
245
- isJsonMode: isJsonMode
247
+ isJsonMode: isJsonMode,
248
+ enableMathRendering: enableMathRendering
246
249
  }, cell.id);
247
250
  })
248
251
  }, rowIndex);
@@ -44,7 +44,9 @@ function TableCell(_ref) {
44
44
  _ref$isEditMode = _ref.isEditMode,
45
45
  isEditMode = _ref$isEditMode === void 0 ? false : _ref$isEditMode,
46
46
  _ref$isJsonMode = _ref.isJsonMode,
47
- isJsonMode = _ref$isJsonMode === void 0 ? false : _ref$isJsonMode;
47
+ isJsonMode = _ref$isJsonMode === void 0 ? false : _ref$isJsonMode,
48
+ _ref$enableMathRender = _ref.enableMathRendering,
49
+ enableMathRendering = _ref$enableMathRender === void 0 ? true : _ref$enableMathRender;
48
50
  var _useState = useState(false),
49
51
  _useState2 = _slicedToArray(_useState, 2),
50
52
  isEditing = _useState2[0],
@@ -101,10 +103,10 @@ function TableCell(_ref) {
101
103
  onNodeClick();
102
104
  }
103
105
  };
104
- // Render math expressions in content
106
+ // Render math expressions in content (conditionally)
105
107
  var renderedContent = useMemo(function () {
106
- return renderMathInHtml(editedContent);
107
- }, [editedContent]);
108
+ return enableMathRendering ? renderMathInHtml(editedContent) : editedContent;
109
+ }, [editedContent, enableMathRendering]);
108
110
  return jsx(CellComponent, {
109
111
  id: id,
110
112
  rowSpan: rowSpan,
@@ -10,6 +10,7 @@ interface EditableContentProps {
10
10
  onJsonClick?: () => void;
11
11
  enableSemanticTags?: boolean;
12
12
  onSemanticTagClick?: (tag: SemanticTag) => void;
13
+ enableMathRendering?: boolean;
13
14
  }
14
- export default function EditableContent({ id, content, onContentChange, isHeading, isEditMode, isJsonMode, onNodeClick, onJsonClick, enableSemanticTags, onSemanticTagClick, }: EditableContentProps): import("react/jsx-runtime").JSX.Element;
15
+ export default function EditableContent({ id, content, onContentChange, isHeading, isEditMode, isJsonMode, onNodeClick, onJsonClick, enableSemanticTags, onSemanticTagClick, enableMathRendering, }: EditableContentProps): import("react/jsx-runtime").JSX.Element;
15
16
  export {};
@@ -3,7 +3,15 @@ interface HtmlViewerProps {
3
3
  selectedBboxId: string | null;
4
4
  isLoading?: boolean;
5
5
  onNodeClick?: (nodeId: string) => void;
6
+ editMode?: boolean;
7
+ jsonMode?: boolean;
8
+ mathRendering?: boolean;
9
+ semanticTags?: boolean;
10
+ headerFooterBadges?: boolean;
11
+ defaultViewMode?: 'read' | 'edit' | 'json';
12
+ onContentChange?: (blockId: string, oldContent: string, newContent: string) => void;
13
+ onExport?: (data: any) => void;
6
14
  }
7
15
  export default function HtmlViewer({ //NOSONAR
8
- jsonData, selectedBboxId, isLoading, onNodeClick, }: HtmlViewerProps): import("react/jsx-runtime").JSX.Element;
16
+ jsonData, selectedBboxId, isLoading, onNodeClick, editMode: enableEditMode, jsonMode: enableJsonMode, mathRendering: enableMathRendering, semanticTags: enableSemanticTags, headerFooterBadges: enableHeaderFooterBadges, defaultViewMode, onContentChange: onContentChangeCallback, onExport: onExportCallback, }: HtmlViewerProps): import("react/jsx-runtime").JSX.Element;
9
17
  export {};
@@ -1,10 +1,16 @@
1
- interface PdfHighlighterpropss {
1
+ interface StructuraProps {
2
+ apiKey: string;
3
+ baseUrl?: string;
2
4
  initialPdfPath?: string | null;
3
5
  initialJsonData?: any;
4
- props: {
5
- APIKey: string;
6
- baseUrl?: string;
7
- };
6
+ editMode?: boolean;
7
+ jsonMode?: boolean;
8
+ mathRendering?: boolean;
9
+ semanticTags?: boolean;
10
+ headerFooterBadges?: boolean;
11
+ defaultViewMode?: 'read' | 'edit' | 'json';
12
+ onContentChange?: (blockId: string, oldContent: string, newContent: string) => void;
13
+ onExport?: (data: any) => void;
8
14
  }
9
- export default function Structura({ initialPdfPath, initialJsonData, props }: PdfHighlighterpropss): import("react/jsx-runtime").JSX.Element;
15
+ export default function Structura({ apiKey, baseUrl, initialPdfPath, initialJsonData, editMode, jsonMode, mathRendering, semanticTags, headerFooterBadges, defaultViewMode, onContentChange, onExport }: StructuraProps): import("react/jsx-runtime").JSX.Element;
10
16
  export {};
@@ -9,6 +9,7 @@ interface TableProps {
9
9
  onNodeClick?: (nodeId: string) => void;
10
10
  isEditMode?: boolean;
11
11
  isJsonMode?: boolean;
12
+ enableMathRendering?: boolean;
12
13
  }
13
- export default function Table({ node, selectedBboxId, onJsonClick, onContentChange, mergedTables, hasLlmHtml, showJsonIcons, onNodeClick, isEditMode, isJsonMode, }: TableProps): import("react/jsx-runtime").JSX.Element | null;
14
+ export default function Table({ node, selectedBboxId, onJsonClick, onContentChange, mergedTables, hasLlmHtml, showJsonIcons, onNodeClick, isEditMode, isJsonMode, enableMathRendering, }: TableProps): import("react/jsx-runtime").JSX.Element | null;
14
15
  export {};
@@ -13,6 +13,7 @@ interface TableCellProps {
13
13
  style?: Record<string, string>;
14
14
  isEditMode?: boolean;
15
15
  isJsonMode?: boolean;
16
+ enableMathRendering?: boolean;
16
17
  }
17
- export default function TableCell({ id, content, onJsonClick, isSelected, isHeader, onContentChange, showJsonIcons, onNodeClick, isDubious, rowSpan, colSpan, style, isEditMode, isJsonMode, }: TableCellProps): import("react/jsx-runtime").JSX.Element;
18
+ export default function TableCell({ id, content, onJsonClick, isSelected, isHeader, onContentChange, showJsonIcons, onNodeClick, isDubious, rowSpan, colSpan, style, isEditMode, isJsonMode, enableMathRendering, }: TableCellProps): import("react/jsx-runtime").JSX.Element;
18
19
  export {};
package/dist/index.d.ts CHANGED
@@ -1,15 +1,21 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  export { Block, BlockOutput, BlockSchema, DocumentOutput } from '@tfw.in/structura-sdk';
3
3
 
4
- interface PdfHighlighterpropss {
4
+ interface StructuraProps {
5
+ apiKey: string;
6
+ baseUrl?: string;
5
7
  initialPdfPath?: string | null;
6
8
  initialJsonData?: any;
7
- props: {
8
- APIKey: string;
9
- baseUrl?: string;
10
- };
9
+ editMode?: boolean;
10
+ jsonMode?: boolean;
11
+ mathRendering?: boolean;
12
+ semanticTags?: boolean;
13
+ headerFooterBadges?: boolean;
14
+ defaultViewMode?: 'read' | 'edit' | 'json';
15
+ onContentChange?: (blockId: string, oldContent: string, newContent: string) => void;
16
+ onExport?: (data: any) => void;
11
17
  }
12
- declare function Structura({ initialPdfPath, initialJsonData, props }: PdfHighlighterpropss): react_jsx_runtime.JSX.Element;
18
+ declare function Structura({ apiKey, baseUrl, initialPdfPath, initialJsonData, editMode, jsonMode, mathRendering, semanticTags, headerFooterBadges, defaultViewMode, onContentChange, onExport }: StructuraProps): react_jsx_runtime.JSX.Element;
13
19
 
14
20
  interface PdfHighlighterProps {
15
21
  initialPdfPath?: string | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tfw.in/structura-lib",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Structura Library Components",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",