@willwade/aac-processors 0.2.19 → 0.2.20

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.
@@ -128,6 +128,14 @@ class GridsetProcessor extends BaseProcessor {
128
128
  generateCommandsFromSemanticAction(button, tree) {
129
129
  const semanticAction = button.semanticAction;
130
130
  if (!semanticAction) {
131
+ // Workspace and LiveCell cells should have no commands
132
+ if (button.contentType === 'Workspace' || button.contentType === 'LiveCell') {
133
+ return '';
134
+ }
135
+ // AutoContent cells may or may not have commands
136
+ if (button.contentType === 'AutoContent') {
137
+ return '';
138
+ }
131
139
  // Default to insert text action with structured XML format
132
140
  // Use two <s> elements: one for the word, one for the space (CDATA preserves whitespace)
133
141
  let text = button.message || button.label || '';
@@ -274,10 +282,10 @@ class GridsetProcessor extends BaseProcessor {
274
282
  };
275
283
  }
276
284
  default: {
277
- // Use two <s> elements: one for the word, one for the space (CDATA preserves whitespace)
278
- // Fallback to insert text with structured XML format
285
+ if (semanticAction.platformData?.grid3) {
286
+ break;
287
+ }
279
288
  let text = semanticAction.text || button.message || button.label || '';
280
- // Remove trailing space from message if present (we'll add it as separate segment)
281
289
  if (text.endsWith(' ')) {
282
290
  text = text.slice(0, -1);
283
291
  }
@@ -1412,7 +1420,6 @@ class GridsetProcessor extends BaseProcessor {
1412
1420
  }
1413
1421
  break;
1414
1422
  case 'AutoContent.Activate':
1415
- // action
1416
1423
  if (!semanticAction) {
1417
1424
  semanticAction = {
1418
1425
  category: AACSemanticCategory.CUSTOM,
@@ -1436,8 +1443,819 @@ class GridsetProcessor extends BaseProcessor {
1436
1443
  };
1437
1444
  }
1438
1445
  break;
1446
+ // --- Navigation extras ---
1447
+ case 'Jump.SetBookmark':
1448
+ case 'Jump.Favorite':
1449
+ if (!semanticAction) {
1450
+ const params = {};
1451
+ const fav = getParam('favorite');
1452
+ if (fav)
1453
+ params.favorite = fav;
1454
+ const act = getParam('action');
1455
+ if (act)
1456
+ params.action = act;
1457
+ const ind = getParam('indicatorenabled');
1458
+ if (ind)
1459
+ params.indicatorenabled = ind;
1460
+ semanticAction = {
1461
+ category: AACSemanticCategory.NAVIGATION,
1462
+ intent: commandId === 'Jump.SetBookmark'
1463
+ ? AACSemanticIntent.TOGGLE_STATE
1464
+ : AACSemanticIntent.NAVIGATE_TO,
1465
+ platformData: { grid3: { commandId, parameters: params } },
1466
+ fallback: {
1467
+ type: 'ACTION',
1468
+ message: commandId === 'Jump.SetBookmark'
1469
+ ? 'Toggle bookmark'
1470
+ : 'Jump to favorite',
1471
+ },
1472
+ };
1473
+ }
1474
+ break;
1475
+ // --- Text input / keyboard ---
1476
+ case 'Action.Space':
1477
+ if (!semanticAction) {
1478
+ semanticAction = {
1479
+ category: AACSemanticCategory.TEXT_EDITING,
1480
+ intent: AACSemanticIntent.INSERT_TEXT,
1481
+ text: ' ',
1482
+ platformData: { grid3: { commandId, parameters: {} } },
1483
+ fallback: { type: 'ACTION', message: 'Space' },
1484
+ };
1485
+ }
1486
+ break;
1487
+ case 'Action.Enter':
1488
+ if (!semanticAction) {
1489
+ semanticAction = {
1490
+ category: AACSemanticCategory.TEXT_EDITING,
1491
+ intent: AACSemanticIntent.INSERT_TEXT,
1492
+ text: '\n',
1493
+ platformData: { grid3: { commandId, parameters: {} } },
1494
+ fallback: { type: 'ACTION', message: 'Enter' },
1495
+ };
1496
+ }
1497
+ break;
1498
+ case 'Action.Number':
1499
+ case 'Action.Punctuation': {
1500
+ const ch = getParam('letter');
1501
+ if (!semanticAction) {
1502
+ semanticAction = {
1503
+ category: AACSemanticCategory.TEXT_EDITING,
1504
+ intent: AACSemanticIntent.INSERT_TEXT,
1505
+ text: ch,
1506
+ platformData: { grid3: { commandId, parameters: { letter: ch } } },
1507
+ fallback: { type: 'ACTION', message: ch },
1508
+ };
1509
+ }
1510
+ break;
1511
+ }
1512
+ case 'Action.Backspace':
1513
+ if (!semanticAction) {
1514
+ semanticAction = {
1515
+ category: AACSemanticCategory.TEXT_EDITING,
1516
+ intent: AACSemanticIntent.DELETE_CHARACTER,
1517
+ platformData: { grid3: { commandId, parameters: {} } },
1518
+ fallback: { type: 'ACTION', message: 'Backspace' },
1519
+ };
1520
+ }
1521
+ break;
1522
+ case 'Action.Copy':
1523
+ if (!semanticAction) {
1524
+ semanticAction = {
1525
+ category: AACSemanticCategory.TEXT_EDITING,
1526
+ intent: AACSemanticIntent.COPY_TEXT,
1527
+ platformData: { grid3: { commandId, parameters: {} } },
1528
+ fallback: { type: 'ACTION', message: 'Copy' },
1529
+ };
1530
+ }
1531
+ break;
1532
+ case 'Action.Paste':
1533
+ if (!semanticAction) {
1534
+ semanticAction = {
1535
+ category: AACSemanticCategory.TEXT_EDITING,
1536
+ intent: AACSemanticIntent.PASTE_TEXT,
1537
+ platformData: { grid3: { commandId, parameters: {} } },
1538
+ fallback: { type: 'ACTION', message: 'Paste' },
1539
+ };
1540
+ }
1541
+ break;
1542
+ case 'Action.SelectAll':
1543
+ if (!semanticAction) {
1544
+ semanticAction = {
1545
+ category: AACSemanticCategory.TEXT_EDITING,
1546
+ intent: AACSemanticIntent.SELECT_ALL,
1547
+ platformData: { grid3: { commandId, parameters: {} } },
1548
+ fallback: { type: 'ACTION', message: 'Select all' },
1549
+ };
1550
+ }
1551
+ break;
1552
+ case 'Action.NextLetter':
1553
+ case 'Action.PreviousLetter':
1554
+ case 'Action.NextWord':
1555
+ case 'Action.PreviousWord':
1556
+ case 'Action.DocumentStart':
1557
+ case 'Action.DocumentEnd': {
1558
+ const dirMap = {
1559
+ 'Action.NextLetter': 'next_letter',
1560
+ 'Action.PreviousLetter': 'previous_letter',
1561
+ 'Action.NextWord': 'next_word',
1562
+ 'Action.PreviousWord': 'previous_word',
1563
+ 'Action.DocumentStart': 'document_start',
1564
+ 'Action.DocumentEnd': 'document_end',
1565
+ };
1566
+ if (!semanticAction) {
1567
+ semanticAction = {
1568
+ category: AACSemanticCategory.TEXT_EDITING,
1569
+ intent: AACSemanticIntent.CURSOR_MOVE,
1570
+ parameters: { direction: dirMap[commandId] },
1571
+ platformData: { grid3: { commandId, parameters: {} } },
1572
+ fallback: {
1573
+ type: 'ACTION',
1574
+ message: `Cursor ${dirMap[commandId].replace('_', ' ')}`,
1575
+ },
1576
+ };
1577
+ }
1578
+ break;
1579
+ }
1580
+ case 'Action.UndoWorkspaceEdit':
1581
+ case 'Action.UndoClear':
1582
+ if (!semanticAction) {
1583
+ semanticAction = {
1584
+ category: AACSemanticCategory.TEXT_EDITING,
1585
+ intent: AACSemanticIntent.UNDO,
1586
+ platformData: { grid3: { commandId, parameters: {} } },
1587
+ fallback: { type: 'ACTION', message: 'Undo' },
1588
+ };
1589
+ }
1590
+ break;
1591
+ case 'Action.Print':
1592
+ if (!semanticAction) {
1593
+ semanticAction = {
1594
+ category: AACSemanticCategory.TEXT_EDITING,
1595
+ intent: AACSemanticIntent.PRINT,
1596
+ platformData: { grid3: { commandId, parameters: {} } },
1597
+ fallback: { type: 'ACTION', message: 'Print' },
1598
+ };
1599
+ }
1600
+ break;
1601
+ case 'Action.SpeakNothing':
1602
+ if (!semanticAction) {
1603
+ semanticAction = {
1604
+ category: AACSemanticCategory.COMMUNICATION,
1605
+ intent: AACSemanticIntent.SPEAK_TEXT,
1606
+ platformData: { grid3: { commandId, parameters: {} } },
1607
+ fallback: { type: 'SPEAK', message: 'Speak' },
1608
+ };
1609
+ }
1610
+ break;
1611
+ // --- Speech ---
1612
+ case 'Speech.Stop':
1613
+ if (!semanticAction) {
1614
+ semanticAction = {
1615
+ category: AACSemanticCategory.COMMUNICATION,
1616
+ intent: AACSemanticIntent.STOP_SPEECH,
1617
+ platformData: { grid3: { commandId, parameters: {} } },
1618
+ fallback: { type: 'ACTION', message: 'Stop speech' },
1619
+ };
1620
+ }
1621
+ break;
1622
+ case 'SpeechPlaySound': {
1623
+ const soundFile = getParam('filedata');
1624
+ const waitSound = getParam('wait');
1625
+ if (!semanticAction) {
1626
+ semanticAction = {
1627
+ category: AACSemanticCategory.MEDIA,
1628
+ intent: AACSemanticIntent.PLAY_SOUND,
1629
+ parameters: { wait: waitSound },
1630
+ platformData: {
1631
+ grid3: {
1632
+ commandId,
1633
+ parameters: { filedata: soundFile, wait: waitSound },
1634
+ },
1635
+ },
1636
+ fallback: { type: 'ACTION', message: 'Play sound' },
1637
+ };
1638
+ }
1639
+ break;
1640
+ }
1641
+ case 'Speech.ChangePublicVoice':
1642
+ case 'Speech.ChangePublicSpeed':
1643
+ case 'Speech.ChangePublicPitch':
1644
+ case 'Speech.ChangePublicVolume':
1645
+ if (!semanticAction) {
1646
+ const speechParams = {};
1647
+ for (const p of paramArr) {
1648
+ const k = p['@_Key'] || p.Key || p.key;
1649
+ if (k)
1650
+ speechParams[k] = p['#text'] ?? p.text;
1651
+ }
1652
+ semanticAction = {
1653
+ category: AACSemanticCategory.ACCESSIBILITY,
1654
+ intent: AACSemanticIntent.PLATFORM_SPECIFIC,
1655
+ platformData: { grid3: { commandId, parameters: speechParams } },
1656
+ fallback: {
1657
+ type: 'ACTION',
1658
+ message: `Change ${commandId.split('.').pop()}`,
1659
+ },
1660
+ };
1661
+ }
1662
+ break;
1663
+ // --- Computer Control ---
1664
+ case 'ComputerControl.Keyboard': {
1665
+ const keystring = getParam('keystring');
1666
+ if (!semanticAction) {
1667
+ semanticAction = {
1668
+ category: AACSemanticCategory.SYSTEM_CONTROL,
1669
+ intent: AACSemanticIntent.SEND_KEYS,
1670
+ text: keystring,
1671
+ platformData: { grid3: { commandId, parameters: { keystring } } },
1672
+ fallback: { type: 'ACTION', message: `Send keys: ${keystring}` },
1673
+ };
1674
+ }
1675
+ break;
1676
+ }
1677
+ case 'ComputerControl.SendKeys': {
1678
+ const keys = getParam('keys');
1679
+ if (!semanticAction) {
1680
+ semanticAction = {
1681
+ category: AACSemanticCategory.SYSTEM_CONTROL,
1682
+ intent: AACSemanticIntent.SEND_KEYS,
1683
+ text: keys,
1684
+ platformData: { grid3: { commandId, parameters: { keys } } },
1685
+ fallback: { type: 'ACTION', message: `Send keys: ${keys}` },
1686
+ };
1687
+ }
1688
+ break;
1689
+ }
1690
+ case 'ComputerControl.LeftClick':
1691
+ case 'ComputerControl.RightClick':
1692
+ case 'ComputerControl.DoubleClick':
1693
+ case 'Mouse.LeftClick':
1694
+ case 'Mouse.RightClick':
1695
+ case 'Mouse.DoubleClick':
1696
+ if (!semanticAction) {
1697
+ semanticAction = {
1698
+ category: AACSemanticCategory.SYSTEM_CONTROL,
1699
+ intent: AACSemanticIntent.MOUSE_CLICK,
1700
+ parameters: {
1701
+ clickType: commandId.includes('Right')
1702
+ ? 'right'
1703
+ : commandId.includes('Double')
1704
+ ? 'double'
1705
+ : 'left',
1706
+ },
1707
+ platformData: { grid3: { commandId, parameters: {} } },
1708
+ fallback: { type: 'ACTION', message: commandId.split('.').pop() },
1709
+ };
1710
+ }
1711
+ break;
1712
+ case 'ComputerControl.MouseMove':
1713
+ case 'Mouse.Move': {
1714
+ const mx = getParam('x');
1715
+ const my = getParam('y');
1716
+ if (!semanticAction) {
1717
+ semanticAction = {
1718
+ category: AACSemanticCategory.SYSTEM_CONTROL,
1719
+ intent: AACSemanticIntent.MOUSE_CLICK,
1720
+ parameters: { action: 'move', x: mx, y: my },
1721
+ platformData: { grid3: { commandId, parameters: { x: mx, y: my } } },
1722
+ fallback: { type: 'ACTION', message: 'Move mouse' },
1723
+ };
1724
+ }
1725
+ break;
1726
+ }
1727
+ case 'ComputerControl.WindowsKey':
1728
+ case 'ComputerControl.MenuKey':
1729
+ if (!semanticAction) {
1730
+ semanticAction = {
1731
+ category: AACSemanticCategory.SYSTEM_CONTROL,
1732
+ intent: AACSemanticIntent.SEND_KEYS,
1733
+ platformData: { grid3: { commandId, parameters: {} } },
1734
+ fallback: { type: 'ACTION', message: commandId.split('.').pop() },
1735
+ };
1736
+ }
1737
+ break;
1738
+ case 'ComputerControl.Shift':
1739
+ if (!semanticAction) {
1740
+ semanticAction = {
1741
+ category: AACSemanticCategory.SYSTEM_CONTROL,
1742
+ intent: AACSemanticIntent.TOGGLE_STATE,
1743
+ parameters: { target: 'shift', action: getParam('action') },
1744
+ platformData: {
1745
+ grid3: { commandId, parameters: { action: getParam('action') } },
1746
+ },
1747
+ fallback: { type: 'ACTION', message: 'Toggle shift' },
1748
+ };
1749
+ }
1750
+ break;
1751
+ case 'ComputerControl.DeviceMute':
1752
+ if (!semanticAction) {
1753
+ semanticAction = {
1754
+ category: AACSemanticCategory.SYSTEM_CONTROL,
1755
+ intent: AACSemanticIntent.DEVICE_MUTE,
1756
+ platformData: {
1757
+ grid3: {
1758
+ commandId,
1759
+ parameters: { indicatorenabled: getParam('indicatorenabled') },
1760
+ },
1761
+ },
1762
+ fallback: { type: 'ACTION', message: 'Mute device' },
1763
+ };
1764
+ }
1765
+ break;
1766
+ // --- Web Browser ---
1767
+ case 'WebBrowser.Navigate':
1768
+ case 'WebBrowser.NavigateUrl': {
1769
+ const url = getParam('url');
1770
+ if (!semanticAction) {
1771
+ semanticAction = {
1772
+ category: AACSemanticCategory.SYSTEM_CONTROL,
1773
+ intent: AACSemanticIntent.WEB_NAVIGATE,
1774
+ text: url,
1775
+ platformData: { grid3: { commandId, parameters: { url } } },
1776
+ fallback: {
1777
+ type: 'ACTION',
1778
+ message: url ? `Navigate to ${url}` : 'Navigate',
1779
+ },
1780
+ };
1781
+ }
1782
+ break;
1783
+ }
1784
+ case 'WebBrowser.Back':
1785
+ case 'WebBrowser.Forward':
1786
+ case 'WebBrowser.Refresh':
1787
+ case 'WebBrowser.Reload':
1788
+ case 'WebBrowser.Stop':
1789
+ case 'WebBrowser.Home':
1790
+ case 'WebAddress.Go':
1791
+ if (!semanticAction) {
1792
+ const webActionLabels = {
1793
+ 'WebBrowser.Back': 'Back',
1794
+ 'WebBrowser.Forward': 'Forward',
1795
+ 'WebBrowser.Refresh': 'Refresh',
1796
+ 'WebBrowser.Reload': 'Reload',
1797
+ 'WebBrowser.Stop': 'Stop loading',
1798
+ 'WebBrowser.Home': 'Home',
1799
+ 'WebAddress.Go': 'Go to address',
1800
+ };
1801
+ semanticAction = {
1802
+ category: AACSemanticCategory.SYSTEM_CONTROL,
1803
+ intent: AACSemanticIntent.WEB_NAVIGATE,
1804
+ parameters: { webAction: commandId },
1805
+ platformData: { grid3: { commandId, parameters: {} } },
1806
+ fallback: {
1807
+ type: 'ACTION',
1808
+ message: webActionLabels[commandId] || commandId,
1809
+ },
1810
+ };
1811
+ }
1812
+ break;
1813
+ case 'WebBrowser.ScrollUp':
1814
+ case 'WebBrowser.ScrollDown':
1815
+ case 'WebBrowser.ScrollLeft':
1816
+ case 'WebBrowser.ScrollRight': {
1817
+ const scrollSize = getParam('size');
1818
+ const scrollDir = commandId.replace('WebBrowser.Scroll', '').toLowerCase();
1819
+ if (!semanticAction) {
1820
+ semanticAction = {
1821
+ category: AACSemanticCategory.SYSTEM_CONTROL,
1822
+ intent: AACSemanticIntent.WEB_SCROLL,
1823
+ parameters: { direction: scrollDir, size: scrollSize },
1824
+ platformData: { grid3: { commandId, parameters: { size: scrollSize } } },
1825
+ fallback: { type: 'ACTION', message: `Scroll ${scrollDir}` },
1826
+ };
1827
+ }
1828
+ break;
1829
+ }
1830
+ case 'WebBrowser.ZoomIn':
1831
+ case 'WebBrowser.ZoomOut':
1832
+ case 'WebBrowser.SetZoom': {
1833
+ const zoomOption = getParam('option');
1834
+ if (!semanticAction) {
1835
+ semanticAction = {
1836
+ category: AACSemanticCategory.SYSTEM_CONTROL,
1837
+ intent: AACSemanticIntent.WEB_SCROLL,
1838
+ parameters: {
1839
+ action: 'zoom',
1840
+ option: zoomOption || commandId.replace('WebBrowser.', ''),
1841
+ },
1842
+ platformData: {
1843
+ grid3: {
1844
+ commandId,
1845
+ parameters: zoomOption ? { option: zoomOption } : {},
1846
+ },
1847
+ },
1848
+ fallback: { type: 'ACTION', message: commandId.replace('WebBrowser.', '') },
1849
+ };
1850
+ }
1851
+ break;
1852
+ }
1853
+ case 'WebBrowser.SpatialNavigateUp':
1854
+ case 'WebBrowser.SpatialNavigateDown':
1855
+ case 'WebBrowser.SpatialNavigateLeft':
1856
+ case 'WebBrowser.SpatialNavigateRight':
1857
+ case 'WebBrowser.NextElement':
1858
+ case 'WebBrowser.PreviousElement': {
1859
+ const focusDir = commandId.includes('Spatial')
1860
+ ? commandId.replace('WebBrowser.SpatialNavigate', '').toLowerCase()
1861
+ : commandId === 'WebBrowser.NextElement'
1862
+ ? 'next'
1863
+ : 'previous';
1864
+ if (!semanticAction) {
1865
+ semanticAction = {
1866
+ category: AACSemanticCategory.SYSTEM_CONTROL,
1867
+ intent: AACSemanticIntent.WEB_FOCUS_ELEMENT,
1868
+ parameters: { direction: focusDir },
1869
+ platformData: { grid3: { commandId, parameters: {} } },
1870
+ fallback: { type: 'ACTION', message: `Focus ${focusDir} element` },
1871
+ };
1872
+ }
1873
+ break;
1874
+ }
1875
+ case 'WebBrowser.ActivateElement':
1876
+ if (!semanticAction) {
1877
+ semanticAction = {
1878
+ category: AACSemanticCategory.SYSTEM_CONTROL,
1879
+ intent: AACSemanticIntent.WEB_ACTIVATE_ELEMENT,
1880
+ platformData: { grid3: { commandId, parameters: {} } },
1881
+ fallback: { type: 'ACTION', message: 'Activate element' },
1882
+ };
1883
+ }
1884
+ break;
1885
+ case 'WebBrowser.ReadingMode':
1886
+ case 'WebBrowser.InsertMobileSite': {
1887
+ const toggleAction = getParam('action');
1888
+ if (!semanticAction) {
1889
+ semanticAction = {
1890
+ category: AACSemanticCategory.SYSTEM_CONTROL,
1891
+ intent: AACSemanticIntent.TOGGLE_STATE,
1892
+ parameters: {
1893
+ target: commandId.replace('WebBrowser.', ''),
1894
+ action: toggleAction,
1895
+ },
1896
+ platformData: {
1897
+ grid3: { commandId, parameters: { action: toggleAction } },
1898
+ },
1899
+ fallback: { type: 'ACTION', message: commandId.replace('WebBrowser.', '') },
1900
+ };
1901
+ }
1902
+ break;
1903
+ }
1904
+ case 'WebBrowser.ExecuteJavaScript': {
1905
+ const jsCommandId = getParam('commandid');
1906
+ if (!semanticAction) {
1907
+ semanticAction = {
1908
+ category: AACSemanticCategory.SYSTEM_CONTROL,
1909
+ intent: AACSemanticIntent.WEB_EXECUTE_SCRIPT,
1910
+ text: jsCommandId,
1911
+ platformData: {
1912
+ grid3: { commandId, parameters: { commandid: jsCommandId } },
1913
+ },
1914
+ fallback: { type: 'ACTION', message: `Execute: ${jsCommandId}` },
1915
+ };
1916
+ }
1917
+ break;
1918
+ }
1919
+ case 'WebBrowser.SpeakJavascriptFunction': {
1920
+ const speakCmdId = getParam('commandid');
1921
+ if (!semanticAction) {
1922
+ semanticAction = {
1923
+ category: AACSemanticCategory.SYSTEM_CONTROL,
1924
+ intent: AACSemanticIntent.WEB_EXECUTE_SCRIPT,
1925
+ parameters: { speak: true },
1926
+ text: speakCmdId,
1927
+ platformData: {
1928
+ grid3: { commandId, parameters: { commandid: speakCmdId } },
1929
+ },
1930
+ fallback: { type: 'ACTION', message: `Read aloud: ${speakCmdId}` },
1931
+ };
1932
+ }
1933
+ break;
1934
+ }
1935
+ case 'WebBrowser.FavoriteAdd':
1936
+ case 'WebBrowser.InsertFavourite':
1937
+ case 'WebBrowser.DeleteFavourite':
1938
+ case 'WebBrowser.MoreLinks': {
1939
+ const favParams = {};
1940
+ for (const p of paramArr) {
1941
+ const k = p['@_Key'] || p.Key || p.key;
1942
+ if (k)
1943
+ favParams[k] = p['#text'] ?? p.text;
1944
+ }
1945
+ if (!semanticAction) {
1946
+ semanticAction = {
1947
+ category: AACSemanticCategory.SYSTEM_CONTROL,
1948
+ intent: AACSemanticIntent.WEB_NAVIGATE,
1949
+ parameters: { webAction: commandId },
1950
+ platformData: { grid3: { commandId, parameters: favParams } },
1951
+ fallback: { type: 'ACTION', message: commandId.replace('WebBrowser.', '') },
1952
+ };
1953
+ }
1954
+ break;
1955
+ }
1956
+ // --- Media ---
1957
+ case 'Media.PlayPause':
1958
+ case 'Media.Next':
1959
+ case 'Media.Previous':
1960
+ case 'Media.VolumeUp':
1961
+ case 'Media.VolumeDown':
1962
+ if (!semanticAction) {
1963
+ semanticAction = {
1964
+ category: AACSemanticCategory.MEDIA,
1965
+ intent: AACSemanticIntent.PLAY_VIDEO,
1966
+ parameters: { mediaAction: commandId.replace('Media.', '') },
1967
+ platformData: { grid3: { commandId, parameters: {} } },
1968
+ fallback: { type: 'ACTION', message: commandId.replace('Media.', '') },
1969
+ };
1970
+ }
1971
+ break;
1972
+ case 'Media.Stop':
1973
+ if (!semanticAction) {
1974
+ semanticAction = {
1975
+ category: AACSemanticCategory.MEDIA,
1976
+ intent: AACSemanticIntent.STOP_MEDIA,
1977
+ platformData: { grid3: { commandId, parameters: {} } },
1978
+ fallback: { type: 'ACTION', message: 'Stop' },
1979
+ };
1980
+ }
1981
+ break;
1982
+ // --- MusicVideo ---
1983
+ case 'MusicVideo.ListVideos':
1984
+ case 'MusicVideo.MoreMusicVideos':
1985
+ case 'MusicVideo.SetVideoFolder':
1986
+ case 'MusicVideo.Stop':
1987
+ case 'MusicVideo.StoreAsAttachmentCommandId': {
1988
+ const mvParams = {};
1989
+ for (const p of paramArr) {
1990
+ const k = p['@_Key'] || p.Key || p.key;
1991
+ if (k)
1992
+ mvParams[k] = p['#text'] ?? p.text;
1993
+ }
1994
+ if (!semanticAction) {
1995
+ semanticAction = {
1996
+ category: AACSemanticCategory.MEDIA,
1997
+ intent: commandId === 'MusicVideo.Stop'
1998
+ ? AACSemanticIntent.STOP_MEDIA
1999
+ : AACSemanticIntent.PLAY_VIDEO,
2000
+ parameters: mvParams,
2001
+ platformData: { grid3: { commandId, parameters: mvParams } },
2002
+ fallback: { type: 'ACTION', message: commandId.replace('MusicVideo.', '') },
2003
+ };
2004
+ }
2005
+ break;
2006
+ }
2007
+ // --- Photos ---
2008
+ case 'Photos.Snapshot':
2009
+ if (!semanticAction) {
2010
+ semanticAction = {
2011
+ category: AACSemanticCategory.MEDIA,
2012
+ intent: AACSemanticIntent.TAKE_PHOTO,
2013
+ platformData: { grid3: { commandId, parameters: {} } },
2014
+ fallback: { type: 'ACTION', message: 'Take photo' },
2015
+ };
2016
+ }
2017
+ break;
2018
+ case 'Photos.ChangeCamera':
2019
+ if (!semanticAction) {
2020
+ semanticAction = {
2021
+ category: AACSemanticCategory.MEDIA,
2022
+ intent: AACSemanticIntent.TOGGLE_STATE,
2023
+ parameters: { target: 'camera' },
2024
+ platformData: {
2025
+ grid3: {
2026
+ commandId,
2027
+ parameters: { indicatorenabled: getParam('indicatorenabled') },
2028
+ },
2029
+ },
2030
+ fallback: { type: 'ACTION', message: 'Change camera' },
2031
+ };
2032
+ }
2033
+ break;
2034
+ case 'Photos.MorePhotos':
2035
+ case 'Photos.MyPictures':
2036
+ case 'Photos.SnapshotsFolder':
2037
+ case 'Photos.StoreAsAttachment':
2038
+ if (!semanticAction) {
2039
+ semanticAction = {
2040
+ category: AACSemanticCategory.MEDIA,
2041
+ intent: AACSemanticIntent.PLATFORM_SPECIFIC,
2042
+ platformData: { grid3: { commandId, parameters: {} } },
2043
+ fallback: { type: 'ACTION', message: commandId.replace('Photos.', '') },
2044
+ };
2045
+ }
2046
+ break;
2047
+ // --- Command Execution ---
2048
+ case 'CommandExecution.Wait': {
2049
+ const waitTime = getParam('waittime');
2050
+ const cancellable = getParam('cancellable');
2051
+ if (!semanticAction) {
2052
+ semanticAction = {
2053
+ category: AACSemanticCategory.SYSTEM_CONTROL,
2054
+ intent: AACSemanticIntent.WAIT,
2055
+ parameters: { waittime: waitTime, cancellable },
2056
+ platformData: {
2057
+ grid3: { commandId, parameters: { waittime: waitTime, cancellable } },
2058
+ },
2059
+ fallback: { type: 'ACTION', message: `Wait ${waitTime}` },
2060
+ };
2061
+ }
2062
+ break;
2063
+ }
2064
+ case 'CommandExecution.AutoRepeat': {
2065
+ const repeatCount = getParam('repeatcount');
2066
+ const repeatGap = getParam('repeatgap');
2067
+ if (!semanticAction) {
2068
+ semanticAction = {
2069
+ category: AACSemanticCategory.SYSTEM_CONTROL,
2070
+ intent: AACSemanticIntent.REPEAT_COMMANDS,
2071
+ parameters: { repeatcount: repeatCount, repeatgap: repeatGap },
2072
+ platformData: {
2073
+ grid3: {
2074
+ commandId,
2075
+ parameters: { repeatcount: repeatCount, repeatgap: repeatGap },
2076
+ },
2077
+ },
2078
+ fallback: { type: 'ACTION', message: `Repeat ${repeatCount} times` },
2079
+ };
2080
+ }
2081
+ break;
2082
+ }
2083
+ // --- Settings ---
2084
+ case 'Settings.GridExplorer':
2085
+ case 'Settings.Open':
2086
+ if (!semanticAction) {
2087
+ semanticAction = {
2088
+ category: AACSemanticCategory.SYSTEM_CONTROL,
2089
+ intent: AACSemanticIntent.PLATFORM_SPECIFIC,
2090
+ platformData: { grid3: { commandId, parameters: {} } },
2091
+ fallback: { type: 'ACTION', message: commandId.replace('Settings.', '') },
2092
+ };
2093
+ }
2094
+ break;
2095
+ case 'Settings.RequiredFeature': {
2096
+ const feature = getParam('feature');
2097
+ if (!semanticAction) {
2098
+ semanticAction = {
2099
+ category: AACSemanticCategory.ACCESSIBILITY,
2100
+ intent: AACSemanticIntent.PLATFORM_SPECIFIC,
2101
+ platformData: { grid3: { commandId, parameters: { feature } } },
2102
+ fallback: { type: 'ACTION', message: `Require ${feature}` },
2103
+ };
2104
+ }
2105
+ break;
2106
+ }
2107
+ case 'Scanning.Start':
2108
+ case 'Scanning.Stop':
2109
+ if (!semanticAction) {
2110
+ semanticAction = {
2111
+ category: AACSemanticCategory.ACCESSIBILITY,
2112
+ intent: commandId === 'Scanning.Start'
2113
+ ? AACSemanticIntent.SCAN_NEXT
2114
+ : AACSemanticIntent.SCAN_SELECT,
2115
+ platformData: { grid3: { commandId, parameters: {} } },
2116
+ fallback: { type: 'ACTION', message: commandId.replace('Scanning.', '') },
2117
+ };
2118
+ }
2119
+ break;
2120
+ // --- Auto Content extras ---
2121
+ case 'Prediction.Clear':
2122
+ if (!semanticAction) {
2123
+ semanticAction = {
2124
+ category: AACSemanticCategory.TEXT_EDITING,
2125
+ intent: AACSemanticIntent.CLEAR_TEXT,
2126
+ platformData: { grid3: { commandId, parameters: {} } },
2127
+ fallback: { type: 'ACTION', message: 'Clear predictions' },
2128
+ };
2129
+ }
2130
+ break;
2131
+ case 'Prediction.AddToWordList':
2132
+ case 'Prediction.DeleteWord':
2133
+ case 'Prediction.MoreWords': {
2134
+ const predParams = {};
2135
+ for (const p of paramArr) {
2136
+ const k = p['@_Key'] || p.Key || p.key;
2137
+ if (k)
2138
+ predParams[k] = p['#text'] ?? p.text;
2139
+ }
2140
+ if (!semanticAction) {
2141
+ semanticAction = {
2142
+ category: AACSemanticCategory.CUSTOM,
2143
+ intent: AACSemanticIntent.PLATFORM_SPECIFIC,
2144
+ platformData: { grid3: { commandId, parameters: predParams } },
2145
+ fallback: { type: 'ACTION', message: commandId.replace('Prediction.', '') },
2146
+ };
2147
+ }
2148
+ break;
2149
+ }
2150
+ case 'Grammar.Change':
2151
+ if (!semanticAction) {
2152
+ semanticAction = {
2153
+ category: AACSemanticCategory.CUSTOM,
2154
+ intent: AACSemanticIntent.PLATFORM_SPECIFIC,
2155
+ platformData: {
2156
+ grid3: { commandId, parameters: { context: getParam('context') } },
2157
+ },
2158
+ fallback: { type: 'ACTION', message: 'Change grammar' },
2159
+ };
2160
+ }
2161
+ break;
2162
+ // --- System ---
2163
+ case 'System.LogOff':
2164
+ case 'System.Lock':
2165
+ case 'System.Sleep':
2166
+ case 'System.Restart':
2167
+ case 'System.ShutDown':
2168
+ if (!semanticAction) {
2169
+ semanticAction = {
2170
+ category: AACSemanticCategory.SYSTEM_CONTROL,
2171
+ intent: AACSemanticIntent.PLATFORM_SPECIFIC,
2172
+ platformData: { grid3: { commandId, parameters: {} } },
2173
+ fallback: { type: 'ACTION', message: commandId.replace('System.', '') },
2174
+ };
2175
+ }
2176
+ break;
2177
+ // --- Window ---
2178
+ case 'Window.Minimize':
2179
+ case 'Window.Maximize':
2180
+ case 'Window.Close':
2181
+ case 'Window.Switch':
2182
+ if (!semanticAction) {
2183
+ semanticAction = {
2184
+ category: AACSemanticCategory.SYSTEM_CONTROL,
2185
+ intent: AACSemanticIntent.PLATFORM_SPECIFIC,
2186
+ platformData: { grid3: { commandId, parameters: {} } },
2187
+ fallback: { type: 'ACTION', message: commandId.replace('Window.', '') },
2188
+ };
2189
+ }
2190
+ break;
2191
+ // --- Email ---
2192
+ case 'Email.SendTo':
2193
+ case 'Email.AddRecipient':
2194
+ case 'Email.SetSubject':
2195
+ case 'Email.AttachFile': {
2196
+ const emailParams = {};
2197
+ for (const p of paramArr) {
2198
+ const k = p['@_Key'] || p.Key || p.key;
2199
+ if (k)
2200
+ emailParams[k] = p['#text'] ?? p.text;
2201
+ }
2202
+ if (!semanticAction) {
2203
+ semanticAction = {
2204
+ category: AACSemanticCategory.SYSTEM_CONTROL,
2205
+ intent: AACSemanticIntent.PLATFORM_SPECIFIC,
2206
+ platformData: { grid3: { commandId, parameters: emailParams } },
2207
+ fallback: { type: 'ACTION', message: commandId.replace('Email.', '') },
2208
+ };
2209
+ }
2210
+ break;
2211
+ }
2212
+ // --- Phone ---
2213
+ case 'Phone.Call':
2214
+ case 'Phone.Answer':
2215
+ case 'Phone.Hangup':
2216
+ if (!semanticAction) {
2217
+ semanticAction = {
2218
+ category: AACSemanticCategory.SYSTEM_CONTROL,
2219
+ intent: AACSemanticIntent.PLATFORM_SPECIFIC,
2220
+ platformData: { grid3: { commandId, parameters: {} } },
2221
+ fallback: { type: 'ACTION', message: commandId.replace('Phone.', '') },
2222
+ };
2223
+ }
2224
+ break;
2225
+ // --- SMS ---
2226
+ case 'Sms.SendTo':
2227
+ case 'Sms.AddRecipient': {
2228
+ const smsParams = {};
2229
+ for (const p of paramArr) {
2230
+ const k = p['@_Key'] || p.Key || p.key;
2231
+ if (k)
2232
+ smsParams[k] = p['#text'] ?? p.text;
2233
+ }
2234
+ if (!semanticAction) {
2235
+ semanticAction = {
2236
+ category: AACSemanticCategory.SYSTEM_CONTROL,
2237
+ intent: AACSemanticIntent.PLATFORM_SPECIFIC,
2238
+ platformData: { grid3: { commandId, parameters: smsParams } },
2239
+ fallback: { type: 'ACTION', message: commandId.replace('Sms.', '') },
2240
+ };
2241
+ }
2242
+ break;
2243
+ }
2244
+ // --- Environment Control ---
2245
+ case 'EnvControl.Send':
2246
+ case 'EnvControl.Learn':
2247
+ if (!semanticAction) {
2248
+ semanticAction = {
2249
+ category: AACSemanticCategory.SYSTEM_CONTROL,
2250
+ intent: AACSemanticIntent.PLATFORM_SPECIFIC,
2251
+ platformData: {
2252
+ grid3: { commandId, parameters: { code: getParam('code') } },
2253
+ },
2254
+ fallback: { type: 'ACTION', message: commandId.replace('EnvControl.', '') },
2255
+ };
2256
+ }
2257
+ break;
1439
2258
  default:
1440
- // Unknown command - preserve as generic action
1441
2259
  if (commandId && !semanticAction) {
1442
2260
  const allParams = Object.fromEntries(paramArr.map((p) => [p.Key || p.key, p['#text']]));
1443
2261
  semanticAction = {
@@ -1451,10 +2269,9 @@ class GridsetProcessor extends BaseProcessor {
1451
2269
  },
1452
2270
  fallback: {
1453
2271
  type: 'ACTION',
1454
- message: 'Unknown command',
2272
+ message: commandId,
1455
2273
  },
1456
2274
  };
1457
- // legacy action not needed for unknown commands
1458
2275
  }
1459
2276
  break;
1460
2277
  }
@@ -1981,6 +2798,28 @@ class GridsetProcessor extends BaseProcessor {
1981
2798
  data: settingsXmlContent,
1982
2799
  });
1983
2800
  // Create Settings0/Styles/style.xml if there are styles
2801
+ // Ensure Workspace and Default styles exist when workspace cells are present
2802
+ const hasAnyWorkspace = Object.values(tree.pages).some((p) => p.buttons.some((b) => b.contentType === 'Workspace' || b.contentType === 'LiveCell'));
2803
+ if (hasAnyWorkspace) {
2804
+ if (!uniqueStyles.has('workspace-style')) {
2805
+ uniqueStyles.set('workspace-style', {
2806
+ id: 'Workspace',
2807
+ style: { backgroundColor: '#FFFFFF00', borderColor: '#FFFFFF00', fontColor: '#000000FF' },
2808
+ });
2809
+ }
2810
+ if (!uniqueStyles.has('default-style')) {
2811
+ uniqueStyles.set('default-style', {
2812
+ id: 'Default',
2813
+ style: {
2814
+ backgroundColor: '#D5DBDBFF',
2815
+ borderColor: '#FFFFFFFF',
2816
+ fontColor: '#000000FF',
2817
+ fontFamily: 'Arial',
2818
+ fontSize: 16,
2819
+ },
2820
+ });
2821
+ }
2822
+ }
1984
2823
  if (uniqueStyles.size > 0) {
1985
2824
  const stylesArray = Array.from(uniqueStyles.values()).map(({ id, style }) => {
1986
2825
  const styleObj = {
@@ -2021,9 +2860,11 @@ class GridsetProcessor extends BaseProcessor {
2021
2860
  const gridFilePaths = [];
2022
2861
  // Create a grid for each page
2023
2862
  Object.values(tree.pages).forEach((page) => {
2863
+ const hasComputerControl = page.buttons.some((b) => b.contentType === 'Workspace' && b.contentSubType === 'ComputerControl');
2024
2864
  const gridData = {
2025
2865
  Grid: {
2026
2866
  '@_xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
2867
+ ...(hasComputerControl ? { ComputerControl: '1' } : {}),
2027
2868
  GridGuid: page.id,
2028
2869
  // Calculate grid dimensions based on actual layout
2029
2870
  ColumnDefinitions: this.calculateColumnDefinitions(page),
@@ -2035,7 +2876,14 @@ class GridsetProcessor extends BaseProcessor {
2035
2876
  // Regular button cells
2036
2877
  ...this.filterPageButtons(page.buttons).map((button, btnIndex) => {
2037
2878
  const buttonStyleId = button.style ? addStyle(button.style) : '';
2038
- // Find button position in grid layout
2879
+ // Force Workspace/LiveCell/AutoContent cells to use their standard style names
2880
+ const effectiveStyleId = button.contentType === 'Workspace'
2881
+ ? 'Workspace'
2882
+ : button.contentType === 'LiveCell'
2883
+ ? 'Default'
2884
+ : button.contentType === 'AutoContent'
2885
+ ? 'Default'
2886
+ : buttonStyleId; // Find button position in grid layout
2039
2887
  const position = this.findButtonPosition(page, button, btnIndex);
2040
2888
  // Use position directly from tree
2041
2889
  const yOffset = 0;
@@ -2097,25 +2945,34 @@ class GridsetProcessor extends BaseProcessor {
2097
2945
  });
2098
2946
  }
2099
2947
  }
2948
+ const isPluginCell = button.contentType === 'Workspace' ||
2949
+ button.contentType === 'LiveCell' ||
2950
+ button.contentType === 'AutoContent';
2951
+ const cellContent = {
2952
+ ContentType: button.contentType === 'Normal' ? undefined : button.contentType,
2953
+ ContentSubType: button.contentSubType,
2954
+ };
2955
+ const commands = this.generateCommandsFromSemanticAction(button, tree);
2956
+ if (commands) {
2957
+ cellContent.Commands = commands;
2958
+ }
2959
+ if (!isPluginCell) {
2960
+ cellContent.CaptionAndImage = captionAndImage;
2961
+ }
2100
2962
  const cellData = {
2101
2963
  '@_X': position.x + 1, // Grid3 uses 1-based X coordinates
2102
2964
  '@_Y': position.y + yOffset + 1, // Grid3 uses 1-based Y coordinates with workspace offset
2103
2965
  '@_ColumnSpan': position.columnSpan,
2104
2966
  '@_RowSpan': position.rowSpan,
2105
- Content: {
2106
- ContentType: button.contentType === 'Normal' ? undefined : button.contentType,
2107
- ContentSubType: button.contentSubType,
2108
- Commands: this.generateCommandsFromSemanticAction(button, tree),
2109
- CaptionAndImage: captionAndImage,
2110
- },
2967
+ Content: cellContent,
2111
2968
  };
2112
2969
  // Add style reference and inline color overrides if available
2113
2970
  // Some Grid3 versions need inline colors in addition to style references
2114
- if (buttonStyleId || button.style) {
2971
+ if (effectiveStyleId || button.style) {
2115
2972
  const styleObj = {};
2116
2973
  // Add style reference if we have one
2117
- if (buttonStyleId) {
2118
- styleObj.BasedOnStyle = buttonStyleId;
2974
+ if (effectiveStyleId) {
2975
+ styleObj.BasedOnStyle = effectiveStyleId;
2119
2976
  }
2120
2977
  // Add inline color overrides for better Grid3 compatibility
2121
2978
  if (button.style?.backgroundColor) {