@lvce-editor/chat-view 1.8.0 โ 1.10.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 +439 -45
- package/package.json +1 -1
|
@@ -1196,12 +1196,12 @@ const settings = i18nString('Settings');
|
|
|
1196
1196
|
const closeChat = i18nString('Close Chat');
|
|
1197
1197
|
const clickToOpenNewChat = i18nString('Click the + button to open a new chat.');
|
|
1198
1198
|
const startConversation = i18nString('Start a conversation by typing below.');
|
|
1199
|
-
|
|
1200
|
-
|
|
1199
|
+
i18nString('You');
|
|
1200
|
+
i18nString('Assistant');
|
|
1201
1201
|
const composePlaceholder = i18nString('Type your message. Enter to send, Shift+Enter for newline.');
|
|
1202
1202
|
const sendMessage = i18nString('Send message');
|
|
1203
1203
|
const send = i18nString('Send');
|
|
1204
|
-
const deleteChatSession = i18nString('Delete chat session');
|
|
1204
|
+
const deleteChatSession$1 = i18nString('Delete chat session');
|
|
1205
1205
|
const defaultSessionTitle = i18nString('Chat 1');
|
|
1206
1206
|
const dummyChatA = i18nString('Dummy Chat A');
|
|
1207
1207
|
const dummyChatB = i18nString('Dummy Chat B');
|
|
@@ -1209,6 +1209,7 @@ const dummyChatC = i18nString('Dummy Chat C');
|
|
|
1209
1209
|
|
|
1210
1210
|
const createDefaultState = () => {
|
|
1211
1211
|
const defaultSessionId = 'session-1';
|
|
1212
|
+
const defaultModelId = 'test';
|
|
1212
1213
|
return {
|
|
1213
1214
|
assetDir: '',
|
|
1214
1215
|
composerValue: '',
|
|
@@ -1221,16 +1222,33 @@ const createDefaultState = () => {
|
|
|
1221
1222
|
inputSource: 'script',
|
|
1222
1223
|
lastSubmittedSessionId: '',
|
|
1223
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
|
+
}],
|
|
1224
1238
|
nextMessageId: 1,
|
|
1225
1239
|
platform: 0,
|
|
1226
1240
|
renamingSessionId: '',
|
|
1241
|
+
selectedModelId: defaultModelId,
|
|
1227
1242
|
selectedSessionId: defaultSessionId,
|
|
1228
1243
|
sessions: [{
|
|
1229
1244
|
id: defaultSessionId,
|
|
1230
1245
|
messages: [],
|
|
1231
1246
|
title: defaultSessionTitle
|
|
1232
1247
|
}],
|
|
1248
|
+
tokensMax: 0,
|
|
1249
|
+
tokensUsed: 0,
|
|
1233
1250
|
uid: 0,
|
|
1251
|
+
usageOverviewEnabled: false,
|
|
1234
1252
|
viewMode: 'list',
|
|
1235
1253
|
warningCount: 0,
|
|
1236
1254
|
width: 0,
|
|
@@ -1274,7 +1292,7 @@ const diffFocus = (oldState, newState) => {
|
|
|
1274
1292
|
};
|
|
1275
1293
|
|
|
1276
1294
|
const isEqual = (oldState, newState) => {
|
|
1277
|
-
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;
|
|
1295
|
+
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.tokensMax === newState.tokensMax && oldState.tokensUsed === newState.tokensUsed && oldState.usageOverviewEnabled === newState.usageOverviewEnabled && oldState.viewMode === newState.viewMode;
|
|
1278
1296
|
};
|
|
1279
1297
|
|
|
1280
1298
|
const RenderItems = 4;
|
|
@@ -1338,6 +1356,8 @@ const Span = 8;
|
|
|
1338
1356
|
const Text = 12;
|
|
1339
1357
|
const P = 50;
|
|
1340
1358
|
const TextArea = 62;
|
|
1359
|
+
const Select$1 = 63;
|
|
1360
|
+
const Option$1 = 64;
|
|
1341
1361
|
const Reference = 100;
|
|
1342
1362
|
|
|
1343
1363
|
const Enter = 3;
|
|
@@ -1667,17 +1687,170 @@ const handleChatListContextMenu = async (name, x, y) => {
|
|
|
1667
1687
|
await invoke('ContextMenu.show', x, y, CHAT_LIST_ITEM_CONTEXT_MENU, sessionId);
|
|
1668
1688
|
};
|
|
1669
1689
|
|
|
1690
|
+
const LVCE_CHAT_SESSIONS_DB_NAME = 'lvce-chat-view-sessions';
|
|
1691
|
+
const LVCE_CHAT_SESSIONS_DB_VERSION = 1;
|
|
1692
|
+
const LVCE_CHAT_SESSIONS_STORE = 'chat-sessions';
|
|
1693
|
+
const toError = error => {
|
|
1694
|
+
if (error instanceof Error) {
|
|
1695
|
+
return error;
|
|
1696
|
+
}
|
|
1697
|
+
return new Error('IndexedDB request failed');
|
|
1698
|
+
};
|
|
1699
|
+
const requestToPromise = async createRequest => {
|
|
1700
|
+
const request = createRequest();
|
|
1701
|
+
return new Promise((resolve, reject) => {
|
|
1702
|
+
request.addEventListener('success', () => {
|
|
1703
|
+
resolve(request.result);
|
|
1704
|
+
});
|
|
1705
|
+
request.addEventListener('error', () => {
|
|
1706
|
+
reject(toError(request.error));
|
|
1707
|
+
});
|
|
1708
|
+
});
|
|
1709
|
+
};
|
|
1710
|
+
const transactionToPromise = async createTransaction => {
|
|
1711
|
+
const transaction = createTransaction();
|
|
1712
|
+
return new Promise((resolve, reject) => {
|
|
1713
|
+
transaction.addEventListener('complete', () => {
|
|
1714
|
+
resolve();
|
|
1715
|
+
});
|
|
1716
|
+
transaction.addEventListener('error', () => {
|
|
1717
|
+
reject(toError(transaction.error));
|
|
1718
|
+
});
|
|
1719
|
+
transaction.addEventListener('abort', () => {
|
|
1720
|
+
reject(toError(transaction.error));
|
|
1721
|
+
});
|
|
1722
|
+
});
|
|
1723
|
+
};
|
|
1724
|
+
const openSessionsDatabase = async () => {
|
|
1725
|
+
const request = indexedDB.open(LVCE_CHAT_SESSIONS_DB_NAME, LVCE_CHAT_SESSIONS_DB_VERSION);
|
|
1726
|
+
request.addEventListener('upgradeneeded', () => {
|
|
1727
|
+
const database = request.result;
|
|
1728
|
+
if (!database.objectStoreNames.contains(LVCE_CHAT_SESSIONS_STORE)) {
|
|
1729
|
+
database.createObjectStore(LVCE_CHAT_SESSIONS_STORE, {
|
|
1730
|
+
keyPath: 'id'
|
|
1731
|
+
});
|
|
1732
|
+
}
|
|
1733
|
+
});
|
|
1734
|
+
return requestToPromise(() => request);
|
|
1735
|
+
};
|
|
1736
|
+
class IndexedDbChatSessionStorage {
|
|
1737
|
+
async getDatabase() {
|
|
1738
|
+
if (!this.databasePromise) {
|
|
1739
|
+
this.databasePromise = openSessionsDatabase();
|
|
1740
|
+
}
|
|
1741
|
+
return this.databasePromise;
|
|
1742
|
+
}
|
|
1743
|
+
async clear() {
|
|
1744
|
+
const database = await this.getDatabase();
|
|
1745
|
+
const transaction = database.transaction(LVCE_CHAT_SESSIONS_STORE, 'readwrite');
|
|
1746
|
+
const createTransaction = () => transaction;
|
|
1747
|
+
const store = transaction.objectStore(LVCE_CHAT_SESSIONS_STORE);
|
|
1748
|
+
store.clear();
|
|
1749
|
+
await transactionToPromise(createTransaction);
|
|
1750
|
+
}
|
|
1751
|
+
async deleteSession(id) {
|
|
1752
|
+
const database = await this.getDatabase();
|
|
1753
|
+
const transaction = database.transaction(LVCE_CHAT_SESSIONS_STORE, 'readwrite');
|
|
1754
|
+
const createTransaction = () => transaction;
|
|
1755
|
+
const store = transaction.objectStore(LVCE_CHAT_SESSIONS_STORE);
|
|
1756
|
+
store.delete(id);
|
|
1757
|
+
await transactionToPromise(createTransaction);
|
|
1758
|
+
}
|
|
1759
|
+
async getSession(id) {
|
|
1760
|
+
const database = await this.getDatabase();
|
|
1761
|
+
const transaction = database.transaction(LVCE_CHAT_SESSIONS_STORE, 'readonly');
|
|
1762
|
+
const store = transaction.objectStore(LVCE_CHAT_SESSIONS_STORE);
|
|
1763
|
+
const result = await requestToPromise(() => store.get(id));
|
|
1764
|
+
return result;
|
|
1765
|
+
}
|
|
1766
|
+
async listSessions() {
|
|
1767
|
+
const database = await this.getDatabase();
|
|
1768
|
+
const transaction = database.transaction(LVCE_CHAT_SESSIONS_STORE, 'readonly');
|
|
1769
|
+
const store = transaction.objectStore(LVCE_CHAT_SESSIONS_STORE);
|
|
1770
|
+
const result = await requestToPromise(() => store.getAll());
|
|
1771
|
+
return result;
|
|
1772
|
+
}
|
|
1773
|
+
async setSession(session) {
|
|
1774
|
+
const database = await this.getDatabase();
|
|
1775
|
+
const transaction = database.transaction(LVCE_CHAT_SESSIONS_STORE, 'readwrite');
|
|
1776
|
+
const createTransaction = () => transaction;
|
|
1777
|
+
const store = transaction.objectStore(LVCE_CHAT_SESSIONS_STORE);
|
|
1778
|
+
store.put(session);
|
|
1779
|
+
await transactionToPromise(createTransaction);
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
class InMemoryChatSessionStorage {
|
|
1784
|
+
sessions = new Map();
|
|
1785
|
+
async clear() {
|
|
1786
|
+
this.sessions.clear();
|
|
1787
|
+
}
|
|
1788
|
+
async deleteSession(id) {
|
|
1789
|
+
this.sessions.delete(id);
|
|
1790
|
+
}
|
|
1791
|
+
async getSession(id) {
|
|
1792
|
+
return this.sessions.get(id);
|
|
1793
|
+
}
|
|
1794
|
+
async listSessions() {
|
|
1795
|
+
return [...this.sessions.values()];
|
|
1796
|
+
}
|
|
1797
|
+
async setSession(session) {
|
|
1798
|
+
this.sessions.set(session.id, session);
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
const createDefaultStorage = () => {
|
|
1803
|
+
if (typeof indexedDB === 'undefined') {
|
|
1804
|
+
return new InMemoryChatSessionStorage();
|
|
1805
|
+
}
|
|
1806
|
+
return new IndexedDbChatSessionStorage();
|
|
1807
|
+
};
|
|
1808
|
+
let chatSessionStorage = createDefaultStorage();
|
|
1809
|
+
const listChatSessions = async () => {
|
|
1810
|
+
const sessions = await chatSessionStorage.listSessions();
|
|
1811
|
+
return sessions.map(session => ({
|
|
1812
|
+
id: session.id,
|
|
1813
|
+
messages: [],
|
|
1814
|
+
title: session.title
|
|
1815
|
+
}));
|
|
1816
|
+
};
|
|
1817
|
+
const getChatSession = async id => {
|
|
1818
|
+
const session = await chatSessionStorage.getSession(id);
|
|
1819
|
+
if (!session) {
|
|
1820
|
+
return undefined;
|
|
1821
|
+
}
|
|
1822
|
+
return {
|
|
1823
|
+
id: session.id,
|
|
1824
|
+
messages: [...session.messages],
|
|
1825
|
+
title: session.title
|
|
1826
|
+
};
|
|
1827
|
+
};
|
|
1828
|
+
const saveChatSession = async session => {
|
|
1829
|
+
await chatSessionStorage.setSession({
|
|
1830
|
+
id: session.id,
|
|
1831
|
+
messages: [...session.messages],
|
|
1832
|
+
title: session.title
|
|
1833
|
+
});
|
|
1834
|
+
};
|
|
1835
|
+
const deleteChatSession = async id => {
|
|
1836
|
+
await chatSessionStorage.deleteSession(id);
|
|
1837
|
+
};
|
|
1838
|
+
const clearChatSessions = async () => {
|
|
1839
|
+
await chatSessionStorage.clear();
|
|
1840
|
+
};
|
|
1841
|
+
|
|
1670
1842
|
const generateSessionId = () => {
|
|
1671
1843
|
return crypto.randomUUID();
|
|
1672
1844
|
};
|
|
1673
1845
|
|
|
1674
|
-
const createSession = state => {
|
|
1846
|
+
const createSession = async state => {
|
|
1675
1847
|
const id = generateSessionId();
|
|
1676
1848
|
const session = {
|
|
1677
1849
|
id,
|
|
1678
1850
|
messages: [],
|
|
1679
1851
|
title: `Chat ${state.sessions.length + 1}`
|
|
1680
1852
|
};
|
|
1853
|
+
await saveChatSession(session);
|
|
1681
1854
|
return {
|
|
1682
1855
|
...state,
|
|
1683
1856
|
renamingSessionId: '',
|
|
@@ -1698,7 +1871,7 @@ const getNextSelectedSessionId = (sessions, deletedId) => {
|
|
|
1698
1871
|
return sessions[nextIndex].id;
|
|
1699
1872
|
};
|
|
1700
1873
|
|
|
1701
|
-
const deleteSession = (state, id) => {
|
|
1874
|
+
const deleteSession = async (state, id) => {
|
|
1702
1875
|
const {
|
|
1703
1876
|
renamingSessionId,
|
|
1704
1877
|
sessions
|
|
@@ -1707,6 +1880,7 @@ const deleteSession = (state, id) => {
|
|
|
1707
1880
|
if (filtered.length === sessions.length) {
|
|
1708
1881
|
return state;
|
|
1709
1882
|
}
|
|
1883
|
+
await deleteChatSession(id);
|
|
1710
1884
|
if (filtered.length === 0) {
|
|
1711
1885
|
return {
|
|
1712
1886
|
...state,
|
|
@@ -1716,11 +1890,22 @@ const deleteSession = (state, id) => {
|
|
|
1716
1890
|
viewMode: 'list'
|
|
1717
1891
|
};
|
|
1718
1892
|
}
|
|
1893
|
+
const nextSelectedSessionId = getNextSelectedSessionId(filtered, id);
|
|
1894
|
+
const loadedSession = await getChatSession(nextSelectedSessionId);
|
|
1895
|
+
const hydratedSessions = filtered.map(session => {
|
|
1896
|
+
if (session.id !== nextSelectedSessionId) {
|
|
1897
|
+
return session;
|
|
1898
|
+
}
|
|
1899
|
+
if (!loadedSession) {
|
|
1900
|
+
return session;
|
|
1901
|
+
}
|
|
1902
|
+
return loadedSession;
|
|
1903
|
+
});
|
|
1719
1904
|
return {
|
|
1720
1905
|
...state,
|
|
1721
1906
|
renamingSessionId: renamingSessionId === id ? '' : renamingSessionId,
|
|
1722
|
-
selectedSessionId:
|
|
1723
|
-
sessions:
|
|
1907
|
+
selectedSessionId: nextSelectedSessionId,
|
|
1908
|
+
sessions: hydratedSessions
|
|
1724
1909
|
};
|
|
1725
1910
|
};
|
|
1726
1911
|
|
|
@@ -1774,14 +1959,27 @@ const handleSubmit = async state => {
|
|
|
1774
1959
|
text: userText,
|
|
1775
1960
|
time: userTime
|
|
1776
1961
|
};
|
|
1962
|
+
let workingSessions = sessions;
|
|
1963
|
+
if (viewMode === 'detail') {
|
|
1964
|
+
const loadedSession = await getChatSession(selectedSessionId);
|
|
1965
|
+
if (loadedSession) {
|
|
1966
|
+
workingSessions = sessions.map(session => {
|
|
1967
|
+
if (session.id !== selectedSessionId) {
|
|
1968
|
+
return session;
|
|
1969
|
+
}
|
|
1970
|
+
return loadedSession;
|
|
1971
|
+
});
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1777
1974
|
let optimisticState;
|
|
1778
1975
|
if (viewMode === 'list') {
|
|
1779
1976
|
const newSessionId = generateSessionId();
|
|
1780
1977
|
const newSession = {
|
|
1781
1978
|
id: newSessionId,
|
|
1782
1979
|
messages: [userMessage],
|
|
1783
|
-
title: `Chat ${
|
|
1980
|
+
title: `Chat ${workingSessions.length + 1}`
|
|
1784
1981
|
};
|
|
1982
|
+
await saveChatSession(newSession);
|
|
1785
1983
|
optimisticState = focusInput({
|
|
1786
1984
|
...state,
|
|
1787
1985
|
composerValue: '',
|
|
@@ -1789,11 +1987,11 @@ const handleSubmit = async state => {
|
|
|
1789
1987
|
lastSubmittedSessionId: newSessionId,
|
|
1790
1988
|
nextMessageId: nextMessageId + 1,
|
|
1791
1989
|
selectedSessionId: newSessionId,
|
|
1792
|
-
sessions: [...
|
|
1990
|
+
sessions: [...workingSessions, newSession],
|
|
1793
1991
|
viewMode: 'detail'
|
|
1794
1992
|
});
|
|
1795
1993
|
} else {
|
|
1796
|
-
const updatedSessions =
|
|
1994
|
+
const updatedSessions = workingSessions.map(session => {
|
|
1797
1995
|
if (session.id !== selectedSessionId) {
|
|
1798
1996
|
return session;
|
|
1799
1997
|
}
|
|
@@ -1802,6 +2000,10 @@ const handleSubmit = async state => {
|
|
|
1802
2000
|
messages: [...session.messages, userMessage]
|
|
1803
2001
|
};
|
|
1804
2002
|
});
|
|
2003
|
+
const selectedSession = updatedSessions.find(session => session.id === selectedSessionId);
|
|
2004
|
+
if (selectedSession) {
|
|
2005
|
+
await saveChatSession(selectedSession);
|
|
2006
|
+
}
|
|
1805
2007
|
optimisticState = focusInput({
|
|
1806
2008
|
...state,
|
|
1807
2009
|
composerValue: '',
|
|
@@ -1824,6 +2026,10 @@ const handleSubmit = async state => {
|
|
|
1824
2026
|
messages: [...session.messages, assistantMessage]
|
|
1825
2027
|
};
|
|
1826
2028
|
});
|
|
2029
|
+
const selectedSession = updatedSessions.find(session => session.id === optimisticState.selectedSessionId);
|
|
2030
|
+
if (selectedSession) {
|
|
2031
|
+
await saveChatSession(selectedSession);
|
|
2032
|
+
}
|
|
1827
2033
|
return focusInput({
|
|
1828
2034
|
...optimisticState,
|
|
1829
2035
|
nextMessageId: optimisticState.nextMessageId + 1,
|
|
@@ -1845,15 +2051,26 @@ const handleClickSend = async state => {
|
|
|
1845
2051
|
return handleSubmit(submitState);
|
|
1846
2052
|
};
|
|
1847
2053
|
|
|
1848
|
-
const selectSession = (state, id) => {
|
|
2054
|
+
const selectSession = async (state, id) => {
|
|
1849
2055
|
const exists = state.sessions.some(session => session.id === id);
|
|
1850
2056
|
if (!exists) {
|
|
1851
2057
|
return state;
|
|
1852
2058
|
}
|
|
2059
|
+
const loadedSession = await getChatSession(id);
|
|
2060
|
+
const sessions = state.sessions.map(session => {
|
|
2061
|
+
if (session.id !== id) {
|
|
2062
|
+
return session;
|
|
2063
|
+
}
|
|
2064
|
+
if (!loadedSession) {
|
|
2065
|
+
return session;
|
|
2066
|
+
}
|
|
2067
|
+
return loadedSession;
|
|
2068
|
+
});
|
|
1853
2069
|
return {
|
|
1854
2070
|
...state,
|
|
1855
2071
|
renamingSessionId: '',
|
|
1856
2072
|
selectedSessionId: id,
|
|
2073
|
+
sessions,
|
|
1857
2074
|
viewMode: 'detail'
|
|
1858
2075
|
};
|
|
1859
2076
|
};
|
|
@@ -2007,7 +2224,7 @@ const handleInputFocus = async (state, name) => {
|
|
|
2007
2224
|
};
|
|
2008
2225
|
};
|
|
2009
2226
|
|
|
2010
|
-
const submitRename = state => {
|
|
2227
|
+
const submitRename = async state => {
|
|
2011
2228
|
const {
|
|
2012
2229
|
composerValue,
|
|
2013
2230
|
renamingSessionId,
|
|
@@ -2029,6 +2246,10 @@ const submitRename = state => {
|
|
|
2029
2246
|
title
|
|
2030
2247
|
};
|
|
2031
2248
|
});
|
|
2249
|
+
const renamedSession = updatedSessions.find(session => session.id === renamingSessionId);
|
|
2250
|
+
if (renamedSession) {
|
|
2251
|
+
await saveChatSession(renamedSession);
|
|
2252
|
+
}
|
|
2032
2253
|
return {
|
|
2033
2254
|
...state,
|
|
2034
2255
|
composerValue: '',
|
|
@@ -2061,6 +2282,13 @@ const handleKeyDown = async (state, key, shiftKey) => {
|
|
|
2061
2282
|
return handleSubmit(submitState);
|
|
2062
2283
|
};
|
|
2063
2284
|
|
|
2285
|
+
const handleModelChange = async (state, value) => {
|
|
2286
|
+
return {
|
|
2287
|
+
...state,
|
|
2288
|
+
selectedModelId: value
|
|
2289
|
+
};
|
|
2290
|
+
};
|
|
2291
|
+
|
|
2064
2292
|
const handleNewline = async state => {
|
|
2065
2293
|
return handleInput(state, `${state.composerValue}\n`);
|
|
2066
2294
|
};
|
|
@@ -2121,6 +2349,19 @@ const getSavedBounds = savedState => {
|
|
|
2121
2349
|
};
|
|
2122
2350
|
};
|
|
2123
2351
|
|
|
2352
|
+
const getSavedSelectedModelId = savedState => {
|
|
2353
|
+
if (!isObject(savedState)) {
|
|
2354
|
+
return undefined;
|
|
2355
|
+
}
|
|
2356
|
+
const {
|
|
2357
|
+
selectedModelId
|
|
2358
|
+
} = savedState;
|
|
2359
|
+
if (typeof selectedModelId !== 'string') {
|
|
2360
|
+
return undefined;
|
|
2361
|
+
}
|
|
2362
|
+
return selectedModelId;
|
|
2363
|
+
};
|
|
2364
|
+
|
|
2124
2365
|
const getSavedSelectedSessionId = savedState => {
|
|
2125
2366
|
if (!isObject(savedState)) {
|
|
2126
2367
|
return undefined;
|
|
@@ -2147,23 +2388,79 @@ const getSavedSessions = savedState => {
|
|
|
2147
2388
|
return sessions;
|
|
2148
2389
|
};
|
|
2149
2390
|
|
|
2391
|
+
const getSavedViewMode = savedState => {
|
|
2392
|
+
if (!isObject(savedState)) {
|
|
2393
|
+
return undefined;
|
|
2394
|
+
}
|
|
2395
|
+
const {
|
|
2396
|
+
viewMode
|
|
2397
|
+
} = savedState;
|
|
2398
|
+
if (viewMode !== 'list' && viewMode !== 'detail') {
|
|
2399
|
+
return undefined;
|
|
2400
|
+
}
|
|
2401
|
+
return viewMode;
|
|
2402
|
+
};
|
|
2403
|
+
|
|
2404
|
+
const toSummarySession = session => {
|
|
2405
|
+
return {
|
|
2406
|
+
id: session.id,
|
|
2407
|
+
messages: [],
|
|
2408
|
+
title: session.title
|
|
2409
|
+
};
|
|
2410
|
+
};
|
|
2411
|
+
const loadSelectedSessionMessages = async (sessions, selectedSessionId) => {
|
|
2412
|
+
if (!selectedSessionId) {
|
|
2413
|
+
return sessions;
|
|
2414
|
+
}
|
|
2415
|
+
const loadedSession = await getChatSession(selectedSessionId);
|
|
2416
|
+
if (!loadedSession) {
|
|
2417
|
+
return sessions;
|
|
2418
|
+
}
|
|
2419
|
+
return sessions.map(session => {
|
|
2420
|
+
if (session.id !== selectedSessionId) {
|
|
2421
|
+
return session;
|
|
2422
|
+
}
|
|
2423
|
+
return loadedSession;
|
|
2424
|
+
});
|
|
2425
|
+
};
|
|
2150
2426
|
const loadContent = async (state, savedState) => {
|
|
2151
2427
|
const savedBounds = getSavedBounds(savedState);
|
|
2152
|
-
const
|
|
2428
|
+
const savedSelectedModelId = getSavedSelectedModelId(savedState);
|
|
2429
|
+
const savedViewMode = getSavedViewMode(savedState);
|
|
2430
|
+
const legacySavedSessions = getSavedSessions(savedState);
|
|
2431
|
+
const storedSessions = await listChatSessions();
|
|
2432
|
+
let sessions = storedSessions;
|
|
2433
|
+
if (sessions.length === 0 && legacySavedSessions && legacySavedSessions.length > 0) {
|
|
2434
|
+
for (const session of legacySavedSessions) {
|
|
2435
|
+
await saveChatSession(session);
|
|
2436
|
+
}
|
|
2437
|
+
sessions = legacySavedSessions.map(toSummarySession);
|
|
2438
|
+
}
|
|
2439
|
+
if (sessions.length === 0 && state.sessions.length > 0) {
|
|
2440
|
+
for (const session of state.sessions) {
|
|
2441
|
+
await saveChatSession(session);
|
|
2442
|
+
}
|
|
2443
|
+
sessions = state.sessions.map(toSummarySession);
|
|
2444
|
+
}
|
|
2153
2445
|
const preferredSessionId = getSavedSelectedSessionId(savedState) || state.selectedSessionId;
|
|
2446
|
+
const preferredModelId = savedSelectedModelId || state.selectedModelId;
|
|
2447
|
+
const selectedModelId = state.models.some(model => model.id === preferredModelId) ? preferredModelId : state.models[0]?.id || '';
|
|
2154
2448
|
const selectedSessionId = sessions.some(session => session.id === preferredSessionId) ? preferredSessionId : sessions[0]?.id || '';
|
|
2155
|
-
|
|
2449
|
+
sessions = await loadSelectedSessionMessages(sessions, selectedSessionId);
|
|
2450
|
+
const preferredViewMode = savedViewMode || state.viewMode;
|
|
2451
|
+
const viewMode = sessions.length === 0 || !selectedSessionId ? 'list' : preferredViewMode === 'detail' ? 'detail' : 'list';
|
|
2156
2452
|
return {
|
|
2157
2453
|
...state,
|
|
2158
2454
|
...savedBounds,
|
|
2159
2455
|
initial: false,
|
|
2456
|
+
selectedModelId,
|
|
2160
2457
|
selectedSessionId,
|
|
2161
2458
|
sessions,
|
|
2162
2459
|
viewMode
|
|
2163
2460
|
};
|
|
2164
2461
|
};
|
|
2165
2462
|
|
|
2166
|
-
const openMockSession = (state, mockSessionId, mockChatMessages) => {
|
|
2463
|
+
const openMockSession = async (state, mockSessionId, mockChatMessages) => {
|
|
2167
2464
|
if (!mockSessionId) {
|
|
2168
2465
|
return state;
|
|
2169
2466
|
}
|
|
@@ -2181,6 +2478,10 @@ const openMockSession = (state, mockSessionId, mockChatMessages) => {
|
|
|
2181
2478
|
messages: mockChatMessages,
|
|
2182
2479
|
title: mockSessionId
|
|
2183
2480
|
}];
|
|
2481
|
+
const selectedSession = sessions.find(session => session.id === mockSessionId);
|
|
2482
|
+
if (selectedSession) {
|
|
2483
|
+
await saveChatSession(selectedSession);
|
|
2484
|
+
}
|
|
2184
2485
|
return {
|
|
2185
2486
|
...state,
|
|
2186
2487
|
renamingSessionId: '',
|
|
@@ -2226,6 +2527,8 @@ const renderFocusContext = (oldState, newState) => {
|
|
|
2226
2527
|
const ChatActions = 'ChatActions';
|
|
2227
2528
|
const ChatName = 'ChatName';
|
|
2228
2529
|
const ChatSendArea = 'ChatSendArea';
|
|
2530
|
+
const ChatSendAreaBottom = 'ChatSendAreaBottom';
|
|
2531
|
+
const ChatSendAreaContent = 'ChatSendAreaContent';
|
|
2229
2532
|
const Chat = 'Chat';
|
|
2230
2533
|
const ChatHeader = 'ChatHeader';
|
|
2231
2534
|
const Button = 'Button';
|
|
@@ -2233,12 +2536,22 @@ const ButtonDisabled = 'ButtonDisabled';
|
|
|
2233
2536
|
const ButtonPrimary = 'ButtonPrimary';
|
|
2234
2537
|
const IconButton = 'IconButton';
|
|
2235
2538
|
const Label = 'Label';
|
|
2539
|
+
const LabelDetail = 'LabelDetail';
|
|
2236
2540
|
const ChatList = 'ChatList';
|
|
2541
|
+
const ChatListEmpty = 'ChatListEmpty';
|
|
2237
2542
|
const ChatListItem = 'ChatListItem';
|
|
2238
2543
|
const ChatListItemLabel = 'ChatListItemLabel';
|
|
2239
2544
|
const Markdown = 'Markdown';
|
|
2240
2545
|
const Message = 'Message';
|
|
2546
|
+
const ChatMessageContent = 'ChatMessageContent';
|
|
2547
|
+
const MessageUser = 'MessageUser';
|
|
2548
|
+
const MessageAssistant = 'MessageAssistant';
|
|
2241
2549
|
const MultilineInputBox = 'MultilineInputBox';
|
|
2550
|
+
const Option = 'Option';
|
|
2551
|
+
const TokenUsageOverview = 'TokenUsageOverview';
|
|
2552
|
+
const TokenUsageRing = 'TokenUsageRing';
|
|
2553
|
+
const TokenUsageRingInner = 'TokenUsageRingInner';
|
|
2554
|
+
const Select = 'Select';
|
|
2242
2555
|
const Viewlet = 'Viewlet';
|
|
2243
2556
|
const ChatWelcomeMessage = 'ChatWelcomeMessage';
|
|
2244
2557
|
|
|
@@ -2254,14 +2567,78 @@ const HandleClickBack = 16;
|
|
|
2254
2567
|
const HandleClickList = 17;
|
|
2255
2568
|
const HandleClickDelete = 18;
|
|
2256
2569
|
const HandleSubmit = 19;
|
|
2570
|
+
const HandleModelChange = 20;
|
|
2257
2571
|
|
|
2258
|
-
const
|
|
2259
|
-
|
|
2572
|
+
const getModelOptionDOm = (model, selectedModelId) => {
|
|
2573
|
+
return [{
|
|
2574
|
+
childCount: 1,
|
|
2575
|
+
className: Option,
|
|
2576
|
+
selected: model.id === selectedModelId,
|
|
2577
|
+
type: Option$1,
|
|
2578
|
+
value: model.id
|
|
2579
|
+
}, text(model.name)];
|
|
2580
|
+
};
|
|
2581
|
+
|
|
2582
|
+
const getSendButtonDom = isSendDisabled => {
|
|
2260
2583
|
const sendButtonClassName = isSendDisabled ? `${Button} ${ButtonPrimary} ${ButtonDisabled}` : `${Button} ${ButtonPrimary}`;
|
|
2261
2584
|
return [{
|
|
2262
|
-
childCount:
|
|
2585
|
+
childCount: 1,
|
|
2586
|
+
className: sendButtonClassName,
|
|
2587
|
+
disabled: isSendDisabled,
|
|
2588
|
+
name: 'send',
|
|
2589
|
+
onClick: HandleSubmit,
|
|
2590
|
+
role: Button$2,
|
|
2591
|
+
title: sendMessage,
|
|
2592
|
+
type: Button$1
|
|
2593
|
+
}, text(send)];
|
|
2594
|
+
};
|
|
2595
|
+
|
|
2596
|
+
const clampToPercentage = (tokensUsed, tokensMax) => {
|
|
2597
|
+
if (tokensMax <= 0) {
|
|
2598
|
+
return 0;
|
|
2599
|
+
}
|
|
2600
|
+
const percentage = tokensUsed / tokensMax * 100;
|
|
2601
|
+
return Math.max(0, Math.min(100, percentage));
|
|
2602
|
+
};
|
|
2603
|
+
|
|
2604
|
+
const getUsageOverviewDom = (tokensUsed, tokensMax) => {
|
|
2605
|
+
const usagePercent = clampToPercentage(tokensUsed, tokensMax);
|
|
2606
|
+
const usageLabel = `${tokensUsed} / ${tokensMax}`;
|
|
2607
|
+
const usageTitle = `${tokensUsed} of ${tokensMax} tokens used (${Math.round(usagePercent)}%)`;
|
|
2608
|
+
return [{
|
|
2609
|
+
childCount: 3,
|
|
2610
|
+
className: TokenUsageOverview,
|
|
2611
|
+
type: Div
|
|
2612
|
+
}, {
|
|
2613
|
+
childCount: 1,
|
|
2614
|
+
className: TokenUsageRing,
|
|
2615
|
+
style: `background: conic-gradient(var(--vscode-button-background) ${usagePercent}%, var(--vscode-editorWidget-border) 0);`,
|
|
2616
|
+
title: usageTitle,
|
|
2617
|
+
type: Div
|
|
2618
|
+
}, {
|
|
2619
|
+
childCount: 0,
|
|
2620
|
+
className: TokenUsageRingInner,
|
|
2621
|
+
style: 'background: var(--vscode-editor-background);',
|
|
2622
|
+
type: Div
|
|
2623
|
+
}, {
|
|
2624
|
+
childCount: 1,
|
|
2625
|
+
className: LabelDetail,
|
|
2626
|
+
title: usageTitle,
|
|
2627
|
+
type: Span
|
|
2628
|
+
}, text(usageLabel)];
|
|
2629
|
+
};
|
|
2630
|
+
|
|
2631
|
+
const getChatSendAreaDom = (composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax) => {
|
|
2632
|
+
const isSendDisabled = composerValue.trim() === '';
|
|
2633
|
+
const modelOptions = models.flatMap(model => getModelOptionDOm(model, selectedModelId));
|
|
2634
|
+
return [{
|
|
2635
|
+
childCount: 1,
|
|
2263
2636
|
className: ChatSendArea,
|
|
2264
2637
|
type: Div
|
|
2638
|
+
}, {
|
|
2639
|
+
childCount: 2,
|
|
2640
|
+
className: ChatSendAreaContent,
|
|
2641
|
+
type: Div
|
|
2265
2642
|
}, {
|
|
2266
2643
|
childCount: 0,
|
|
2267
2644
|
className: MultilineInputBox,
|
|
@@ -2272,15 +2649,17 @@ const getChatSendAreaDom = composerValue => {
|
|
|
2272
2649
|
type: TextArea,
|
|
2273
2650
|
value: composerValue
|
|
2274
2651
|
}, {
|
|
2275
|
-
childCount:
|
|
2276
|
-
className:
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2652
|
+
childCount: usageOverviewEnabled ? 3 : 2,
|
|
2653
|
+
className: ChatSendAreaBottom,
|
|
2654
|
+
type: Div
|
|
2655
|
+
}, {
|
|
2656
|
+
childCount: models.length,
|
|
2657
|
+
className: Select,
|
|
2658
|
+
name: 'model',
|
|
2659
|
+
onInput: HandleModelChange,
|
|
2660
|
+
type: Select$1,
|
|
2661
|
+
value: selectedModelId
|
|
2662
|
+
}, ...modelOptions, ...(usageOverviewEnabled ? getUsageOverviewDom(tokensUsed, tokensMax) : []), ...getSendButtonDom(isSendDisabled)];
|
|
2284
2663
|
};
|
|
2285
2664
|
|
|
2286
2665
|
const getHeaderActionVirtualDom = item => {
|
|
@@ -2328,7 +2707,11 @@ const getChatHeaderBackButtonVirtualDom = () => {
|
|
|
2328
2707
|
role: Button$2,
|
|
2329
2708
|
title: backToChats,
|
|
2330
2709
|
type: Button$1
|
|
2331
|
-
},
|
|
2710
|
+
}, {
|
|
2711
|
+
childCount: 0,
|
|
2712
|
+
className: 'MaskIcon MaskIconArrowLeft',
|
|
2713
|
+
type: Div
|
|
2714
|
+
}];
|
|
2332
2715
|
};
|
|
2333
2716
|
|
|
2334
2717
|
const getChatHeaderDomDetailMode = selectedSessionTitle => {
|
|
@@ -2348,15 +2731,16 @@ const getChatHeaderDomDetailMode = selectedSessionTitle => {
|
|
|
2348
2731
|
};
|
|
2349
2732
|
|
|
2350
2733
|
const getChatMessageDom = message => {
|
|
2734
|
+
const roleClassName = message.role === 'user' ? MessageUser : MessageAssistant;
|
|
2351
2735
|
return [{
|
|
2352
|
-
childCount:
|
|
2353
|
-
className: Message,
|
|
2736
|
+
childCount: 1,
|
|
2737
|
+
className: mergeClassNames(Message, roleClassName),
|
|
2354
2738
|
type: Div
|
|
2355
2739
|
}, {
|
|
2356
2740
|
childCount: 1,
|
|
2357
|
-
className:
|
|
2741
|
+
className: ChatMessageContent,
|
|
2358
2742
|
type: Div
|
|
2359
|
-
},
|
|
2743
|
+
}, {
|
|
2360
2744
|
childCount: 1,
|
|
2361
2745
|
className: Markdown,
|
|
2362
2746
|
type: P
|
|
@@ -2378,7 +2762,7 @@ const getMessagesDom = messages => {
|
|
|
2378
2762
|
}, ...messages.flatMap(getChatMessageDom)];
|
|
2379
2763
|
};
|
|
2380
2764
|
|
|
2381
|
-
const getChatModeDetailVirtualDom = (sessions, selectedSessionId, composerValue) => {
|
|
2765
|
+
const getChatModeDetailVirtualDom = (sessions, selectedSessionId, composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax) => {
|
|
2382
2766
|
const selectedSession = sessions.find(session => session.id === selectedSessionId);
|
|
2383
2767
|
const selectedSessionTitle = selectedSession?.title || chatTitle;
|
|
2384
2768
|
const messages = selectedSession ? selectedSession.messages : [];
|
|
@@ -2386,7 +2770,7 @@ const getChatModeDetailVirtualDom = (sessions, selectedSessionId, composerValue)
|
|
|
2386
2770
|
childCount: 3,
|
|
2387
2771
|
className: mergeClassNames(Viewlet, Chat),
|
|
2388
2772
|
type: Div
|
|
2389
|
-
}, ...getChatHeaderDomDetailMode(selectedSessionTitle), ...getMessagesDom(messages), ...getChatSendAreaDom(composerValue)];
|
|
2773
|
+
}, ...getChatHeaderDomDetailMode(selectedSessionTitle), ...getMessagesDom(messages), ...getChatSendAreaDom(composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax)];
|
|
2390
2774
|
};
|
|
2391
2775
|
|
|
2392
2776
|
const getChatHeaderListModeDom = () => {
|
|
@@ -2404,7 +2788,7 @@ const getChatHeaderListModeDom = () => {
|
|
|
2404
2788
|
const getEmptyChatSessionsDom = () => {
|
|
2405
2789
|
return [{
|
|
2406
2790
|
childCount: 1,
|
|
2407
|
-
className:
|
|
2791
|
+
className: ChatListEmpty,
|
|
2408
2792
|
type: Div
|
|
2409
2793
|
}, {
|
|
2410
2794
|
childCount: 1,
|
|
@@ -2438,7 +2822,7 @@ const getSessionDom = session => {
|
|
|
2438
2822
|
onClick: HandleClickDelete,
|
|
2439
2823
|
role: Button$2,
|
|
2440
2824
|
tabIndex: 0,
|
|
2441
|
-
title: deleteChatSession,
|
|
2825
|
+
title: deleteChatSession$1,
|
|
2442
2826
|
type: Button$1
|
|
2443
2827
|
}, text('๐')];
|
|
2444
2828
|
};
|
|
@@ -2455,12 +2839,12 @@ const getChatListDom = (sessions, selectedSessionId) => {
|
|
|
2455
2839
|
}, ...sessions.flatMap(getSessionDom)];
|
|
2456
2840
|
};
|
|
2457
2841
|
|
|
2458
|
-
const getChatModeListVirtualDom = (sessions, selectedSessionId, composerValue) => {
|
|
2842
|
+
const getChatModeListVirtualDom = (sessions, selectedSessionId, composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax) => {
|
|
2459
2843
|
return [{
|
|
2460
2844
|
childCount: 3,
|
|
2461
2845
|
className: mergeClassNames(Viewlet, Chat),
|
|
2462
2846
|
type: Div
|
|
2463
|
-
}, ...getChatHeaderListModeDom(), ...getChatListDom(sessions), ...getChatSendAreaDom(composerValue)];
|
|
2847
|
+
}, ...getChatHeaderListModeDom(), ...getChatListDom(sessions), ...getChatSendAreaDom(composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax)];
|
|
2464
2848
|
};
|
|
2465
2849
|
|
|
2466
2850
|
const getChatModeUnsupportedVirtualDom = () => {
|
|
@@ -2470,12 +2854,12 @@ const getChatModeUnsupportedVirtualDom = () => {
|
|
|
2470
2854
|
}, text('Unknown view mode')];
|
|
2471
2855
|
};
|
|
2472
2856
|
|
|
2473
|
-
const getChatVirtualDom = (sessions, selectedSessionId, composerValue, viewMode) => {
|
|
2857
|
+
const getChatVirtualDom = (sessions, selectedSessionId, composerValue, viewMode, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax) => {
|
|
2474
2858
|
switch (viewMode) {
|
|
2475
2859
|
case 'detail':
|
|
2476
|
-
return getChatModeDetailVirtualDom(sessions, selectedSessionId, composerValue);
|
|
2860
|
+
return getChatModeDetailVirtualDom(sessions, selectedSessionId, composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax);
|
|
2477
2861
|
case 'list':
|
|
2478
|
-
return getChatModeListVirtualDom(sessions, selectedSessionId, composerValue);
|
|
2862
|
+
return getChatModeListVirtualDom(sessions, selectedSessionId, composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax);
|
|
2479
2863
|
default:
|
|
2480
2864
|
return getChatModeUnsupportedVirtualDom();
|
|
2481
2865
|
}
|
|
@@ -2485,15 +2869,20 @@ const renderItems = (oldState, newState) => {
|
|
|
2485
2869
|
const {
|
|
2486
2870
|
composerValue,
|
|
2487
2871
|
initial,
|
|
2872
|
+
models,
|
|
2873
|
+
selectedModelId,
|
|
2488
2874
|
selectedSessionId,
|
|
2489
2875
|
sessions,
|
|
2876
|
+
tokensMax,
|
|
2877
|
+
tokensUsed,
|
|
2490
2878
|
uid,
|
|
2879
|
+
usageOverviewEnabled,
|
|
2491
2880
|
viewMode
|
|
2492
2881
|
} = newState;
|
|
2493
2882
|
if (initial) {
|
|
2494
2883
|
return [SetDom2, uid, []];
|
|
2495
2884
|
}
|
|
2496
|
-
const dom = getChatVirtualDom(sessions, selectedSessionId, composerValue, viewMode);
|
|
2885
|
+
const dom = getChatVirtualDom(sessions, selectedSessionId, composerValue, viewMode, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax);
|
|
2497
2886
|
return [SetDom2, uid, dom];
|
|
2498
2887
|
};
|
|
2499
2888
|
|
|
@@ -2581,6 +2970,9 @@ const renderEventListeners = () => {
|
|
|
2581
2970
|
}, {
|
|
2582
2971
|
name: HandleInput,
|
|
2583
2972
|
params: ['handleInput', TargetValue]
|
|
2973
|
+
}, {
|
|
2974
|
+
name: HandleModelChange,
|
|
2975
|
+
params: ['handleModelChange', TargetValue]
|
|
2584
2976
|
}, {
|
|
2585
2977
|
name: HandleFocus,
|
|
2586
2978
|
params: ['handleInputFocus', TargetName]
|
|
@@ -2598,6 +2990,7 @@ const rerender = state => {
|
|
|
2598
2990
|
};
|
|
2599
2991
|
|
|
2600
2992
|
const reset = async state => {
|
|
2993
|
+
await clearChatSessions();
|
|
2601
2994
|
return {
|
|
2602
2995
|
...state,
|
|
2603
2996
|
composerValue: '',
|
|
@@ -2620,8 +3013,8 @@ const saveState = state => {
|
|
|
2620
3013
|
height,
|
|
2621
3014
|
nextMessageId,
|
|
2622
3015
|
renamingSessionId,
|
|
3016
|
+
selectedModelId,
|
|
2623
3017
|
selectedSessionId,
|
|
2624
|
-
sessions,
|
|
2625
3018
|
viewMode,
|
|
2626
3019
|
width,
|
|
2627
3020
|
x,
|
|
@@ -2632,8 +3025,8 @@ const saveState = state => {
|
|
|
2632
3025
|
height,
|
|
2633
3026
|
nextMessageId,
|
|
2634
3027
|
renamingSessionId,
|
|
3028
|
+
selectedModelId,
|
|
2635
3029
|
selectedSessionId,
|
|
2636
|
-
sessions,
|
|
2637
3030
|
viewMode,
|
|
2638
3031
|
width,
|
|
2639
3032
|
x,
|
|
@@ -2681,6 +3074,7 @@ const commandMap = {
|
|
|
2681
3074
|
'Chat.handleInput': wrapCommand(handleInput),
|
|
2682
3075
|
'Chat.handleInputFocus': wrapCommand(handleInputFocus),
|
|
2683
3076
|
'Chat.handleKeyDown': wrapCommand(handleKeyDown),
|
|
3077
|
+
'Chat.handleModelChange': wrapCommand(handleModelChange),
|
|
2684
3078
|
'Chat.handleSubmit': wrapCommand(handleSubmit),
|
|
2685
3079
|
'Chat.initialize': initialize,
|
|
2686
3080
|
'Chat.loadContent': wrapCommand(loadContent),
|