@tfw.in/structura-lib 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +9 -361
  2. package/dist/cjs/EditableContent.js +45 -18
  3. package/dist/cjs/HtmlViewer.js +217 -83
  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 +20 -66
  9. package/dist/cjs/Table.js +73 -8
  10. package/dist/cjs/TableCell.js +33 -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 +49 -19
  16. package/dist/esm/HtmlViewer.js +259 -100
  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 +21 -69
  22. package/dist/esm/Table.js +82 -8
  23. package/dist/esm/TableCell.js +32 -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 +7 -1
  30. package/dist/esm/types/HtmlViewer.d.ts +1 -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 +1 -2
  35. package/dist/esm/types/Table.d.ts +3 -1
  36. package/dist/esm/types/TableCell.d.ts +6 -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 +78 -4
  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,379 +1,27 @@
1
1
  # Structura Library
2
2
 
3
- A React component library for viewing and editing structured documents with PDF rendering and persistent storage.
4
-
5
- ## Features
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
3
+ A React component library for document viewing and processing.
13
4
 
14
5
  ## Installation
15
6
 
16
7
  ```bash
17
- npm install @tfw.in/structura-lib
8
+ npm install structura_lib
18
9
  ```
19
10
 
20
- ## Quick Start
11
+ ## Usage
21
12
 
22
- ### 1. Basic Usage
23
-
24
- ```tsx
25
- import { Structura } from '@tfw.in/structura-lib';
26
- import '@tfw.in/structura-lib/dist/esm/styles.css';
13
+ ```jsx
14
+ import { Structura } from 'structura-lib';
15
+ import 'structura_lib/dist/styles.css'
27
16
 
28
17
  function App() {
29
18
  return (
30
- <Structura
31
- initialPdfPath="/document.pdf"
32
- initialJsonData={jsonData}
19
+ <Structura
33
20
  props={{
34
- APIKey: "your-api-key",
35
- onSave: (editedData) => {
36
- console.log('Document saved:', editedData);
37
- }
21
+ APIKey: "your-api-key"
22
+ baseURL: "optional-api-base-url"
38
23
  }}
39
24
  />
40
25
  );
41
26
  }
42
27
  ```
43
-
44
- ### 2. With Backend Storage
45
-
46
- ```tsx
47
- import { Structura } from '@tfw.in/structura-lib';
48
- import '@tfw.in/structura-lib/dist/esm/styles.css';
49
-
50
- 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
- });
63
-
64
- const result = await response.json();
65
- console.log('Saved:', result);
66
- };
67
-
68
- return (
69
- <Structura
70
- initialPdfPath="/document.pdf"
71
- initialJsonData={jsonData}
72
- props={{
73
- APIKey: "your-api-key",
74
- onSave: handleSave
75
- }}
76
- />
77
- );
78
- }
79
- ```
80
-
81
- ## Props
82
-
83
- ### `Structura` Component
84
-
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
- ```
103
-
104
- Server runs on `http://localhost:3002`
105
-
106
- ### API Endpoints
107
-
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
- ```
134
-
135
- ## Usage Patterns
136
-
137
- ### Pattern 1: Database-First Loading
138
-
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;
333
- }
334
- ```
335
-
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`
354
-
355
- ### Database not persisting
356
-
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
360
-
361
- ### PDF not loading
362
-
363
- 1. Check CORS settings on PDF server
364
- 2. Verify PDF path is correct
365
- 3. Check browser console for errors
366
-
367
- ## License
368
-
369
- 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,12 @@ 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
29
37
  }) {
30
38
  const [isEditing, setIsEditing] = React.useState(false);
31
39
  const [editedContent, setEditedContent] = React.useState(content);
@@ -55,24 +63,22 @@ function EditableContent({
55
63
  const getHeadingStyles = level => {
56
64
  switch (level) {
57
65
  case 1:
58
- return "text-2xl font-bold";
59
- case 2:
60
66
  return "text-xl font-bold";
61
- case 3:
67
+ case 2:
62
68
  return "text-lg font-bold";
63
- case 4:
69
+ case 3:
64
70
  return "text-base font-bold";
65
- case 5:
71
+ case 4:
66
72
  return "text-sm font-bold";
67
- case 6:
73
+ case 5:
68
74
  return "text-xs font-bold";
75
+ case 6:
76
+ return "text-xs font-semibold";
69
77
  default:
70
- return "text-base";
78
+ return "text-sm";
71
79
  }
72
80
  };
73
- const handleDoubleClick = e => {
74
- // Stop propagation to prevent parent click handlers from firing
75
- e.stopPropagation();
81
+ const startEditing = () => {
76
82
  if (onContentChange) {
77
83
  // Extract text content for textarea while preserving structure
78
84
  setTextAreaContent(extractTextPreservingStructure(editedContent));
@@ -111,6 +117,18 @@ function EditableContent({
111
117
  e.stopPropagation();
112
118
  return;
113
119
  }
120
+ // In JSON mode, show JSON on click
121
+ if (isJsonMode && onJsonClick) {
122
+ e.stopPropagation();
123
+ onJsonClick();
124
+ return;
125
+ }
126
+ // In edit mode, single click activates editing
127
+ if (isEditMode && onContentChange) {
128
+ e.stopPropagation();
129
+ startEditing();
130
+ return;
131
+ }
114
132
  // If there's an onNodeClick handler, call it
115
133
  if (onNodeClick) {
116
134
  e.stopPropagation(); // Prevent click from reaching parent containers
@@ -124,25 +142,34 @@ function EditableContent({
124
142
  handleBlur();
125
143
  }
126
144
  };
145
+ // Render math expressions in content
146
+ const renderedContent = React.useMemo(() => {
147
+ return MathRenderer.renderMathInHtml(editedContent);
148
+ }, [editedContent]);
127
149
  return jsxRuntime.jsx("div", {
128
- className: `w-full ${isEdited ? "bg-yellow-100" : ""} ${onNodeClick && !isEditing ? "cursor-pointer" : ""}`,
150
+ 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
151
  onClick: handleClick,
130
- onDoubleClick: handleDoubleClick,
131
152
  children: isEditing ? jsxRuntime.jsx("textarea", {
132
153
  value: textAreaContent,
133
154
  onChange: handleChange,
134
155
  onBlur: handleBlur,
135
156
  onKeyDown: handleKeyDown,
136
157
  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",
158
+ className: "w-full p-1 border border-blue-400 rounded focus:outline-none focus:ring-2 focus:ring-blue-500",
138
159
  onClick: e => e.stopPropagation(),
139
160
  rows: 3
140
161
  }) : jsxRuntime.jsx("div", {
141
162
  ref: contentRef,
142
- className: `prose max-w-none ${isHeading ? getHeadingStyles(headingLevel) : ""}`,
143
- dangerouslySetInnerHTML: {
144
- __html: editedContent
145
- }
163
+ className: `prose prose-sm max-w-none ${isHeading ? getHeadingStyles(headingLevel) : "text-sm"}`,
164
+ children: enableSemanticTags && SemanticTagParser.hasSemanticTags(editedContent) ? jsxRuntime.jsx(SemanticTagRenderer.default, {
165
+ content: editedContent,
166
+ showTooltips: true,
167
+ onTagClick: onSemanticTagClick
168
+ }) : jsxRuntime.jsx("span", {
169
+ dangerouslySetInnerHTML: {
170
+ __html: renderedContent
171
+ }
172
+ })
146
173
  })
147
174
  });
148
175
  }