@lvce-editor/chat-view 1.5.0 → 1.6.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.
@@ -972,7 +972,6 @@ const Span = 8;
972
972
  const Text = 12;
973
973
  const P = 50;
974
974
  const TextArea = 62;
975
- const Strong = 70;
976
975
  const Reference = 100;
977
976
 
978
977
  const ClientX = 'event.clientX';
@@ -985,8 +984,10 @@ const TargetValue = 'event.target.value';
985
984
  const ExtensionHostWorker = 44;
986
985
  const RendererWorker = 1;
987
986
 
987
+ const FocusSelector = 'Viewlet.focusSelector';
988
988
  const SetCss = 'Viewlet.setCss';
989
989
  const SetDom2 = 'Viewlet.setDom2';
990
+ const SetValueByName = 'Viewlet.setValueByName';
990
991
  const SetPatches = 'Viewlet.setPatches';
991
992
 
992
993
  const createMockRpc = ({
@@ -1191,6 +1192,13 @@ const terminate = () => {
1191
1192
  globalThis.close();
1192
1193
  };
1193
1194
 
1195
+ const clearInput = async state => {
1196
+ return {
1197
+ ...state,
1198
+ composerValue: ''
1199
+ };
1200
+ };
1201
+
1194
1202
  const emptyObject = {};
1195
1203
  const RE_PLACEHOLDER = /\{(PH\d+)\}/g;
1196
1204
  const i18nString = (key, placeholders = emptyObject) => {
@@ -1228,9 +1236,13 @@ const createDefaultState = () => {
1228
1236
  assetDir: '',
1229
1237
  composerValue: '',
1230
1238
  errorCount: 0,
1231
- ignoreNextInput: false,
1239
+ focus: 'composer',
1240
+ focused: false,
1241
+ height: 0,
1232
1242
  initial: true,
1243
+ inputSource: 'script',
1233
1244
  lastSubmittedSessionId: '',
1245
+ listItemHeight: 40,
1234
1246
  nextMessageId: 1,
1235
1247
  platform: 0,
1236
1248
  renamingSessionId: '',
@@ -1242,7 +1254,10 @@ const createDefaultState = () => {
1242
1254
  }],
1243
1255
  uid: 0,
1244
1256
  viewMode: 'list',
1245
- warningCount: 0
1257
+ warningCount: 0,
1258
+ width: 0,
1259
+ x: 0,
1260
+ y: 0
1246
1261
  };
1247
1262
  };
1248
1263
 
@@ -1255,12 +1270,16 @@ const {
1255
1270
  wrapGetter
1256
1271
  } = create$1();
1257
1272
 
1258
- const create = (uid, uri, x, y, width, height, platform, assetDir) => {
1273
+ const create = (uid, _uri, x, y, width, height, platform, assetDir) => {
1259
1274
  const state = {
1260
1275
  ...createDefaultState(),
1261
1276
  assetDir,
1277
+ height,
1262
1278
  platform,
1263
- uid
1279
+ uid,
1280
+ width,
1281
+ x,
1282
+ y
1264
1283
  };
1265
1284
  set(uid, state, state);
1266
1285
  };
@@ -1269,16 +1288,32 @@ const isEqual$1 = (oldState, newState) => {
1269
1288
  return oldState.initial === newState.initial;
1270
1289
  };
1271
1290
 
1291
+ const diffFocus = (oldState, newState) => {
1292
+ if (!newState.focused) {
1293
+ return true;
1294
+ }
1295
+ return oldState.focus === newState.focus && oldState.focused === newState.focused;
1296
+ };
1297
+
1272
1298
  const isEqual = (oldState, newState) => {
1273
- return oldState.composerValue === newState.composerValue && oldState.ignoreNextInput === newState.ignoreNextInput && oldState.initial === newState.initial && oldState.renamingSessionId === newState.renamingSessionId && oldState.selectedSessionId === newState.selectedSessionId && oldState.sessions === newState.sessions && oldState.viewMode === newState.viewMode;
1299
+ 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;
1274
1300
  };
1275
1301
 
1276
1302
  const RenderItems = 4;
1303
+ const RenderFocus = 6;
1304
+ const RenderValue = 8;
1277
1305
  const RenderCss = 10;
1278
1306
  const RenderIncremental = 11;
1279
1307
 
1280
- const modules = [isEqual, isEqual$1];
1281
- const numbers = [RenderIncremental, RenderCss];
1308
+ const diffValue = (oldState, newState) => {
1309
+ if (oldState.composerValue === newState.composerValue) {
1310
+ return true;
1311
+ }
1312
+ return newState.inputSource !== 'script';
1313
+ };
1314
+
1315
+ const modules = [isEqual, diffValue, diffFocus, isEqual$1];
1316
+ const numbers = [RenderIncremental, RenderValue, RenderFocus, RenderCss];
1282
1317
 
1283
1318
  const diff = (oldState, newState) => {
1284
1319
  const diffResult = [];
@@ -1319,83 +1354,171 @@ const generateSessionId = () => {
1319
1354
  return crypto.randomUUID();
1320
1355
  };
1321
1356
 
1357
+ const createSession = state => {
1358
+ const id = generateSessionId();
1359
+ const session = {
1360
+ id,
1361
+ messages: [],
1362
+ title: `Chat ${state.sessions.length + 1}`
1363
+ };
1364
+ return {
1365
+ ...state,
1366
+ renamingSessionId: '',
1367
+ selectedSessionId: id,
1368
+ sessions: [...state.sessions, session]
1369
+ };
1370
+ };
1371
+
1372
+ const getNextSelectedSessionId = (sessions, deletedId) => {
1373
+ if (sessions.length === 0) {
1374
+ return '';
1375
+ }
1376
+ const index = sessions.findIndex(session => session.id === deletedId);
1377
+ if (index === -1) {
1378
+ return sessions[0].id;
1379
+ }
1380
+ const nextIndex = Math.min(index, sessions.length - 1);
1381
+ return sessions[nextIndex].id;
1382
+ };
1383
+
1384
+ const deleteSession = (state, id) => {
1385
+ const {
1386
+ renamingSessionId,
1387
+ sessions
1388
+ } = state;
1389
+ const filtered = sessions.filter(session => session.id !== id);
1390
+ if (filtered.length === sessions.length) {
1391
+ return state;
1392
+ }
1393
+ if (filtered.length === 0) {
1394
+ return {
1395
+ ...state,
1396
+ renamingSessionId: '',
1397
+ selectedSessionId: '',
1398
+ sessions: [],
1399
+ viewMode: 'list'
1400
+ };
1401
+ }
1402
+ return {
1403
+ ...state,
1404
+ renamingSessionId: renamingSessionId === id ? '' : renamingSessionId,
1405
+ selectedSessionId: getNextSelectedSessionId(filtered, id),
1406
+ sessions: filtered
1407
+ };
1408
+ };
1409
+
1410
+ const focusInput = state => {
1411
+ return {
1412
+ ...state,
1413
+ focus: 'composer',
1414
+ focused: true
1415
+ };
1416
+ };
1417
+
1418
+ const delay = async ms => {
1419
+ await new Promise(resolve => setTimeout(resolve, ms));
1420
+ };
1421
+ const getMockAiResponse = userMessage => {
1422
+ return `Mock AI response: I received "${userMessage}".`;
1423
+ };
1322
1424
  const handleSubmit = async state => {
1323
1425
  const {
1324
1426
  composerValue,
1325
1427
  nextMessageId,
1326
1428
  selectedSessionId,
1327
- sessions
1429
+ sessions,
1430
+ viewMode
1328
1431
  } = state;
1329
- const text = composerValue.trim();
1330
- const time = new Date().toLocaleTimeString([], {
1432
+ const userText = composerValue.trim();
1433
+ if (!userText) {
1434
+ return state;
1435
+ }
1436
+ const userTime = new Date().toLocaleTimeString([], {
1331
1437
  hour: '2-digit',
1332
1438
  minute: '2-digit'
1333
1439
  });
1334
- if (!text) {
1335
- return {
1336
- ...state,
1337
- ignoreNextInput: true
1440
+ const userMessage = {
1441
+ id: `message-${nextMessageId}`,
1442
+ role: 'user',
1443
+ text: userText,
1444
+ time: userTime
1445
+ };
1446
+ let optimisticState;
1447
+ if (viewMode === 'list') {
1448
+ const newSessionId = generateSessionId();
1449
+ const newSession = {
1450
+ id: newSessionId,
1451
+ messages: [userMessage],
1452
+ title: `Chat ${sessions.length + 1}`
1338
1453
  };
1454
+ optimisticState = focusInput({
1455
+ ...state,
1456
+ composerValue: '',
1457
+ inputSource: 'script',
1458
+ lastSubmittedSessionId: newSessionId,
1459
+ nextMessageId: nextMessageId + 1,
1460
+ selectedSessionId: newSessionId,
1461
+ sessions: [...sessions, newSession],
1462
+ viewMode: 'detail'
1463
+ });
1464
+ } else {
1465
+ const updatedSessions = sessions.map(session => {
1466
+ if (session.id !== selectedSessionId) {
1467
+ return session;
1468
+ }
1469
+ return {
1470
+ ...session,
1471
+ messages: [...session.messages, userMessage]
1472
+ };
1473
+ });
1474
+ optimisticState = focusInput({
1475
+ ...state,
1476
+ composerValue: '',
1477
+ inputSource: 'script',
1478
+ lastSubmittedSessionId: selectedSessionId,
1479
+ nextMessageId: nextMessageId + 1,
1480
+ sessions: updatedSessions
1481
+ });
1339
1482
  }
1340
- const updatedSessions = sessions.map(session => {
1341
- if (session.id !== selectedSessionId) {
1483
+ set(state.uid, state, optimisticState);
1484
+ // @ts-ignore
1485
+ await invoke('Chat.rerender');
1486
+ await delay(800);
1487
+ const assistantTime = new Date().toLocaleTimeString([], {
1488
+ hour: '2-digit',
1489
+ minute: '2-digit'
1490
+ });
1491
+ const assistantMessage = {
1492
+ id: `message-${optimisticState.nextMessageId}`,
1493
+ role: 'assistant',
1494
+ text: getMockAiResponse(userText),
1495
+ time: assistantTime
1496
+ };
1497
+ const updatedSessions = optimisticState.sessions.map(session => {
1498
+ if (session.id !== optimisticState.selectedSessionId) {
1342
1499
  return session;
1343
1500
  }
1344
- const message = {
1345
- id: `message-${nextMessageId}`,
1346
- role: 'user',
1347
- text,
1348
- time
1349
- };
1350
1501
  return {
1351
1502
  ...session,
1352
- messages: [...session.messages, message]
1503
+ messages: [...session.messages, assistantMessage]
1353
1504
  };
1354
1505
  });
1355
- return {
1356
- ...state,
1357
- composerValue: '',
1358
- ignoreNextInput: true,
1359
- lastSubmittedSessionId: selectedSessionId,
1360
- nextMessageId: nextMessageId + 1,
1506
+ return focusInput({
1507
+ ...optimisticState,
1508
+ nextMessageId: optimisticState.nextMessageId + 1,
1361
1509
  sessions: updatedSessions
1362
- };
1510
+ });
1363
1511
  };
1364
1512
 
1365
- const CREATE_SESSION = 'create-session';
1366
- const SESSION_PREFIX = 'session:';
1367
- const RENAME_PREFIX = 'session-rename:';
1368
- const DELETE_PREFIX = 'session-delete:';
1369
- const SEND = 'send';
1370
- const BACK = 'back';
1371
1513
  const handleClickSend = async state => {
1372
- return handleSubmit(state);
1373
- };
1374
- const getNextSelectedSessionId = (sessions, deletedId) => {
1375
- if (sessions.length === 0) {
1376
- return '';
1377
- }
1378
- const index = sessions.findIndex(session => session.id === deletedId);
1379
- if (index === -1) {
1380
- return sessions[0].id;
1381
- }
1382
- const nextIndex = Math.min(index, sessions.length - 1);
1383
- return sessions[nextIndex].id;
1384
- };
1385
- const createSession = state => {
1386
- const id = generateSessionId();
1387
- const session = {
1388
- id,
1389
- messages: [],
1390
- title: `Chat ${state.sessions.length + 1}`
1391
- };
1392
- return {
1514
+ const hasSelectedSession = state.sessions.some(session => session.id === state.selectedSessionId);
1515
+ const submitState = state.viewMode === 'list' && hasSelectedSession ? {
1393
1516
  ...state,
1394
- renamingSessionId: '',
1395
- selectedSessionId: id,
1396
- sessions: [...state.sessions, session]
1397
- };
1517
+ viewMode: 'detail'
1518
+ } : state;
1519
+ return handleSubmit(submitState);
1398
1520
  };
1521
+
1399
1522
  const selectSession = (state, id) => {
1400
1523
  const exists = state.sessions.some(session => session.id === id);
1401
1524
  if (!exists) {
@@ -1408,6 +1531,7 @@ const selectSession = (state, id) => {
1408
1531
  viewMode: 'detail'
1409
1532
  };
1410
1533
  };
1534
+
1411
1535
  const startRename = (state, id) => {
1412
1536
  const session = state.sessions.find(item => item.id === id);
1413
1537
  if (!session) {
@@ -1416,32 +1540,47 @@ const startRename = (state, id) => {
1416
1540
  return {
1417
1541
  ...state,
1418
1542
  composerValue: session.title,
1543
+ inputSource: 'script',
1419
1544
  renamingSessionId: id,
1420
1545
  selectedSessionId: id
1421
1546
  };
1422
1547
  };
1423
- const deleteSession = (state, id) => {
1424
- const filtered = state.sessions.filter(session => session.id !== id);
1425
- if (filtered.length === state.sessions.length) {
1548
+
1549
+ const HEADER_HEIGHT = 40;
1550
+ const handleClickList = async (state, eventX, eventY) => {
1551
+ const {
1552
+ height,
1553
+ listItemHeight,
1554
+ sessions,
1555
+ width,
1556
+ x,
1557
+ y
1558
+ } = state;
1559
+ if (eventX < x || eventY < y) {
1426
1560
  return state;
1427
1561
  }
1428
- if (filtered.length === 0) {
1429
- return {
1430
- ...state,
1431
- renamingSessionId: '',
1432
- selectedSessionId: '',
1433
- sessions: [],
1434
- viewMode: 'list'
1435
- };
1562
+ if (eventX >= x + width || eventY >= y + height) {
1563
+ return state;
1436
1564
  }
1437
- return {
1438
- ...state,
1439
- renamingSessionId: state.renamingSessionId === id ? '' : state.renamingSessionId,
1440
- selectedSessionId: getNextSelectedSessionId(filtered, id),
1441
- sessions: filtered
1442
- };
1565
+ const listY = eventY - y - HEADER_HEIGHT;
1566
+ if (listY < 0) {
1567
+ return state;
1568
+ }
1569
+ const itemHeight = listItemHeight > 0 ? listItemHeight : 40;
1570
+ const index = Math.floor(listY / itemHeight);
1571
+ const session = sessions[index];
1572
+ if (!session) {
1573
+ return state;
1574
+ }
1575
+ return selectSession(state, session.id);
1443
1576
  };
1444
- const handleClick = async (state, name) => {
1577
+
1578
+ const CREATE_SESSION = 'create-session';
1579
+ const SESSION_PREFIX = 'session:';
1580
+ const RENAME_PREFIX = 'session-rename:';
1581
+ const SESSION_DELETE = 'SessionDelete';
1582
+ const SEND = 'send';
1583
+ const handleClick = async (state, name, id = '') => {
1445
1584
  if (!name) {
1446
1585
  return state;
1447
1586
  }
@@ -1456,40 +1595,76 @@ const handleClick = async (state, name) => {
1456
1595
  const id = name.slice(RENAME_PREFIX.length);
1457
1596
  return startRename(state, id);
1458
1597
  }
1459
- if (name.startsWith(DELETE_PREFIX)) {
1460
- const id = name.slice(DELETE_PREFIX.length);
1598
+ if (name === SESSION_DELETE) {
1461
1599
  return deleteSession(state, id);
1462
1600
  }
1463
1601
  if (name === SEND) {
1464
1602
  return handleClickSend(state);
1465
1603
  }
1466
- if (name === BACK) {
1467
- return {
1468
- ...state,
1469
- renamingSessionId: '',
1470
- viewMode: 'list'
1471
- };
1472
- }
1473
1604
  return state;
1474
1605
  };
1475
1606
 
1607
+ const handleClickBack = async state => {
1608
+ return {
1609
+ ...state,
1610
+ renamingSessionId: '',
1611
+ viewMode: 'list'
1612
+ };
1613
+ };
1614
+
1476
1615
  const handleClickClose = async () => {
1477
1616
  // @ts-ignore
1478
- await invoke('Chat.terminate');
1617
+ await invoke('Layout.hideSecondarySideBar');
1479
1618
  };
1480
1619
 
1481
- const handleClickSettings = async () => {};
1620
+ const handleClickDelete = async (state, sessionId = '') => {
1621
+ return deleteSession(state, sessionId);
1622
+ };
1482
1623
 
1483
- const handleInput = async (state, value) => {
1484
- if (state.ignoreNextInput) {
1624
+ const handleClickNew = async state => {
1625
+ return createSession(state);
1626
+ };
1627
+
1628
+ const handleClickSettings = async () => {
1629
+ // TODO
1630
+ };
1631
+
1632
+ const handleInput = async (state, value, inputSource = 'user') => {
1633
+ return {
1634
+ ...state,
1635
+ composerValue: value,
1636
+ inputSource
1637
+ };
1638
+ };
1639
+
1640
+ const handleInputFocus = async (state, name) => {
1641
+ if (name === 'composer') {
1642
+ return focusInput(state);
1643
+ }
1644
+ if (name === 'send') {
1645
+ return {
1646
+ ...state,
1647
+ focus: 'send-button',
1648
+ focused: true
1649
+ };
1650
+ }
1651
+ if (name.startsWith('session:') || name === 'SessionDelete') {
1485
1652
  return {
1486
1653
  ...state,
1487
- ignoreNextInput: false
1654
+ focus: 'list',
1655
+ focused: true
1656
+ };
1657
+ }
1658
+ if (name === 'create-session' || name === 'settings' || name === 'close-chat' || name === 'back') {
1659
+ return {
1660
+ ...state,
1661
+ focus: 'header',
1662
+ focused: true
1488
1663
  };
1489
1664
  }
1490
1665
  return {
1491
1666
  ...state,
1492
- composerValue: value
1667
+ focused: false
1493
1668
  };
1494
1669
  };
1495
1670
 
@@ -1518,19 +1693,33 @@ const submitRename = state => {
1518
1693
  return {
1519
1694
  ...state,
1520
1695
  composerValue: '',
1521
- ignoreNextInput: true,
1696
+ inputSource: 'script',
1522
1697
  renamingSessionId: '',
1523
1698
  sessions: updatedSessions
1524
1699
  };
1525
1700
  };
1701
+
1526
1702
  const handleKeyDown = async (state, key, shiftKey) => {
1703
+ const {
1704
+ composerValue,
1705
+ renamingSessionId,
1706
+ selectedSessionId,
1707
+ sessions,
1708
+ viewMode
1709
+ } = state;
1527
1710
  if (key !== 'Enter' || shiftKey) {
1528
1711
  return state;
1529
1712
  }
1530
- if (state.renamingSessionId) {
1713
+ if (renamingSessionId) {
1531
1714
  return submitRename(state);
1532
1715
  }
1533
- return handleSubmit(state);
1716
+ const hasInput = composerValue.trim().length > 0;
1717
+ const hasSelectedSession = sessions.some(session => session.id === selectedSessionId);
1718
+ const submitState = viewMode === 'list' && hasInput && hasSelectedSession ? {
1719
+ ...state,
1720
+ viewMode: 'detail'
1721
+ } : state;
1722
+ return handleSubmit(submitState);
1534
1723
  };
1535
1724
 
1536
1725
  const id = 7201;
@@ -1559,6 +1748,36 @@ const isObject = value => {
1559
1748
  return typeof value === 'object' && value !== null;
1560
1749
  };
1561
1750
 
1751
+ const getSavedBounds = savedState => {
1752
+ if (!isObject(savedState)) {
1753
+ return undefined;
1754
+ }
1755
+ const {
1756
+ height,
1757
+ width,
1758
+ x,
1759
+ y
1760
+ } = savedState;
1761
+ if (typeof x !== 'number') {
1762
+ return undefined;
1763
+ }
1764
+ if (typeof y !== 'number') {
1765
+ return undefined;
1766
+ }
1767
+ if (typeof width !== 'number') {
1768
+ return undefined;
1769
+ }
1770
+ if (typeof height !== 'number') {
1771
+ return undefined;
1772
+ }
1773
+ return {
1774
+ height,
1775
+ width,
1776
+ x,
1777
+ y
1778
+ };
1779
+ };
1780
+
1562
1781
  const getSavedSelectedSessionId = savedState => {
1563
1782
  if (!isObject(savedState)) {
1564
1783
  return undefined;
@@ -1586,12 +1805,14 @@ const getSavedSessions = savedState => {
1586
1805
  };
1587
1806
 
1588
1807
  const loadContent = async (state, savedState) => {
1808
+ const savedBounds = getSavedBounds(savedState);
1589
1809
  const sessions = getSavedSessions(savedState) || state.sessions;
1590
1810
  const preferredSessionId = getSavedSelectedSessionId(savedState) || state.selectedSessionId;
1591
1811
  const selectedSessionId = sessions.some(session => session.id === preferredSessionId) ? preferredSessionId : sessions[0]?.id || '';
1592
1812
  const viewMode = sessions.length === 0 ? 'list' : state.viewMode === 'detail' ? 'detail' : 'list';
1593
1813
  return {
1594
1814
  ...state,
1815
+ ...savedBounds,
1595
1816
  initial: false,
1596
1817
  selectedSessionId,
1597
1818
  sessions,
@@ -1607,6 +1828,26 @@ const renderCss = (oldState, newState) => {
1607
1828
  return [SetCss, newState.uid, css];
1608
1829
  };
1609
1830
 
1831
+ const getFocusSelector = focus => {
1832
+ switch (focus) {
1833
+ case 'composer':
1834
+ case 'input':
1835
+ return '[name="composer"]';
1836
+ case 'header':
1837
+ return '[name="create-session"]';
1838
+ case 'list':
1839
+ return '[name^="session:"]';
1840
+ case 'send-button':
1841
+ return '[name="send"]';
1842
+ default:
1843
+ return '[name="composer"]';
1844
+ }
1845
+ };
1846
+ const renderFocus = (oldState, newState) => {
1847
+ const selector = getFocusSelector(newState.focus);
1848
+ return [FocusSelector, selector];
1849
+ };
1850
+
1610
1851
  const mergeClassNames = (...classNames) => {
1611
1852
  return classNames.filter(Boolean).join(' ');
1612
1853
  };
@@ -1913,13 +2154,13 @@ const ChatSendArea = 'ChatSendArea';
1913
2154
  const Chat = 'Chat';
1914
2155
  const ChatHeader = 'ChatHeader';
1915
2156
  const Button = 'Button';
2157
+ const ButtonDisabled = 'ButtonDisabled';
1916
2158
  const ButtonPrimary = 'ButtonPrimary';
1917
- const ChatDetails = 'ChatDetails';
1918
- const ChatDetailsContent = 'ChatDetailsContent';
1919
2159
  const IconButton = 'IconButton';
1920
2160
  const Label = 'Label';
1921
2161
  const ChatList = 'ChatList';
1922
2162
  const ChatListItem = 'ChatListItem';
2163
+ const ChatListItemLabel = 'ChatListItemLabel';
1923
2164
  const Markdown = 'Markdown';
1924
2165
  const Message = 'Message';
1925
2166
  const MultilineInputBox = 'MultilineInputBox';
@@ -1927,23 +2168,72 @@ const Viewlet = 'Viewlet';
1927
2168
  const ChatWelcomeMessage = 'ChatWelcomeMessage';
1928
2169
 
1929
2170
  const HandleContextMenu = 2;
2171
+ const HandleFocus = 3;
1930
2172
  const HandleInput = 4;
1931
2173
  const HandleClick = 11;
1932
2174
  const HandleKeyDown = 12;
1933
2175
  const HandleClickClose = 13;
1934
2176
  const HandleClickSettings = 14;
2177
+ const HandleClickNew = 15;
2178
+ const HandleClickBack = 16;
2179
+ const HandleClickList = 17;
2180
+ const HandleClickDelete = 18;
2181
+ const HandleSubmit = 19;
2182
+
2183
+ const getChatSendAreaDom = composerValue => {
2184
+ const isSendDisabled = composerValue.trim() === '';
2185
+ const sendButtonClassName = isSendDisabled ? `${Button} ${ButtonPrimary} ${ButtonDisabled}` : `${Button} ${ButtonPrimary}`;
2186
+ return [{
2187
+ childCount: 2,
2188
+ className: ChatSendArea,
2189
+ type: Div
2190
+ }, {
2191
+ childCount: 0,
2192
+ className: MultilineInputBox,
2193
+ name: 'composer',
2194
+ onInput: HandleInput,
2195
+ placeholder: composePlaceholder,
2196
+ rows: 4,
2197
+ type: TextArea,
2198
+ value: composerValue
2199
+ }, {
2200
+ childCount: 1,
2201
+ className: sendButtonClassName,
2202
+ disabled: isSendDisabled,
2203
+ name: 'send',
2204
+ onClick: HandleSubmit,
2205
+ role: Button$2,
2206
+ title: sendMessage,
2207
+ type: Button$1
2208
+ }, text(send)];
2209
+ };
2210
+
2211
+ const getHeaderActionVirtualDom = item => {
2212
+ return [{
2213
+ childCount: 1,
2214
+ className: IconButton,
2215
+ name: item.name,
2216
+ onClick: item.onClick,
2217
+ role: Button$2,
2218
+ title: item.title,
2219
+ type: Button$1
2220
+ }, text(item.icon)];
2221
+ };
1935
2222
 
1936
2223
  const getChatHeaderActionsDom = () => {
1937
2224
  const items = [{
1938
2225
  icon: '+',
1939
2226
  name: 'create-session',
2227
+ onClick: HandleClickNew,
1940
2228
  title: newChat
1941
2229
  }, {
1942
2230
  icon: '⚙',
2231
+ name: 'settings',
1943
2232
  onClick: HandleClickSettings,
1944
2233
  title: settings
1945
2234
  }, {
1946
2235
  icon: '×',
2236
+ name: 'close-chat',
1947
2237
  onClick: HandleClickClose,
1948
2238
  title: closeChat
1949
2239
  }];
@@ -1951,24 +2241,7 @@ const getChatHeaderActionsDom = () => {
1951
2241
  childCount: items.length,
1952
2242
  className: ChatActions,
1953
2243
  type: Div
1954
- }, ...items.flatMap(item => {
1955
- const name = 'name' in item ? item.name : undefined;
1956
- const onClick = 'onClick' in item ? item.onClick : undefined;
1957
- return [{
1958
- childCount: 1,
1959
- className: IconButton,
1960
- ...(name ? {
1961
- name
1962
- } : {}),
1963
- ...(onClick ? {
1964
- onClick
1965
- } : {}),
1966
- role: Button$2,
1967
- tabIndex: 0,
1968
- title: item.title,
1969
- type: Button$1
1970
- }, text(item.icon)];
1971
- })];
2244
+ }, ...items.flatMap(getHeaderActionVirtualDom)];
1972
2245
  };
1973
2246
 
1974
2247
  const getChatHeaderBackButtonVirtualDom = () => {
@@ -1976,8 +2249,8 @@ const getChatHeaderBackButtonVirtualDom = () => {
1976
2249
  childCount: 1,
1977
2250
  className: IconButton,
1978
2251
  name: 'back',
2252
+ onClick: HandleClickBack,
1979
2253
  role: Button$2,
1980
- tabIndex: 0,
1981
2254
  title: backToChats,
1982
2255
  type: Button$1
1983
2256
  }, text('←')];
@@ -1999,46 +2272,6 @@ const getChatHeaderDomDetailMode = selectedSessionTitle => {
1999
2272
  }, text(selectedSessionTitle), ...getChatHeaderActionsDom()];
2000
2273
  };
2001
2274
 
2002
- const getChatDetailsDom = (selectedSessionTitle, messagesNodes, composerValue) => {
2003
- return [{
2004
- childCount: 3,
2005
- className: ChatDetails,
2006
- type: Div
2007
- }, {
2008
- childCount: 1,
2009
- className: Label,
2010
- type: Span
2011
- }, text(selectedSessionTitle), {
2012
- childCount: Math.max(messagesNodes.length, 0),
2013
- className: ChatDetailsContent,
2014
- type: Div
2015
- }, ...messagesNodes, {
2016
- childCount: 2,
2017
- className: ChatSendArea,
2018
- type: Div
2019
- }, {
2020
- childCount: 0,
2021
- className: MultilineInputBox,
2022
- name: 'composer',
2023
- placeholder: composePlaceholder,
2024
- rows: 4,
2025
- type: TextArea,
2026
- value: composerValue
2027
- }, {
2028
- childCount: 1,
2029
- className: Button + ' ' + ButtonPrimary,
2030
- name: 'send',
2031
- role: Button$2,
2032
- tabIndex: 0,
2033
- title: sendMessage,
2034
- type: Button$1
2035
- }, text(send)];
2036
- };
2037
-
2038
- const getChatContentDom = (viewMode, sessionsLength, emptyStateNodes, sessionNodes, selectedSessionTitle, messagesNodes, composerValue) => {
2039
- return getChatDetailsDom(selectedSessionTitle, messagesNodes, composerValue);
2040
- };
2041
-
2042
2275
  const getChatMessageDom = message => {
2043
2276
  return [{
2044
2277
  childCount: 2,
@@ -2047,13 +2280,14 @@ const getChatMessageDom = message => {
2047
2280
  }, {
2048
2281
  childCount: 1,
2049
2282
  className: Label,
2050
- type: Strong
2283
+ type: Div
2051
2284
  }, text(`${message.role === 'user' ? you : assistant} · ${message.time}`), {
2052
2285
  childCount: 1,
2053
2286
  className: Markdown,
2054
2287
  type: P
2055
2288
  }, text(message.text)];
2056
2289
  };
2290
+
2057
2291
  const getMessagesDom = messages => {
2058
2292
  if (messages.length === 0) {
2059
2293
  return [{
@@ -2062,25 +2296,22 @@ const getMessagesDom = messages => {
2062
2296
  type: Div
2063
2297
  }, text(startConversation)];
2064
2298
  }
2065
- return messages.flatMap(message => {
2066
- return getChatMessageDom(message);
2067
- });
2299
+ return [{
2300
+ childCount: messages.length,
2301
+ className: 'ChatMessages',
2302
+ type: Div
2303
+ }, ...messages.flatMap(getChatMessageDom)];
2068
2304
  };
2069
2305
 
2070
2306
  const getChatModeDetailVirtualDom = (sessions, selectedSessionId, composerValue) => {
2071
2307
  const selectedSession = sessions.find(session => session.id === selectedSessionId);
2072
2308
  const selectedSessionTitle = selectedSession?.title || chatTitle;
2073
2309
  const messages = selectedSession ? selectedSession.messages : [];
2074
- const messagesNodes = getMessagesDom(messages);
2075
- const contentNodes = getChatContentDom('detail', sessions.length, [], [], selectedSessionTitle, messagesNodes, composerValue);
2076
2310
  return [{
2077
- childCount: 2,
2078
- className: Viewlet + ' Chat',
2079
- onClick: HandleClick,
2080
- onInput: HandleInput,
2081
- onKeyDown: HandleKeyDown,
2311
+ childCount: 3,
2312
+ className: mergeClassNames(Viewlet, Chat),
2082
2313
  type: Div
2083
- }, ...getChatHeaderDomDetailMode(selectedSessionTitle), ...contentNodes];
2314
+ }, ...getChatHeaderDomDetailMode(selectedSessionTitle), ...getMessagesDom(messages), ...getChatSendAreaDom(composerValue)];
2084
2315
  };
2085
2316
 
2086
2317
  const getChatHeaderListModeDom = () => {
@@ -2095,18 +2326,19 @@ const getChatHeaderListModeDom = () => {
2095
2326
  }, text(chats), ...getChatHeaderActionsDom()];
2096
2327
  };
2097
2328
 
2098
- const getEmptyChatSessionsDom = sessionsLength => {
2099
- if (sessionsLength !== 0) {
2100
- return [];
2101
- }
2329
+ const getEmptyChatSessionsDom = () => {
2102
2330
  return [{
2331
+ childCount: 1,
2332
+ className: ChatList,
2333
+ type: Div
2334
+ }, {
2103
2335
  childCount: 1,
2104
2336
  className: Label,
2105
2337
  type: Div
2106
2338
  }, text(clickToOpenNewChat)];
2107
2339
  };
2108
2340
 
2109
- const getSessionDom = (session, _selectedSessionId) => {
2341
+ const getSessionDom = session => {
2110
2342
  const sessionClassName = ChatListItem;
2111
2343
  return [{
2112
2344
  childCount: 2,
@@ -2114,7 +2346,7 @@ const getSessionDom = (session, _selectedSessionId) => {
2114
2346
  type: Div
2115
2347
  }, {
2116
2348
  childCount: 1,
2117
- className: ChatName,
2349
+ className: ChatListItemLabel,
2118
2350
  name: `session:${session.id}`,
2119
2351
  onContextMenu: HandleContextMenu,
2120
2352
  tabIndex: 0,
@@ -2126,7 +2358,9 @@ const getSessionDom = (session, _selectedSessionId) => {
2126
2358
  }, {
2127
2359
  childCount: 1,
2128
2360
  className: IconButton,
2129
- name: `session-delete:${session.id}`,
2361
+ 'data-id': session.id,
2362
+ name: 'SessionDelete',
2363
+ onClick: HandleClickDelete,
2130
2364
  role: Button$2,
2131
2365
  tabIndex: 0,
2132
2366
  title: deleteChatSession,
@@ -2134,21 +2368,24 @@ const getSessionDom = (session, _selectedSessionId) => {
2134
2368
  }, text('🗑')];
2135
2369
  };
2136
2370
 
2137
- const getChatModeListVirtualDom = (sessions, selectedSessionId) => {
2138
- const sessionNodes = sessions.flatMap(session => getSessionDom(session));
2139
- const emptyStateNodes = getEmptyChatSessionsDom(sessions.length);
2371
+ const getChatListDom = (sessions, selectedSessionId) => {
2372
+ if (sessions.length === 0) {
2373
+ return getEmptyChatSessionsDom();
2374
+ }
2140
2375
  return [{
2141
- childCount: 2,
2142
- className: mergeClassNames(Viewlet, Chat),
2143
- onClick: HandleClick,
2144
- onInput: HandleInput,
2145
- onKeyDown: HandleKeyDown,
2146
- type: Div
2147
- }, ...getChatHeaderListModeDom(), {
2148
- childCount: sessions.length === 0 ? 1 : sessions.length,
2376
+ childCount: sessions.length,
2149
2377
  className: ChatList,
2378
+ onClick: HandleClickList,
2150
2379
  type: Div
2151
- }, ...(sessions.length === 0 ? emptyStateNodes : sessionNodes)];
2380
+ }, ...sessions.flatMap(getSessionDom)];
2381
+ };
2382
+
2383
+ const getChatModeListVirtualDom = (sessions, selectedSessionId, composerValue) => {
2384
+ return [{
2385
+ childCount: 3,
2386
+ className: mergeClassNames(Viewlet, Chat),
2387
+ type: Div
2388
+ }, ...getChatHeaderListModeDom(), ...getChatListDom(sessions), ...getChatSendAreaDom(composerValue)];
2152
2389
  };
2153
2390
 
2154
2391
  const getChatModeUnsupportedVirtualDom = () => {
@@ -2157,12 +2394,13 @@ const getChatModeUnsupportedVirtualDom = () => {
2157
2394
  type: Div
2158
2395
  }, text('Unknown view mode')];
2159
2396
  };
2397
+
2160
2398
  const getChatVirtualDom = (sessions, selectedSessionId, composerValue, viewMode) => {
2161
2399
  switch (viewMode) {
2162
2400
  case 'detail':
2163
2401
  return getChatModeDetailVirtualDom(sessions, selectedSessionId, composerValue);
2164
2402
  case 'list':
2165
- return getChatModeListVirtualDom(sessions);
2403
+ return getChatModeListVirtualDom(sessions, selectedSessionId, composerValue);
2166
2404
  default:
2167
2405
  return getChatModeUnsupportedVirtualDom();
2168
2406
  }
@@ -2191,14 +2429,25 @@ const renderIncremental = (oldState, newState) => {
2191
2429
  return [SetPatches, newState.uid, patches];
2192
2430
  };
2193
2431
 
2432
+ const renderValue = (oldState, newState) => {
2433
+ const {
2434
+ composerValue
2435
+ } = newState;
2436
+ return [SetValueByName, newState.uid, 'composer', composerValue];
2437
+ };
2438
+
2194
2439
  const getRenderer = diffType => {
2195
2440
  switch (diffType) {
2196
2441
  case RenderCss:
2197
2442
  return renderCss;
2443
+ case RenderFocus:
2444
+ return renderFocus;
2198
2445
  case RenderIncremental:
2199
2446
  return renderIncremental;
2200
2447
  case RenderItems:
2201
2448
  return renderItems;
2449
+ case RenderValue:
2450
+ return renderValue;
2202
2451
  default:
2203
2452
  throw new Error('unknown renderer');
2204
2453
  }
@@ -2233,22 +2482,50 @@ const renderEventListeners = () => {
2233
2482
  preventDefault: true
2234
2483
  }, {
2235
2484
  name: HandleClick,
2236
- params: ['handleClick', TargetName]
2485
+ params: ['handleClick', TargetName, 'event.target.dataset.id']
2486
+ }, {
2487
+ name: HandleClickDelete,
2488
+ params: ['handleClickDelete', 'event.target.dataset.id']
2237
2489
  }, {
2238
2490
  name: HandleClickClose,
2239
2491
  params: ['handleClickClose']
2240
2492
  }, {
2241
2493
  name: HandleClickSettings,
2242
2494
  params: ['handleClickSettings']
2495
+ }, {
2496
+ name: HandleClickNew,
2497
+ params: ['handleClickNew']
2498
+ }, {
2499
+ name: HandleClickBack,
2500
+ params: ['handleClickBack']
2501
+ }, {
2502
+ name: HandleClickList,
2503
+ params: ['handleClickList', ClientX, ClientY]
2243
2504
  }, {
2244
2505
  name: HandleInput,
2245
2506
  params: ['handleInput', TargetValue]
2507
+ }, {
2508
+ name: HandleFocus,
2509
+ params: ['handleInputFocus', TargetName]
2246
2510
  }, {
2247
2511
  name: HandleKeyDown,
2248
2512
  params: ['handleKeyDown', Key, ShiftKey]
2513
+ }, {
2514
+ name: HandleSubmit,
2515
+ params: ['handleSubmit']
2249
2516
  }];
2250
2517
  };
2251
2518
 
2519
+ const reset = async state => {
2520
+ return {
2521
+ ...state,
2522
+ composerValue: '',
2523
+ selectedSessionId: '',
2524
+ sessions: [],
2525
+ viewMode: 'list'
2526
+ };
2527
+ };
2528
+
2252
2529
  const resize = (state, dimensions) => {
2253
2530
  return {
2254
2531
  ...state,
@@ -2259,19 +2536,27 @@ const resize = (state, dimensions) => {
2259
2536
  const saveState = state => {
2260
2537
  const {
2261
2538
  composerValue,
2539
+ height,
2262
2540
  nextMessageId,
2263
2541
  renamingSessionId,
2264
2542
  selectedSessionId,
2265
2543
  sessions,
2266
- viewMode
2544
+ viewMode,
2545
+ width,
2546
+ x,
2547
+ y
2267
2548
  } = state;
2268
2549
  return {
2269
2550
  composerValue,
2551
+ height,
2270
2552
  nextMessageId,
2271
2553
  renamingSessionId,
2272
2554
  selectedSessionId,
2273
2555
  sessions,
2274
- viewMode
2556
+ viewMode,
2557
+ width,
2558
+ x,
2559
+ y
2275
2560
  };
2276
2561
  };
2277
2562
 
@@ -2298,15 +2583,21 @@ const setChatList = state => {
2298
2583
  };
2299
2584
 
2300
2585
  const commandMap = {
2586
+ 'Chat.clearInput': wrapCommand(clearInput),
2301
2587
  'Chat.create': create,
2302
2588
  'Chat.diff2': diff2,
2303
2589
  'Chat.getCommandIds': getCommandIds,
2304
2590
  'Chat.getKeyBindings': getKeyBindings,
2305
2591
  'Chat.handleChatListContextMenu': handleChatListContextMenu,
2306
2592
  'Chat.handleClick': wrapCommand(handleClick),
2593
+ 'Chat.handleClickBack': wrapCommand(handleClickBack),
2307
2594
  'Chat.handleClickClose': handleClickClose,
2595
+ 'Chat.handleClickDelete': wrapCommand(handleClickDelete),
2596
+ 'Chat.handleClickList': wrapCommand(handleClickList),
2597
+ 'Chat.handleClickNew': wrapCommand(handleClickNew),
2308
2598
  'Chat.handleClickSettings': handleClickSettings,
2309
2599
  'Chat.handleInput': wrapCommand(handleInput),
2600
+ 'Chat.handleInputFocus': wrapCommand(handleInputFocus),
2310
2601
  'Chat.handleKeyDown': wrapCommand(handleKeyDown),
2311
2602
  'Chat.handleSubmit': wrapCommand(handleSubmit),
2312
2603
  'Chat.initialize': initialize,
@@ -2314,6 +2605,7 @@ const commandMap = {
2314
2605
  'Chat.loadContent2': wrapCommand(loadContent),
2315
2606
  'Chat.render2': render2,
2316
2607
  'Chat.renderEventListeners': renderEventListeners,
2608
+ 'Chat.reset': wrapCommand(reset),
2317
2609
  'Chat.resize': wrapCommand(resize),
2318
2610
  'Chat.saveState': wrapGetter(saveState),
2319
2611
  'Chat.setChatList': wrapCommand(setChatList),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/chat-view",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Chat View Worker",
5
5
  "repository": {
6
6
  "type": "git",