@willwade/aac-processors 0.1.11 → 0.1.13

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 (101) hide show
  1. package/dist/browser/processors/gridset/resolver.js +10 -0
  2. package/dist/browser/processors/gridsetProcessor.js +155 -9
  3. package/dist/processors/gridset/resolver.js +10 -0
  4. package/dist/processors/gridsetProcessor.js +155 -9
  5. package/package.json +1 -3
  6. package/examples/.coverage +0 -0
  7. package/examples/.keep +0 -1
  8. package/examples/README.md +0 -55
  9. package/examples/browser-test.html +0 -331
  10. package/examples/communikate.dot +0 -2637
  11. package/examples/demo.js +0 -143
  12. package/examples/example-images/FileMap.xml +0 -51
  13. package/examples/example-images/Grids/Start/0-1-0-text-0.png +0 -0
  14. package/examples/example-images/Grids/Start/0-2-0-text-0.jpeg +0 -0
  15. package/examples/example-images/Grids/Start/0-3-0-text-0.jpeg +0 -0
  16. package/examples/example-images/Grids/Start/0-4-0-text-0.jpeg +0 -0
  17. package/examples/example-images/Grids/Start/0-5-0-text-0.jpeg +0 -0
  18. package/examples/example-images/Grids/Start/1-1-0-text-0.jpeg +0 -0
  19. package/examples/example-images/Grids/Start/1-2-0-text-0.jpeg +0 -0
  20. package/examples/example-images/Grids/Start/1-3-0-text-0.jpeg +0 -0
  21. package/examples/example-images/Grids/Start/1-4-0-text-0.jpeg +0 -0
  22. package/examples/example-images/Grids/Start/1-5-0-text-0.jpeg +0 -0
  23. package/examples/example-images/Grids/Start/10-3-0-text-0.jpeg +0 -0
  24. package/examples/example-images/Grids/Start/10-4-0-text-0.jpeg +0 -0
  25. package/examples/example-images/Grids/Start/11-3-0-text-0.jpeg +0 -0
  26. package/examples/example-images/Grids/Start/11-4-0-text-0.jpeg +0 -0
  27. package/examples/example-images/Grids/Start/2-1-0-text-0.jpeg +0 -0
  28. package/examples/example-images/Grids/Start/2-2-0-text-0.jpeg +0 -0
  29. package/examples/example-images/Grids/Start/2-3-0-text-0.jpeg +0 -0
  30. package/examples/example-images/Grids/Start/2-4-0-text-0.jpeg +0 -0
  31. package/examples/example-images/Grids/Start/2-5-0-text-0.jpeg +0 -0
  32. package/examples/example-images/Grids/Start/3-1-0-text-0.jpeg +0 -0
  33. package/examples/example-images/Grids/Start/3-3-0-text-0.jpeg +0 -0
  34. package/examples/example-images/Grids/Start/3-4-0-text-0.jpeg +0 -0
  35. package/examples/example-images/Grids/Start/3-5-0-text-0.jpeg +0 -0
  36. package/examples/example-images/Grids/Start/4-1-0-text-0.jpeg +0 -0
  37. package/examples/example-images/Grids/Start/4-3-0-text-0.jpeg +0 -0
  38. package/examples/example-images/Grids/Start/4-4-0-text-0.jpeg +0 -0
  39. package/examples/example-images/Grids/Start/4-5-0-text-0.jpeg +0 -0
  40. package/examples/example-images/Grids/Start/5-4-0-text-0.jpeg +0 -0
  41. package/examples/example-images/Grids/Start/5-5-0-text-0.jpeg +0 -0
  42. package/examples/example-images/Grids/Start/6-3-0-text-0.jpeg +0 -0
  43. package/examples/example-images/Grids/Start/6-4-0-text-0.jpeg +0 -0
  44. package/examples/example-images/Grids/Start/7-1-0-text-0.jpeg +0 -0
  45. package/examples/example-images/Grids/Start/7-2-0-text-0.jpeg +0 -0
  46. package/examples/example-images/Grids/Start/7-3-0-text-0.jpeg +0 -0
  47. package/examples/example-images/Grids/Start/7-4-0-text-0.jpeg +0 -0
  48. package/examples/example-images/Grids/Start/8-1-0-text-0.jpeg +0 -0
  49. package/examples/example-images/Grids/Start/8-2-0-text-0.jpeg +0 -0
  50. package/examples/example-images/Grids/Start/8-3-0-text-0.jpeg +0 -0
  51. package/examples/example-images/Grids/Start/8-4-0-text-0.jpeg +0 -0
  52. package/examples/example-images/Grids/Start/9-1.png +0 -0
  53. package/examples/example-images/Grids/Start/9-2.png +0 -0
  54. package/examples/example-images/Grids/Start/9-3-0-text-0.jpeg +0 -0
  55. package/examples/example-images/Grids/Start/9-4-0-text-0.jpeg +0 -0
  56. package/examples/example-images/Grids/Start/grid.xml +0 -1325
  57. package/examples/example-images/Settings0/Styles/styles.xml +0 -39
  58. package/examples/example-images/Settings0/WebBrowser/webbrowserextensions.xml +0 -3
  59. package/examples/example-images/Settings0/settings.xml +0 -16
  60. package/examples/example-images.gridset +0 -0
  61. package/examples/example-images.zip +0 -0
  62. package/examples/example.ce +0 -0
  63. package/examples/example.dot +0 -14
  64. package/examples/example.grd +0 -1
  65. package/examples/example.gridset +0 -0
  66. package/examples/example.obf +0 -27
  67. package/examples/example.obz +0 -0
  68. package/examples/example.opml +0 -18
  69. package/examples/example.spb +0 -0
  70. package/examples/example.sps +0 -0
  71. package/examples/example2.grd +0 -1
  72. package/examples/obf/aboutme.json +0 -376
  73. package/examples/obf/array.json +0 -6
  74. package/examples/obf/hash.json +0 -4
  75. package/examples/obf/links.obz +0 -0
  76. package/examples/obf/simple.obf +0 -53
  77. package/examples/package-lock.json +0 -1326
  78. package/examples/package.json +0 -10
  79. package/examples/styled-output/converted-snap-to-touchchat.ce +0 -0
  80. package/examples/styled-output/styled-example.ce +0 -0
  81. package/examples/styled-output/styled-example.gridset +0 -0
  82. package/examples/styled-output/styled-example.obf +0 -37
  83. package/examples/styled-output/styled-example.spb +0 -0
  84. package/examples/styling-example.ts +0 -316
  85. package/examples/translate.js +0 -39
  86. package/examples/translate_demo.js +0 -254
  87. package/examples/typescript-demo.ts +0 -251
  88. package/examples/vitedemo/README.md +0 -164
  89. package/examples/vitedemo/index.html +0 -580
  90. package/examples/vitedemo/package-lock.json +0 -1751
  91. package/examples/vitedemo/package.json +0 -24
  92. package/examples/vitedemo/src/main.ts +0 -1001
  93. package/examples/vitedemo/src/vite-env.d.ts +0 -1
  94. package/examples/vitedemo/test-files/example.dot +0 -14
  95. package/examples/vitedemo/test-files/example.grd +0 -1
  96. package/examples/vitedemo/test-files/example.gridset +0 -0
  97. package/examples/vitedemo/test-files/example.obz +0 -0
  98. package/examples/vitedemo/test-files/example.opml +0 -18
  99. package/examples/vitedemo/test-files/simple.obf +0 -53
  100. package/examples/vitedemo/tsconfig.json +0 -24
  101. package/examples/vitedemo/vite.config.ts +0 -57
@@ -42,6 +42,13 @@ export function resolveGrid3CellImage(zip, args, zipEntries) {
42
42
  const y = args.y;
43
43
  const entries = new Set(listZipEntries(zip, zipEntries));
44
44
  const has = (p) => entries.has(normalizeZipPathLocal(p));
45
+ // Debug logging for cells that fail to resolve
46
+ const shouldDebug = imageName?.startsWith('-') && x !== undefined && y !== undefined;
47
+ const debugLog = (msg) => {
48
+ if (shouldDebug) {
49
+ console.log(`[Resolver] ${baseDir} (${x},${y}) "${imageName}": ${msg}`);
50
+ }
51
+ };
45
52
  // Built-in resource like [grid3x]... (old format, not symbol library)
46
53
  // Check this BEFORE general symbol references to avoid misclassification
47
54
  if (imageName && imageName.startsWith('[')) {
@@ -72,6 +79,7 @@ export function resolveGrid3CellImage(zip, args, zipEntries) {
72
79
  // to be prefixed with the cell coordinates
73
80
  if (imageName.startsWith('-') && x != null && y != null) {
74
81
  const coordPrefixed = joinBaseDir(baseDir, `${x}-${y}${imageName}`);
82
+ debugLog(`trying coord-prefixed: ${coordPrefixed}, found: ${has(coordPrefixed)}`);
75
83
  if (has(coordPrefixed))
76
84
  return coordPrefixed;
77
85
  }
@@ -114,12 +122,14 @@ export function resolveGrid3CellImage(zip, args, zipEntries) {
114
122
  `${x}-${y}.jpg`,
115
123
  `${x}-${y}.png`,
116
124
  ].map((n) => joinBaseDir(baseDir, n));
125
+ debugLog(`trying candidates: ${candidates.filter(has).join(', ') || 'none found'}`);
117
126
  for (const c of candidates) {
118
127
  if (has(c))
119
128
  return c;
120
129
  }
121
130
  }
122
131
  }
132
+ debugLog(`NOT FOUND - returning null`);
123
133
  return null;
124
134
  }
125
135
  /**
@@ -325,8 +325,8 @@ class GridsetProcessor extends BaseProcessor {
325
325
  if (typeof val === 'number')
326
326
  return String(val);
327
327
  if (typeof val === 'object') {
328
- if ('#text' in val)
329
- return String(val['#text']);
328
+ // Don't immediately return #text - it might be whitespace alongside structured content
329
+ // Process structured format first: <p><s><r>text</r></s></p>
330
330
  // Handle Grid3 structured format <p><s><r>text</r></s></p>
331
331
  // Can start at p, s, or r level
332
332
  const parts = [];
@@ -342,8 +342,17 @@ class GridsetProcessor extends BaseProcessor {
342
342
  }
343
343
  continue;
344
344
  }
345
- if (typeof r === 'object' && r !== null && '#text' in r) {
346
- parts.push(String(r['#text']));
345
+ if (typeof r === 'object' && r !== null) {
346
+ // Check for #text (regular text) or #cdata (CDATA sections)
347
+ if ('#text' in r) {
348
+ parts.push(String(r['#text']));
349
+ }
350
+ else if ('#cdata' in r) {
351
+ parts.push(String(r['#cdata']));
352
+ }
353
+ else {
354
+ parts.push(String(r));
355
+ }
347
356
  }
348
357
  else {
349
358
  parts.push(String(r));
@@ -396,7 +405,15 @@ class GridsetProcessor extends BaseProcessor {
396
405
  }
397
406
  const password = this.getGridsetPassword(filePathOrBuffer);
398
407
  const entries = getZipEntriesFromAdapter(zipResult.zip, password);
399
- const parser = new XMLParser({ ignoreAttributes: false });
408
+ const options = {
409
+ ignoreAttributes: false,
410
+ ignoreDeclaration: true,
411
+ parseTagValue: false,
412
+ trimValues: false,
413
+ textNodeName: '#text',
414
+ cdataProp: '#cdata',
415
+ };
416
+ const parser = new XMLParser(options);
400
417
  const isEncryptedArchive = typeof filePathOrBuffer === 'string' && filePathOrBuffer.toLowerCase().endsWith('.gridsetx');
401
418
  const encryptedContentPassword = this.getGridsetPassword(filePathOrBuffer);
402
419
  // Initialize metadata
@@ -634,6 +651,13 @@ class GridsetProcessor extends BaseProcessor {
634
651
  if (text) {
635
652
  const val = this.textOf(text);
636
653
  if (val) {
654
+ // Debug: log WordList items with spaces to check extraction
655
+ if (pageWordListItems.length < 3) {
656
+ console.log(`[WordList] Extracted text: "${val}" (length: ${val.length}, has spaces: ${val.includes(' ')})`);
657
+ console.log(`[WordList] Chars:`, Array.from(val)
658
+ .map((c) => `"${c}" (${c.charCodeAt(0)})`)
659
+ .join(', '));
660
+ }
637
661
  pageWordListItems.push({
638
662
  text: val,
639
663
  image: item.Image || item.image || undefined,
@@ -646,15 +670,130 @@ class GridsetProcessor extends BaseProcessor {
646
670
  // Track WordList AutoContent cells and their positions for "more" button placement
647
671
  const wordListAutoContentCells = [];
648
672
  let wordListCellIndex = 0;
673
+ // Helper function to find next available position in grid (auto-flow)
674
+ // Returns {x, y} for next available slot that can accommodate the given span
675
+ const findNextAvailablePosition = (width, height, gridLayout) => {
676
+ for (let y = 0; y < maxRows; y++) {
677
+ for (let x = 0; x <= maxCols - width; x++) {
678
+ // Check if this position and the required span area are all free
679
+ let fits = true;
680
+ for (let dy = 0; dy < height && y + dy < maxRows; dy++) {
681
+ for (let dx = 0; dx < width && x + dx < maxCols; dx++) {
682
+ if (gridLayout[y + dy][x + dx] !== null) {
683
+ fits = false;
684
+ break;
685
+ }
686
+ }
687
+ if (!fits)
688
+ break;
689
+ }
690
+ if (fits) {
691
+ return { x, y };
692
+ }
693
+ }
694
+ }
695
+ // If no position found, return 0,0 (will be placed at first available)
696
+ return { x: 0, y: 0 };
697
+ };
698
+ // Helper function to find next available X position in a specific row
699
+ const findNextAvailableXInRow = (rowY, width, gridLayout) => {
700
+ for (let x = 0; x <= maxCols - width; x++) {
701
+ let fits = true;
702
+ for (let dx = 0; dx < width; dx++) {
703
+ if (gridLayout[rowY][x + dx] !== null) {
704
+ fits = false;
705
+ break;
706
+ }
707
+ }
708
+ if (fits)
709
+ return x;
710
+ }
711
+ return 0;
712
+ };
713
+ const cellsWithExplicitPosition = [];
714
+ const cellsWithYOnly = [];
715
+ const cellsWithXOnly = [];
716
+ const cellsWithAutoFlow = [];
649
717
  cellArr.forEach((cell, idx) => {
650
718
  if (!cell || !cell.Content)
651
719
  return;
652
- // Extract position information from cell attributes
653
- // Grid3 uses 1-based coordinates, convert to 0-based for internal use
654
- const cellX = Math.max(0, parseInt(String(cell['@_X'] || '1'), 10) - 1);
655
- const cellY = Math.max(0, parseInt(String(cell['@_Y'] || '1'), 10) - 1);
720
+ const hasX = cell['@_X'] !== undefined;
721
+ const hasY = cell['@_Y'] !== undefined;
722
+ if (hasX && hasY) {
723
+ cellsWithExplicitPosition.push({ cell, idx });
724
+ }
725
+ else if (hasY && !hasX) {
726
+ cellsWithYOnly.push({ cell, idx });
727
+ }
728
+ else if (!hasY && hasX) {
729
+ cellsWithXOnly.push({ cell, idx });
730
+ }
731
+ else {
732
+ cellsWithAutoFlow.push({ cell, idx });
733
+ }
734
+ });
735
+ // Process cells in order: explicit -> Y-only -> X-only -> auto-flow
736
+ const allCellsToProcess = [
737
+ ...cellsWithExplicitPosition,
738
+ ...cellsWithYOnly,
739
+ ...cellsWithXOnly,
740
+ ...cellsWithAutoFlow,
741
+ ];
742
+ allCellsToProcess.forEach(({ cell, idx }) => {
743
+ // Extract span information first
656
744
  const colSpan = parseInt(String(cell['@_ColumnSpan'] || '1'), 10);
657
745
  const rowSpan = parseInt(String(cell['@_RowSpan'] || '1'), 10);
746
+ // Determine position based on what attributes are present
747
+ const hasX = cell['@_X'] !== undefined;
748
+ const hasY = cell['@_Y'] !== undefined;
749
+ let cellX;
750
+ let cellY;
751
+ if (hasX && hasY) {
752
+ // Explicit position: both X and Y provided
753
+ // Grid 3 XML coordinates are already 0-based, use them directly
754
+ cellX = Math.max(0, parseInt(String(cell['@_X']), 10));
755
+ cellY = Math.max(0, parseInt(String(cell['@_Y']), 10));
756
+ }
757
+ else if (hasY && !hasX) {
758
+ // Y-only: auto-flow X in the specified row
759
+ // Grid 3 XML coordinates are already 0-based, use them directly
760
+ cellY = Math.max(0, parseInt(String(cell['@_Y']), 10));
761
+ cellX = findNextAvailableXInRow(cellY, colSpan, gridLayout);
762
+ }
763
+ else if (!hasY && hasX) {
764
+ // X-only: place at specified X in next available row
765
+ // Grid 3 XML coordinates are already 0-based, use them directly
766
+ cellX = Math.max(0, parseInt(String(cell['@_X']), 10));
767
+ // Find first row where this X position is available
768
+ cellY = 0;
769
+ let found = false;
770
+ for (let y = 0; y < maxRows; y++) {
771
+ let fits = true;
772
+ for (let dx = 0; dx < colSpan && cellX + dx < maxCols; dx++) {
773
+ if (gridLayout[y][cellX + dx] !== null) {
774
+ fits = false;
775
+ break;
776
+ }
777
+ }
778
+ if (fits) {
779
+ cellY = y;
780
+ found = true;
781
+ break;
782
+ }
783
+ }
784
+ if (!found) {
785
+ // No available row found, use auto-flow
786
+ const pos = findNextAvailablePosition(colSpan, rowSpan, gridLayout);
787
+ cellX = pos.x;
788
+ cellY = pos.y;
789
+ }
790
+ }
791
+ else {
792
+ // No position: auto-flow both X and Y
793
+ const pos = findNextAvailablePosition(colSpan, rowSpan, gridLayout);
794
+ cellX = pos.x;
795
+ cellY = pos.y;
796
+ }
658
797
  // Extract scan block number (1-8) for block scanning support
659
798
  const scanBlock = parseInt(String(cell['@_ScanBlock'] || '1'), 10);
660
799
  // Extract visibility from Grid 3's <Visibility> child element
@@ -797,6 +936,13 @@ class GridsetProcessor extends BaseProcessor {
797
936
  y: cellY,
798
937
  dynamicFiles,
799
938
  }, entries) || undefined;
939
+ // Debug: log resolution for cells with images
940
+ if (declaredImageName && resolvedImageEntry) {
941
+ console.log(`[GridsetProcessor] Cell (${cellX + 1},${cellY + 1}) [XML coords]: ${declaredImageName} -> ${resolvedImageEntry}`);
942
+ }
943
+ else if (declaredImageName && !resolvedImageEntry) {
944
+ console.log(`[GridsetProcessor] Cell (${cellX + 1},${cellY + 1}) [XML coords]: ${declaredImageName} -> NOT FOUND`);
945
+ }
800
946
  // Check if image is a symbol library reference
801
947
  let symbolLibraryRef = null;
802
948
  if (declaredImageName && isSymbolLibraryReference(declaredImageName)) {
@@ -47,6 +47,13 @@ function resolveGrid3CellImage(zip, args, zipEntries) {
47
47
  const y = args.y;
48
48
  const entries = new Set(listZipEntries(zip, zipEntries));
49
49
  const has = (p) => entries.has(normalizeZipPathLocal(p));
50
+ // Debug logging for cells that fail to resolve
51
+ const shouldDebug = imageName?.startsWith('-') && x !== undefined && y !== undefined;
52
+ const debugLog = (msg) => {
53
+ if (shouldDebug) {
54
+ console.log(`[Resolver] ${baseDir} (${x},${y}) "${imageName}": ${msg}`);
55
+ }
56
+ };
50
57
  // Built-in resource like [grid3x]... (old format, not symbol library)
51
58
  // Check this BEFORE general symbol references to avoid misclassification
52
59
  if (imageName && imageName.startsWith('[')) {
@@ -77,6 +84,7 @@ function resolveGrid3CellImage(zip, args, zipEntries) {
77
84
  // to be prefixed with the cell coordinates
78
85
  if (imageName.startsWith('-') && x != null && y != null) {
79
86
  const coordPrefixed = joinBaseDir(baseDir, `${x}-${y}${imageName}`);
87
+ debugLog(`trying coord-prefixed: ${coordPrefixed}, found: ${has(coordPrefixed)}`);
80
88
  if (has(coordPrefixed))
81
89
  return coordPrefixed;
82
90
  }
@@ -119,12 +127,14 @@ function resolveGrid3CellImage(zip, args, zipEntries) {
119
127
  `${x}-${y}.jpg`,
120
128
  `${x}-${y}.png`,
121
129
  ].map((n) => joinBaseDir(baseDir, n));
130
+ debugLog(`trying candidates: ${candidates.filter(has).join(', ') || 'none found'}`);
122
131
  for (const c of candidates) {
123
132
  if (has(c))
124
133
  return c;
125
134
  }
126
135
  }
127
136
  }
137
+ debugLog(`NOT FOUND - returning null`);
128
138
  return null;
129
139
  }
130
140
  /**
@@ -351,8 +351,8 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
351
351
  if (typeof val === 'number')
352
352
  return String(val);
353
353
  if (typeof val === 'object') {
354
- if ('#text' in val)
355
- return String(val['#text']);
354
+ // Don't immediately return #text - it might be whitespace alongside structured content
355
+ // Process structured format first: <p><s><r>text</r></s></p>
356
356
  // Handle Grid3 structured format <p><s><r>text</r></s></p>
357
357
  // Can start at p, s, or r level
358
358
  const parts = [];
@@ -368,8 +368,17 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
368
368
  }
369
369
  continue;
370
370
  }
371
- if (typeof r === 'object' && r !== null && '#text' in r) {
372
- parts.push(String(r['#text']));
371
+ if (typeof r === 'object' && r !== null) {
372
+ // Check for #text (regular text) or #cdata (CDATA sections)
373
+ if ('#text' in r) {
374
+ parts.push(String(r['#text']));
375
+ }
376
+ else if ('#cdata' in r) {
377
+ parts.push(String(r['#cdata']));
378
+ }
379
+ else {
380
+ parts.push(String(r));
381
+ }
373
382
  }
374
383
  else {
375
384
  parts.push(String(r));
@@ -422,7 +431,15 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
422
431
  }
423
432
  const password = this.getGridsetPassword(filePathOrBuffer);
424
433
  const entries = (0, password_1.getZipEntriesFromAdapter)(zipResult.zip, password);
425
- const parser = new fast_xml_parser_1.XMLParser({ ignoreAttributes: false });
434
+ const options = {
435
+ ignoreAttributes: false,
436
+ ignoreDeclaration: true,
437
+ parseTagValue: false,
438
+ trimValues: false,
439
+ textNodeName: '#text',
440
+ cdataProp: '#cdata',
441
+ };
442
+ const parser = new fast_xml_parser_1.XMLParser(options);
426
443
  const isEncryptedArchive = typeof filePathOrBuffer === 'string' && filePathOrBuffer.toLowerCase().endsWith('.gridsetx');
427
444
  const encryptedContentPassword = this.getGridsetPassword(filePathOrBuffer);
428
445
  // Initialize metadata
@@ -660,6 +677,13 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
660
677
  if (text) {
661
678
  const val = this.textOf(text);
662
679
  if (val) {
680
+ // Debug: log WordList items with spaces to check extraction
681
+ if (pageWordListItems.length < 3) {
682
+ console.log(`[WordList] Extracted text: "${val}" (length: ${val.length}, has spaces: ${val.includes(' ')})`);
683
+ console.log(`[WordList] Chars:`, Array.from(val)
684
+ .map((c) => `"${c}" (${c.charCodeAt(0)})`)
685
+ .join(', '));
686
+ }
663
687
  pageWordListItems.push({
664
688
  text: val,
665
689
  image: item.Image || item.image || undefined,
@@ -672,15 +696,130 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
672
696
  // Track WordList AutoContent cells and their positions for "more" button placement
673
697
  const wordListAutoContentCells = [];
674
698
  let wordListCellIndex = 0;
699
+ // Helper function to find next available position in grid (auto-flow)
700
+ // Returns {x, y} for next available slot that can accommodate the given span
701
+ const findNextAvailablePosition = (width, height, gridLayout) => {
702
+ for (let y = 0; y < maxRows; y++) {
703
+ for (let x = 0; x <= maxCols - width; x++) {
704
+ // Check if this position and the required span area are all free
705
+ let fits = true;
706
+ for (let dy = 0; dy < height && y + dy < maxRows; dy++) {
707
+ for (let dx = 0; dx < width && x + dx < maxCols; dx++) {
708
+ if (gridLayout[y + dy][x + dx] !== null) {
709
+ fits = false;
710
+ break;
711
+ }
712
+ }
713
+ if (!fits)
714
+ break;
715
+ }
716
+ if (fits) {
717
+ return { x, y };
718
+ }
719
+ }
720
+ }
721
+ // If no position found, return 0,0 (will be placed at first available)
722
+ return { x: 0, y: 0 };
723
+ };
724
+ // Helper function to find next available X position in a specific row
725
+ const findNextAvailableXInRow = (rowY, width, gridLayout) => {
726
+ for (let x = 0; x <= maxCols - width; x++) {
727
+ let fits = true;
728
+ for (let dx = 0; dx < width; dx++) {
729
+ if (gridLayout[rowY][x + dx] !== null) {
730
+ fits = false;
731
+ break;
732
+ }
733
+ }
734
+ if (fits)
735
+ return x;
736
+ }
737
+ return 0;
738
+ };
739
+ const cellsWithExplicitPosition = [];
740
+ const cellsWithYOnly = [];
741
+ const cellsWithXOnly = [];
742
+ const cellsWithAutoFlow = [];
675
743
  cellArr.forEach((cell, idx) => {
676
744
  if (!cell || !cell.Content)
677
745
  return;
678
- // Extract position information from cell attributes
679
- // Grid3 uses 1-based coordinates, convert to 0-based for internal use
680
- const cellX = Math.max(0, parseInt(String(cell['@_X'] || '1'), 10) - 1);
681
- const cellY = Math.max(0, parseInt(String(cell['@_Y'] || '1'), 10) - 1);
746
+ const hasX = cell['@_X'] !== undefined;
747
+ const hasY = cell['@_Y'] !== undefined;
748
+ if (hasX && hasY) {
749
+ cellsWithExplicitPosition.push({ cell, idx });
750
+ }
751
+ else if (hasY && !hasX) {
752
+ cellsWithYOnly.push({ cell, idx });
753
+ }
754
+ else if (!hasY && hasX) {
755
+ cellsWithXOnly.push({ cell, idx });
756
+ }
757
+ else {
758
+ cellsWithAutoFlow.push({ cell, idx });
759
+ }
760
+ });
761
+ // Process cells in order: explicit -> Y-only -> X-only -> auto-flow
762
+ const allCellsToProcess = [
763
+ ...cellsWithExplicitPosition,
764
+ ...cellsWithYOnly,
765
+ ...cellsWithXOnly,
766
+ ...cellsWithAutoFlow,
767
+ ];
768
+ allCellsToProcess.forEach(({ cell, idx }) => {
769
+ // Extract span information first
682
770
  const colSpan = parseInt(String(cell['@_ColumnSpan'] || '1'), 10);
683
771
  const rowSpan = parseInt(String(cell['@_RowSpan'] || '1'), 10);
772
+ // Determine position based on what attributes are present
773
+ const hasX = cell['@_X'] !== undefined;
774
+ const hasY = cell['@_Y'] !== undefined;
775
+ let cellX;
776
+ let cellY;
777
+ if (hasX && hasY) {
778
+ // Explicit position: both X and Y provided
779
+ // Grid 3 XML coordinates are already 0-based, use them directly
780
+ cellX = Math.max(0, parseInt(String(cell['@_X']), 10));
781
+ cellY = Math.max(0, parseInt(String(cell['@_Y']), 10));
782
+ }
783
+ else if (hasY && !hasX) {
784
+ // Y-only: auto-flow X in the specified row
785
+ // Grid 3 XML coordinates are already 0-based, use them directly
786
+ cellY = Math.max(0, parseInt(String(cell['@_Y']), 10));
787
+ cellX = findNextAvailableXInRow(cellY, colSpan, gridLayout);
788
+ }
789
+ else if (!hasY && hasX) {
790
+ // X-only: place at specified X in next available row
791
+ // Grid 3 XML coordinates are already 0-based, use them directly
792
+ cellX = Math.max(0, parseInt(String(cell['@_X']), 10));
793
+ // Find first row where this X position is available
794
+ cellY = 0;
795
+ let found = false;
796
+ for (let y = 0; y < maxRows; y++) {
797
+ let fits = true;
798
+ for (let dx = 0; dx < colSpan && cellX + dx < maxCols; dx++) {
799
+ if (gridLayout[y][cellX + dx] !== null) {
800
+ fits = false;
801
+ break;
802
+ }
803
+ }
804
+ if (fits) {
805
+ cellY = y;
806
+ found = true;
807
+ break;
808
+ }
809
+ }
810
+ if (!found) {
811
+ // No available row found, use auto-flow
812
+ const pos = findNextAvailablePosition(colSpan, rowSpan, gridLayout);
813
+ cellX = pos.x;
814
+ cellY = pos.y;
815
+ }
816
+ }
817
+ else {
818
+ // No position: auto-flow both X and Y
819
+ const pos = findNextAvailablePosition(colSpan, rowSpan, gridLayout);
820
+ cellX = pos.x;
821
+ cellY = pos.y;
822
+ }
684
823
  // Extract scan block number (1-8) for block scanning support
685
824
  const scanBlock = parseInt(String(cell['@_ScanBlock'] || '1'), 10);
686
825
  // Extract visibility from Grid 3's <Visibility> child element
@@ -823,6 +962,13 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
823
962
  y: cellY,
824
963
  dynamicFiles,
825
964
  }, entries) || undefined;
965
+ // Debug: log resolution for cells with images
966
+ if (declaredImageName && resolvedImageEntry) {
967
+ console.log(`[GridsetProcessor] Cell (${cellX + 1},${cellY + 1}) [XML coords]: ${declaredImageName} -> ${resolvedImageEntry}`);
968
+ }
969
+ else if (declaredImageName && !resolvedImageEntry) {
970
+ console.log(`[GridsetProcessor] Cell (${cellX + 1},${cellY + 1}) [XML coords]: ${declaredImageName} -> NOT FOUND`);
971
+ }
826
972
  // Check if image is a symbol library reference
827
973
  let symbolLibraryRef = null;
828
974
  if (declaredImageName && (0, resolver_2.isSymbolLibraryReference)(declaredImageName)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@willwade/aac-processors",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
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",
@@ -80,13 +80,11 @@
80
80
  "files": [
81
81
  "dist/**/*",
82
82
  "docs/**/*",
83
- "examples/**/*",
84
83
  "README.md",
85
84
  "LICENSE"
86
85
  ],
87
86
  "directories": {
88
87
  "doc": "docs",
89
- "example": "examples",
90
88
  "test": "test"
91
89
  },
92
90
  "scripts": {
Binary file
package/examples/.keep DELETED
@@ -1 +0,0 @@
1
-
@@ -1,55 +0,0 @@
1
- # AAC Processors Example Pagesets
2
-
3
- This directory contains example AAC pagesets in various formats used for testing and demonstration purposes.
4
-
5
- ## Available Pagesets
6
-
7
- ### Grid3 Format (.gridset)
8
- - **example.gridset** - Main example pageset with multiple grids and wordlists
9
- - **example-images.gridset** - Example pageset with embedded images
10
-
11
- ### Snap Format (.spb, .sps)
12
- - **example.spb** - Snap pageset (binary format)
13
- - **example.sps** - Snap pageset (alternative format)
14
-
15
- ### TouchChat Format (.ce)
16
- - **example.ce** - TouchChat pageset
17
-
18
- ### OBF/OBZ Format (.obf, .obz)
19
- - **example.obf** - OBF pageset (JSON-based)
20
- - **example.obz** - OBZ pageset (compressed)
21
-
22
- **obf/** - Directory containing validation test samples from the obf-node project:
23
- - **simple.obf** - Simple, valid OBF file for basic validation tests
24
- - **aboutme.json** - Invalid OBF (missing locale field) for error testing
25
- - **hash.json** - Non-OBF JSON structure for format detection tests
26
- - **array.json** - JSON array (not object) for structure validation tests
27
- - **links.obz** - OBZ package with links for zip archive validation
28
-
29
- ### Asterics Grid Format (.grd)
30
- - **example.grd** - Asterics Grid pageset
31
- - **example2.grd** - Alternative Asterics Grid pageset
32
-
33
- ### DOT Format (.dot)
34
- - **example.dot** - Simple DOT format pageset
35
- - **communikate.dot** - Communikate DOT format pageset
36
-
37
- ### OPML Format (.opml)
38
- - **example.opml** - OPML pageset
39
-
40
- ### Styled Output
41
- - **styled-output/** - Directory containing example pagesets with styling applied
42
-
43
- ## Usage
44
-
45
- These pagesets are used by:
46
- - Unit tests in the main test suite
47
- - Demo scripts in the `scripts/` directory
48
- - Integration examples
49
-
50
- To run demo scripts that use these pagesets, see the [scripts/README.md](../scripts/README.md).
51
-
52
- ## Browser Demo (Vite)
53
-
54
- For browser testing, use the Vite demo in `examples/vitedemo`. It bundles the
55
- library and exercises real processors in a browser environment.