@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.
Files changed (49) hide show
  1. package/README.md +72 -323
  2. package/dist/cjs/EditableContent.js +46 -18
  3. package/dist/cjs/HtmlViewer.js +238 -85
  4. package/dist/cjs/MathRenderer.js +88 -0
  5. package/dist/cjs/PdfDocumentViewer.js +1 -1
  6. package/dist/cjs/SemanticTagParser.js +189 -0
  7. package/dist/cjs/SemanticTagRenderer.js +135 -0
  8. package/dist/cjs/Structura.js +49 -76
  9. package/dist/cjs/Table.js +75 -8
  10. package/dist/cjs/TableCell.js +34 -10
  11. package/dist/cjs/index.js +12 -0
  12. package/dist/cjs/node_modules/react-icons/fa/index.esm.js +6 -0
  13. package/dist/cjs/styles.css +2 -4
  14. package/dist/cjs/styles.css.map +1 -1
  15. package/dist/esm/EditableContent.js +51 -19
  16. package/dist/esm/HtmlViewer.js +287 -103
  17. package/dist/esm/MathRenderer.js +85 -0
  18. package/dist/esm/PdfDocumentViewer.js +1 -1
  19. package/dist/esm/SemanticTagParser.js +187 -0
  20. package/dist/esm/SemanticTagRenderer.js +140 -0
  21. package/dist/esm/Structura.js +57 -80
  22. package/dist/esm/Table.js +85 -8
  23. package/dist/esm/TableCell.js +34 -6
  24. package/dist/esm/index.js +3 -0
  25. package/dist/esm/node_modules/react-icons/fa/index.esm.js +5 -1
  26. package/dist/esm/styles.css +2 -4
  27. package/dist/esm/styles.css.map +1 -1
  28. package/dist/esm/types/DocumentOutline.d.ts +7 -0
  29. package/dist/esm/types/EditableContent.d.ts +8 -1
  30. package/dist/esm/types/HtmlViewer.d.ts +9 -2
  31. package/dist/esm/types/MathRenderer.d.ts +25 -0
  32. package/dist/esm/types/SemanticTagParser.d.ts +33 -0
  33. package/dist/esm/types/SemanticTagRenderer.d.ts +17 -0
  34. package/dist/esm/types/Structura.d.ts +13 -8
  35. package/dist/esm/types/Table.d.ts +4 -1
  36. package/dist/esm/types/TableCell.d.ts +7 -1
  37. package/dist/esm/types/helpers/index.d.ts +0 -1
  38. package/dist/esm/types/index.d.ts +3 -0
  39. package/dist/esm/types/test-app/src/App.d.ts +1 -2
  40. package/dist/index.d.ts +90 -10
  41. package/package.json +9 -16
  42. package/PRODUCTION_ARCHITECTURE.md +0 -511
  43. package/SAVE_FUNCTIONALITY_COMPLETE.md +0 -448
  44. package/dist/cjs/ui/badge.js +0 -34
  45. package/dist/esm/types/helpers/jsonToHtml.d.ts +0 -40
  46. package/dist/esm/ui/badge.js +0 -31
  47. package/server/README.md +0 -203
  48. package/server/db.js +0 -142
  49. package/server/server.js +0 -165
@@ -1,6 +1,9 @@
1
1
  import { slicedToArray as _slicedToArray } from './_virtual/_rollupPluginBabelHelpers.js';
2
2
  import { jsx } from 'react/jsx-runtime';
3
- import { useState, useRef, useEffect } from 'react';
3
+ import { useState, useRef, useEffect, useMemo } from 'react';
4
+ import SemanticTagRenderer from './SemanticTagRenderer.js';
5
+ import { hasSemanticTags } from './SemanticTagParser.js';
6
+ import { renderMathInHtml } from './MathRenderer.js';
4
7
 
5
8
  // Helper function to extract text content while preserving structure
6
9
  var extractTextPreservingStructure = function extractTextPreservingStructure(html) {
@@ -23,7 +26,17 @@ function EditableContent(_ref) {
23
26
  onContentChange = _ref.onContentChange,
24
27
  _ref$isHeading = _ref.isHeading,
25
28
  isHeading = _ref$isHeading === void 0 ? false : _ref$isHeading,
26
- onNodeClick = _ref.onNodeClick;
29
+ _ref$isEditMode = _ref.isEditMode,
30
+ isEditMode = _ref$isEditMode === void 0 ? false : _ref$isEditMode,
31
+ _ref$isJsonMode = _ref.isJsonMode,
32
+ isJsonMode = _ref$isJsonMode === void 0 ? false : _ref$isJsonMode,
33
+ onNodeClick = _ref.onNodeClick,
34
+ onJsonClick = _ref.onJsonClick,
35
+ _ref$enableSemanticTa = _ref.enableSemanticTags,
36
+ enableSemanticTags = _ref$enableSemanticTa === void 0 ? true : _ref$enableSemanticTa,
37
+ onSemanticTagClick = _ref.onSemanticTagClick,
38
+ _ref$enableMathRender = _ref.enableMathRendering,
39
+ enableMathRendering = _ref$enableMathRender === void 0 ? true : _ref$enableMathRender;
27
40
  var _useState = useState(false),
28
41
  _useState2 = _slicedToArray(_useState, 2),
29
42
  isEditing = _useState2[0],
@@ -64,24 +77,22 @@ function EditableContent(_ref) {
64
77
  var getHeadingStyles = function getHeadingStyles(level) {
65
78
  switch (level) {
66
79
  case 1:
67
- return "text-2xl font-bold";
68
- case 2:
69
80
  return "text-xl font-bold";
70
- case 3:
81
+ case 2:
71
82
  return "text-lg font-bold";
72
- case 4:
83
+ case 3:
73
84
  return "text-base font-bold";
74
- case 5:
85
+ case 4:
75
86
  return "text-sm font-bold";
76
- case 6:
87
+ case 5:
77
88
  return "text-xs font-bold";
89
+ case 6:
90
+ return "text-xs font-semibold";
78
91
  default:
79
- return "text-base";
92
+ return "text-sm";
80
93
  }
81
94
  };
82
- var handleDoubleClick = function handleDoubleClick(e) {
83
- // Stop propagation to prevent parent click handlers from firing
84
- e.stopPropagation();
95
+ var startEditing = function startEditing() {
85
96
  if (onContentChange) {
86
97
  // Extract text content for textarea while preserving structure
87
98
  setTextAreaContent(extractTextPreservingStructure(editedContent));
@@ -120,6 +131,18 @@ function EditableContent(_ref) {
120
131
  e.stopPropagation();
121
132
  return;
122
133
  }
134
+ // In JSON mode, show JSON on click
135
+ if (isJsonMode && onJsonClick) {
136
+ e.stopPropagation();
137
+ onJsonClick();
138
+ return;
139
+ }
140
+ // In edit mode, single click activates editing
141
+ if (isEditMode && onContentChange) {
142
+ e.stopPropagation();
143
+ startEditing();
144
+ return;
145
+ }
123
146
  // If there's an onNodeClick handler, call it
124
147
  if (onNodeClick) {
125
148
  e.stopPropagation(); // Prevent click from reaching parent containers
@@ -133,27 +156,36 @@ function EditableContent(_ref) {
133
156
  handleBlur();
134
157
  }
135
158
  };
159
+ // Render math expressions in content (conditionally)
160
+ var renderedContent = useMemo(function () {
161
+ return enableMathRendering ? renderMathInHtml(editedContent) : editedContent;
162
+ }, [editedContent, enableMathRendering]);
136
163
  return jsx("div", {
137
- className: "w-full ".concat(isEdited ? "bg-yellow-100" : "", " ").concat(onNodeClick && !isEditing ? "cursor-pointer" : ""),
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" : ""),
138
165
  onClick: handleClick,
139
- onDoubleClick: handleDoubleClick,
140
166
  children: isEditing ? jsx("textarea", {
141
167
  value: textAreaContent,
142
168
  onChange: handleChange,
143
169
  onBlur: handleBlur,
144
170
  onKeyDown: handleKeyDown,
145
171
  autoFocus: true,
146
- className: "w-full p-1 border border-blue-400 rounded focus:outline-none focus:ring-2 focus:ring-blue-500 bg-white text-gray-900",
172
+ className: "w-full p-1 border border-blue-400 rounded focus:outline-none focus:ring-2 focus:ring-blue-500",
147
173
  onClick: function onClick(e) {
148
174
  return e.stopPropagation();
149
175
  },
150
176
  rows: 3
151
177
  }) : jsx("div", {
152
178
  ref: contentRef,
153
- className: "prose max-w-none ".concat(isHeading ? getHeadingStyles(headingLevel) : ""),
154
- dangerouslySetInnerHTML: {
155
- __html: editedContent
156
- }
179
+ className: "prose prose-sm max-w-none ".concat(isHeading ? getHeadingStyles(headingLevel) : "text-sm"),
180
+ children: enableSemanticTags && hasSemanticTags(editedContent) ? jsx(SemanticTagRenderer, {
181
+ content: editedContent,
182
+ showTooltips: true,
183
+ onTagClick: onSemanticTagClick
184
+ }) : jsx("span", {
185
+ dangerouslySetInnerHTML: {
186
+ __html: renderedContent
187
+ }
188
+ })
157
189
  })
158
190
  });
159
191
  }
@@ -2,10 +2,11 @@ import { slicedToArray as _slicedToArray, objectSpread2 as _objectSpread2, defin
2
2
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
3
  import { useState, useEffect, useCallback, useMemo } from 'react';
4
4
  import { VscJson } from 'react-icons/vsc';
5
- import { FaFileDownload, FaChartBar } from './node_modules/react-icons/fa/index.esm.js';
5
+ import { FaEdit, FaTags, FaFileDownload, FaChartBar } from './node_modules/react-icons/fa/index.esm.js';
6
6
  import Table from './Table.js';
7
7
  import EditableContent from './EditableContent.js';
8
8
  import { calculateDifferences } from './accuracyMetrics.js';
9
+ import { getTagTypeInfo } from './SemanticTagParser.js';
9
10
 
10
11
  // Utility function to clean HTML content
11
12
  var cleanHtml = function cleanHtml(html) {
@@ -140,16 +141,127 @@ function AnalyticsModal(_ref2) {
140
141
  })
141
142
  });
142
143
  }
144
+ function TagModal(_ref5) {
145
+ var isOpen = _ref5.isOpen,
146
+ onClose = _ref5.onClose,
147
+ tag = _ref5.tag;
148
+ if (!isOpen || !tag) return null;
149
+ var typeInfo = getTagTypeInfo(tag.type);
150
+ return jsx("div", {
151
+ className: "fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50",
152
+ children: jsxs("div", {
153
+ className: "bg-white rounded-lg p-6 max-w-md w-full shadow-xl",
154
+ children: [jsxs("div", {
155
+ className: "flex justify-between items-center mb-4",
156
+ children: [jsxs("div", {
157
+ className: "flex items-center gap-2",
158
+ children: [jsx("span", {
159
+ className: "text-2xl",
160
+ children: typeInfo.icon
161
+ }), jsxs("h3", {
162
+ className: "text-lg font-semibold",
163
+ children: [typeInfo.label, " Tag"]
164
+ })]
165
+ }), jsx("button", {
166
+ onClick: onClose,
167
+ className: "text-gray-500 hover:text-gray-700",
168
+ children: "\u2715"
169
+ })]
170
+ }), jsx("div", {
171
+ className: "p-4 rounded-lg border ".concat(typeInfo.bgColor, " mb-4"),
172
+ children: jsxs("div", {
173
+ className: "text-2xl font-bold ".concat(typeInfo.color),
174
+ children: [tag.value, tag.attributes.uom && jsx("span", {
175
+ className: "text-lg ml-2 opacity-75",
176
+ children: tag.attributes.uom
177
+ })]
178
+ })
179
+ }), jsxs("div", {
180
+ className: "space-y-3",
181
+ children: [jsxs("div", {
182
+ className: "flex justify-between py-2 border-b",
183
+ children: [jsx("span", {
184
+ className: "text-gray-600",
185
+ children: "Type"
186
+ }), jsx("span", {
187
+ className: "font-medium ".concat(typeInfo.color),
188
+ children: tag.type
189
+ })]
190
+ }), tag.attributes.uom && jsxs("div", {
191
+ className: "flex justify-between py-2 border-b",
192
+ children: [jsx("span", {
193
+ className: "text-gray-600",
194
+ children: "Unit of Measure"
195
+ }), jsx("span", {
196
+ className: "font-medium",
197
+ children: tag.attributes.uom
198
+ })]
199
+ }), tag.attributes.id && jsxs("div", {
200
+ className: "py-2 border-b",
201
+ children: [jsx("span", {
202
+ className: "text-gray-600 block mb-1",
203
+ children: "Tag ID"
204
+ }), jsx("code", {
205
+ className: "text-xs bg-gray-100 p-2 rounded block break-all",
206
+ children: tag.attributes.id
207
+ })]
208
+ }), tag.attributes.format && jsxs("div", {
209
+ className: "flex justify-between py-2 border-b",
210
+ children: [jsx("span", {
211
+ className: "text-gray-600",
212
+ children: "Format"
213
+ }), jsx("span", {
214
+ className: "font-mono text-sm",
215
+ children: tag.attributes.format
216
+ })]
217
+ }), tag.attributes.filter && jsxs("div", {
218
+ className: "flex justify-between py-2 border-b",
219
+ children: [jsx("span", {
220
+ className: "text-gray-600",
221
+ children: "File Filter"
222
+ }), jsx("span", {
223
+ className: "font-mono text-sm",
224
+ children: tag.attributes.filter
225
+ })]
226
+ }), jsxs("div", {
227
+ className: "py-2",
228
+ children: [jsx("span", {
229
+ className: "text-gray-600 block mb-1",
230
+ children: "Raw Tag"
231
+ }), jsx("code", {
232
+ className: "text-xs bg-gray-100 p-2 rounded block break-all text-gray-700",
233
+ children: tag.rawMatch
234
+ })]
235
+ })]
236
+ })]
237
+ })
238
+ });
239
+ }
143
240
  // Update icon usage in the JSX
144
241
  var VscJsonIcon = VscJson;
145
242
  var FaFileDownloadIcon = FaFileDownload;
146
243
  var FaChartBarIcon = FaChartBar;
147
- function HtmlViewer(_ref5) {
148
- var jsonData = _ref5.jsonData,
149
- selectedBboxId = _ref5.selectedBboxId,
150
- isLoading = _ref5.isLoading,
151
- onNodeClick = _ref5.onNodeClick,
152
- onSave = _ref5.onSave;
244
+ var FaEditIcon = FaEdit;
245
+ var FaTagsIcon = FaTags;
246
+ function HtmlViewer(_ref6) {
247
+ var jsonData = _ref6.jsonData,
248
+ selectedBboxId = _ref6.selectedBboxId,
249
+ isLoading = _ref6.isLoading,
250
+ onNodeClick = _ref6.onNodeClick,
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;
153
265
  var _useState = useState(jsonData),
154
266
  _useState2 = _slicedToArray(_useState, 2),
155
267
  editedData = _useState2[0],
@@ -166,15 +278,11 @@ function HtmlViewer(_ref5) {
166
278
  _useState8 = _slicedToArray(_useState7, 2),
167
279
  modalData = _useState8[0],
168
280
  setModalData = _useState8[1];
169
- var _useState9 = useState(true),
281
+ var _useState9 = useState(defaultViewMode),
170
282
  _useState0 = _slicedToArray(_useState9, 2),
171
- showJsonIcons = _useState0[0],
172
- setShowJsonIcons = _useState0[1];
173
- var _useState1 = useState("Show JSON"),
174
- _useState10 = _slicedToArray(_useState1, 2),
175
- activeFormat = _useState10[0],
176
- setActiveFormat = _useState10[1];
177
- var _useState11 = useState({
283
+ viewMode = _useState0[0],
284
+ setViewMode = _useState0[1];
285
+ var _useState1 = useState({
178
286
  totalChanges: 0,
179
287
  totalCharactersEdited: 0,
180
288
  totalWordsEdited: 0,
@@ -182,13 +290,25 @@ function HtmlViewer(_ref5) {
182
290
  changeHistory: [],
183
291
  blockTypeStats: {}
184
292
  }),
293
+ _useState10 = _slicedToArray(_useState1, 2),
294
+ accuracyMetrics = _useState10[0],
295
+ setAccuracyMetrics = _useState10[1];
296
+ var _useState11 = useState(false),
185
297
  _useState12 = _slicedToArray(_useState11, 2),
186
- accuracyMetrics = _useState12[0],
187
- setAccuracyMetrics = _useState12[1];
298
+ isAnalyticsOpen = _useState12[0],
299
+ setIsAnalyticsOpen = _useState12[1];
188
300
  var _useState13 = useState(false),
189
301
  _useState14 = _slicedToArray(_useState13, 2),
190
- isAnalyticsOpen = _useState14[0],
191
- setIsAnalyticsOpen = _useState14[1];
302
+ isTagModalOpen = _useState14[0],
303
+ setIsTagModalOpen = _useState14[1];
304
+ var _useState15 = useState(null),
305
+ _useState16 = _slicedToArray(_useState15, 2),
306
+ selectedTag = _useState16[0],
307
+ setSelectedTag = _useState16[1];
308
+ var _useState17 = useState(enableSemanticTags),
309
+ _useState18 = _slicedToArray(_useState17, 2),
310
+ showSemanticTags = _useState18[0],
311
+ setShowSemanticTags = _useState18[1];
192
312
  useEffect(function () {
193
313
  // Reset state when jsonData changes
194
314
  // console.log("HtmlViewer received new jsonData");
@@ -207,6 +327,10 @@ function HtmlViewer(_ref5) {
207
327
  setModalData(node);
208
328
  setIsModalOpen(true);
209
329
  };
330
+ var handleSemanticTagClick = function handleSemanticTagClick(tag) {
331
+ setSelectedTag(tag);
332
+ setIsTagModalOpen(true);
333
+ };
210
334
  var updateAccuracyMetrics = useCallback(function (nodeId, blockType, originalContent, newContent) {
211
335
  var differences = calculateDifferences(originalContent, newContent);
212
336
  var change = {
@@ -247,12 +371,13 @@ function HtmlViewer(_ref5) {
247
371
  }, [jsonData]);
248
372
  var handleContentChange = useCallback(function (nodeId, newContent) {
249
373
  setHasChanges(true);
374
+ var originalContent = "";
250
375
  setEditedData(function (prevData) {
251
376
  var newData = JSON.parse(JSON.stringify(prevData));
252
377
  var _updateNode = function updateNode(node) {
253
378
  if (!node) return false;
254
379
  if (node.id === nodeId) {
255
- var originalContent = node.html || "";
380
+ originalContent = node.html || "";
256
381
  var blockType = node.block_type || "unknown";
257
382
  // Update accuracy metrics before changing content
258
383
  updateAccuracyMetrics(nodeId, blockType, originalContent, newContent);
@@ -278,9 +403,17 @@ function HtmlViewer(_ref5) {
278
403
  _updateNode(newData);
279
404
  return newData;
280
405
  });
281
- }, [updateAccuracyMetrics]);
406
+ // Call external callback if provided
407
+ if (onContentChangeCallback) {
408
+ onContentChangeCallback(nodeId, originalContent, newContent);
409
+ }
410
+ }, [updateAccuracyMetrics, onContentChangeCallback]);
282
411
  var handleDownload = function handleDownload() {
283
412
  if (!editedData) return;
413
+ // Call external callback if provided
414
+ if (onExportCallback) {
415
+ onExportCallback(editedData);
416
+ }
284
417
  var jsonString = JSON.stringify(editedData, null, 2);
285
418
  var blob = new Blob([jsonString], {
286
419
  type: "application/json"
@@ -296,10 +429,7 @@ function HtmlViewer(_ref5) {
296
429
  };
297
430
  // Function to get the appropriate HTML content based on settings
298
431
  var getHtmlContent = function getHtmlContent(node) {
299
- // For individual nodes in the tree view, just return their HTML directly
300
- // This preserves the node structure and click interactivity
301
- // getBlockHtml/jsonToHtml should only be used for full page rendering/export
302
- return cleanHtml(node.html || '');
432
+ return cleanHtml(node.html || "");
303
433
  };
304
434
  var mergedTablesMap = useMemo(function () {
305
435
  var map = new Map();
@@ -450,10 +580,13 @@ function HtmlViewer(_ref5) {
450
580
  var hasChildren = node.children && node.children.length > 0;
451
581
  var isTable = ((_a = node.block_type) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === "table";
452
582
  var isTableOfContents = node.block_type === "TableOfContents";
453
- var isText = node.block_type === "Text";
454
- var isHandwritten = node.block_type === "Handwriting";
455
- var isSectionHeader = node.block_type === "SectionHeader";
583
+ node.block_type === "Text";
584
+ node.block_type === "Handwriting";
585
+ node.block_type === "SectionHeader";
456
586
  var isPage = node.block_type === "Page";
587
+ var isFigure = node.block_type === "Figure" || node.block_type === "Picture";
588
+ var isPageHeader = node.block_type === "PageHeader";
589
+ var isPageFooter = node.block_type === "PageFooter";
457
590
  // Get the appropriate HTML content
458
591
  var htmlContent = getHtmlContent(node);
459
592
  var isHeading = htmlContent && (htmlContent.startsWith("<h1") || htmlContent.startsWith("<h2") || htmlContent.startsWith("<h3") || htmlContent.startsWith("<h4"));
@@ -474,50 +607,84 @@ function HtmlViewer(_ref5) {
474
607
  onNodeClick(node.id);
475
608
  }
476
609
  };
477
- return jsxs("div", {
610
+ return jsx("div", {
478
611
  id: node.id,
479
- className: "p-2 my-1 rounded transition-colors relative group max-w-full overflow-hidden ".concat(isSelected ? "bg-blue-100 border-2 border-blue-500" : "", " ").concat(onNodeClick && !isPage ? "cursor-pointer hover:bg-gray-50" : ""),
612
+ className: "px-1 py-0.5 rounded transition-colors relative group max-w-full overflow-hidden ".concat(isSelected ? "bg-blue-100 border-2 border-blue-500" : "", " ").concat(onNodeClick && !isPage ? "cursor-pointer hover:bg-gray-50" : ""),
480
613
  onClick: isPage ? undefined : handleContentClick,
481
- children: [!isPage && showJsonIcons && jsx("button", {
482
- onClick: function onClick(e) {
483
- e.stopPropagation(); // Prevent triggering parent onClick
484
- handleJsonClick(node);
485
- },
486
- className: "absolute right-2 top-2 z-10 ".concat(showJsonIcons ? "opacity-0 group-hover:opacity-100 transition-opacity" : "hidden", " text-gray-500 hover:text-gray-700"),
487
- title: "View JSON",
488
- children: jsx(VscJsonIcon, {
489
- size: 18
490
- })
491
- }), isTable || isTableOfContents ? jsx(Table, {
614
+ children: isTable || isTableOfContents ? jsx(Table, {
492
615
  node: node,
493
616
  selectedBboxId: selectedBboxId,
494
617
  onJsonClick: handleJsonClick,
495
618
  onContentChange: handleContentChange,
496
619
  mergedTables: node.merged_table_id ? (mergedTablesMap.get(node.merged_table_id) || []).slice(1) : [],
497
620
  hasLlmHtml: !!node.llm_table_html,
498
- showJsonIcons: showJsonIcons,
499
- onNodeClick: onNodeClick
500
- }) : isText || isHandwritten || isSectionHeader ? jsx(EditableContent, {
501
- id: node.id,
502
- content: htmlContent,
503
- onContentChange: handleContentChange,
504
- isHeading: !!isHeading,
505
- onNodeClick: onNodeClick && !isPage ? function () {
506
- return onNodeClick(node.id);
507
- } : undefined
508
- }) : jsxs(Fragment, {
509
- children: [jsx("div", {
510
- className: "prose max-w-none w-full overflow-hidden break-words",
621
+ showJsonIcons: false,
622
+ onNodeClick: onNodeClick,
623
+ isEditMode: viewMode === 'edit',
624
+ isJsonMode: viewMode === 'json',
625
+ enableMathRendering: enableMathRendering
626
+ }) : isFigure && node.images ? jsx("div", {
627
+ className: "my-2 ".concat(viewMode === 'json' ? 'cursor-pointer hover:opacity-80' : ''),
628
+ onClick: viewMode === 'json' ? function () {
629
+ return handleJsonClick(node);
630
+ } : undefined,
631
+ children: Object.entries(node.images).map(function (_ref7) {
632
+ var _ref8 = _slicedToArray(_ref7, 2),
633
+ key = _ref8[0],
634
+ imageData = _ref8[1];
635
+ return jsx("img", {
636
+ src: typeof imageData === 'string' && imageData.startsWith('data:') ? imageData : "data:image/png;base64,".concat(imageData),
637
+ alt: "Figure ".concat(node.id),
638
+ className: "max-w-full h-auto rounded border border-gray-200"
639
+ }, key);
640
+ })
641
+ }) : (isPageHeader || isPageFooter) && enableHeaderFooterBadges ? jsxs("div", {
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' : ''),
643
+ onClick: viewMode === 'json' ? function () {
644
+ return handleJsonClick(node);
645
+ } : undefined,
646
+ children: [jsx("span", {
647
+ className: "font-semibold uppercase tracking-wide ".concat(isPageHeader ? 'text-amber-600' : 'text-slate-500'),
648
+ children: isPageHeader ? '⬆ Header' : '⬇ Footer'
649
+ }), htmlContent && jsx("span", {
650
+ className: "text-gray-600",
511
651
  dangerouslySetInnerHTML: {
512
652
  __html: htmlContent
513
653
  }
654
+ }), node.images && Object.entries(node.images).map(function (_ref9) {
655
+ var _ref0 = _slicedToArray(_ref9, 2),
656
+ key = _ref0[0],
657
+ imageData = _ref0[1];
658
+ return jsx("img", {
659
+ src: typeof imageData === 'string' && imageData.startsWith('data:') ? imageData : "data:image/png;base64,".concat(imageData),
660
+ alt: "".concat(isPageHeader ? 'Header' : 'Footer', " image"),
661
+ className: "h-6 w-auto"
662
+ }, key);
663
+ })]
664
+ }) : jsxs(Fragment, {
665
+ children: [jsx(EditableContent, {
666
+ id: node.id,
667
+ content: htmlContent,
668
+ onContentChange: handleContentChange,
669
+ isHeading: !!isHeading,
670
+ isEditMode: viewMode === 'edit',
671
+ isJsonMode: viewMode === 'json',
672
+ onJsonClick: function onJsonClick() {
673
+ return handleJsonClick(node);
674
+ },
675
+ onNodeClick: onNodeClick && !isPage ? function () {
676
+ return onNodeClick(node.id);
677
+ } : undefined,
678
+ enableSemanticTags: showSemanticTags,
679
+ onSemanticTagClick: handleSemanticTagClick,
680
+ enableMathRendering: enableMathRendering
514
681
  }), hasChildren && jsx("div", {
515
- className: "ml-4 mt-2 border-l-2 border-gray-200 pl-4 max-w-full overflow-hidden",
682
+ className: "ml-2 mt-1 border-l border-gray-200 pl-2 max-w-full overflow-hidden",
516
683
  children: node.children.map(function (child) {
517
684
  return _renderHtmlContent(child);
518
685
  })
519
686
  })]
520
- })]
687
+ })
521
688
  }, node.id);
522
689
  };
523
690
  var getAllNodes = function getAllNodes(data) {
@@ -535,69 +702,80 @@ function HtmlViewer(_ref5) {
535
702
  });
536
703
  };
537
704
  var allNodes = getAllNodes(editedData);
538
- // Update the header section to include both buttons
705
+ // Update the header section with toggle buttons
539
706
  var renderHeader = function renderHeader() {
540
707
  return jsxs("div", {
541
- className: "sticky top-0 z-20 bg-white border-b border-gray-200 p-4 flex justify-between items-center flex-shrink-0",
542
- children: [jsx("div", {
543
- className: "flex gap-2",
544
- children: hasChanges && jsxs(Fragment, {
545
- children: [onSave && jsxs("button", {
546
- onClick: function onClick() {
547
- return onSave(editedData);
548
- },
549
- className: "inline-flex items-center gap-2 px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2",
550
- title: "Save Changes",
551
- children: [jsx(FaFileDownloadIcon, {
552
- size: 18
553
- }), "Save"]
554
- }), jsxs("button", {
555
- onClick: handleDownload,
556
- className: "inline-flex items-center gap-2 px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2",
557
- title: "Download Updated JSON",
558
- children: [jsx(FaFileDownloadIcon, {
559
- size: 18
560
- }), "Download"]
561
- })]
562
- })
563
- }), jsxs("div", {
564
- className: "flex gap-2 items-center",
565
- children: [jsx("button", {
566
- onClick: function onClick() {
567
- return setIsAnalyticsOpen(true);
568
- },
569
- className: "inline-flex items-center gap-2 px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2",
570
- title: "View Analytics",
571
- children: jsx(FaChartBarIcon, {
572
- size: 18
573
- })
574
- }), jsxs("div", {
708
+ className: "sticky top-0 z-20 bg-white border-b border-gray-200 p-3 flex justify-between items-center flex-shrink-0",
709
+ children: [jsxs("div", {
710
+ className: "flex gap-1",
711
+ children: [(enableJsonMode || enableEditMode) && jsxs("div", {
575
712
  className: "flex bg-gray-100 rounded-lg p-1",
576
- children: [jsx("button", {
713
+ children: [enableJsonMode && jsxs("button", {
577
714
  onClick: function onClick() {
578
- setShowJsonIcons(true);
579
- setActiveFormat("Show JSON");
715
+ return setViewMode(viewMode === 'json' ? 'read' : 'json');
580
716
  },
581
- className: "py-1.5 px-3 text-sm font-medium rounded-md transition-colors whitespace-nowrap ".concat(activeFormat === "Show JSON" ? "bg-white text-gray-900 shadow-sm" : "text-gray-600 hover:text-gray-900"),
582
- children: "Show JSON"
583
- }), jsx("button", {
717
+ className: "inline-flex items-center gap-2 px-3 py-1.5 rounded-md transition-colors ".concat(viewMode === 'json' ? "bg-blue-600 text-white shadow-sm" : "text-gray-600 hover:text-gray-900 hover:bg-gray-200"),
718
+ title: "JSON Mode - Click any element to view its JSON",
719
+ children: [jsx(VscJsonIcon, {
720
+ size: 16
721
+ }), jsx("span", {
722
+ className: "text-sm font-medium",
723
+ children: "JSON"
724
+ })]
725
+ }), enableEditMode && jsxs("button", {
584
726
  onClick: function onClick() {
585
- setShowJsonIcons(false);
586
- setActiveFormat("Hide JSON");
727
+ return setViewMode(viewMode === 'edit' ? 'read' : 'edit');
587
728
  },
588
- className: "py-1.5 px-3 text-sm font-medium rounded-md transition-colors whitespace-nowrap ".concat(activeFormat === "Hide JSON" ? "bg-white text-gray-900 shadow-sm" : "text-gray-600 hover:text-gray-900"),
589
- children: "Hide JSON"
729
+ className: "inline-flex items-center gap-2 px-3 py-1.5 rounded-md transition-colors ".concat(viewMode === 'edit' ? "bg-green-600 text-white shadow-sm" : "text-gray-600 hover:text-gray-900 hover:bg-gray-200"),
730
+ title: "Edit Mode - Click any element to edit it",
731
+ children: [jsx(FaEditIcon, {
732
+ size: 16
733
+ }), jsx("span", {
734
+ className: "text-sm font-medium",
735
+ children: "Edit"
736
+ })]
737
+ })]
738
+ }), enableSemanticTags && jsxs("button", {
739
+ onClick: function onClick() {
740
+ return setShowSemanticTags(!showSemanticTags);
741
+ },
742
+ className: "inline-flex items-center gap-2 px-3 py-1.5 rounded-md transition-colors ml-2 ".concat(showSemanticTags ? "bg-amber-100 text-amber-700 border border-amber-300" : "bg-gray-100 text-gray-500 border border-gray-200"),
743
+ title: showSemanticTags ? "Hide Semantic Tags" : "Show Semantic Tags",
744
+ children: [jsx(FaTagsIcon, {
745
+ size: 16
746
+ }), jsx("span", {
747
+ className: "text-sm font-medium",
748
+ children: "Tags"
749
+ })]
750
+ }), hasChanges && jsxs("button", {
751
+ onClick: handleDownload,
752
+ className: "inline-flex items-center gap-2 px-3 py-1.5 bg-purple-600 text-white rounded-md hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:ring-offset-2 ml-2",
753
+ title: "Download Updated JSON",
754
+ children: [jsx(FaFileDownloadIcon, {
755
+ size: 16
756
+ }), jsx("span", {
757
+ className: "text-sm font-medium",
758
+ children: "Save"
590
759
  })]
591
760
  })]
761
+ }), jsx("button", {
762
+ onClick: function onClick() {
763
+ return setIsAnalyticsOpen(true);
764
+ },
765
+ className: "inline-flex items-center gap-1 px-3 py-1.5 text-gray-600 hover:text-gray-900 hover:bg-gray-100 rounded-md",
766
+ title: "View Analytics",
767
+ children: jsx(FaChartBarIcon, {
768
+ size: 16
769
+ })
592
770
  })]
593
771
  });
594
772
  };
595
773
  return jsxs("div", {
596
774
  className: "w-full h-full max-w-full flex flex-col overflow-hidden",
597
775
  children: [renderHeader(), jsx("div", {
598
- className: "flex-1 overflow-auto min-h-0 max-w-full",
776
+ className: "flex-1 overflow-auto min-h-0 max-w-full ".concat(viewMode === 'edit' ? 'bg-green-50/50' : viewMode === 'json' ? 'bg-blue-50/50' : ''),
599
777
  children: jsx("div", {
600
- className: "p-4 max-w-full",
778
+ className: "p-2 max-w-full",
601
779
  children: allNodes.map(function (node) {
602
780
  return jsx("div", {
603
781
  className: "w-full max-w-full overflow-hidden break-words",
@@ -617,6 +795,12 @@ function HtmlViewer(_ref5) {
617
795
  return setIsModalOpen(false);
618
796
  },
619
797
  data: modalData
798
+ }), jsx(TagModal, {
799
+ isOpen: isTagModalOpen,
800
+ onClose: function onClose() {
801
+ return setIsTagModalOpen(false);
802
+ },
803
+ tag: selectedTag
620
804
  })]
621
805
  });
622
806
  }