@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
package/README.md CHANGED
@@ -1,15 +1,15 @@
1
- # Structura Library
1
+ # @tfw.in/structura-lib
2
2
 
3
- A React component library for viewing and editing structured documents with PDF rendering and persistent storage.
3
+ A React component library for PDF document viewing with structured data extraction and rendering.
4
4
 
5
5
  ## Features
6
6
 
7
- - 📄 **PDF Viewer** - Side-by-side PDF and structured content view
8
- - ✏️ **Inline Editing** - Double-click to edit any text element
9
- - 💾 **Persistent Storage** - Automatic save to SQLite database
10
- - 🔄 **Version History** - Track all edits with timestamps
11
- - 🎯 **Bidirectional Mapping** - Click highlighting between PDF and content
12
- - 📦 **Production Ready** - Zero configuration, auto-initialization
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
13
13
 
14
14
  ## Installation
15
15
 
@@ -17,363 +17,112 @@ A React component library for viewing and editing structured documents with PDF
17
17
  npm install @tfw.in/structura-lib
18
18
  ```
19
19
 
20
- ## Quick Start
20
+ ## Usage
21
21
 
22
- ### 1. Basic Usage
23
-
24
- ```tsx
22
+ ```jsx
25
23
  import { Structura } from '@tfw.in/structura-lib';
26
24
  import '@tfw.in/structura-lib/dist/esm/styles.css';
27
25
 
28
26
  function App() {
29
27
  return (
30
28
  <Structura
31
- initialPdfPath="/document.pdf"
32
- initialJsonData={jsonData}
33
- props={{
34
- APIKey: "your-api-key",
35
- onSave: (editedData) => {
36
- console.log('Document saved:', editedData);
37
- }
38
- }}
29
+ apiKey="your-api-key"
30
+ baseUrl="https://api.example.com"
39
31
  />
40
32
  );
41
33
  }
42
34
  ```
43
35
 
44
- ### 2. With Backend Storage
36
+ ## Props
45
37
 
46
- ```tsx
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
47
56
  import { Structura } from '@tfw.in/structura-lib';
48
57
  import '@tfw.in/structura-lib/dist/esm/styles.css';
49
58
 
50
59
  function App() {
51
- const [jsonData, setJsonData] = useState(null);
52
-
53
- const handleSave = async (editedData) => {
54
- const response = await fetch('http://localhost:3002/api/save', {
55
- method: 'POST',
56
- headers: { 'Content-Type': 'application/json' },
57
- body: JSON.stringify({
58
- pdfName: 'document.pdf',
59
- editedJson: editedData,
60
- originalJson: jsonData // Only on first save
61
- })
62
- });
60
+ const handleContentChange = (blockId, oldContent, newContent) => {
61
+ console.log(`Block ${blockId} changed`);
62
+ console.log('Old:', oldContent);
63
+ console.log('New:', newContent);
64
+ };
63
65
 
64
- const result = await response.json();
65
- console.log('Saved:', result);
66
+ const handleExport = (data) => {
67
+ console.log('Exported data:', data);
68
+ // Save to your backend, etc.
66
69
  };
67
70
 
68
71
  return (
69
72
  <Structura
70
- initialPdfPath="/document.pdf"
71
- initialJsonData={jsonData}
72
- props={{
73
- APIKey: "your-api-key",
74
- onSave: handleSave
75
- }}
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}
76
83
  />
77
84
  );
78
85
  }
79
86
  ```
80
87
 
81
- ## Props
82
-
83
- ### `Structura` Component
88
+ ## Math Rendering
84
89
 
85
- | Prop | Type | Required | Description |
86
- |------|------|----------|-------------|
87
- | `initialPdfPath` | `string \| null` | No | URL or path to PDF file |
88
- | `initialJsonData` | `any \| null` | No | Pre-loaded JSON data (bypasses API call) |
89
- | `props.APIKey` | `string` | Yes | Structura API key for processing |
90
- | `props.baseUrl` | `string` | No | Custom API base URL |
91
- | `props.onSave` | `(data: any) => void \| Promise<void>` | No | Callback when user saves edits |
92
-
93
- ## Backend Server (Optional)
94
-
95
- The library includes an Express server for persistent storage.
96
-
97
- ### Start the Server
98
-
99
- ```bash
100
- cd node_modules/@tfw.in/structura-lib
101
- npm run server
102
- ```
90
+ The library automatically renders LaTeX math expressions in content:
103
91
 
104
- Server runs on `http://localhost:3002`
92
+ - Inline math: `$x^2 + y^2 = z^2$`
93
+ - Display math: `$$\sum_{i=1}^{n} x_i$$`
105
94
 
106
- ### API Endpoints
95
+ You can also use the math utilities directly:
107
96
 
108
- #### POST /api/save
109
- Save edited document.
110
-
111
- ```bash
112
- curl -X POST http://localhost:3002/api/save \
113
- -H "Content-Type: application/json" \
114
- -d '{
115
- "pdfName": "document.pdf",
116
- "editedJson": {...},
117
- "originalJson": {...}
118
- }'
119
- ```
120
-
121
- #### GET /api/load/:pdfName
122
- Load document with latest edit.
123
-
124
- ```bash
125
- curl http://localhost:3002/api/load/document.pdf
126
- ```
127
-
128
- #### GET /api/history/:pdfName
129
- Get full edit history.
130
-
131
- ```bash
132
- curl http://localhost:3002/api/history/document.pdf
133
- ```
97
+ ```jsx
98
+ import { MathContent, renderMathInHtml, containsMath } from '@tfw.in/structura-lib';
134
99
 
135
- ## Usage Patterns
100
+ // React component
101
+ <MathContent html="The formula is $E = mc^2$" />
136
102
 
137
- ### Pattern 1: Database-First Loading
103
+ // Utility function
104
+ const rendered = renderMathInHtml("Price is $20$ dollars");
138
105
 
139
- ```tsx
140
- useEffect(() => {
141
- const pdfName = 'document.pdf';
142
-
143
- // Try database first
144
- fetch(`http://localhost:3002/api/load/${pdfName}`)
145
- .then(res => res.json())
146
- .then(result => {
147
- setJsonData(result.document.currentJson);
148
- })
149
- .catch(() => {
150
- // Fallback to file
151
- fetch('/data.json')
152
- .then(res => res.json())
153
- .then(data => setJsonData(data));
154
- });
155
- }, []);
156
- ```
157
-
158
- ### Pattern 2: Auto-Save on Edit
159
-
160
- ```tsx
161
- const handleSave = async (editedData) => {
162
- await fetch('http://localhost:3002/api/save', {
163
- method: 'POST',
164
- headers: { 'Content-Type': 'application/json' },
165
- body: JSON.stringify({
166
- pdfName: pdfUrl.split('/').pop(),
167
- editedJson: editedData
168
- })
169
- });
170
-
171
- // Reload from database to verify
172
- const response = await fetch(`http://localhost:3002/api/load/${pdfName}`);
173
- const result = await response.json();
174
- setJsonData(result.document.currentJson);
175
- };
176
- ```
177
-
178
- ### Pattern 3: URL Parameters
179
-
180
- ```tsx
181
- // Load from URL: ?pdf=/doc.pdf&json=/data.json
182
- useEffect(() => {
183
- const params = new URLSearchParams(window.location.search);
184
- const pdf = params.get('pdf');
185
- const json = params.get('json');
186
-
187
- if (pdf && json) {
188
- setPdfUrl(pdf);
189
- fetch(json)
190
- .then(res => res.json())
191
- .then(data => setJsonData(data));
192
- }
193
- }, []);
194
- ```
195
-
196
- ## Features
197
-
198
- ### Inline Editing
199
-
200
- Double-click any text to edit:
201
- - Edits are tracked automatically
202
- - Green **Save** button appears when changes are made
203
- - Yellow highlight indicates edited content
204
-
205
- ### Version History
206
-
207
- Every save creates a new version:
208
- - Full edit history preserved
209
- - Timestamps for each edit
210
- - Can restore any previous version
211
-
212
- ### Bidirectional Mapping
213
-
214
- Click elements to highlight corresponding content:
215
- - PDF → HTML viewer
216
- - HTML viewer → PDF location
217
- - Smooth scrolling and highlighting
218
-
219
- ## Development
220
-
221
- ### Build the Library
222
-
223
- ```bash
224
- npm run build
225
- ```
226
-
227
- ### Run Test App
228
-
229
- ```bash
230
- cd test-app
231
- npm install
232
- npm run dev
233
- ```
234
-
235
- Open: `http://localhost:5175`
236
-
237
- ### Run with Backend
238
-
239
- Terminal 1:
240
- ```bash
241
- npm run server
242
- ```
243
-
244
- Terminal 2:
245
- ```bash
246
- cd test-app
247
- npm run dev
248
- ```
249
-
250
- ## Database Schema
251
-
252
- ### documents table
253
- ```sql
254
- CREATE TABLE documents (
255
- id INTEGER PRIMARY KEY AUTOINCREMENT,
256
- pdf_name TEXT NOT NULL,
257
- original_json TEXT NOT NULL,
258
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP
259
- );
260
- ```
261
-
262
- ### edits table
263
- ```sql
264
- CREATE TABLE edits (
265
- id INTEGER PRIMARY KEY AUTOINCREMENT,
266
- document_id INTEGER NOT NULL,
267
- edited_json TEXT NOT NULL,
268
- edit_summary TEXT,
269
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
270
- FOREIGN KEY (document_id) REFERENCES documents(id)
271
- );
272
- ```
273
-
274
- ## Architecture
275
-
276
- ```
277
- ┌──────────────────┐
278
- │ React Component │
279
- │ (Structura) │
280
- └────────┬─────────┘
281
-
282
- ├─── PDF Viewer (Left)
283
- │ - Render PDF pages
284
- │ - Click to highlight
285
-
286
- └─── HTML Viewer (Right)
287
- - Structured content
288
- - Inline editing
289
- - Save button
290
-
291
-
292
- ┌──────────────┐
293
- │ Express API │
294
- │ (port 3002) │
295
- └──────┬───────┘
296
-
297
-
298
- ┌──────────────┐
299
- │ SQLite DB │
300
- │ (edits.db) │
301
- └──────────────┘
302
- ```
303
-
304
- ## Production Deployment
305
-
306
- ### Using PM2
307
-
308
- ```bash
309
- npm install -g pm2
310
- pm2 start server/server.js --name structura-server
311
- pm2 save
312
- pm2 startup
313
- ```
314
-
315
- ### Docker
316
-
317
- ```dockerfile
318
- FROM node:18
319
- WORKDIR /app
320
- COPY package*.json ./
321
- RUN npm install --production
322
- COPY . .
323
- EXPOSE 3002
324
- CMD ["node", "server/server.js"]
325
- ```
326
-
327
- ### Nginx
328
-
329
- ```nginx
330
- location /api/ {
331
- proxy_pass http://localhost:3002;
332
- client_max_body_size 50M;
106
+ // Check for math content
107
+ if (containsMath(text)) {
108
+ // handle math
333
109
  }
334
110
  ```
335
111
 
336
- ## Configuration
337
-
338
- ### Environment Variables
339
-
340
- ```bash
341
- PORT=3002 # Server port
342
- NODE_ENV=production # Environment
343
- DATABASE_PATH=./edits.db # SQLite path
344
- MAX_JSON_SIZE=50mb # Request limit
345
- ```
346
-
347
- ## Troubleshooting
348
-
349
- ### Save button not appearing
350
-
351
- 1. Check `onSave` prop is provided
352
- 2. Make an edit (double-click text)
353
- 3. Rebuild library: `npm run build`
112
+ ## Semantic Tags
354
113
 
355
- ### Database not persisting
114
+ Parse and render semantic tags for document corrections:
356
115
 
357
- 1. Check server is running: `curl http://localhost:3002/health`
358
- 2. Check database file exists: `ls -la server/edits.db`
359
- 3. Check browser console for errors
116
+ ```jsx
117
+ import { SemanticTagRenderer, parseSemanticTags } from '@tfw.in/structura-lib';
360
118
 
361
- ### PDF not loading
119
+ // Render with visual highlighting
120
+ <SemanticTagRenderer content="Text with <add>additions</add> and <del>deletions</del>" />
362
121
 
363
- 1. Check CORS settings on PDF server
364
- 2. Verify PDF path is correct
365
- 3. Check browser console for errors
122
+ // Parse tags programmatically
123
+ const parsed = parseSemanticTags(content);
124
+ ```
366
125
 
367
126
  ## License
368
127
 
369
128
  MIT
370
-
371
- ## Author
372
-
373
- TFW
374
-
375
- ## Support
376
-
377
- For issues and questions:
378
- - GitHub: https://github.com/ChakshuGautam/structura-lib
379
- - Documentation: See `/server/README.md` for API details
@@ -4,6 +4,9 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var jsxRuntime = require('react/jsx-runtime');
6
6
  var React = require('react');
7
+ var SemanticTagRenderer = require('./SemanticTagRenderer.js');
8
+ var SemanticTagParser = require('./SemanticTagParser.js');
9
+ var MathRenderer = require('./MathRenderer.js');
7
10
 
8
11
  // Helper function to extract text content while preserving structure
9
12
  const extractTextPreservingStructure = html => {
@@ -25,7 +28,13 @@ function EditableContent({
25
28
  content,
26
29
  onContentChange,
27
30
  isHeading = false,
28
- onNodeClick
31
+ isEditMode = false,
32
+ isJsonMode = false,
33
+ onNodeClick,
34
+ onJsonClick,
35
+ enableSemanticTags = true,
36
+ onSemanticTagClick,
37
+ enableMathRendering = true
29
38
  }) {
30
39
  const [isEditing, setIsEditing] = React.useState(false);
31
40
  const [editedContent, setEditedContent] = React.useState(content);
@@ -55,24 +64,22 @@ function EditableContent({
55
64
  const getHeadingStyles = level => {
56
65
  switch (level) {
57
66
  case 1:
58
- return "text-2xl font-bold";
59
- case 2:
60
67
  return "text-xl font-bold";
61
- case 3:
68
+ case 2:
62
69
  return "text-lg font-bold";
63
- case 4:
70
+ case 3:
64
71
  return "text-base font-bold";
65
- case 5:
72
+ case 4:
66
73
  return "text-sm font-bold";
67
- case 6:
74
+ case 5:
68
75
  return "text-xs font-bold";
76
+ case 6:
77
+ return "text-xs font-semibold";
69
78
  default:
70
- return "text-base";
79
+ return "text-sm";
71
80
  }
72
81
  };
73
- const handleDoubleClick = e => {
74
- // Stop propagation to prevent parent click handlers from firing
75
- e.stopPropagation();
82
+ const startEditing = () => {
76
83
  if (onContentChange) {
77
84
  // Extract text content for textarea while preserving structure
78
85
  setTextAreaContent(extractTextPreservingStructure(editedContent));
@@ -111,6 +118,18 @@ function EditableContent({
111
118
  e.stopPropagation();
112
119
  return;
113
120
  }
121
+ // In JSON mode, show JSON on click
122
+ if (isJsonMode && onJsonClick) {
123
+ e.stopPropagation();
124
+ onJsonClick();
125
+ return;
126
+ }
127
+ // In edit mode, single click activates editing
128
+ if (isEditMode && onContentChange) {
129
+ e.stopPropagation();
130
+ startEditing();
131
+ return;
132
+ }
114
133
  // If there's an onNodeClick handler, call it
115
134
  if (onNodeClick) {
116
135
  e.stopPropagation(); // Prevent click from reaching parent containers
@@ -124,25 +143,34 @@ function EditableContent({
124
143
  handleBlur();
125
144
  }
126
145
  };
146
+ // Render math expressions in content (conditionally)
147
+ const renderedContent = React.useMemo(() => {
148
+ return enableMathRendering ? MathRenderer.renderMathInHtml(editedContent) : editedContent;
149
+ }, [editedContent, enableMathRendering]);
127
150
  return jsxRuntime.jsx("div", {
128
- className: `w-full ${isEdited ? "bg-yellow-100" : ""} ${onNodeClick && !isEditing ? "cursor-pointer" : ""}`,
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" : ""}`,
129
152
  onClick: handleClick,
130
- onDoubleClick: handleDoubleClick,
131
153
  children: isEditing ? jsxRuntime.jsx("textarea", {
132
154
  value: textAreaContent,
133
155
  onChange: handleChange,
134
156
  onBlur: handleBlur,
135
157
  onKeyDown: handleKeyDown,
136
158
  autoFocus: true,
137
- 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",
159
+ className: "w-full p-1 border border-blue-400 rounded focus:outline-none focus:ring-2 focus:ring-blue-500",
138
160
  onClick: e => e.stopPropagation(),
139
161
  rows: 3
140
162
  }) : jsxRuntime.jsx("div", {
141
163
  ref: contentRef,
142
- className: `prose max-w-none ${isHeading ? getHeadingStyles(headingLevel) : ""}`,
143
- dangerouslySetInnerHTML: {
144
- __html: editedContent
145
- }
164
+ className: `prose prose-sm max-w-none ${isHeading ? getHeadingStyles(headingLevel) : "text-sm"}`,
165
+ children: enableSemanticTags && SemanticTagParser.hasSemanticTags(editedContent) ? jsxRuntime.jsx(SemanticTagRenderer.default, {
166
+ content: editedContent,
167
+ showTooltips: true,
168
+ onTagClick: onSemanticTagClick
169
+ }) : jsxRuntime.jsx("span", {
170
+ dangerouslySetInnerHTML: {
171
+ __html: renderedContent
172
+ }
173
+ })
146
174
  })
147
175
  });
148
176
  }