@willwade/aac-processors 0.1.5 → 0.1.7
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 +14 -0
- package/dist/browser/index.browser.js +15 -1
- package/dist/browser/processors/gridset/password.js +11 -0
- package/dist/browser/processors/gridsetProcessor.js +42 -46
- package/dist/browser/processors/obfProcessor.js +47 -63
- package/dist/browser/processors/snapProcessor.js +1031 -0
- package/dist/browser/processors/touchchatProcessor.js +1004 -0
- package/dist/browser/utils/io.js +36 -2
- package/dist/browser/utils/sqlite.js +109 -0
- package/dist/browser/utils/zip.js +54 -0
- package/dist/browser/validation/gridsetValidator.js +7 -27
- package/dist/browser/validation/obfValidator.js +9 -4
- package/dist/browser/validation/snapValidator.js +197 -0
- package/dist/browser/validation/touchChatValidator.js +201 -0
- package/dist/index.browser.d.ts +7 -0
- package/dist/index.browser.js +19 -2
- package/dist/processors/gridset/helpers.js +3 -4
- package/dist/processors/gridset/index.d.ts +1 -1
- package/dist/processors/gridset/index.js +3 -2
- package/dist/processors/gridset/password.d.ts +3 -2
- package/dist/processors/gridset/password.js +12 -0
- package/dist/processors/gridset/wordlistHelpers.js +107 -51
- package/dist/processors/gridsetProcessor.js +40 -44
- package/dist/processors/obfProcessor.js +46 -62
- package/dist/processors/snapProcessor.js +60 -54
- package/dist/processors/touchchatProcessor.js +38 -36
- package/dist/utils/io.d.ts +4 -0
- package/dist/utils/io.js +40 -2
- package/dist/utils/sqlite.d.ts +21 -0
- package/dist/utils/sqlite.js +137 -0
- package/dist/utils/zip.d.ts +7 -0
- package/dist/utils/zip.js +80 -0
- package/dist/validation/applePanelsValidator.js +11 -28
- package/dist/validation/astericsValidator.js +11 -30
- package/dist/validation/dotValidator.js +11 -30
- package/dist/validation/excelValidator.js +5 -6
- package/dist/validation/gridsetValidator.js +29 -26
- package/dist/validation/index.d.ts +2 -1
- package/dist/validation/index.js +9 -32
- package/dist/validation/obfValidator.js +8 -3
- package/dist/validation/obfsetValidator.js +11 -30
- package/dist/validation/opmlValidator.js +11 -30
- package/dist/validation/snapValidator.js +6 -9
- package/dist/validation/touchChatValidator.js +6 -7
- package/docs/BROWSER_USAGE.md +2 -10
- package/examples/README.md +3 -75
- package/examples/vitedemo/README.md +13 -7
- package/examples/vitedemo/index.html +51 -2
- package/examples/vitedemo/package-lock.json +9 -0
- package/examples/vitedemo/package.json +1 -0
- package/examples/vitedemo/src/main.ts +132 -2
- package/examples/vitedemo/src/vite-env.d.ts +1 -0
- package/examples/vitedemo/vite.config.ts +26 -7
- package/package.json +3 -1
- package/examples/browser-test-server.js +0 -81
package/examples/README.md
CHANGED
|
@@ -49,79 +49,7 @@ These pagesets are used by:
|
|
|
49
49
|
|
|
50
50
|
To run demo scripts that use these pagesets, see the [scripts/README.md](../scripts/README.md).
|
|
51
51
|
|
|
52
|
-
## Browser
|
|
52
|
+
## Browser Demo (Vite)
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
AACProcessors is built with TypeScript and outputs CommonJS modules. To use it in a browser, you **must use a bundler** (Vite, Webpack, Rollup, etc.). The browser test page below only validates the library structure - it cannot run actual processors without a bundler.
|
|
57
|
-
|
|
58
|
-
### Browser Test Page
|
|
59
|
-
|
|
60
|
-
A dedicated browser test page is available for validating the library structure:
|
|
61
|
-
|
|
62
|
-
**Start the test server:**
|
|
63
|
-
```bash
|
|
64
|
-
node examples/browser-test-server.js
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
**Open in your browser:**
|
|
68
|
-
```
|
|
69
|
-
http://localhost:8080/examples/browser-test.html
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
**What it tests:**
|
|
73
|
-
- ✅ Browser build files exist and are accessible
|
|
74
|
-
- ✅ Type definitions are present
|
|
75
|
-
- ✅ Processor exports are available
|
|
76
|
-
- ❌ **Does NOT run actual processors** (requires bundler)
|
|
77
|
-
|
|
78
|
-
### What Gets Tested
|
|
79
|
-
|
|
80
|
-
The browser test page (`browser-test.html`) verifies:
|
|
81
|
-
|
|
82
|
-
1. **Module Loading** - Can the browser load the ES modules?
|
|
83
|
-
2. **Factory Functions** - Do `getProcessor()` and `getSupportedExtensions()` work?
|
|
84
|
-
3. **Processor Instantiation** - Can processors be created?
|
|
85
|
-
4. **File Loading** - Can files be loaded from `<input type="file">`?
|
|
86
|
-
5. **Buffer Handling** - Do ArrayBuffers/Uint8Arrays work correctly?
|
|
87
|
-
6. **Tree Structure** - Can AACTree be created from files?
|
|
88
|
-
7. **Text Extraction** - Can texts be extracted from files?
|
|
89
|
-
|
|
90
|
-
### Supported File Types in Browser
|
|
91
|
-
|
|
92
|
-
The browser test page supports all browser-compatible processors:
|
|
93
|
-
|
|
94
|
-
- **DotProcessor** (.dot) - OpenSymbols Board files
|
|
95
|
-
- **OpmlProcessor** (.opml) - OPML outline files
|
|
96
|
-
- **ObfProcessor** (.obf/.obz) - Open Board Format files
|
|
97
|
-
- **GridsetProcessor** (.gridset) - Grid 3 gridset files (not .gridsetx)
|
|
98
|
-
- **ApplePanelsProcessor** (.plist) - Apple Panels files
|
|
99
|
-
- **AstericsGridProcessor** (.grd) - Asterics Grid files
|
|
100
|
-
|
|
101
|
-
### Manual Testing
|
|
102
|
-
|
|
103
|
-
1. Open the browser console (F12 or Cmd+Option+I)
|
|
104
|
-
2. Click "Select a file to test" to upload an AAC file
|
|
105
|
-
3. Click "Test File" to process it
|
|
106
|
-
4. Check the results panel for page count, button count, and extracted texts
|
|
107
|
-
|
|
108
|
-
### Automated Tests
|
|
109
|
-
|
|
110
|
-
Click "Run All Browser Tests" to run automated checks:
|
|
111
|
-
- Factory function tests
|
|
112
|
-
- Extension support tests
|
|
113
|
-
- Processor instantiation tests
|
|
114
|
-
- Buffer handling tests
|
|
115
|
-
|
|
116
|
-
### Node-Only Processors
|
|
117
|
-
|
|
118
|
-
The following processors are **not available** in the browser:
|
|
119
|
-
- **SnapProcessor** (.spb/.sps) - Requires SQLite
|
|
120
|
-
- **TouchChatProcessor** (.ce) - Requires SQLite
|
|
121
|
-
- **ExcelProcessor** (.xlsx) - Uses fs at top level
|
|
122
|
-
|
|
123
|
-
### Notes
|
|
124
|
-
|
|
125
|
-
- Gridset `.gridsetx` files (encrypted) are not supported in browser
|
|
126
|
-
- All processors work with Buffer, Uint8Array, and ArrayBuffer inputs
|
|
127
|
-
- File paths are not supported in browser (use file inputs or fetch)
|
|
54
|
+
For browser testing, use the Vite demo in `examples/vitedemo`. It bundles the
|
|
55
|
+
library and exercises real processors in a browser environment.
|
|
@@ -5,7 +5,7 @@ A real browser demo that uses Vite to bundle AACProcessors for browser use.
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- ✅ **Real file processing** - Upload and process actual AAC files
|
|
8
|
-
- ✅ **All browser-compatible processors** - Tests Dot, OPML, OBF/OBZ, Gridset, ApplePanels, AstericsGrid
|
|
8
|
+
- ✅ **All browser-compatible processors** - Tests Dot, OPML, OBF/OBZ, Gridset, Snap, TouchChat, ApplePanels, AstericsGrid
|
|
9
9
|
- ✅ **Interactive UI** - Drag & drop files, view pages and buttons
|
|
10
10
|
- ✅ **Text-to-speech** - Click SPEAK buttons to hear messages (browser speech API)
|
|
11
11
|
- ✅ **Navigation** - Click NAVIGATE buttons to jump between pages
|
|
@@ -35,15 +35,14 @@ The demo will open automatically at: http://localhost:3000
|
|
|
35
35
|
This demo is intended for `npm run dev` only. The production build currently fails because the
|
|
36
36
|
demo source includes strict TypeScript issues, so it will not work "out of the box."
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
`node examples/browser-test-server.js`
|
|
38
|
+
This demo is the recommended browser test environment.
|
|
40
39
|
|
|
41
40
|
## How to Use
|
|
42
41
|
|
|
43
42
|
1. **Upload a file**
|
|
44
43
|
- Drag & drop an AAC file onto the upload area
|
|
45
44
|
- Or click to open file picker
|
|
46
|
-
- Supported formats: .dot, .opml, .obf, .obz, .gridset, .plist, .grd
|
|
45
|
+
- Supported formats: .dot, .opml, .obf, .obz, .gridset, .sps, .spb, .ce, .plist, .grd
|
|
47
46
|
|
|
48
47
|
2. **Process the file**
|
|
49
48
|
- Click "Process File" button
|
|
@@ -57,7 +56,7 @@ If you need a no-build browser check, use the browser test page served by:
|
|
|
57
56
|
4. **Run compatibility tests**
|
|
58
57
|
- Click "Run Compatibility Tests"
|
|
59
58
|
- See test results in the left panel
|
|
60
|
-
- Tests all
|
|
59
|
+
- Tests all browser-compatible processors
|
|
61
60
|
|
|
62
61
|
## Supported File Types
|
|
63
62
|
|
|
@@ -67,6 +66,8 @@ If you need a no-build browser check, use the browser test page served by:
|
|
|
67
66
|
| OPML | .opml | OpmlProcessor |
|
|
68
67
|
| OBF/OBZ | .obf, .obz | ObfProcessor |
|
|
69
68
|
| Gridset | .gridset | GridsetProcessor |
|
|
69
|
+
| Snap | .sps, .spb | SnapProcessor |
|
|
70
|
+
| TouchChat| .ce | TouchChatProcessor |
|
|
70
71
|
| Apple | .plist | ApplePanelsProcessor |
|
|
71
72
|
| Asterics | .grd | AstericsGridProcessor |
|
|
72
73
|
|
|
@@ -102,10 +103,15 @@ export default defineConfig({
|
|
|
102
103
|
|
|
103
104
|
This allows direct TypeScript import without pre-building.
|
|
104
105
|
|
|
105
|
-
### Import Example
|
|
106
|
+
### Import Example (with SQLite WASM)
|
|
106
107
|
|
|
107
108
|
```typescript
|
|
108
|
-
import { getProcessor } from 'aac-processors';
|
|
109
|
+
import { configureSqlJs, getProcessor } from 'aac-processors';
|
|
110
|
+
import sqlWasmUrl from 'sql.js/dist/sql-wasm.wasm?url';
|
|
111
|
+
|
|
112
|
+
configureSqlJs({
|
|
113
|
+
locateFile: () => sqlWasmUrl
|
|
114
|
+
});
|
|
109
115
|
|
|
110
116
|
// Get processor for file type
|
|
111
117
|
const processor = getProcessor('.obf');
|
|
@@ -191,6 +191,48 @@
|
|
|
191
191
|
font-size: 13px;
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
+
.validation-panel {
|
|
195
|
+
margin-top: 15px;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.validation-summary {
|
|
199
|
+
background: #f8f9fa;
|
|
200
|
+
border-radius: 8px;
|
|
201
|
+
padding: 10px 12px;
|
|
202
|
+
font-size: 13px;
|
|
203
|
+
font-weight: 600;
|
|
204
|
+
margin-bottom: 10px;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.validation-summary.success {
|
|
208
|
+
border-left: 4px solid #2ecc71;
|
|
209
|
+
color: #2d7a4f;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.validation-summary.error {
|
|
213
|
+
border-left: 4px solid #e74c3c;
|
|
214
|
+
color: #b03a2e;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.validation-list {
|
|
218
|
+
max-height: 180px;
|
|
219
|
+
overflow-y: auto;
|
|
220
|
+
font-size: 12px;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.validation-item {
|
|
224
|
+
padding: 6px 0;
|
|
225
|
+
border-bottom: 1px solid #ececec;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.validation-item.warn {
|
|
229
|
+
color: #c18401;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.validation-item.error {
|
|
233
|
+
color: #b03a2e;
|
|
234
|
+
}
|
|
235
|
+
|
|
194
236
|
.processor-name {
|
|
195
237
|
font-weight: 600;
|
|
196
238
|
color: #333;
|
|
@@ -423,11 +465,11 @@
|
|
|
423
465
|
<div class="upload-icon">📤</div>
|
|
424
466
|
<p><strong>Drop file here</strong> or click to upload</p>
|
|
425
467
|
<p style="font-size: 12px; color: #999; margin-top: 5px;">
|
|
426
|
-
Supports: .dot, .opml, .obf, .obz, .gridset, .plist, .grd
|
|
468
|
+
Supports: .dot, .opml, .obf, .obz, .gridset, .sps, .spb, .ce, .plist, .grd
|
|
427
469
|
</p>
|
|
428
470
|
</div>
|
|
429
471
|
|
|
430
|
-
<input type="file" id="fileInput" accept=".dot,.opml,.obf,.obz,.gridset,.plist,.grd">
|
|
472
|
+
<input type="file" id="fileInput" accept=".dot,.opml,.obf,.obz,.gridset,.sps,.spb,.ce,.plist,.grd">
|
|
431
473
|
|
|
432
474
|
<div id="fileInfo" style="display: none;">
|
|
433
475
|
<div class="processor-info">
|
|
@@ -437,6 +479,7 @@
|
|
|
437
479
|
</div>
|
|
438
480
|
|
|
439
481
|
<button class="btn" id="processBtn" disabled>🚀 Process File</button>
|
|
482
|
+
<button class="btn btn-secondary" id="validateBtn" disabled>✅ Validate File</button>
|
|
440
483
|
<button class="btn btn-secondary" id="runTestsBtn">🧪 Run Compatibility Tests</button>
|
|
441
484
|
<button class="btn btn-secondary" id="clearBtn">🗑️ Clear Results</button>
|
|
442
485
|
|
|
@@ -467,6 +510,12 @@
|
|
|
467
510
|
<div class="panel-title">Test Results</div>
|
|
468
511
|
<div id="testList"></div>
|
|
469
512
|
</div>
|
|
513
|
+
|
|
514
|
+
<div class="validation-panel" id="validationPanel" style="display: none;">
|
|
515
|
+
<div class="panel-title">Validation</div>
|
|
516
|
+
<div class="validation-summary" id="validationSummary"></div>
|
|
517
|
+
<div class="validation-list" id="validationList"></div>
|
|
518
|
+
</div>
|
|
470
519
|
</div>
|
|
471
520
|
|
|
472
521
|
<!-- Right Panel: Results -->
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"@willwade/aac-processors": "file:../..",
|
|
12
12
|
"events": "^3.3.0",
|
|
13
13
|
"jszip": "^3.10.1",
|
|
14
|
+
"sql.js": "^1.13.0",
|
|
14
15
|
"stream-browserify": "^3.0.0",
|
|
15
16
|
"timers-browserify": "^2.0.12",
|
|
16
17
|
"util": "^0.12.5"
|
|
@@ -34,6 +35,7 @@
|
|
|
34
35
|
"fast-xml-parser": "^5.2.0",
|
|
35
36
|
"jszip": "^3.10.1",
|
|
36
37
|
"plist": "^3.1.0",
|
|
38
|
+
"sql.js": "^1.13.0",
|
|
37
39
|
"xml2js": "^0.6.2",
|
|
38
40
|
"yauzl": "^3.2.0"
|
|
39
41
|
},
|
|
@@ -47,6 +49,7 @@
|
|
|
47
49
|
"@types/jest": "^29.5.12",
|
|
48
50
|
"@types/node": "^20.11.24",
|
|
49
51
|
"@types/plist": "^3.0.5",
|
|
52
|
+
"@types/sql.js": "^1.4.9",
|
|
50
53
|
"@typescript-eslint/eslint-plugin": "^7.1.0",
|
|
51
54
|
"@typescript-eslint/parser": "^7.1.0",
|
|
52
55
|
"eslint": "^8.56.0",
|
|
@@ -1547,6 +1550,12 @@
|
|
|
1547
1550
|
"node": ">=0.10.0"
|
|
1548
1551
|
}
|
|
1549
1552
|
},
|
|
1553
|
+
"node_modules/sql.js": {
|
|
1554
|
+
"version": "1.13.0",
|
|
1555
|
+
"resolved": "https://registry.npmjs.org/sql.js/-/sql.js-1.13.0.tgz",
|
|
1556
|
+
"integrity": "sha512-RJbVP1HRDlUUXahJ7VMTcu9Rm1Nzw+EBpoPr94vnbD4LwR715F3CcxE2G2k45PewcaZ57pjetYa+LoSJLAASgA==",
|
|
1557
|
+
"license": "MIT"
|
|
1558
|
+
},
|
|
1550
1559
|
"node_modules/stream-browserify": {
|
|
1551
1560
|
"version": "3.0.0",
|
|
1552
1561
|
"resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz",
|
|
@@ -66,23 +66,34 @@ if (typeof (window as any).Buffer === 'undefined') {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
import {
|
|
69
|
+
configureSqlJs,
|
|
69
70
|
getProcessor,
|
|
70
71
|
getSupportedExtensions,
|
|
71
72
|
DotProcessor,
|
|
72
73
|
OpmlProcessor,
|
|
73
74
|
ObfProcessor,
|
|
74
75
|
GridsetProcessor,
|
|
76
|
+
SnapProcessor,
|
|
77
|
+
TouchChatProcessor,
|
|
75
78
|
ApplePanelsProcessor,
|
|
76
79
|
AstericsGridProcessor,
|
|
77
80
|
AACTree,
|
|
78
81
|
AACPage,
|
|
79
82
|
AACButton
|
|
80
83
|
} from 'aac-processors';
|
|
84
|
+
import { validateFileOrBuffer, type ValidationResult } from 'aac-processors/validation';
|
|
85
|
+
|
|
86
|
+
import sqlWasmUrl from 'sql.js/dist/sql-wasm.wasm?url';
|
|
87
|
+
|
|
88
|
+
configureSqlJs({
|
|
89
|
+
locateFile: () => sqlWasmUrl
|
|
90
|
+
});
|
|
81
91
|
|
|
82
92
|
// UI Elements
|
|
83
93
|
const dropArea = document.getElementById('dropArea') as HTMLElement;
|
|
84
94
|
const fileInput = document.getElementById('fileInput') as HTMLInputElement;
|
|
85
95
|
const processBtn = document.getElementById('processBtn') as HTMLButtonElement;
|
|
96
|
+
const validateBtn = document.getElementById('validateBtn') as HTMLButtonElement;
|
|
86
97
|
const runTestsBtn = document.getElementById('runTestsBtn') as HTMLButtonElement;
|
|
87
98
|
const clearBtn = document.getElementById('clearBtn') as HTMLButtonElement;
|
|
88
99
|
const fileInfo = document.getElementById('fileInfo') as HTMLElement;
|
|
@@ -93,6 +104,9 @@ const results = document.getElementById('results') as HTMLElement;
|
|
|
93
104
|
const logPanel = document.getElementById('logPanel') as HTMLElement;
|
|
94
105
|
const testResults = document.getElementById('testResults') as HTMLElement;
|
|
95
106
|
const testList = document.getElementById('testList') as HTMLElement;
|
|
107
|
+
const validationPanel = document.getElementById('validationPanel') as HTMLElement;
|
|
108
|
+
const validationSummary = document.getElementById('validationSummary') as HTMLElement;
|
|
109
|
+
const validationList = document.getElementById('validationList') as HTMLElement;
|
|
96
110
|
const tabButtons = document.querySelectorAll('.tab-btn') as NodeListOf<HTMLButtonElement>;
|
|
97
111
|
const inspectTab = document.getElementById('inspectTab') as HTMLElement;
|
|
98
112
|
const pagesetTab = document.getElementById('pagesetTab') as HTMLElement;
|
|
@@ -209,6 +223,7 @@ function handleFile(file: File) {
|
|
|
209
223
|
fileDetails.textContent = extension;
|
|
210
224
|
fileInfo.style.display = 'block';
|
|
211
225
|
processBtn.disabled = true;
|
|
226
|
+
validateBtn.disabled = true;
|
|
212
227
|
return;
|
|
213
228
|
}
|
|
214
229
|
|
|
@@ -219,12 +234,14 @@ function handleFile(file: File) {
|
|
|
219
234
|
fileDetails.textContent = `${file.name} • ${formatFileSize(file.size)}`;
|
|
220
235
|
fileInfo.style.display = 'block';
|
|
221
236
|
processBtn.disabled = false;
|
|
237
|
+
validateBtn.disabled = false;
|
|
222
238
|
currentSourceLabel = file.name;
|
|
223
239
|
|
|
224
240
|
log(`Using processor: ${currentProcessor.constructor.name}`, 'success');
|
|
225
241
|
} catch (error) {
|
|
226
242
|
log(`Error getting processor: ${(error as Error).message}`, 'error');
|
|
227
243
|
processBtn.disabled = true;
|
|
244
|
+
validateBtn.disabled = true;
|
|
228
245
|
}
|
|
229
246
|
}
|
|
230
247
|
|
|
@@ -305,6 +322,79 @@ processBtn.addEventListener('click', async () => {
|
|
|
305
322
|
}
|
|
306
323
|
});
|
|
307
324
|
|
|
325
|
+
function collectValidationMessages(
|
|
326
|
+
result: ValidationResult,
|
|
327
|
+
prefix = ''
|
|
328
|
+
): Array<{ type: 'error' | 'warn'; message: string }> {
|
|
329
|
+
const messages: Array<{ type: 'error' | 'warn'; message: string }> = [];
|
|
330
|
+
const label = prefix ? `${prefix}: ` : '';
|
|
331
|
+
result.results.forEach((check) => {
|
|
332
|
+
if (!check.valid && check.error) {
|
|
333
|
+
messages.push({ type: 'error', message: `${label}${check.description}: ${check.error}` });
|
|
334
|
+
}
|
|
335
|
+
if (check.warnings?.length) {
|
|
336
|
+
check.warnings.forEach((warning) => {
|
|
337
|
+
messages.push({ type: 'warn', message: `${label}${check.description}: ${warning}` });
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
result.sub_results?.forEach((sub) => {
|
|
342
|
+
const nextPrefix = `${label}${sub.filename || sub.format}`;
|
|
343
|
+
messages.push(...collectValidationMessages(sub, nextPrefix));
|
|
344
|
+
});
|
|
345
|
+
return messages;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function renderValidationResult(result: ValidationResult) {
|
|
349
|
+
validationPanel.style.display = 'block';
|
|
350
|
+
validationSummary.classList.remove('success', 'error');
|
|
351
|
+
validationSummary.classList.add(result.valid ? 'success' : 'error');
|
|
352
|
+
validationSummary.textContent = `${result.valid ? '✅ Valid' : '❌ Invalid'} • ${result.format.toUpperCase()} • ${result.errors} errors, ${result.warnings} warnings`;
|
|
353
|
+
|
|
354
|
+
validationList.innerHTML = '';
|
|
355
|
+
const messages = collectValidationMessages(result).slice(0, 30);
|
|
356
|
+
if (messages.length === 0) {
|
|
357
|
+
const empty = document.createElement('div');
|
|
358
|
+
empty.className = 'validation-item';
|
|
359
|
+
empty.textContent = 'No issues reported.';
|
|
360
|
+
validationList.appendChild(empty);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
messages.forEach((entry) => {
|
|
365
|
+
const item = document.createElement('div');
|
|
366
|
+
item.className = `validation-item ${entry.type}`;
|
|
367
|
+
item.textContent = entry.message;
|
|
368
|
+
validationList.appendChild(item);
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
validateBtn.addEventListener('click', async () => {
|
|
373
|
+
if (!currentFile) return;
|
|
374
|
+
log('Validating file...', 'info');
|
|
375
|
+
|
|
376
|
+
try {
|
|
377
|
+
validateBtn.disabled = true;
|
|
378
|
+
const arrayBuffer = await currentFile.arrayBuffer();
|
|
379
|
+
const result = await validateFileOrBuffer(new Uint8Array(arrayBuffer), currentFile.name);
|
|
380
|
+
renderValidationResult(result);
|
|
381
|
+
log(
|
|
382
|
+
`${result.valid ? '✅' : '❌'} Validation complete: ${result.errors} errors, ${result.warnings} warnings`,
|
|
383
|
+
result.valid ? 'success' : 'warn'
|
|
384
|
+
);
|
|
385
|
+
} catch (error) {
|
|
386
|
+
const errorMsg = (error as Error).message;
|
|
387
|
+
validationPanel.style.display = 'block';
|
|
388
|
+
validationSummary.classList.remove('success');
|
|
389
|
+
validationSummary.classList.add('error');
|
|
390
|
+
validationSummary.textContent = `❌ Validation failed: ${errorMsg}`;
|
|
391
|
+
validationList.innerHTML = '';
|
|
392
|
+
log(`❌ Validation failed: ${errorMsg}`, 'error');
|
|
393
|
+
} finally {
|
|
394
|
+
validateBtn.disabled = !currentFile;
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
|
|
308
398
|
// Display results
|
|
309
399
|
function displayResults(tree: AACTree) {
|
|
310
400
|
results.innerHTML = '';
|
|
@@ -414,6 +504,9 @@ clearBtn.addEventListener('click', () => {
|
|
|
414
504
|
stats.style.display = 'none';
|
|
415
505
|
results.innerHTML = '<p style="color: #999; text-align: center; padding: 40px;">Load a file to see its contents here</p>';
|
|
416
506
|
testResults.style.display = 'none';
|
|
507
|
+
validationPanel.style.display = 'none';
|
|
508
|
+
validationSummary.textContent = '';
|
|
509
|
+
validationList.innerHTML = '';
|
|
417
510
|
logPanel.innerHTML = '<div class="log-entry log-info">Cleared. Ready to process files...</div>';
|
|
418
511
|
pagesetOutput.textContent = 'Generate or convert a pageset to preview the output JSON.';
|
|
419
512
|
updateConvertButtons();
|
|
@@ -717,11 +810,15 @@ runTestsBtn.addEventListener('click', async () => {
|
|
|
717
810
|
const opmlProc = getProcessor('.opml');
|
|
718
811
|
const obfProc = getProcessor('.obf');
|
|
719
812
|
const gridsetProc = getProcessor('.gridset');
|
|
813
|
+
const snapProc = getProcessor('.sps');
|
|
814
|
+
const touchChatProc = getProcessor('.ce');
|
|
720
815
|
return (
|
|
721
816
|
dotProc instanceof DotProcessor &&
|
|
722
817
|
opmlProc instanceof OpmlProcessor &&
|
|
723
818
|
obfProc instanceof ObfProcessor &&
|
|
724
|
-
gridsetProc instanceof GridsetProcessor
|
|
819
|
+
gridsetProc instanceof GridsetProcessor &&
|
|
820
|
+
snapProc instanceof SnapProcessor &&
|
|
821
|
+
touchChatProc instanceof TouchChatProcessor
|
|
725
822
|
);
|
|
726
823
|
}
|
|
727
824
|
},
|
|
@@ -729,7 +826,18 @@ runTestsBtn.addEventListener('click', async () => {
|
|
|
729
826
|
name: 'getSupportedExtensions() returns all extensions',
|
|
730
827
|
fn: async () => {
|
|
731
828
|
const extensions = getSupportedExtensions();
|
|
732
|
-
const expected = [
|
|
829
|
+
const expected = [
|
|
830
|
+
'.dot',
|
|
831
|
+
'.opml',
|
|
832
|
+
'.obf',
|
|
833
|
+
'.obz',
|
|
834
|
+
'.gridset',
|
|
835
|
+
'.spb',
|
|
836
|
+
'.sps',
|
|
837
|
+
'.ce',
|
|
838
|
+
'.plist',
|
|
839
|
+
'.grd'
|
|
840
|
+
];
|
|
733
841
|
return expected.every((ext) => extensions.includes(ext));
|
|
734
842
|
}
|
|
735
843
|
},
|
|
@@ -777,6 +885,28 @@ runTestsBtn.addEventListener('click', async () => {
|
|
|
777
885
|
}
|
|
778
886
|
}
|
|
779
887
|
},
|
|
888
|
+
{
|
|
889
|
+
name: 'SnapProcessor instantiation',
|
|
890
|
+
fn: async () => {
|
|
891
|
+
try {
|
|
892
|
+
new SnapProcessor();
|
|
893
|
+
return true;
|
|
894
|
+
} catch {
|
|
895
|
+
return false;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
},
|
|
899
|
+
{
|
|
900
|
+
name: 'TouchChatProcessor instantiation',
|
|
901
|
+
fn: async () => {
|
|
902
|
+
try {
|
|
903
|
+
new TouchChatProcessor();
|
|
904
|
+
return true;
|
|
905
|
+
} catch {
|
|
906
|
+
return false;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
},
|
|
780
910
|
{
|
|
781
911
|
name: 'ApplePanelsProcessor instantiation',
|
|
782
912
|
fn: async () => {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
@@ -3,13 +3,32 @@ import path from 'path';
|
|
|
3
3
|
|
|
4
4
|
export default defineConfig({
|
|
5
5
|
resolve: {
|
|
6
|
-
alias:
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
alias: [
|
|
7
|
+
{
|
|
8
|
+
find: /^aac-processors\/validation$/,
|
|
9
|
+
replacement: path.resolve(__dirname, '../../src/validation.ts'),
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
find: /^aac-processors$/,
|
|
13
|
+
replacement: path.resolve(__dirname, '../../src/index.browser.ts'),
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
find: /^stream$/,
|
|
17
|
+
replacement: path.resolve(__dirname, 'node_modules/stream-browserify'),
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
find: /^events$/,
|
|
21
|
+
replacement: path.resolve(__dirname, 'node_modules/events'),
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
find: /^timers$/,
|
|
25
|
+
replacement: path.resolve(__dirname, 'node_modules/timers-browserify'),
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
find: /^util$/,
|
|
29
|
+
replacement: path.resolve(__dirname, 'node_modules/util'),
|
|
30
|
+
},
|
|
31
|
+
],
|
|
13
32
|
},
|
|
14
33
|
optimizeDeps: {
|
|
15
34
|
exclude: ['aac-processors'],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@willwade/aac-processors",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "A comprehensive TypeScript library for processing AAC (Augmentative and Alternative Communication) file formats with translation support",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"browser": "dist/browser/index.browser.js",
|
|
@@ -151,6 +151,7 @@
|
|
|
151
151
|
"@types/jest": "^29.5.12",
|
|
152
152
|
"@types/node": "^20.11.24",
|
|
153
153
|
"@types/plist": "^3.0.5",
|
|
154
|
+
"@types/sql.js": "^1.4.9",
|
|
154
155
|
"@typescript-eslint/eslint-plugin": "^7.1.0",
|
|
155
156
|
"@typescript-eslint/parser": "^7.1.0",
|
|
156
157
|
"eslint": "^8.56.0",
|
|
@@ -174,6 +175,7 @@
|
|
|
174
175
|
"fast-xml-parser": "^5.2.0",
|
|
175
176
|
"jszip": "^3.10.1",
|
|
176
177
|
"plist": "^3.1.0",
|
|
178
|
+
"sql.js": "^1.13.0",
|
|
177
179
|
"xml2js": "^0.6.2",
|
|
178
180
|
"yauzl": "^3.2.0"
|
|
179
181
|
},
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Simple HTTP server for testing AACProcessors in a browser
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* node examples/browser-test-server.js
|
|
8
|
-
*
|
|
9
|
-
* Then open: http://localhost:8080/examples/browser-test.html
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const http = require('http');
|
|
13
|
-
const fs = require('fs');
|
|
14
|
-
const path = require('path');
|
|
15
|
-
|
|
16
|
-
const PORT = 8080;
|
|
17
|
-
const MIME_TYPES = {
|
|
18
|
-
'.html': 'text/html',
|
|
19
|
-
'.js': 'text/javascript',
|
|
20
|
-
'.css': 'text/css',
|
|
21
|
-
'.json': 'application/json',
|
|
22
|
-
'.png': 'image/png',
|
|
23
|
-
'.jpg': 'image/jpg',
|
|
24
|
-
'.gif': 'image/gif',
|
|
25
|
-
'.svg': 'image/svg+xml',
|
|
26
|
-
'.ico': 'image/x-icon',
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const server = http.createServer((req, res) => {
|
|
30
|
-
// Enable CORS
|
|
31
|
-
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
32
|
-
res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
|
|
33
|
-
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
34
|
-
|
|
35
|
-
if (req.method === 'OPTIONS') {
|
|
36
|
-
res.writeHead(200);
|
|
37
|
-
res.end();
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Parse URL
|
|
42
|
-
let filePath = '.' + req.url;
|
|
43
|
-
if (filePath === './') {
|
|
44
|
-
filePath = './examples/browser-test.html';
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Get file extension
|
|
48
|
-
const extname = String(path.extname(filePath)).toLowerCase();
|
|
49
|
-
const contentType = MIME_TYPES[extname] || 'application/octet-stream';
|
|
50
|
-
|
|
51
|
-
// Read and serve file
|
|
52
|
-
fs.readFile(filePath, (error, content) => {
|
|
53
|
-
if (error) {
|
|
54
|
-
if (error.code === 'ENOENT') {
|
|
55
|
-
res.writeHead(404, { 'Content-Type': 'text/html' });
|
|
56
|
-
res.end('<h1>404 Not Found</h1>', 'utf-8');
|
|
57
|
-
} else {
|
|
58
|
-
res.writeHead(500);
|
|
59
|
-
res.end(`Server Error: ${error.code}`, 'utf-8');
|
|
60
|
-
}
|
|
61
|
-
} else {
|
|
62
|
-
res.writeHead(200, { 'Content-Type': contentType });
|
|
63
|
-
res.end(content, 'utf-8');
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
server.listen(PORT, () => {
|
|
69
|
-
console.log(`
|
|
70
|
-
╔════════════════════════════════════════════════════════════╗
|
|
71
|
-
║ AACProcessors Browser Test Server ║
|
|
72
|
-
╠════════════════════════════════════════════════════════════╣
|
|
73
|
-
║ ║
|
|
74
|
-
║ Server running at: ║
|
|
75
|
-
║ 🌐 http://localhost:${PORT}/examples/browser-test.html ║
|
|
76
|
-
║ ║
|
|
77
|
-
║ Press Ctrl+C to stop ║
|
|
78
|
-
║ ║
|
|
79
|
-
╚════════════════════════════════════════════════════════════╝
|
|
80
|
-
`);
|
|
81
|
-
});
|