@lvce-editor/chat-view 1.7.0 → 1.9.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.
- package/dist/chatViewWorkerMain.js +812 -412
- package/package.json +2 -1
|
@@ -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
|
}) => {
|
|
@@ -1224,7 +1201,7 @@ const assistant = i18nString('Assistant');
|
|
|
1224
1201
|
const composePlaceholder = i18nString('Type your message. Enter to send, Shift+Enter for newline.');
|
|
1225
1202
|
const sendMessage = i18nString('Send message');
|
|
1226
1203
|
const send = i18nString('Send');
|
|
1227
|
-
const deleteChatSession = i18nString('Delete chat session');
|
|
1204
|
+
const deleteChatSession$1 = i18nString('Delete chat session');
|
|
1228
1205
|
const defaultSessionTitle = i18nString('Chat 1');
|
|
1229
1206
|
const dummyChatA = i18nString('Dummy Chat A');
|
|
1230
1207
|
const dummyChatB = i18nString('Dummy Chat B');
|
|
@@ -1232,6 +1209,7 @@ const dummyChatC = i18nString('Dummy Chat C');
|
|
|
1232
1209
|
|
|
1233
1210
|
const createDefaultState = () => {
|
|
1234
1211
|
const defaultSessionId = 'session-1';
|
|
1212
|
+
const defaultModelId = 'test';
|
|
1235
1213
|
return {
|
|
1236
1214
|
assetDir: '',
|
|
1237
1215
|
composerValue: '',
|
|
@@ -1244,9 +1222,23 @@ const createDefaultState = () => {
|
|
|
1244
1222
|
inputSource: 'script',
|
|
1245
1223
|
lastSubmittedSessionId: '',
|
|
1246
1224
|
listItemHeight: 40,
|
|
1225
|
+
models: [{
|
|
1226
|
+
id: defaultModelId,
|
|
1227
|
+
name: 'test'
|
|
1228
|
+
}, {
|
|
1229
|
+
id: 'codex-5.3',
|
|
1230
|
+
name: 'Codex 5.3'
|
|
1231
|
+
}, {
|
|
1232
|
+
id: 'claude-code',
|
|
1233
|
+
name: 'Claude Code'
|
|
1234
|
+
}, {
|
|
1235
|
+
id: 'claude-haiku',
|
|
1236
|
+
name: 'Claude Haiku'
|
|
1237
|
+
}],
|
|
1247
1238
|
nextMessageId: 1,
|
|
1248
1239
|
platform: 0,
|
|
1249
1240
|
renamingSessionId: '',
|
|
1241
|
+
selectedModelId: defaultModelId,
|
|
1250
1242
|
selectedSessionId: defaultSessionId,
|
|
1251
1243
|
sessions: [{
|
|
1252
1244
|
id: defaultSessionId,
|
|
@@ -1297,11 +1289,12 @@ const diffFocus = (oldState, newState) => {
|
|
|
1297
1289
|
};
|
|
1298
1290
|
|
|
1299
1291
|
const isEqual = (oldState, newState) => {
|
|
1300
|
-
return oldState.composerValue === newState.composerValue && oldState.initial === newState.initial && oldState.renamingSessionId === newState.renamingSessionId && oldState.selectedSessionId === newState.selectedSessionId && oldState.sessions === newState.sessions && oldState.viewMode === newState.viewMode;
|
|
1292
|
+
return oldState.composerValue === newState.composerValue && oldState.initial === newState.initial && oldState.renamingSessionId === newState.renamingSessionId && oldState.selectedModelId === newState.selectedModelId && oldState.selectedSessionId === newState.selectedSessionId && oldState.sessions === newState.sessions && oldState.viewMode === newState.viewMode;
|
|
1301
1293
|
};
|
|
1302
1294
|
|
|
1303
1295
|
const RenderItems = 4;
|
|
1304
1296
|
const RenderFocus = 6;
|
|
1297
|
+
const RenderFocusContext = 7;
|
|
1305
1298
|
const RenderValue = 8;
|
|
1306
1299
|
const RenderCss = 10;
|
|
1307
1300
|
const RenderIncremental = 11;
|
|
@@ -1324,44 +1317,537 @@ const diff = (oldState, newState) => {
|
|
|
1324
1317
|
diffResult.push(numbers[i]);
|
|
1325
1318
|
}
|
|
1326
1319
|
}
|
|
1327
|
-
return diffResult;
|
|
1328
|
-
};
|
|
1320
|
+
return diffResult;
|
|
1321
|
+
};
|
|
1322
|
+
|
|
1323
|
+
const diff2 = uid => {
|
|
1324
|
+
const {
|
|
1325
|
+
newState,
|
|
1326
|
+
oldState
|
|
1327
|
+
} = get(uid);
|
|
1328
|
+
const result = diff(oldState, newState);
|
|
1329
|
+
return result;
|
|
1330
|
+
};
|
|
1331
|
+
|
|
1332
|
+
const ClientX = 'event.clientX';
|
|
1333
|
+
const ClientY = 'event.clientY';
|
|
1334
|
+
const Key = 'event.key';
|
|
1335
|
+
const ShiftKey = 'event.shiftKey';
|
|
1336
|
+
const TargetName = 'event.target.name';
|
|
1337
|
+
const TargetValue = 'event.target.value';
|
|
1338
|
+
|
|
1339
|
+
const FocusSelector = 'Viewlet.focusSelector';
|
|
1340
|
+
const SetCss = 'Viewlet.setCss';
|
|
1341
|
+
const SetDom2 = 'Viewlet.setDom2';
|
|
1342
|
+
const SetFocusContext = 'Viewlet.setFocusContext';
|
|
1343
|
+
const SetValueByName = 'Viewlet.setValueByName';
|
|
1344
|
+
const SetPatches = 'Viewlet.setPatches';
|
|
1345
|
+
|
|
1346
|
+
const FocusChatInput = 8000;
|
|
1347
|
+
|
|
1348
|
+
const Button$2 = 'button';
|
|
1349
|
+
|
|
1350
|
+
const Button$1 = 1;
|
|
1351
|
+
const Div = 4;
|
|
1352
|
+
const Span = 8;
|
|
1353
|
+
const Text = 12;
|
|
1354
|
+
const P = 50;
|
|
1355
|
+
const TextArea = 62;
|
|
1356
|
+
const Select$1 = 63;
|
|
1357
|
+
const Option$1 = 64;
|
|
1358
|
+
const Reference = 100;
|
|
1359
|
+
|
|
1360
|
+
const Enter = 3;
|
|
1361
|
+
|
|
1362
|
+
const Shift = 1 << 10 >>> 0;
|
|
1363
|
+
|
|
1364
|
+
const mergeClassNames = (...classNames) => {
|
|
1365
|
+
return classNames.filter(Boolean).join(' ');
|
|
1366
|
+
};
|
|
1367
|
+
|
|
1368
|
+
const text = data => {
|
|
1369
|
+
return {
|
|
1370
|
+
childCount: 0,
|
|
1371
|
+
text: data,
|
|
1372
|
+
type: Text
|
|
1373
|
+
};
|
|
1374
|
+
};
|
|
1375
|
+
|
|
1376
|
+
const SetText = 1;
|
|
1377
|
+
const Replace = 2;
|
|
1378
|
+
const SetAttribute = 3;
|
|
1379
|
+
const RemoveAttribute = 4;
|
|
1380
|
+
const Add = 6;
|
|
1381
|
+
const NavigateChild = 7;
|
|
1382
|
+
const NavigateParent = 8;
|
|
1383
|
+
const RemoveChild = 9;
|
|
1384
|
+
const NavigateSibling = 10;
|
|
1385
|
+
const SetReferenceNodeUid = 11;
|
|
1386
|
+
|
|
1387
|
+
const isKey = key => {
|
|
1388
|
+
return key !== 'type' && key !== 'childCount';
|
|
1389
|
+
};
|
|
1390
|
+
|
|
1391
|
+
const getKeys = node => {
|
|
1392
|
+
const keys = Object.keys(node).filter(isKey);
|
|
1393
|
+
return keys;
|
|
1394
|
+
};
|
|
1395
|
+
|
|
1396
|
+
const arrayToTree = nodes => {
|
|
1397
|
+
const result = [];
|
|
1398
|
+
let i = 0;
|
|
1399
|
+
while (i < nodes.length) {
|
|
1400
|
+
const node = nodes[i];
|
|
1401
|
+
const {
|
|
1402
|
+
children,
|
|
1403
|
+
nodesConsumed
|
|
1404
|
+
} = getChildrenWithCount(nodes, i + 1, node.childCount || 0);
|
|
1405
|
+
result.push({
|
|
1406
|
+
node,
|
|
1407
|
+
children
|
|
1408
|
+
});
|
|
1409
|
+
i += 1 + nodesConsumed;
|
|
1410
|
+
}
|
|
1411
|
+
return result;
|
|
1412
|
+
};
|
|
1413
|
+
const getChildrenWithCount = (nodes, startIndex, childCount) => {
|
|
1414
|
+
if (childCount === 0) {
|
|
1415
|
+
return {
|
|
1416
|
+
children: [],
|
|
1417
|
+
nodesConsumed: 0
|
|
1418
|
+
};
|
|
1419
|
+
}
|
|
1420
|
+
const children = [];
|
|
1421
|
+
let i = startIndex;
|
|
1422
|
+
let remaining = childCount;
|
|
1423
|
+
let totalConsumed = 0;
|
|
1424
|
+
while (remaining > 0 && i < nodes.length) {
|
|
1425
|
+
const node = nodes[i];
|
|
1426
|
+
const nodeChildCount = node.childCount || 0;
|
|
1427
|
+
const {
|
|
1428
|
+
children: nodeChildren,
|
|
1429
|
+
nodesConsumed
|
|
1430
|
+
} = getChildrenWithCount(nodes, i + 1, nodeChildCount);
|
|
1431
|
+
children.push({
|
|
1432
|
+
node,
|
|
1433
|
+
children: nodeChildren
|
|
1434
|
+
});
|
|
1435
|
+
const nodeSize = 1 + nodesConsumed;
|
|
1436
|
+
i += nodeSize;
|
|
1437
|
+
totalConsumed += nodeSize;
|
|
1438
|
+
remaining--;
|
|
1439
|
+
}
|
|
1440
|
+
return {
|
|
1441
|
+
children,
|
|
1442
|
+
nodesConsumed: totalConsumed
|
|
1443
|
+
};
|
|
1444
|
+
};
|
|
1445
|
+
|
|
1446
|
+
const compareNodes = (oldNode, newNode) => {
|
|
1447
|
+
const patches = [];
|
|
1448
|
+
// Check if node type changed - return null to signal incompatible nodes
|
|
1449
|
+
// (caller should handle this with a Replace operation)
|
|
1450
|
+
if (oldNode.type !== newNode.type) {
|
|
1451
|
+
return null;
|
|
1452
|
+
}
|
|
1453
|
+
// Handle reference nodes - special handling for uid changes
|
|
1454
|
+
if (oldNode.type === Reference) {
|
|
1455
|
+
if (oldNode.uid !== newNode.uid) {
|
|
1456
|
+
patches.push({
|
|
1457
|
+
type: SetReferenceNodeUid,
|
|
1458
|
+
uid: newNode.uid
|
|
1459
|
+
});
|
|
1460
|
+
}
|
|
1461
|
+
return patches;
|
|
1462
|
+
}
|
|
1463
|
+
// Handle text nodes
|
|
1464
|
+
if (oldNode.type === Text && newNode.type === Text) {
|
|
1465
|
+
if (oldNode.text !== newNode.text) {
|
|
1466
|
+
patches.push({
|
|
1467
|
+
type: SetText,
|
|
1468
|
+
value: newNode.text
|
|
1469
|
+
});
|
|
1470
|
+
}
|
|
1471
|
+
return patches;
|
|
1472
|
+
}
|
|
1473
|
+
// Compare attributes
|
|
1474
|
+
const oldKeys = getKeys(oldNode);
|
|
1475
|
+
const newKeys = getKeys(newNode);
|
|
1476
|
+
// Check for attribute changes
|
|
1477
|
+
for (const key of newKeys) {
|
|
1478
|
+
if (oldNode[key] !== newNode[key]) {
|
|
1479
|
+
patches.push({
|
|
1480
|
+
type: SetAttribute,
|
|
1481
|
+
key,
|
|
1482
|
+
value: newNode[key]
|
|
1483
|
+
});
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
// Check for removed attributes
|
|
1487
|
+
for (const key of oldKeys) {
|
|
1488
|
+
if (!(key in newNode)) {
|
|
1489
|
+
patches.push({
|
|
1490
|
+
type: RemoveAttribute,
|
|
1491
|
+
key
|
|
1492
|
+
});
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
return patches;
|
|
1496
|
+
};
|
|
1497
|
+
|
|
1498
|
+
const treeToArray = node => {
|
|
1499
|
+
const result = [node.node];
|
|
1500
|
+
for (const child of node.children) {
|
|
1501
|
+
result.push(...treeToArray(child));
|
|
1502
|
+
}
|
|
1503
|
+
return result;
|
|
1504
|
+
};
|
|
1505
|
+
|
|
1506
|
+
const diffChildren = (oldChildren, newChildren, patches) => {
|
|
1507
|
+
const maxLength = Math.max(oldChildren.length, newChildren.length);
|
|
1508
|
+
// Track where we are: -1 means at parent, >= 0 means at child index
|
|
1509
|
+
let currentChildIndex = -1;
|
|
1510
|
+
// Collect indices of children to remove (we'll add these patches at the end in reverse order)
|
|
1511
|
+
const indicesToRemove = [];
|
|
1512
|
+
for (let i = 0; i < maxLength; i++) {
|
|
1513
|
+
const oldNode = oldChildren[i];
|
|
1514
|
+
const newNode = newChildren[i];
|
|
1515
|
+
if (!oldNode && !newNode) {
|
|
1516
|
+
continue;
|
|
1517
|
+
}
|
|
1518
|
+
if (!oldNode) {
|
|
1519
|
+
// Add new node - we should be at the parent
|
|
1520
|
+
if (currentChildIndex >= 0) {
|
|
1521
|
+
// Navigate back to parent
|
|
1522
|
+
patches.push({
|
|
1523
|
+
type: NavigateParent
|
|
1524
|
+
});
|
|
1525
|
+
currentChildIndex = -1;
|
|
1526
|
+
}
|
|
1527
|
+
// Flatten the entire subtree so renderInternal can handle it
|
|
1528
|
+
const flatNodes = treeToArray(newNode);
|
|
1529
|
+
patches.push({
|
|
1530
|
+
type: Add,
|
|
1531
|
+
nodes: flatNodes
|
|
1532
|
+
});
|
|
1533
|
+
} else if (newNode) {
|
|
1534
|
+
// Compare nodes to see if we need any patches
|
|
1535
|
+
const nodePatches = compareNodes(oldNode.node, newNode.node);
|
|
1536
|
+
// If nodePatches is null, the node types are incompatible - need to replace
|
|
1537
|
+
if (nodePatches === null) {
|
|
1538
|
+
// Navigate to this child
|
|
1539
|
+
if (currentChildIndex === -1) {
|
|
1540
|
+
patches.push({
|
|
1541
|
+
type: NavigateChild,
|
|
1542
|
+
index: i
|
|
1543
|
+
});
|
|
1544
|
+
currentChildIndex = i;
|
|
1545
|
+
} else if (currentChildIndex !== i) {
|
|
1546
|
+
patches.push({
|
|
1547
|
+
type: NavigateSibling,
|
|
1548
|
+
index: i
|
|
1549
|
+
});
|
|
1550
|
+
currentChildIndex = i;
|
|
1551
|
+
}
|
|
1552
|
+
// Replace the entire subtree
|
|
1553
|
+
const flatNodes = treeToArray(newNode);
|
|
1554
|
+
patches.push({
|
|
1555
|
+
type: Replace,
|
|
1556
|
+
nodes: flatNodes
|
|
1557
|
+
});
|
|
1558
|
+
// After replace, we're at the new element (same position)
|
|
1559
|
+
continue;
|
|
1560
|
+
}
|
|
1561
|
+
// Check if we need to recurse into children
|
|
1562
|
+
const hasChildrenToCompare = oldNode.children.length > 0 || newNode.children.length > 0;
|
|
1563
|
+
// Only navigate to this element if we need to do something
|
|
1564
|
+
if (nodePatches.length > 0 || hasChildrenToCompare) {
|
|
1565
|
+
// Navigate to this child if not already there
|
|
1566
|
+
if (currentChildIndex === -1) {
|
|
1567
|
+
patches.push({
|
|
1568
|
+
type: NavigateChild,
|
|
1569
|
+
index: i
|
|
1570
|
+
});
|
|
1571
|
+
currentChildIndex = i;
|
|
1572
|
+
} else if (currentChildIndex !== i) {
|
|
1573
|
+
patches.push({
|
|
1574
|
+
type: NavigateSibling,
|
|
1575
|
+
index: i
|
|
1576
|
+
});
|
|
1577
|
+
currentChildIndex = i;
|
|
1578
|
+
}
|
|
1579
|
+
// Apply node patches (these apply to the current element, not children)
|
|
1580
|
+
if (nodePatches.length > 0) {
|
|
1581
|
+
patches.push(...nodePatches);
|
|
1582
|
+
}
|
|
1583
|
+
// Compare children recursively
|
|
1584
|
+
if (hasChildrenToCompare) {
|
|
1585
|
+
diffChildren(oldNode.children, newNode.children, patches);
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
} else {
|
|
1589
|
+
// Remove old node - collect the index for later removal
|
|
1590
|
+
indicesToRemove.push(i);
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
// Navigate back to parent if we ended at a child
|
|
1594
|
+
if (currentChildIndex >= 0) {
|
|
1595
|
+
patches.push({
|
|
1596
|
+
type: NavigateParent
|
|
1597
|
+
});
|
|
1598
|
+
currentChildIndex = -1;
|
|
1599
|
+
}
|
|
1600
|
+
// Add remove patches in reverse order (highest index first)
|
|
1601
|
+
// This ensures indices remain valid as we remove
|
|
1602
|
+
for (let j = indicesToRemove.length - 1; j >= 0; j--) {
|
|
1603
|
+
patches.push({
|
|
1604
|
+
type: RemoveChild,
|
|
1605
|
+
index: indicesToRemove[j]
|
|
1606
|
+
});
|
|
1607
|
+
}
|
|
1608
|
+
};
|
|
1609
|
+
const diffTrees = (oldTree, newTree, patches, path) => {
|
|
1610
|
+
// At the root level (path.length === 0), we're already AT the element
|
|
1611
|
+
// So we compare the root node directly, then compare its children
|
|
1612
|
+
if (path.length === 0 && oldTree.length === 1 && newTree.length === 1) {
|
|
1613
|
+
const oldNode = oldTree[0];
|
|
1614
|
+
const newNode = newTree[0];
|
|
1615
|
+
// Compare root nodes
|
|
1616
|
+
const nodePatches = compareNodes(oldNode.node, newNode.node);
|
|
1617
|
+
// If nodePatches is null, the root node types are incompatible - need to replace
|
|
1618
|
+
if (nodePatches === null) {
|
|
1619
|
+
const flatNodes = treeToArray(newNode);
|
|
1620
|
+
patches.push({
|
|
1621
|
+
type: Replace,
|
|
1622
|
+
nodes: flatNodes
|
|
1623
|
+
});
|
|
1624
|
+
return;
|
|
1625
|
+
}
|
|
1626
|
+
if (nodePatches.length > 0) {
|
|
1627
|
+
patches.push(...nodePatches);
|
|
1628
|
+
}
|
|
1629
|
+
// Compare children
|
|
1630
|
+
if (oldNode.children.length > 0 || newNode.children.length > 0) {
|
|
1631
|
+
diffChildren(oldNode.children, newNode.children, patches);
|
|
1632
|
+
}
|
|
1633
|
+
} else {
|
|
1634
|
+
// Non-root level or multiple root elements - use the regular comparison
|
|
1635
|
+
diffChildren(oldTree, newTree, patches);
|
|
1636
|
+
}
|
|
1637
|
+
};
|
|
1638
|
+
|
|
1639
|
+
const removeTrailingNavigationPatches = patches => {
|
|
1640
|
+
// Find the last non-navigation patch
|
|
1641
|
+
let lastNonNavigationIndex = -1;
|
|
1642
|
+
for (let i = patches.length - 1; i >= 0; i--) {
|
|
1643
|
+
const patch = patches[i];
|
|
1644
|
+
if (patch.type !== NavigateChild && patch.type !== NavigateParent && patch.type !== NavigateSibling) {
|
|
1645
|
+
lastNonNavigationIndex = i;
|
|
1646
|
+
break;
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
// Return patches up to and including the last non-navigation patch
|
|
1650
|
+
return lastNonNavigationIndex === -1 ? [] : patches.slice(0, lastNonNavigationIndex + 1);
|
|
1651
|
+
};
|
|
1652
|
+
|
|
1653
|
+
const diffTree = (oldNodes, newNodes) => {
|
|
1654
|
+
// Step 1: Convert flat arrays to tree structures
|
|
1655
|
+
const oldTree = arrayToTree(oldNodes);
|
|
1656
|
+
const newTree = arrayToTree(newNodes);
|
|
1657
|
+
// Step 3: Compare the trees
|
|
1658
|
+
const patches = [];
|
|
1659
|
+
diffTrees(oldTree, newTree, patches, []);
|
|
1660
|
+
// Remove trailing navigation patches since they serve no purpose
|
|
1661
|
+
return removeTrailingNavigationPatches(patches);
|
|
1662
|
+
};
|
|
1663
|
+
|
|
1664
|
+
const getKeyBindings = () => {
|
|
1665
|
+
return [{
|
|
1666
|
+
command: 'Chat.handleSubmit',
|
|
1667
|
+
key: Enter,
|
|
1668
|
+
when: FocusChatInput
|
|
1669
|
+
}, {
|
|
1670
|
+
command: 'Chat.enterNewLine',
|
|
1671
|
+
key: Shift | Enter,
|
|
1672
|
+
when: FocusChatInput
|
|
1673
|
+
}];
|
|
1674
|
+
};
|
|
1675
|
+
|
|
1676
|
+
const SESSION_PREFIX$1 = 'session:';
|
|
1677
|
+
const CHAT_LIST_ITEM_CONTEXT_MENU = 'ChatListItemContextMenu';
|
|
1678
|
+
const handleChatListContextMenu = async (name, x, y) => {
|
|
1679
|
+
if (!name || !name.startsWith(SESSION_PREFIX$1)) {
|
|
1680
|
+
return;
|
|
1681
|
+
}
|
|
1682
|
+
const sessionId = name.slice(SESSION_PREFIX$1.length);
|
|
1683
|
+
// @ts-ignore
|
|
1684
|
+
await invoke('ContextMenu.show', x, y, CHAT_LIST_ITEM_CONTEXT_MENU, sessionId);
|
|
1685
|
+
};
|
|
1686
|
+
|
|
1687
|
+
const LVCE_CHAT_SESSIONS_DB_NAME = 'lvce-chat-view-sessions';
|
|
1688
|
+
const LVCE_CHAT_SESSIONS_DB_VERSION = 1;
|
|
1689
|
+
const LVCE_CHAT_SESSIONS_STORE = 'chat-sessions';
|
|
1690
|
+
const toError = error => {
|
|
1691
|
+
if (error instanceof Error) {
|
|
1692
|
+
return error;
|
|
1693
|
+
}
|
|
1694
|
+
return new Error('IndexedDB request failed');
|
|
1695
|
+
};
|
|
1696
|
+
const requestToPromise = async createRequest => {
|
|
1697
|
+
const request = createRequest();
|
|
1698
|
+
return new Promise((resolve, reject) => {
|
|
1699
|
+
request.addEventListener('success', () => {
|
|
1700
|
+
resolve(request.result);
|
|
1701
|
+
});
|
|
1702
|
+
request.addEventListener('error', () => {
|
|
1703
|
+
reject(toError(request.error));
|
|
1704
|
+
});
|
|
1705
|
+
});
|
|
1706
|
+
};
|
|
1707
|
+
const transactionToPromise = async createTransaction => {
|
|
1708
|
+
const transaction = createTransaction();
|
|
1709
|
+
return new Promise((resolve, reject) => {
|
|
1710
|
+
transaction.addEventListener('complete', () => {
|
|
1711
|
+
resolve();
|
|
1712
|
+
});
|
|
1713
|
+
transaction.addEventListener('error', () => {
|
|
1714
|
+
reject(toError(transaction.error));
|
|
1715
|
+
});
|
|
1716
|
+
transaction.addEventListener('abort', () => {
|
|
1717
|
+
reject(toError(transaction.error));
|
|
1718
|
+
});
|
|
1719
|
+
});
|
|
1720
|
+
};
|
|
1721
|
+
const openSessionsDatabase = async () => {
|
|
1722
|
+
const request = indexedDB.open(LVCE_CHAT_SESSIONS_DB_NAME, LVCE_CHAT_SESSIONS_DB_VERSION);
|
|
1723
|
+
request.addEventListener('upgradeneeded', () => {
|
|
1724
|
+
const database = request.result;
|
|
1725
|
+
if (!database.objectStoreNames.contains(LVCE_CHAT_SESSIONS_STORE)) {
|
|
1726
|
+
database.createObjectStore(LVCE_CHAT_SESSIONS_STORE, {
|
|
1727
|
+
keyPath: 'id'
|
|
1728
|
+
});
|
|
1729
|
+
}
|
|
1730
|
+
});
|
|
1731
|
+
return requestToPromise(() => request);
|
|
1732
|
+
};
|
|
1733
|
+
class IndexedDbChatSessionStorage {
|
|
1734
|
+
async getDatabase() {
|
|
1735
|
+
if (!this.databasePromise) {
|
|
1736
|
+
this.databasePromise = openSessionsDatabase();
|
|
1737
|
+
}
|
|
1738
|
+
return this.databasePromise;
|
|
1739
|
+
}
|
|
1740
|
+
async clear() {
|
|
1741
|
+
const database = await this.getDatabase();
|
|
1742
|
+
const transaction = database.transaction(LVCE_CHAT_SESSIONS_STORE, 'readwrite');
|
|
1743
|
+
const createTransaction = () => transaction;
|
|
1744
|
+
const store = transaction.objectStore(LVCE_CHAT_SESSIONS_STORE);
|
|
1745
|
+
store.clear();
|
|
1746
|
+
await transactionToPromise(createTransaction);
|
|
1747
|
+
}
|
|
1748
|
+
async deleteSession(id) {
|
|
1749
|
+
const database = await this.getDatabase();
|
|
1750
|
+
const transaction = database.transaction(LVCE_CHAT_SESSIONS_STORE, 'readwrite');
|
|
1751
|
+
const createTransaction = () => transaction;
|
|
1752
|
+
const store = transaction.objectStore(LVCE_CHAT_SESSIONS_STORE);
|
|
1753
|
+
store.delete(id);
|
|
1754
|
+
await transactionToPromise(createTransaction);
|
|
1755
|
+
}
|
|
1756
|
+
async getSession(id) {
|
|
1757
|
+
const database = await this.getDatabase();
|
|
1758
|
+
const transaction = database.transaction(LVCE_CHAT_SESSIONS_STORE, 'readonly');
|
|
1759
|
+
const store = transaction.objectStore(LVCE_CHAT_SESSIONS_STORE);
|
|
1760
|
+
const result = await requestToPromise(() => store.get(id));
|
|
1761
|
+
return result;
|
|
1762
|
+
}
|
|
1763
|
+
async listSessions() {
|
|
1764
|
+
const database = await this.getDatabase();
|
|
1765
|
+
const transaction = database.transaction(LVCE_CHAT_SESSIONS_STORE, 'readonly');
|
|
1766
|
+
const store = transaction.objectStore(LVCE_CHAT_SESSIONS_STORE);
|
|
1767
|
+
const result = await requestToPromise(() => store.getAll());
|
|
1768
|
+
return result;
|
|
1769
|
+
}
|
|
1770
|
+
async setSession(session) {
|
|
1771
|
+
const database = await this.getDatabase();
|
|
1772
|
+
const transaction = database.transaction(LVCE_CHAT_SESSIONS_STORE, 'readwrite');
|
|
1773
|
+
const createTransaction = () => transaction;
|
|
1774
|
+
const store = transaction.objectStore(LVCE_CHAT_SESSIONS_STORE);
|
|
1775
|
+
store.put(session);
|
|
1776
|
+
await transactionToPromise(createTransaction);
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1329
1779
|
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
}
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
}
|
|
1780
|
+
class InMemoryChatSessionStorage {
|
|
1781
|
+
sessions = new Map();
|
|
1782
|
+
async clear() {
|
|
1783
|
+
this.sessions.clear();
|
|
1784
|
+
}
|
|
1785
|
+
async deleteSession(id) {
|
|
1786
|
+
this.sessions.delete(id);
|
|
1787
|
+
}
|
|
1788
|
+
async getSession(id) {
|
|
1789
|
+
return this.sessions.get(id);
|
|
1790
|
+
}
|
|
1791
|
+
async listSessions() {
|
|
1792
|
+
return [...this.sessions.values()];
|
|
1793
|
+
}
|
|
1794
|
+
async setSession(session) {
|
|
1795
|
+
this.sessions.set(session.id, session);
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1338
1798
|
|
|
1339
|
-
const
|
|
1340
|
-
|
|
1799
|
+
const createDefaultStorage = () => {
|
|
1800
|
+
if (typeof indexedDB === 'undefined') {
|
|
1801
|
+
return new InMemoryChatSessionStorage();
|
|
1802
|
+
}
|
|
1803
|
+
return new IndexedDbChatSessionStorage();
|
|
1341
1804
|
};
|
|
1342
|
-
|
|
1343
|
-
const
|
|
1344
|
-
const
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1805
|
+
let chatSessionStorage = createDefaultStorage();
|
|
1806
|
+
const listChatSessions = async () => {
|
|
1807
|
+
const sessions = await chatSessionStorage.listSessions();
|
|
1808
|
+
return sessions.map(session => ({
|
|
1809
|
+
id: session.id,
|
|
1810
|
+
messages: [],
|
|
1811
|
+
title: session.title
|
|
1812
|
+
}));
|
|
1813
|
+
};
|
|
1814
|
+
const getChatSession = async id => {
|
|
1815
|
+
const session = await chatSessionStorage.getSession(id);
|
|
1816
|
+
if (!session) {
|
|
1817
|
+
return undefined;
|
|
1348
1818
|
}
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1819
|
+
return {
|
|
1820
|
+
id: session.id,
|
|
1821
|
+
messages: [...session.messages],
|
|
1822
|
+
title: session.title
|
|
1823
|
+
};
|
|
1824
|
+
};
|
|
1825
|
+
const saveChatSession = async session => {
|
|
1826
|
+
await chatSessionStorage.setSession({
|
|
1827
|
+
id: session.id,
|
|
1828
|
+
messages: [...session.messages],
|
|
1829
|
+
title: session.title
|
|
1830
|
+
});
|
|
1831
|
+
};
|
|
1832
|
+
const deleteChatSession = async id => {
|
|
1833
|
+
await chatSessionStorage.deleteSession(id);
|
|
1834
|
+
};
|
|
1835
|
+
const clearChatSessions = async () => {
|
|
1836
|
+
await chatSessionStorage.clear();
|
|
1352
1837
|
};
|
|
1353
1838
|
|
|
1354
1839
|
const generateSessionId = () => {
|
|
1355
1840
|
return crypto.randomUUID();
|
|
1356
1841
|
};
|
|
1357
1842
|
|
|
1358
|
-
const createSession = state => {
|
|
1843
|
+
const createSession = async state => {
|
|
1359
1844
|
const id = generateSessionId();
|
|
1360
1845
|
const session = {
|
|
1361
1846
|
id,
|
|
1362
1847
|
messages: [],
|
|
1363
1848
|
title: `Chat ${state.sessions.length + 1}`
|
|
1364
1849
|
};
|
|
1850
|
+
await saveChatSession(session);
|
|
1365
1851
|
return {
|
|
1366
1852
|
...state,
|
|
1367
1853
|
renamingSessionId: '',
|
|
@@ -1382,7 +1868,7 @@ const getNextSelectedSessionId = (sessions, deletedId) => {
|
|
|
1382
1868
|
return sessions[nextIndex].id;
|
|
1383
1869
|
};
|
|
1384
1870
|
|
|
1385
|
-
const deleteSession = (state, id) => {
|
|
1871
|
+
const deleteSession = async (state, id) => {
|
|
1386
1872
|
const {
|
|
1387
1873
|
renamingSessionId,
|
|
1388
1874
|
sessions
|
|
@@ -1391,6 +1877,7 @@ const deleteSession = (state, id) => {
|
|
|
1391
1877
|
if (filtered.length === sessions.length) {
|
|
1392
1878
|
return state;
|
|
1393
1879
|
}
|
|
1880
|
+
await deleteChatSession(id);
|
|
1394
1881
|
if (filtered.length === 0) {
|
|
1395
1882
|
return {
|
|
1396
1883
|
...state,
|
|
@@ -1400,11 +1887,22 @@ const deleteSession = (state, id) => {
|
|
|
1400
1887
|
viewMode: 'list'
|
|
1401
1888
|
};
|
|
1402
1889
|
}
|
|
1890
|
+
const nextSelectedSessionId = getNextSelectedSessionId(filtered, id);
|
|
1891
|
+
const loadedSession = await getChatSession(nextSelectedSessionId);
|
|
1892
|
+
const hydratedSessions = filtered.map(session => {
|
|
1893
|
+
if (session.id !== nextSelectedSessionId) {
|
|
1894
|
+
return session;
|
|
1895
|
+
}
|
|
1896
|
+
if (!loadedSession) {
|
|
1897
|
+
return session;
|
|
1898
|
+
}
|
|
1899
|
+
return loadedSession;
|
|
1900
|
+
});
|
|
1403
1901
|
return {
|
|
1404
1902
|
...state,
|
|
1405
1903
|
renamingSessionId: renamingSessionId === id ? '' : renamingSessionId,
|
|
1406
|
-
selectedSessionId:
|
|
1407
|
-
sessions:
|
|
1904
|
+
selectedSessionId: nextSelectedSessionId,
|
|
1905
|
+
sessions: hydratedSessions
|
|
1408
1906
|
};
|
|
1409
1907
|
};
|
|
1410
1908
|
|
|
@@ -1458,14 +1956,27 @@ const handleSubmit = async state => {
|
|
|
1458
1956
|
text: userText,
|
|
1459
1957
|
time: userTime
|
|
1460
1958
|
};
|
|
1959
|
+
let workingSessions = sessions;
|
|
1960
|
+
if (viewMode === 'detail') {
|
|
1961
|
+
const loadedSession = await getChatSession(selectedSessionId);
|
|
1962
|
+
if (loadedSession) {
|
|
1963
|
+
workingSessions = sessions.map(session => {
|
|
1964
|
+
if (session.id !== selectedSessionId) {
|
|
1965
|
+
return session;
|
|
1966
|
+
}
|
|
1967
|
+
return loadedSession;
|
|
1968
|
+
});
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1461
1971
|
let optimisticState;
|
|
1462
1972
|
if (viewMode === 'list') {
|
|
1463
1973
|
const newSessionId = generateSessionId();
|
|
1464
1974
|
const newSession = {
|
|
1465
1975
|
id: newSessionId,
|
|
1466
1976
|
messages: [userMessage],
|
|
1467
|
-
title: `Chat ${
|
|
1977
|
+
title: `Chat ${workingSessions.length + 1}`
|
|
1468
1978
|
};
|
|
1979
|
+
await saveChatSession(newSession);
|
|
1469
1980
|
optimisticState = focusInput({
|
|
1470
1981
|
...state,
|
|
1471
1982
|
composerValue: '',
|
|
@@ -1473,11 +1984,11 @@ const handleSubmit = async state => {
|
|
|
1473
1984
|
lastSubmittedSessionId: newSessionId,
|
|
1474
1985
|
nextMessageId: nextMessageId + 1,
|
|
1475
1986
|
selectedSessionId: newSessionId,
|
|
1476
|
-
sessions: [...
|
|
1987
|
+
sessions: [...workingSessions, newSession],
|
|
1477
1988
|
viewMode: 'detail'
|
|
1478
1989
|
});
|
|
1479
1990
|
} else {
|
|
1480
|
-
const updatedSessions =
|
|
1991
|
+
const updatedSessions = workingSessions.map(session => {
|
|
1481
1992
|
if (session.id !== selectedSessionId) {
|
|
1482
1993
|
return session;
|
|
1483
1994
|
}
|
|
@@ -1486,6 +1997,10 @@ const handleSubmit = async state => {
|
|
|
1486
1997
|
messages: [...session.messages, userMessage]
|
|
1487
1998
|
};
|
|
1488
1999
|
});
|
|
2000
|
+
const selectedSession = updatedSessions.find(session => session.id === selectedSessionId);
|
|
2001
|
+
if (selectedSession) {
|
|
2002
|
+
await saveChatSession(selectedSession);
|
|
2003
|
+
}
|
|
1489
2004
|
optimisticState = focusInput({
|
|
1490
2005
|
...state,
|
|
1491
2006
|
composerValue: '',
|
|
@@ -1508,6 +2023,10 @@ const handleSubmit = async state => {
|
|
|
1508
2023
|
messages: [...session.messages, assistantMessage]
|
|
1509
2024
|
};
|
|
1510
2025
|
});
|
|
2026
|
+
const selectedSession = updatedSessions.find(session => session.id === optimisticState.selectedSessionId);
|
|
2027
|
+
if (selectedSession) {
|
|
2028
|
+
await saveChatSession(selectedSession);
|
|
2029
|
+
}
|
|
1511
2030
|
return focusInput({
|
|
1512
2031
|
...optimisticState,
|
|
1513
2032
|
nextMessageId: optimisticState.nextMessageId + 1,
|
|
@@ -1529,15 +2048,26 @@ const handleClickSend = async state => {
|
|
|
1529
2048
|
return handleSubmit(submitState);
|
|
1530
2049
|
};
|
|
1531
2050
|
|
|
1532
|
-
const selectSession = (state, id) => {
|
|
2051
|
+
const selectSession = async (state, id) => {
|
|
1533
2052
|
const exists = state.sessions.some(session => session.id === id);
|
|
1534
2053
|
if (!exists) {
|
|
1535
2054
|
return state;
|
|
1536
2055
|
}
|
|
2056
|
+
const loadedSession = await getChatSession(id);
|
|
2057
|
+
const sessions = state.sessions.map(session => {
|
|
2058
|
+
if (session.id !== id) {
|
|
2059
|
+
return session;
|
|
2060
|
+
}
|
|
2061
|
+
if (!loadedSession) {
|
|
2062
|
+
return session;
|
|
2063
|
+
}
|
|
2064
|
+
return loadedSession;
|
|
2065
|
+
});
|
|
1537
2066
|
return {
|
|
1538
2067
|
...state,
|
|
1539
2068
|
renamingSessionId: '',
|
|
1540
2069
|
selectedSessionId: id,
|
|
2070
|
+
sessions,
|
|
1541
2071
|
viewMode: 'detail'
|
|
1542
2072
|
};
|
|
1543
2073
|
};
|
|
@@ -1649,6 +2179,7 @@ const handleClickNew = async state => {
|
|
|
1649
2179
|
|
|
1650
2180
|
const handleClickSettings = async () => {
|
|
1651
2181
|
// TODO
|
|
2182
|
+
await invoke('Main.openUri', 'app://settings.json');
|
|
1652
2183
|
};
|
|
1653
2184
|
|
|
1654
2185
|
const handleInput = async (state, value, inputSource = 'user') => {
|
|
@@ -1690,7 +2221,7 @@ const handleInputFocus = async (state, name) => {
|
|
|
1690
2221
|
};
|
|
1691
2222
|
};
|
|
1692
2223
|
|
|
1693
|
-
const submitRename = state => {
|
|
2224
|
+
const submitRename = async state => {
|
|
1694
2225
|
const {
|
|
1695
2226
|
composerValue,
|
|
1696
2227
|
renamingSessionId,
|
|
@@ -1712,6 +2243,10 @@ const submitRename = state => {
|
|
|
1712
2243
|
title
|
|
1713
2244
|
};
|
|
1714
2245
|
});
|
|
2246
|
+
const renamedSession = updatedSessions.find(session => session.id === renamingSessionId);
|
|
2247
|
+
if (renamedSession) {
|
|
2248
|
+
await saveChatSession(renamedSession);
|
|
2249
|
+
}
|
|
1715
2250
|
return {
|
|
1716
2251
|
...state,
|
|
1717
2252
|
composerValue: '',
|
|
@@ -1744,6 +2279,17 @@ const handleKeyDown = async (state, key, shiftKey) => {
|
|
|
1744
2279
|
return handleSubmit(submitState);
|
|
1745
2280
|
};
|
|
1746
2281
|
|
|
2282
|
+
const handleModelChange = async (state, value) => {
|
|
2283
|
+
return {
|
|
2284
|
+
...state,
|
|
2285
|
+
selectedModelId: value
|
|
2286
|
+
};
|
|
2287
|
+
};
|
|
2288
|
+
|
|
2289
|
+
const handleNewline = async state => {
|
|
2290
|
+
return handleInput(state, `${state.composerValue}\n`);
|
|
2291
|
+
};
|
|
2292
|
+
|
|
1747
2293
|
const id = 7201;
|
|
1748
2294
|
const sendMessagePortToExtensionHostWorker = async port => {
|
|
1749
2295
|
await sendMessagePortToExtensionHostWorker$1(port, id);
|
|
@@ -1800,379 +2346,186 @@ const getSavedBounds = savedState => {
|
|
|
1800
2346
|
};
|
|
1801
2347
|
};
|
|
1802
2348
|
|
|
1803
|
-
const
|
|
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 => {
|
|
2349
|
+
const getSavedSelectedModelId = savedState => {
|
|
1817
2350
|
if (!isObject(savedState)) {
|
|
1818
2351
|
return undefined;
|
|
1819
2352
|
}
|
|
1820
2353
|
const {
|
|
1821
|
-
|
|
2354
|
+
selectedModelId
|
|
1822
2355
|
} = savedState;
|
|
1823
|
-
if (
|
|
2356
|
+
if (typeof selectedModelId !== 'string') {
|
|
1824
2357
|
return undefined;
|
|
1825
2358
|
}
|
|
1826
|
-
return
|
|
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
|
-
}
|
|
1994
|
-
}
|
|
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
|
-
}
|
|
2003
|
-
}
|
|
2004
|
-
return patches;
|
|
2359
|
+
return selectedModelId;
|
|
2005
2360
|
};
|
|
2006
2361
|
|
|
2007
|
-
const
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
result.push(...treeToArray(child));
|
|
2362
|
+
const getSavedSelectedSessionId = savedState => {
|
|
2363
|
+
if (!isObject(savedState)) {
|
|
2364
|
+
return undefined;
|
|
2011
2365
|
}
|
|
2012
|
-
|
|
2366
|
+
const {
|
|
2367
|
+
selectedSessionId
|
|
2368
|
+
} = savedState;
|
|
2369
|
+
if (typeof selectedSessionId !== 'string') {
|
|
2370
|
+
return undefined;
|
|
2371
|
+
}
|
|
2372
|
+
return selectedSessionId;
|
|
2013
2373
|
};
|
|
2014
2374
|
|
|
2015
|
-
const
|
|
2016
|
-
|
|
2017
|
-
|
|
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
|
-
}
|
|
2375
|
+
const getSavedSessions = savedState => {
|
|
2376
|
+
if (!isObject(savedState)) {
|
|
2377
|
+
return undefined;
|
|
2101
2378
|
}
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
currentChildIndex = -1;
|
|
2379
|
+
const {
|
|
2380
|
+
sessions
|
|
2381
|
+
} = savedState;
|
|
2382
|
+
if (!Array.isArray(sessions)) {
|
|
2383
|
+
return undefined;
|
|
2108
2384
|
}
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2385
|
+
return sessions;
|
|
2386
|
+
};
|
|
2387
|
+
|
|
2388
|
+
const getSavedViewMode = savedState => {
|
|
2389
|
+
if (!isObject(savedState)) {
|
|
2390
|
+
return undefined;
|
|
2391
|
+
}
|
|
2392
|
+
const {
|
|
2393
|
+
viewMode
|
|
2394
|
+
} = savedState;
|
|
2395
|
+
if (viewMode !== 'list' && viewMode !== 'detail') {
|
|
2396
|
+
return undefined;
|
|
2116
2397
|
}
|
|
2398
|
+
return viewMode;
|
|
2117
2399
|
};
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2400
|
+
|
|
2401
|
+
const toSummarySession = session => {
|
|
2402
|
+
return {
|
|
2403
|
+
id: session.id,
|
|
2404
|
+
messages: [],
|
|
2405
|
+
title: session.title
|
|
2406
|
+
};
|
|
2407
|
+
};
|
|
2408
|
+
const loadSelectedSessionMessages = async (sessions, selectedSessionId) => {
|
|
2409
|
+
if (!selectedSessionId) {
|
|
2410
|
+
return sessions;
|
|
2411
|
+
}
|
|
2412
|
+
const loadedSession = await getChatSession(selectedSessionId);
|
|
2413
|
+
if (!loadedSession) {
|
|
2414
|
+
return sessions;
|
|
2415
|
+
}
|
|
2416
|
+
return sessions.map(session => {
|
|
2417
|
+
if (session.id !== selectedSessionId) {
|
|
2418
|
+
return session;
|
|
2134
2419
|
}
|
|
2135
|
-
|
|
2136
|
-
|
|
2420
|
+
return loadedSession;
|
|
2421
|
+
});
|
|
2422
|
+
};
|
|
2423
|
+
const loadContent = async (state, savedState) => {
|
|
2424
|
+
const savedBounds = getSavedBounds(savedState);
|
|
2425
|
+
const savedSelectedModelId = getSavedSelectedModelId(savedState);
|
|
2426
|
+
const savedViewMode = getSavedViewMode(savedState);
|
|
2427
|
+
const legacySavedSessions = getSavedSessions(savedState);
|
|
2428
|
+
const storedSessions = await listChatSessions();
|
|
2429
|
+
let sessions = storedSessions;
|
|
2430
|
+
if (sessions.length === 0 && legacySavedSessions && legacySavedSessions.length > 0) {
|
|
2431
|
+
for (const session of legacySavedSessions) {
|
|
2432
|
+
await saveChatSession(session);
|
|
2137
2433
|
}
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2434
|
+
sessions = legacySavedSessions.map(toSummarySession);
|
|
2435
|
+
}
|
|
2436
|
+
if (sessions.length === 0 && state.sessions.length > 0) {
|
|
2437
|
+
for (const session of state.sessions) {
|
|
2438
|
+
await saveChatSession(session);
|
|
2141
2439
|
}
|
|
2142
|
-
|
|
2143
|
-
// Non-root level or multiple root elements - use the regular comparison
|
|
2144
|
-
diffChildren(oldTree, newTree, patches);
|
|
2440
|
+
sessions = state.sessions.map(toSummarySession);
|
|
2145
2441
|
}
|
|
2442
|
+
const preferredSessionId = getSavedSelectedSessionId(savedState) || state.selectedSessionId;
|
|
2443
|
+
const preferredModelId = savedSelectedModelId || state.selectedModelId;
|
|
2444
|
+
const selectedModelId = state.models.some(model => model.id === preferredModelId) ? preferredModelId : state.models[0]?.id || '';
|
|
2445
|
+
const selectedSessionId = sessions.some(session => session.id === preferredSessionId) ? preferredSessionId : sessions[0]?.id || '';
|
|
2446
|
+
sessions = await loadSelectedSessionMessages(sessions, selectedSessionId);
|
|
2447
|
+
const preferredViewMode = savedViewMode || state.viewMode;
|
|
2448
|
+
const viewMode = sessions.length === 0 || !selectedSessionId ? 'list' : preferredViewMode === 'detail' ? 'detail' : 'list';
|
|
2449
|
+
return {
|
|
2450
|
+
...state,
|
|
2451
|
+
...savedBounds,
|
|
2452
|
+
initial: false,
|
|
2453
|
+
selectedModelId,
|
|
2454
|
+
selectedSessionId,
|
|
2455
|
+
sessions,
|
|
2456
|
+
viewMode
|
|
2457
|
+
};
|
|
2146
2458
|
};
|
|
2147
2459
|
|
|
2148
|
-
const
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2460
|
+
const openMockSession = async (state, mockSessionId, mockChatMessages) => {
|
|
2461
|
+
if (!mockSessionId) {
|
|
2462
|
+
return state;
|
|
2463
|
+
}
|
|
2464
|
+
const existingSession = state.sessions.find(session => session.id === mockSessionId);
|
|
2465
|
+
const sessions = existingSession ? state.sessions.map(session => {
|
|
2466
|
+
if (session.id !== mockSessionId) {
|
|
2467
|
+
return session;
|
|
2156
2468
|
}
|
|
2469
|
+
return {
|
|
2470
|
+
...session,
|
|
2471
|
+
messages: mockChatMessages
|
|
2472
|
+
};
|
|
2473
|
+
}) : [...state.sessions, {
|
|
2474
|
+
id: mockSessionId,
|
|
2475
|
+
messages: mockChatMessages,
|
|
2476
|
+
title: mockSessionId
|
|
2477
|
+
}];
|
|
2478
|
+
const selectedSession = sessions.find(session => session.id === mockSessionId);
|
|
2479
|
+
if (selectedSession) {
|
|
2480
|
+
await saveChatSession(selectedSession);
|
|
2157
2481
|
}
|
|
2158
|
-
|
|
2159
|
-
|
|
2482
|
+
return {
|
|
2483
|
+
...state,
|
|
2484
|
+
renamingSessionId: '',
|
|
2485
|
+
selectedSessionId: mockSessionId,
|
|
2486
|
+
sessions,
|
|
2487
|
+
viewMode: 'detail'
|
|
2488
|
+
};
|
|
2160
2489
|
};
|
|
2161
2490
|
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2491
|
+
// TODO render things like scrollbar height,scrollbar offset, textarea height,
|
|
2492
|
+
// list height
|
|
2493
|
+
const css = `
|
|
2494
|
+
`;
|
|
2495
|
+
const renderCss = (oldState, newState) => {
|
|
2496
|
+
return [SetCss, newState.uid, css];
|
|
2497
|
+
};
|
|
2498
|
+
|
|
2499
|
+
const getFocusSelector = focus => {
|
|
2500
|
+
switch (focus) {
|
|
2501
|
+
case 'composer':
|
|
2502
|
+
case 'input':
|
|
2503
|
+
return '[name="composer"]';
|
|
2504
|
+
case 'header':
|
|
2505
|
+
return '[name="create-session"]';
|
|
2506
|
+
case 'list':
|
|
2507
|
+
return '[name^="session:"]';
|
|
2508
|
+
case 'send-button':
|
|
2509
|
+
return '[name="send"]';
|
|
2510
|
+
default:
|
|
2511
|
+
return '[name="composer"]';
|
|
2512
|
+
}
|
|
2513
|
+
};
|
|
2514
|
+
const renderFocus = (oldState, newState) => {
|
|
2515
|
+
const selector = getFocusSelector(newState.focus);
|
|
2516
|
+
return [FocusSelector, selector];
|
|
2517
|
+
};
|
|
2518
|
+
|
|
2519
|
+
const renderFocusContext = (oldState, newState) => {
|
|
2520
|
+
const when = 2344;
|
|
2521
|
+
return [SetFocusContext, newState.uid, when];
|
|
2171
2522
|
};
|
|
2172
2523
|
|
|
2173
2524
|
const ChatActions = 'ChatActions';
|
|
2174
2525
|
const ChatName = 'ChatName';
|
|
2175
2526
|
const ChatSendArea = 'ChatSendArea';
|
|
2527
|
+
const ChatSendAreaBottom = 'ChatSendAreaBottom';
|
|
2528
|
+
const ChatSendAreaContent = 'ChatSendAreaContent';
|
|
2176
2529
|
const Chat = 'Chat';
|
|
2177
2530
|
const ChatHeader = 'ChatHeader';
|
|
2178
2531
|
const Button = 'Button';
|
|
@@ -2181,11 +2534,16 @@ const ButtonPrimary = 'ButtonPrimary';
|
|
|
2181
2534
|
const IconButton = 'IconButton';
|
|
2182
2535
|
const Label = 'Label';
|
|
2183
2536
|
const ChatList = 'ChatList';
|
|
2537
|
+
const ChatListEmpty = 'ChatListEmpty';
|
|
2184
2538
|
const ChatListItem = 'ChatListItem';
|
|
2185
2539
|
const ChatListItemLabel = 'ChatListItemLabel';
|
|
2186
2540
|
const Markdown = 'Markdown';
|
|
2187
2541
|
const Message = 'Message';
|
|
2542
|
+
const MessageUser = 'MessageUser';
|
|
2543
|
+
const MessageAssistant = 'MessageAssistant';
|
|
2188
2544
|
const MultilineInputBox = 'MultilineInputBox';
|
|
2545
|
+
const Option = 'Option';
|
|
2546
|
+
const Select = 'Select';
|
|
2189
2547
|
const Viewlet = 'Viewlet';
|
|
2190
2548
|
const ChatWelcomeMessage = 'ChatWelcomeMessage';
|
|
2191
2549
|
|
|
@@ -2201,14 +2559,28 @@ const HandleClickBack = 16;
|
|
|
2201
2559
|
const HandleClickList = 17;
|
|
2202
2560
|
const HandleClickDelete = 18;
|
|
2203
2561
|
const HandleSubmit = 19;
|
|
2562
|
+
const HandleModelChange = 20;
|
|
2204
2563
|
|
|
2205
|
-
const getChatSendAreaDom = composerValue => {
|
|
2564
|
+
const getChatSendAreaDom = (composerValue, models, selectedModelId) => {
|
|
2206
2565
|
const isSendDisabled = composerValue.trim() === '';
|
|
2207
2566
|
const sendButtonClassName = isSendDisabled ? `${Button} ${ButtonPrimary} ${ButtonDisabled}` : `${Button} ${ButtonPrimary}`;
|
|
2567
|
+
const modelOptions = models.flatMap(model => {
|
|
2568
|
+
return [{
|
|
2569
|
+
childCount: 1,
|
|
2570
|
+
className: Option,
|
|
2571
|
+
selected: model.id === selectedModelId,
|
|
2572
|
+
type: Option$1,
|
|
2573
|
+
value: model.id
|
|
2574
|
+
}, text(model.name)];
|
|
2575
|
+
});
|
|
2208
2576
|
return [{
|
|
2209
|
-
childCount:
|
|
2577
|
+
childCount: 1,
|
|
2210
2578
|
className: ChatSendArea,
|
|
2211
2579
|
type: Div
|
|
2580
|
+
}, {
|
|
2581
|
+
childCount: 2,
|
|
2582
|
+
className: ChatSendAreaContent,
|
|
2583
|
+
type: Div
|
|
2212
2584
|
}, {
|
|
2213
2585
|
childCount: 0,
|
|
2214
2586
|
className: MultilineInputBox,
|
|
@@ -2219,6 +2591,17 @@ const getChatSendAreaDom = composerValue => {
|
|
|
2219
2591
|
type: TextArea,
|
|
2220
2592
|
value: composerValue
|
|
2221
2593
|
}, {
|
|
2594
|
+
childCount: 2,
|
|
2595
|
+
className: ChatSendAreaBottom,
|
|
2596
|
+
type: Div
|
|
2597
|
+
}, {
|
|
2598
|
+
childCount: models.length,
|
|
2599
|
+
className: Select,
|
|
2600
|
+
name: 'model',
|
|
2601
|
+
onInput: HandleModelChange,
|
|
2602
|
+
type: Select$1,
|
|
2603
|
+
value: selectedModelId
|
|
2604
|
+
}, ...modelOptions, {
|
|
2222
2605
|
childCount: 1,
|
|
2223
2606
|
className: sendButtonClassName,
|
|
2224
2607
|
disabled: isSendDisabled,
|
|
@@ -2295,9 +2678,10 @@ const getChatHeaderDomDetailMode = selectedSessionTitle => {
|
|
|
2295
2678
|
};
|
|
2296
2679
|
|
|
2297
2680
|
const getChatMessageDom = message => {
|
|
2681
|
+
const roleClassName = message.role === 'user' ? MessageUser : MessageAssistant;
|
|
2298
2682
|
return [{
|
|
2299
2683
|
childCount: 2,
|
|
2300
|
-
className: Message,
|
|
2684
|
+
className: mergeClassNames(Message, roleClassName),
|
|
2301
2685
|
type: Div
|
|
2302
2686
|
}, {
|
|
2303
2687
|
childCount: 1,
|
|
@@ -2325,7 +2709,7 @@ const getMessagesDom = messages => {
|
|
|
2325
2709
|
}, ...messages.flatMap(getChatMessageDom)];
|
|
2326
2710
|
};
|
|
2327
2711
|
|
|
2328
|
-
const getChatModeDetailVirtualDom = (sessions, selectedSessionId, composerValue) => {
|
|
2712
|
+
const getChatModeDetailVirtualDom = (sessions, selectedSessionId, composerValue, models, selectedModelId) => {
|
|
2329
2713
|
const selectedSession = sessions.find(session => session.id === selectedSessionId);
|
|
2330
2714
|
const selectedSessionTitle = selectedSession?.title || chatTitle;
|
|
2331
2715
|
const messages = selectedSession ? selectedSession.messages : [];
|
|
@@ -2333,7 +2717,7 @@ const getChatModeDetailVirtualDom = (sessions, selectedSessionId, composerValue)
|
|
|
2333
2717
|
childCount: 3,
|
|
2334
2718
|
className: mergeClassNames(Viewlet, Chat),
|
|
2335
2719
|
type: Div
|
|
2336
|
-
}, ...getChatHeaderDomDetailMode(selectedSessionTitle), ...getMessagesDom(messages), ...getChatSendAreaDom(composerValue)];
|
|
2720
|
+
}, ...getChatHeaderDomDetailMode(selectedSessionTitle), ...getMessagesDom(messages), ...getChatSendAreaDom(composerValue, models, selectedModelId)];
|
|
2337
2721
|
};
|
|
2338
2722
|
|
|
2339
2723
|
const getChatHeaderListModeDom = () => {
|
|
@@ -2351,7 +2735,7 @@ const getChatHeaderListModeDom = () => {
|
|
|
2351
2735
|
const getEmptyChatSessionsDom = () => {
|
|
2352
2736
|
return [{
|
|
2353
2737
|
childCount: 1,
|
|
2354
|
-
className:
|
|
2738
|
+
className: ChatListEmpty,
|
|
2355
2739
|
type: Div
|
|
2356
2740
|
}, {
|
|
2357
2741
|
childCount: 1,
|
|
@@ -2385,7 +2769,7 @@ const getSessionDom = session => {
|
|
|
2385
2769
|
onClick: HandleClickDelete,
|
|
2386
2770
|
role: Button$2,
|
|
2387
2771
|
tabIndex: 0,
|
|
2388
|
-
title: deleteChatSession,
|
|
2772
|
+
title: deleteChatSession$1,
|
|
2389
2773
|
type: Button$1
|
|
2390
2774
|
}, text('🗑')];
|
|
2391
2775
|
};
|
|
@@ -2402,12 +2786,12 @@ const getChatListDom = (sessions, selectedSessionId) => {
|
|
|
2402
2786
|
}, ...sessions.flatMap(getSessionDom)];
|
|
2403
2787
|
};
|
|
2404
2788
|
|
|
2405
|
-
const getChatModeListVirtualDom = (sessions, selectedSessionId, composerValue) => {
|
|
2789
|
+
const getChatModeListVirtualDom = (sessions, selectedSessionId, composerValue, models, selectedModelId) => {
|
|
2406
2790
|
return [{
|
|
2407
2791
|
childCount: 3,
|
|
2408
2792
|
className: mergeClassNames(Viewlet, Chat),
|
|
2409
2793
|
type: Div
|
|
2410
|
-
}, ...getChatHeaderListModeDom(), ...getChatListDom(sessions), ...getChatSendAreaDom(composerValue)];
|
|
2794
|
+
}, ...getChatHeaderListModeDom(), ...getChatListDom(sessions), ...getChatSendAreaDom(composerValue, models, selectedModelId)];
|
|
2411
2795
|
};
|
|
2412
2796
|
|
|
2413
2797
|
const getChatModeUnsupportedVirtualDom = () => {
|
|
@@ -2417,12 +2801,12 @@ const getChatModeUnsupportedVirtualDom = () => {
|
|
|
2417
2801
|
}, text('Unknown view mode')];
|
|
2418
2802
|
};
|
|
2419
2803
|
|
|
2420
|
-
const getChatVirtualDom = (sessions, selectedSessionId, composerValue, viewMode) => {
|
|
2804
|
+
const getChatVirtualDom = (sessions, selectedSessionId, composerValue, viewMode, models, selectedModelId) => {
|
|
2421
2805
|
switch (viewMode) {
|
|
2422
2806
|
case 'detail':
|
|
2423
|
-
return getChatModeDetailVirtualDom(sessions, selectedSessionId, composerValue);
|
|
2807
|
+
return getChatModeDetailVirtualDom(sessions, selectedSessionId, composerValue, models, selectedModelId);
|
|
2424
2808
|
case 'list':
|
|
2425
|
-
return getChatModeListVirtualDom(sessions, selectedSessionId, composerValue);
|
|
2809
|
+
return getChatModeListVirtualDom(sessions, selectedSessionId, composerValue, models, selectedModelId);
|
|
2426
2810
|
default:
|
|
2427
2811
|
return getChatModeUnsupportedVirtualDom();
|
|
2428
2812
|
}
|
|
@@ -2432,6 +2816,8 @@ const renderItems = (oldState, newState) => {
|
|
|
2432
2816
|
const {
|
|
2433
2817
|
composerValue,
|
|
2434
2818
|
initial,
|
|
2819
|
+
models,
|
|
2820
|
+
selectedModelId,
|
|
2435
2821
|
selectedSessionId,
|
|
2436
2822
|
sessions,
|
|
2437
2823
|
uid,
|
|
@@ -2440,7 +2826,7 @@ const renderItems = (oldState, newState) => {
|
|
|
2440
2826
|
if (initial) {
|
|
2441
2827
|
return [SetDom2, uid, []];
|
|
2442
2828
|
}
|
|
2443
|
-
const dom = getChatVirtualDom(sessions, selectedSessionId, composerValue, viewMode);
|
|
2829
|
+
const dom = getChatVirtualDom(sessions, selectedSessionId, composerValue, viewMode, models, selectedModelId);
|
|
2444
2830
|
return [SetDom2, uid, dom];
|
|
2445
2831
|
};
|
|
2446
2832
|
|
|
@@ -2464,6 +2850,8 @@ const getRenderer = diffType => {
|
|
|
2464
2850
|
return renderCss;
|
|
2465
2851
|
case RenderFocus:
|
|
2466
2852
|
return renderFocus;
|
|
2853
|
+
case RenderFocusContext:
|
|
2854
|
+
return renderFocusContext;
|
|
2467
2855
|
case RenderIncremental:
|
|
2468
2856
|
return renderIncremental;
|
|
2469
2857
|
case RenderItems:
|
|
@@ -2526,6 +2914,9 @@ const renderEventListeners = () => {
|
|
|
2526
2914
|
}, {
|
|
2527
2915
|
name: HandleInput,
|
|
2528
2916
|
params: ['handleInput', TargetValue]
|
|
2917
|
+
}, {
|
|
2918
|
+
name: HandleModelChange,
|
|
2919
|
+
params: ['handleModelChange', TargetValue]
|
|
2529
2920
|
}, {
|
|
2530
2921
|
name: HandleFocus,
|
|
2531
2922
|
params: ['handleInputFocus', TargetName]
|
|
@@ -2538,7 +2929,12 @@ const renderEventListeners = () => {
|
|
|
2538
2929
|
}];
|
|
2539
2930
|
};
|
|
2540
2931
|
|
|
2932
|
+
const rerender = state => {
|
|
2933
|
+
return structuredClone(state);
|
|
2934
|
+
};
|
|
2935
|
+
|
|
2541
2936
|
const reset = async state => {
|
|
2937
|
+
await clearChatSessions();
|
|
2542
2938
|
return {
|
|
2543
2939
|
...state,
|
|
2544
2940
|
composerValue: '',
|
|
@@ -2561,8 +2957,8 @@ const saveState = state => {
|
|
|
2561
2957
|
height,
|
|
2562
2958
|
nextMessageId,
|
|
2563
2959
|
renamingSessionId,
|
|
2960
|
+
selectedModelId,
|
|
2564
2961
|
selectedSessionId,
|
|
2565
|
-
sessions,
|
|
2566
2962
|
viewMode,
|
|
2567
2963
|
width,
|
|
2568
2964
|
x,
|
|
@@ -2573,8 +2969,8 @@ const saveState = state => {
|
|
|
2573
2969
|
height,
|
|
2574
2970
|
nextMessageId,
|
|
2575
2971
|
renamingSessionId,
|
|
2972
|
+
selectedModelId,
|
|
2576
2973
|
selectedSessionId,
|
|
2577
|
-
sessions,
|
|
2578
2974
|
viewMode,
|
|
2579
2975
|
width,
|
|
2580
2976
|
x,
|
|
@@ -2608,6 +3004,7 @@ const commandMap = {
|
|
|
2608
3004
|
'Chat.clearInput': wrapCommand(clearInput),
|
|
2609
3005
|
'Chat.create': create,
|
|
2610
3006
|
'Chat.diff2': diff2,
|
|
3007
|
+
'Chat.enterNewLine': wrapCommand(handleNewline),
|
|
2611
3008
|
'Chat.getCommandIds': getCommandIds,
|
|
2612
3009
|
'Chat.getKeyBindings': getKeyBindings,
|
|
2613
3010
|
'Chat.handleChatListContextMenu': handleChatListContextMenu,
|
|
@@ -2621,12 +3018,15 @@ const commandMap = {
|
|
|
2621
3018
|
'Chat.handleInput': wrapCommand(handleInput),
|
|
2622
3019
|
'Chat.handleInputFocus': wrapCommand(handleInputFocus),
|
|
2623
3020
|
'Chat.handleKeyDown': wrapCommand(handleKeyDown),
|
|
3021
|
+
'Chat.handleModelChange': wrapCommand(handleModelChange),
|
|
2624
3022
|
'Chat.handleSubmit': wrapCommand(handleSubmit),
|
|
2625
3023
|
'Chat.initialize': initialize,
|
|
2626
3024
|
'Chat.loadContent': wrapCommand(loadContent),
|
|
2627
3025
|
'Chat.loadContent2': wrapCommand(loadContent),
|
|
3026
|
+
'Chat.openMockSession': wrapCommand(openMockSession),
|
|
2628
3027
|
'Chat.render2': render2,
|
|
2629
3028
|
'Chat.renderEventListeners': renderEventListeners,
|
|
3029
|
+
'Chat.rerender': wrapCommand(rerender),
|
|
2630
3030
|
'Chat.reset': wrapCommand(reset),
|
|
2631
3031
|
'Chat.resize': wrapCommand(resize),
|
|
2632
3032
|
'Chat.saveState': wrapGetter(saveState),
|