@lvce-editor/chat-view 1.7.0 → 1.8.0

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.
@@ -964,32 +964,9 @@ const create$3 = async ({
964
964
  return rpc;
965
965
  };
966
966
 
967
- const Button$2 = 'button';
968
-
969
- const Button$1 = 1;
970
- const Div = 4;
971
- const Span = 8;
972
- const Text = 12;
973
- const P = 50;
974
- const TextArea = 62;
975
- const Reference = 100;
976
-
977
- const ClientX = 'event.clientX';
978
- const ClientY = 'event.clientY';
979
- const Key = 'event.key';
980
- const ShiftKey = 'event.shiftKey';
981
- const TargetName = 'event.target.name';
982
- const TargetValue = 'event.target.value';
983
-
984
967
  const ExtensionHostWorker = 44;
985
968
  const RendererWorker = 1;
986
969
 
987
- const FocusSelector = 'Viewlet.focusSelector';
988
- const SetCss = 'Viewlet.setCss';
989
- const SetDom2 = 'Viewlet.setDom2';
990
- const SetValueByName = 'Viewlet.setValueByName';
991
- const SetPatches = 'Viewlet.setPatches';
992
-
993
970
  const createMockRpc = ({
994
971
  commandMap
995
972
  }) => {
@@ -1302,6 +1279,7 @@ const isEqual = (oldState, newState) => {
1302
1279
 
1303
1280
  const RenderItems = 4;
1304
1281
  const RenderFocus = 6;
1282
+ const RenderFocusContext = 7;
1305
1283
  const RenderValue = 8;
1306
1284
  const RenderCss = 10;
1307
1285
  const RenderIncremental = 11;
@@ -1310,34 +1288,372 @@ const diffValue = (oldState, newState) => {
1310
1288
  if (oldState.composerValue === newState.composerValue) {
1311
1289
  return true;
1312
1290
  }
1313
- return newState.inputSource !== 'script';
1291
+ return newState.inputSource !== 'script';
1292
+ };
1293
+
1294
+ const modules = [isEqual, diffValue, diffFocus, isEqual$1];
1295
+ const numbers = [RenderIncremental, RenderValue, RenderFocus, RenderCss];
1296
+
1297
+ const diff = (oldState, newState) => {
1298
+ const diffResult = [];
1299
+ for (let i = 0; i < modules.length; i++) {
1300
+ const fn = modules[i];
1301
+ if (!fn(oldState, newState)) {
1302
+ diffResult.push(numbers[i]);
1303
+ }
1304
+ }
1305
+ return diffResult;
1306
+ };
1307
+
1308
+ const diff2 = uid => {
1309
+ const {
1310
+ newState,
1311
+ oldState
1312
+ } = get(uid);
1313
+ const result = diff(oldState, newState);
1314
+ return result;
1315
+ };
1316
+
1317
+ const ClientX = 'event.clientX';
1318
+ const ClientY = 'event.clientY';
1319
+ const Key = 'event.key';
1320
+ const ShiftKey = 'event.shiftKey';
1321
+ const TargetName = 'event.target.name';
1322
+ const TargetValue = 'event.target.value';
1323
+
1324
+ const FocusSelector = 'Viewlet.focusSelector';
1325
+ const SetCss = 'Viewlet.setCss';
1326
+ const SetDom2 = 'Viewlet.setDom2';
1327
+ const SetFocusContext = 'Viewlet.setFocusContext';
1328
+ const SetValueByName = 'Viewlet.setValueByName';
1329
+ const SetPatches = 'Viewlet.setPatches';
1330
+
1331
+ const FocusChatInput = 8000;
1332
+
1333
+ const Button$2 = 'button';
1334
+
1335
+ const Button$1 = 1;
1336
+ const Div = 4;
1337
+ const Span = 8;
1338
+ const Text = 12;
1339
+ const P = 50;
1340
+ const TextArea = 62;
1341
+ const Reference = 100;
1342
+
1343
+ const Enter = 3;
1344
+
1345
+ const Shift = 1 << 10 >>> 0;
1346
+
1347
+ const mergeClassNames = (...classNames) => {
1348
+ return classNames.filter(Boolean).join(' ');
1349
+ };
1350
+
1351
+ const text = data => {
1352
+ return {
1353
+ childCount: 0,
1354
+ text: data,
1355
+ type: Text
1356
+ };
1357
+ };
1358
+
1359
+ const SetText = 1;
1360
+ const Replace = 2;
1361
+ const SetAttribute = 3;
1362
+ const RemoveAttribute = 4;
1363
+ const Add = 6;
1364
+ const NavigateChild = 7;
1365
+ const NavigateParent = 8;
1366
+ const RemoveChild = 9;
1367
+ const NavigateSibling = 10;
1368
+ const SetReferenceNodeUid = 11;
1369
+
1370
+ const isKey = key => {
1371
+ return key !== 'type' && key !== 'childCount';
1372
+ };
1373
+
1374
+ const getKeys = node => {
1375
+ const keys = Object.keys(node).filter(isKey);
1376
+ return keys;
1377
+ };
1378
+
1379
+ const arrayToTree = nodes => {
1380
+ const result = [];
1381
+ let i = 0;
1382
+ while (i < nodes.length) {
1383
+ const node = nodes[i];
1384
+ const {
1385
+ children,
1386
+ nodesConsumed
1387
+ } = getChildrenWithCount(nodes, i + 1, node.childCount || 0);
1388
+ result.push({
1389
+ node,
1390
+ children
1391
+ });
1392
+ i += 1 + nodesConsumed;
1393
+ }
1394
+ return result;
1395
+ };
1396
+ const getChildrenWithCount = (nodes, startIndex, childCount) => {
1397
+ if (childCount === 0) {
1398
+ return {
1399
+ children: [],
1400
+ nodesConsumed: 0
1401
+ };
1402
+ }
1403
+ const children = [];
1404
+ let i = startIndex;
1405
+ let remaining = childCount;
1406
+ let totalConsumed = 0;
1407
+ while (remaining > 0 && i < nodes.length) {
1408
+ const node = nodes[i];
1409
+ const nodeChildCount = node.childCount || 0;
1410
+ const {
1411
+ children: nodeChildren,
1412
+ nodesConsumed
1413
+ } = getChildrenWithCount(nodes, i + 1, nodeChildCount);
1414
+ children.push({
1415
+ node,
1416
+ children: nodeChildren
1417
+ });
1418
+ const nodeSize = 1 + nodesConsumed;
1419
+ i += nodeSize;
1420
+ totalConsumed += nodeSize;
1421
+ remaining--;
1422
+ }
1423
+ return {
1424
+ children,
1425
+ nodesConsumed: totalConsumed
1426
+ };
1427
+ };
1428
+
1429
+ const compareNodes = (oldNode, newNode) => {
1430
+ const patches = [];
1431
+ // Check if node type changed - return null to signal incompatible nodes
1432
+ // (caller should handle this with a Replace operation)
1433
+ if (oldNode.type !== newNode.type) {
1434
+ return null;
1435
+ }
1436
+ // Handle reference nodes - special handling for uid changes
1437
+ if (oldNode.type === Reference) {
1438
+ if (oldNode.uid !== newNode.uid) {
1439
+ patches.push({
1440
+ type: SetReferenceNodeUid,
1441
+ uid: newNode.uid
1442
+ });
1443
+ }
1444
+ return patches;
1445
+ }
1446
+ // Handle text nodes
1447
+ if (oldNode.type === Text && newNode.type === Text) {
1448
+ if (oldNode.text !== newNode.text) {
1449
+ patches.push({
1450
+ type: SetText,
1451
+ value: newNode.text
1452
+ });
1453
+ }
1454
+ return patches;
1455
+ }
1456
+ // Compare attributes
1457
+ const oldKeys = getKeys(oldNode);
1458
+ const newKeys = getKeys(newNode);
1459
+ // Check for attribute changes
1460
+ for (const key of newKeys) {
1461
+ if (oldNode[key] !== newNode[key]) {
1462
+ patches.push({
1463
+ type: SetAttribute,
1464
+ key,
1465
+ value: newNode[key]
1466
+ });
1467
+ }
1468
+ }
1469
+ // Check for removed attributes
1470
+ for (const key of oldKeys) {
1471
+ if (!(key in newNode)) {
1472
+ patches.push({
1473
+ type: RemoveAttribute,
1474
+ key
1475
+ });
1476
+ }
1477
+ }
1478
+ return patches;
1479
+ };
1480
+
1481
+ const treeToArray = node => {
1482
+ const result = [node.node];
1483
+ for (const child of node.children) {
1484
+ result.push(...treeToArray(child));
1485
+ }
1486
+ return result;
1487
+ };
1488
+
1489
+ const diffChildren = (oldChildren, newChildren, patches) => {
1490
+ const maxLength = Math.max(oldChildren.length, newChildren.length);
1491
+ // Track where we are: -1 means at parent, >= 0 means at child index
1492
+ let currentChildIndex = -1;
1493
+ // Collect indices of children to remove (we'll add these patches at the end in reverse order)
1494
+ const indicesToRemove = [];
1495
+ for (let i = 0; i < maxLength; i++) {
1496
+ const oldNode = oldChildren[i];
1497
+ const newNode = newChildren[i];
1498
+ if (!oldNode && !newNode) {
1499
+ continue;
1500
+ }
1501
+ if (!oldNode) {
1502
+ // Add new node - we should be at the parent
1503
+ if (currentChildIndex >= 0) {
1504
+ // Navigate back to parent
1505
+ patches.push({
1506
+ type: NavigateParent
1507
+ });
1508
+ currentChildIndex = -1;
1509
+ }
1510
+ // Flatten the entire subtree so renderInternal can handle it
1511
+ const flatNodes = treeToArray(newNode);
1512
+ patches.push({
1513
+ type: Add,
1514
+ nodes: flatNodes
1515
+ });
1516
+ } else if (newNode) {
1517
+ // Compare nodes to see if we need any patches
1518
+ const nodePatches = compareNodes(oldNode.node, newNode.node);
1519
+ // If nodePatches is null, the node types are incompatible - need to replace
1520
+ if (nodePatches === null) {
1521
+ // Navigate to this child
1522
+ if (currentChildIndex === -1) {
1523
+ patches.push({
1524
+ type: NavigateChild,
1525
+ index: i
1526
+ });
1527
+ currentChildIndex = i;
1528
+ } else if (currentChildIndex !== i) {
1529
+ patches.push({
1530
+ type: NavigateSibling,
1531
+ index: i
1532
+ });
1533
+ currentChildIndex = i;
1534
+ }
1535
+ // Replace the entire subtree
1536
+ const flatNodes = treeToArray(newNode);
1537
+ patches.push({
1538
+ type: Replace,
1539
+ nodes: flatNodes
1540
+ });
1541
+ // After replace, we're at the new element (same position)
1542
+ continue;
1543
+ }
1544
+ // Check if we need to recurse into children
1545
+ const hasChildrenToCompare = oldNode.children.length > 0 || newNode.children.length > 0;
1546
+ // Only navigate to this element if we need to do something
1547
+ if (nodePatches.length > 0 || hasChildrenToCompare) {
1548
+ // Navigate to this child if not already there
1549
+ if (currentChildIndex === -1) {
1550
+ patches.push({
1551
+ type: NavigateChild,
1552
+ index: i
1553
+ });
1554
+ currentChildIndex = i;
1555
+ } else if (currentChildIndex !== i) {
1556
+ patches.push({
1557
+ type: NavigateSibling,
1558
+ index: i
1559
+ });
1560
+ currentChildIndex = i;
1561
+ }
1562
+ // Apply node patches (these apply to the current element, not children)
1563
+ if (nodePatches.length > 0) {
1564
+ patches.push(...nodePatches);
1565
+ }
1566
+ // Compare children recursively
1567
+ if (hasChildrenToCompare) {
1568
+ diffChildren(oldNode.children, newNode.children, patches);
1569
+ }
1570
+ }
1571
+ } else {
1572
+ // Remove old node - collect the index for later removal
1573
+ indicesToRemove.push(i);
1574
+ }
1575
+ }
1576
+ // Navigate back to parent if we ended at a child
1577
+ if (currentChildIndex >= 0) {
1578
+ patches.push({
1579
+ type: NavigateParent
1580
+ });
1581
+ currentChildIndex = -1;
1582
+ }
1583
+ // Add remove patches in reverse order (highest index first)
1584
+ // This ensures indices remain valid as we remove
1585
+ for (let j = indicesToRemove.length - 1; j >= 0; j--) {
1586
+ patches.push({
1587
+ type: RemoveChild,
1588
+ index: indicesToRemove[j]
1589
+ });
1590
+ }
1591
+ };
1592
+ const diffTrees = (oldTree, newTree, patches, path) => {
1593
+ // At the root level (path.length === 0), we're already AT the element
1594
+ // So we compare the root node directly, then compare its children
1595
+ if (path.length === 0 && oldTree.length === 1 && newTree.length === 1) {
1596
+ const oldNode = oldTree[0];
1597
+ const newNode = newTree[0];
1598
+ // Compare root nodes
1599
+ const nodePatches = compareNodes(oldNode.node, newNode.node);
1600
+ // If nodePatches is null, the root node types are incompatible - need to replace
1601
+ if (nodePatches === null) {
1602
+ const flatNodes = treeToArray(newNode);
1603
+ patches.push({
1604
+ type: Replace,
1605
+ nodes: flatNodes
1606
+ });
1607
+ return;
1608
+ }
1609
+ if (nodePatches.length > 0) {
1610
+ patches.push(...nodePatches);
1611
+ }
1612
+ // Compare children
1613
+ if (oldNode.children.length > 0 || newNode.children.length > 0) {
1614
+ diffChildren(oldNode.children, newNode.children, patches);
1615
+ }
1616
+ } else {
1617
+ // Non-root level or multiple root elements - use the regular comparison
1618
+ diffChildren(oldTree, newTree, patches);
1619
+ }
1314
1620
  };
1315
1621
 
1316
- const modules = [isEqual, diffValue, diffFocus, isEqual$1];
1317
- const numbers = [RenderIncremental, RenderValue, RenderFocus, RenderCss];
1318
-
1319
- const diff = (oldState, newState) => {
1320
- const diffResult = [];
1321
- for (let i = 0; i < modules.length; i++) {
1322
- const fn = modules[i];
1323
- if (!fn(oldState, newState)) {
1324
- diffResult.push(numbers[i]);
1622
+ const removeTrailingNavigationPatches = patches => {
1623
+ // Find the last non-navigation patch
1624
+ let lastNonNavigationIndex = -1;
1625
+ for (let i = patches.length - 1; i >= 0; i--) {
1626
+ const patch = patches[i];
1627
+ if (patch.type !== NavigateChild && patch.type !== NavigateParent && patch.type !== NavigateSibling) {
1628
+ lastNonNavigationIndex = i;
1629
+ break;
1325
1630
  }
1326
1631
  }
1327
- return diffResult;
1632
+ // Return patches up to and including the last non-navigation patch
1633
+ return lastNonNavigationIndex === -1 ? [] : patches.slice(0, lastNonNavigationIndex + 1);
1328
1634
  };
1329
1635
 
1330
- const diff2 = uid => {
1331
- const {
1332
- newState,
1333
- oldState
1334
- } = get(uid);
1335
- const result = diff(oldState, newState);
1336
- return result;
1636
+ const diffTree = (oldNodes, newNodes) => {
1637
+ // Step 1: Convert flat arrays to tree structures
1638
+ const oldTree = arrayToTree(oldNodes);
1639
+ const newTree = arrayToTree(newNodes);
1640
+ // Step 3: Compare the trees
1641
+ const patches = [];
1642
+ diffTrees(oldTree, newTree, patches, []);
1643
+ // Remove trailing navigation patches since they serve no purpose
1644
+ return removeTrailingNavigationPatches(patches);
1337
1645
  };
1338
1646
 
1339
1647
  const getKeyBindings = () => {
1340
- return [];
1648
+ return [{
1649
+ command: 'Chat.handleSubmit',
1650
+ key: Enter,
1651
+ when: FocusChatInput
1652
+ }, {
1653
+ command: 'Chat.enterNewLine',
1654
+ key: Shift | Enter,
1655
+ when: FocusChatInput
1656
+ }];
1341
1657
  };
1342
1658
 
1343
1659
  const SESSION_PREFIX$1 = 'session:';
@@ -1649,6 +1965,7 @@ const handleClickNew = async state => {
1649
1965
 
1650
1966
  const handleClickSettings = async () => {
1651
1967
  // TODO
1968
+ await invoke('Main.openUri', 'app://settings.json');
1652
1969
  };
1653
1970
 
1654
1971
  const handleInput = async (state, value, inputSource = 'user') => {
@@ -1744,6 +2061,10 @@ const handleKeyDown = async (state, key, shiftKey) => {
1744
2061
  return handleSubmit(submitState);
1745
2062
  };
1746
2063
 
2064
+ const handleNewline = async state => {
2065
+ return handleInput(state, `${state.composerValue}\n`);
2066
+ };
2067
+
1747
2068
  const id = 7201;
1748
2069
  const sendMessagePortToExtensionHostWorker = async port => {
1749
2070
  await sendMessagePortToExtensionHostWorker$1(port, id);
@@ -1777,397 +2098,129 @@ const getSavedBounds = savedState => {
1777
2098
  const {
1778
2099
  height,
1779
2100
  width,
1780
- x,
1781
- y
1782
- } = savedState;
1783
- if (typeof x !== 'number') {
1784
- return undefined;
1785
- }
1786
- if (typeof y !== 'number') {
1787
- return undefined;
1788
- }
1789
- if (typeof width !== 'number') {
1790
- return undefined;
1791
- }
1792
- if (typeof height !== 'number') {
1793
- return undefined;
1794
- }
1795
- return {
1796
- height,
1797
- width,
1798
- x,
1799
- y
1800
- };
1801
- };
1802
-
1803
- const getSavedSelectedSessionId = savedState => {
1804
- if (!isObject(savedState)) {
1805
- return undefined;
1806
- }
1807
- const {
1808
- selectedSessionId
1809
- } = savedState;
1810
- if (typeof selectedSessionId !== 'string') {
1811
- return undefined;
1812
- }
1813
- return selectedSessionId;
1814
- };
1815
-
1816
- const getSavedSessions = savedState => {
1817
- if (!isObject(savedState)) {
1818
- return undefined;
1819
- }
1820
- const {
1821
- sessions
1822
- } = savedState;
1823
- if (!Array.isArray(sessions)) {
1824
- return undefined;
1825
- }
1826
- return sessions;
1827
- };
1828
-
1829
- const loadContent = async (state, savedState) => {
1830
- const savedBounds = getSavedBounds(savedState);
1831
- const sessions = getSavedSessions(savedState) || state.sessions;
1832
- const preferredSessionId = getSavedSelectedSessionId(savedState) || state.selectedSessionId;
1833
- const selectedSessionId = sessions.some(session => session.id === preferredSessionId) ? preferredSessionId : sessions[0]?.id || '';
1834
- const viewMode = sessions.length === 0 ? 'list' : state.viewMode === 'detail' ? 'detail' : 'list';
1835
- return {
1836
- ...state,
1837
- ...savedBounds,
1838
- initial: false,
1839
- selectedSessionId,
1840
- sessions,
1841
- viewMode
1842
- };
1843
- };
1844
-
1845
- // TODO render things like scrollbar height,scrollbar offset, textarea height,
1846
- // list height
1847
- const css = `
1848
- `;
1849
- const renderCss = (oldState, newState) => {
1850
- return [SetCss, newState.uid, css];
1851
- };
1852
-
1853
- const getFocusSelector = focus => {
1854
- switch (focus) {
1855
- case 'composer':
1856
- case 'input':
1857
- return '[name="composer"]';
1858
- case 'header':
1859
- return '[name="create-session"]';
1860
- case 'list':
1861
- return '[name^="session:"]';
1862
- case 'send-button':
1863
- return '[name="send"]';
1864
- default:
1865
- return '[name="composer"]';
1866
- }
1867
- };
1868
- const renderFocus = (oldState, newState) => {
1869
- const selector = getFocusSelector(newState.focus);
1870
- return [FocusSelector, selector];
1871
- };
1872
-
1873
- const mergeClassNames = (...classNames) => {
1874
- return classNames.filter(Boolean).join(' ');
1875
- };
1876
-
1877
- const text = data => {
1878
- return {
1879
- childCount: 0,
1880
- text: data,
1881
- type: Text
1882
- };
1883
- };
1884
-
1885
- const SetText = 1;
1886
- const Replace = 2;
1887
- const SetAttribute = 3;
1888
- const RemoveAttribute = 4;
1889
- const Add = 6;
1890
- const NavigateChild = 7;
1891
- const NavigateParent = 8;
1892
- const RemoveChild = 9;
1893
- const NavigateSibling = 10;
1894
- const SetReferenceNodeUid = 11;
1895
-
1896
- const isKey = key => {
1897
- return key !== 'type' && key !== 'childCount';
1898
- };
1899
-
1900
- const getKeys = node => {
1901
- const keys = Object.keys(node).filter(isKey);
1902
- return keys;
1903
- };
1904
-
1905
- const arrayToTree = nodes => {
1906
- const result = [];
1907
- let i = 0;
1908
- while (i < nodes.length) {
1909
- const node = nodes[i];
1910
- const {
1911
- children,
1912
- nodesConsumed
1913
- } = getChildrenWithCount(nodes, i + 1, node.childCount || 0);
1914
- result.push({
1915
- node,
1916
- children
1917
- });
1918
- i += 1 + nodesConsumed;
1919
- }
1920
- return result;
1921
- };
1922
- const getChildrenWithCount = (nodes, startIndex, childCount) => {
1923
- if (childCount === 0) {
1924
- return {
1925
- children: [],
1926
- nodesConsumed: 0
1927
- };
1928
- }
1929
- const children = [];
1930
- let i = startIndex;
1931
- let remaining = childCount;
1932
- let totalConsumed = 0;
1933
- while (remaining > 0 && i < nodes.length) {
1934
- const node = nodes[i];
1935
- const nodeChildCount = node.childCount || 0;
1936
- const {
1937
- children: nodeChildren,
1938
- nodesConsumed
1939
- } = getChildrenWithCount(nodes, i + 1, nodeChildCount);
1940
- children.push({
1941
- node,
1942
- children: nodeChildren
1943
- });
1944
- const nodeSize = 1 + nodesConsumed;
1945
- i += nodeSize;
1946
- totalConsumed += nodeSize;
1947
- remaining--;
1948
- }
1949
- return {
1950
- children,
1951
- nodesConsumed: totalConsumed
1952
- };
1953
- };
1954
-
1955
- const compareNodes = (oldNode, newNode) => {
1956
- const patches = [];
1957
- // Check if node type changed - return null to signal incompatible nodes
1958
- // (caller should handle this with a Replace operation)
1959
- if (oldNode.type !== newNode.type) {
1960
- return null;
1961
- }
1962
- // Handle reference nodes - special handling for uid changes
1963
- if (oldNode.type === Reference) {
1964
- if (oldNode.uid !== newNode.uid) {
1965
- patches.push({
1966
- type: SetReferenceNodeUid,
1967
- uid: newNode.uid
1968
- });
1969
- }
1970
- return patches;
1971
- }
1972
- // Handle text nodes
1973
- if (oldNode.type === Text && newNode.type === Text) {
1974
- if (oldNode.text !== newNode.text) {
1975
- patches.push({
1976
- type: SetText,
1977
- value: newNode.text
1978
- });
1979
- }
1980
- return patches;
1981
- }
1982
- // Compare attributes
1983
- const oldKeys = getKeys(oldNode);
1984
- const newKeys = getKeys(newNode);
1985
- // Check for attribute changes
1986
- for (const key of newKeys) {
1987
- if (oldNode[key] !== newNode[key]) {
1988
- patches.push({
1989
- type: SetAttribute,
1990
- key,
1991
- value: newNode[key]
1992
- });
1993
- }
2101
+ x,
2102
+ y
2103
+ } = savedState;
2104
+ if (typeof x !== 'number') {
2105
+ return undefined;
1994
2106
  }
1995
- // Check for removed attributes
1996
- for (const key of oldKeys) {
1997
- if (!(key in newNode)) {
1998
- patches.push({
1999
- type: RemoveAttribute,
2000
- key
2001
- });
2002
- }
2107
+ if (typeof y !== 'number') {
2108
+ return undefined;
2003
2109
  }
2004
- return patches;
2110
+ if (typeof width !== 'number') {
2111
+ return undefined;
2112
+ }
2113
+ if (typeof height !== 'number') {
2114
+ return undefined;
2115
+ }
2116
+ return {
2117
+ height,
2118
+ width,
2119
+ x,
2120
+ y
2121
+ };
2005
2122
  };
2006
2123
 
2007
- const treeToArray = node => {
2008
- const result = [node.node];
2009
- for (const child of node.children) {
2010
- result.push(...treeToArray(child));
2124
+ const getSavedSelectedSessionId = savedState => {
2125
+ if (!isObject(savedState)) {
2126
+ return undefined;
2011
2127
  }
2012
- return result;
2128
+ const {
2129
+ selectedSessionId
2130
+ } = savedState;
2131
+ if (typeof selectedSessionId !== 'string') {
2132
+ return undefined;
2133
+ }
2134
+ return selectedSessionId;
2013
2135
  };
2014
2136
 
2015
- const diffChildren = (oldChildren, newChildren, patches) => {
2016
- const maxLength = Math.max(oldChildren.length, newChildren.length);
2017
- // Track where we are: -1 means at parent, >= 0 means at child index
2018
- let currentChildIndex = -1;
2019
- // Collect indices of children to remove (we'll add these patches at the end in reverse order)
2020
- const indicesToRemove = [];
2021
- for (let i = 0; i < maxLength; i++) {
2022
- const oldNode = oldChildren[i];
2023
- const newNode = newChildren[i];
2024
- if (!oldNode && !newNode) {
2025
- continue;
2026
- }
2027
- if (!oldNode) {
2028
- // Add new node - we should be at the parent
2029
- if (currentChildIndex >= 0) {
2030
- // Navigate back to parent
2031
- patches.push({
2032
- type: NavigateParent
2033
- });
2034
- currentChildIndex = -1;
2035
- }
2036
- // Flatten the entire subtree so renderInternal can handle it
2037
- const flatNodes = treeToArray(newNode);
2038
- patches.push({
2039
- type: Add,
2040
- nodes: flatNodes
2041
- });
2042
- } else if (newNode) {
2043
- // Compare nodes to see if we need any patches
2044
- const nodePatches = compareNodes(oldNode.node, newNode.node);
2045
- // If nodePatches is null, the node types are incompatible - need to replace
2046
- if (nodePatches === null) {
2047
- // Navigate to this child
2048
- if (currentChildIndex === -1) {
2049
- patches.push({
2050
- type: NavigateChild,
2051
- index: i
2052
- });
2053
- currentChildIndex = i;
2054
- } else if (currentChildIndex !== i) {
2055
- patches.push({
2056
- type: NavigateSibling,
2057
- index: i
2058
- });
2059
- currentChildIndex = i;
2060
- }
2061
- // Replace the entire subtree
2062
- const flatNodes = treeToArray(newNode);
2063
- patches.push({
2064
- type: Replace,
2065
- nodes: flatNodes
2066
- });
2067
- // After replace, we're at the new element (same position)
2068
- continue;
2069
- }
2070
- // Check if we need to recurse into children
2071
- const hasChildrenToCompare = oldNode.children.length > 0 || newNode.children.length > 0;
2072
- // Only navigate to this element if we need to do something
2073
- if (nodePatches.length > 0 || hasChildrenToCompare) {
2074
- // Navigate to this child if not already there
2075
- if (currentChildIndex === -1) {
2076
- patches.push({
2077
- type: NavigateChild,
2078
- index: i
2079
- });
2080
- currentChildIndex = i;
2081
- } else if (currentChildIndex !== i) {
2082
- patches.push({
2083
- type: NavigateSibling,
2084
- index: i
2085
- });
2086
- currentChildIndex = i;
2087
- }
2088
- // Apply node patches (these apply to the current element, not children)
2089
- if (nodePatches.length > 0) {
2090
- patches.push(...nodePatches);
2091
- }
2092
- // Compare children recursively
2093
- if (hasChildrenToCompare) {
2094
- diffChildren(oldNode.children, newNode.children, patches);
2095
- }
2096
- }
2097
- } else {
2098
- // Remove old node - collect the index for later removal
2099
- indicesToRemove.push(i);
2100
- }
2101
- }
2102
- // Navigate back to parent if we ended at a child
2103
- if (currentChildIndex >= 0) {
2104
- patches.push({
2105
- type: NavigateParent
2106
- });
2107
- currentChildIndex = -1;
2137
+ const getSavedSessions = savedState => {
2138
+ if (!isObject(savedState)) {
2139
+ return undefined;
2108
2140
  }
2109
- // Add remove patches in reverse order (highest index first)
2110
- // This ensures indices remain valid as we remove
2111
- for (let j = indicesToRemove.length - 1; j >= 0; j--) {
2112
- patches.push({
2113
- type: RemoveChild,
2114
- index: indicesToRemove[j]
2115
- });
2141
+ const {
2142
+ sessions
2143
+ } = savedState;
2144
+ if (!Array.isArray(sessions)) {
2145
+ return undefined;
2116
2146
  }
2147
+ return sessions;
2117
2148
  };
2118
- const diffTrees = (oldTree, newTree, patches, path) => {
2119
- // At the root level (path.length === 0), we're already AT the element
2120
- // So we compare the root node directly, then compare its children
2121
- if (path.length === 0 && oldTree.length === 1 && newTree.length === 1) {
2122
- const oldNode = oldTree[0];
2123
- const newNode = newTree[0];
2124
- // Compare root nodes
2125
- const nodePatches = compareNodes(oldNode.node, newNode.node);
2126
- // If nodePatches is null, the root node types are incompatible - need to replace
2127
- if (nodePatches === null) {
2128
- const flatNodes = treeToArray(newNode);
2129
- patches.push({
2130
- type: Replace,
2131
- nodes: flatNodes
2132
- });
2133
- return;
2134
- }
2135
- if (nodePatches.length > 0) {
2136
- patches.push(...nodePatches);
2137
- }
2138
- // Compare children
2139
- if (oldNode.children.length > 0 || newNode.children.length > 0) {
2140
- diffChildren(oldNode.children, newNode.children, patches);
2141
- }
2142
- } else {
2143
- // Non-root level or multiple root elements - use the regular comparison
2144
- diffChildren(oldTree, newTree, patches);
2145
- }
2149
+
2150
+ const loadContent = async (state, savedState) => {
2151
+ const savedBounds = getSavedBounds(savedState);
2152
+ const sessions = getSavedSessions(savedState) || state.sessions;
2153
+ const preferredSessionId = getSavedSelectedSessionId(savedState) || state.selectedSessionId;
2154
+ const selectedSessionId = sessions.some(session => session.id === preferredSessionId) ? preferredSessionId : sessions[0]?.id || '';
2155
+ const viewMode = sessions.length === 0 ? 'list' : state.viewMode === 'detail' ? 'detail' : 'list';
2156
+ return {
2157
+ ...state,
2158
+ ...savedBounds,
2159
+ initial: false,
2160
+ selectedSessionId,
2161
+ sessions,
2162
+ viewMode
2163
+ };
2146
2164
  };
2147
2165
 
2148
- const removeTrailingNavigationPatches = patches => {
2149
- // Find the last non-navigation patch
2150
- let lastNonNavigationIndex = -1;
2151
- for (let i = patches.length - 1; i >= 0; i--) {
2152
- const patch = patches[i];
2153
- if (patch.type !== NavigateChild && patch.type !== NavigateParent && patch.type !== NavigateSibling) {
2154
- lastNonNavigationIndex = i;
2155
- break;
2166
+ const openMockSession = (state, mockSessionId, mockChatMessages) => {
2167
+ if (!mockSessionId) {
2168
+ return state;
2169
+ }
2170
+ const existingSession = state.sessions.find(session => session.id === mockSessionId);
2171
+ const sessions = existingSession ? state.sessions.map(session => {
2172
+ if (session.id !== mockSessionId) {
2173
+ return session;
2156
2174
  }
2175
+ return {
2176
+ ...session,
2177
+ messages: mockChatMessages
2178
+ };
2179
+ }) : [...state.sessions, {
2180
+ id: mockSessionId,
2181
+ messages: mockChatMessages,
2182
+ title: mockSessionId
2183
+ }];
2184
+ return {
2185
+ ...state,
2186
+ renamingSessionId: '',
2187
+ selectedSessionId: mockSessionId,
2188
+ sessions,
2189
+ viewMode: 'detail'
2190
+ };
2191
+ };
2192
+
2193
+ // TODO render things like scrollbar height,scrollbar offset, textarea height,
2194
+ // list height
2195
+ const css = `
2196
+ `;
2197
+ const renderCss = (oldState, newState) => {
2198
+ return [SetCss, newState.uid, css];
2199
+ };
2200
+
2201
+ const getFocusSelector = focus => {
2202
+ switch (focus) {
2203
+ case 'composer':
2204
+ case 'input':
2205
+ return '[name="composer"]';
2206
+ case 'header':
2207
+ return '[name="create-session"]';
2208
+ case 'list':
2209
+ return '[name^="session:"]';
2210
+ case 'send-button':
2211
+ return '[name="send"]';
2212
+ default:
2213
+ return '[name="composer"]';
2157
2214
  }
2158
- // Return patches up to and including the last non-navigation patch
2159
- return lastNonNavigationIndex === -1 ? [] : patches.slice(0, lastNonNavigationIndex + 1);
2215
+ };
2216
+ const renderFocus = (oldState, newState) => {
2217
+ const selector = getFocusSelector(newState.focus);
2218
+ return [FocusSelector, selector];
2160
2219
  };
2161
2220
 
2162
- const diffTree = (oldNodes, newNodes) => {
2163
- // Step 1: Convert flat arrays to tree structures
2164
- const oldTree = arrayToTree(oldNodes);
2165
- const newTree = arrayToTree(newNodes);
2166
- // Step 3: Compare the trees
2167
- const patches = [];
2168
- diffTrees(oldTree, newTree, patches, []);
2169
- // Remove trailing navigation patches since they serve no purpose
2170
- return removeTrailingNavigationPatches(patches);
2221
+ const renderFocusContext = (oldState, newState) => {
2222
+ const when = 2344;
2223
+ return [SetFocusContext, newState.uid, when];
2171
2224
  };
2172
2225
 
2173
2226
  const ChatActions = 'ChatActions';
@@ -2464,6 +2517,8 @@ const getRenderer = diffType => {
2464
2517
  return renderCss;
2465
2518
  case RenderFocus:
2466
2519
  return renderFocus;
2520
+ case RenderFocusContext:
2521
+ return renderFocusContext;
2467
2522
  case RenderIncremental:
2468
2523
  return renderIncremental;
2469
2524
  case RenderItems:
@@ -2538,6 +2593,10 @@ const renderEventListeners = () => {
2538
2593
  }];
2539
2594
  };
2540
2595
 
2596
+ const rerender = state => {
2597
+ return structuredClone(state);
2598
+ };
2599
+
2541
2600
  const reset = async state => {
2542
2601
  return {
2543
2602
  ...state,
@@ -2608,6 +2667,7 @@ const commandMap = {
2608
2667
  'Chat.clearInput': wrapCommand(clearInput),
2609
2668
  'Chat.create': create,
2610
2669
  'Chat.diff2': diff2,
2670
+ 'Chat.enterNewLine': wrapCommand(handleNewline),
2611
2671
  'Chat.getCommandIds': getCommandIds,
2612
2672
  'Chat.getKeyBindings': getKeyBindings,
2613
2673
  'Chat.handleChatListContextMenu': handleChatListContextMenu,
@@ -2625,8 +2685,10 @@ const commandMap = {
2625
2685
  'Chat.initialize': initialize,
2626
2686
  'Chat.loadContent': wrapCommand(loadContent),
2627
2687
  'Chat.loadContent2': wrapCommand(loadContent),
2688
+ 'Chat.openMockSession': wrapCommand(openMockSession),
2628
2689
  'Chat.render2': render2,
2629
2690
  'Chat.renderEventListeners': renderEventListeners,
2691
+ 'Chat.rerender': wrapCommand(rerender),
2630
2692
  'Chat.reset': wrapCommand(reset),
2631
2693
  'Chat.resize': wrapCommand(resize),
2632
2694
  'Chat.saveState': wrapGetter(saveState),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/chat-view",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "Chat View Worker",
5
5
  "repository": {
6
6
  "type": "git",
@@ -11,6 +11,7 @@
11
11
  "type": "module",
12
12
  "main": "dist/chatViewWorkerMain.js",
13
13
  "dependencies": {
14
+ "@lvce-editor/constants": "^4.1.0",
14
15
  "@lvce-editor/i18n": "^2.1.0",
15
16
  "@lvce-editor/virtual-dom-worker": "^8.9.0"
16
17
  }