@lvce-editor/chat-view 1.12.0 → 1.14.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 +663 -242
- package/package.json +1 -1
|
@@ -486,7 +486,7 @@ const execute = (command, ...args) => {
|
|
|
486
486
|
|
|
487
487
|
const Two$1 = '2.0';
|
|
488
488
|
const callbacks = Object.create(null);
|
|
489
|
-
const get$
|
|
489
|
+
const get$3 = id => {
|
|
490
490
|
return callbacks[id];
|
|
491
491
|
};
|
|
492
492
|
const remove$1 = id => {
|
|
@@ -635,7 +635,7 @@ const warn = (...args) => {
|
|
|
635
635
|
console.warn(...args);
|
|
636
636
|
};
|
|
637
637
|
const resolve = (id, response) => {
|
|
638
|
-
const fn = get$
|
|
638
|
+
const fn = get$3(id);
|
|
639
639
|
if (!fn) {
|
|
640
640
|
console.log(response);
|
|
641
641
|
warn(`callback ${id} may already be disposed`);
|
|
@@ -991,7 +991,7 @@ const rpcs = Object.create(null);
|
|
|
991
991
|
const set$3 = (id, rpc) => {
|
|
992
992
|
rpcs[id] = rpc;
|
|
993
993
|
};
|
|
994
|
-
const get$
|
|
994
|
+
const get$2 = id => {
|
|
995
995
|
return rpcs[id];
|
|
996
996
|
};
|
|
997
997
|
const remove = id => {
|
|
@@ -1002,18 +1002,18 @@ const remove = id => {
|
|
|
1002
1002
|
const create$2 = rpcId => {
|
|
1003
1003
|
return {
|
|
1004
1004
|
async dispose() {
|
|
1005
|
-
const rpc = get$
|
|
1005
|
+
const rpc = get$2(rpcId);
|
|
1006
1006
|
await rpc.dispose();
|
|
1007
1007
|
},
|
|
1008
1008
|
// @ts-ignore
|
|
1009
1009
|
invoke(method, ...params) {
|
|
1010
|
-
const rpc = get$
|
|
1010
|
+
const rpc = get$2(rpcId);
|
|
1011
1011
|
// @ts-ignore
|
|
1012
1012
|
return rpc.invoke(method, ...params);
|
|
1013
1013
|
},
|
|
1014
1014
|
// @ts-ignore
|
|
1015
1015
|
invokeAndTransfer(method, ...params) {
|
|
1016
|
-
const rpc = get$
|
|
1016
|
+
const rpc = get$2(rpcId);
|
|
1017
1017
|
// @ts-ignore
|
|
1018
1018
|
return rpc.invokeAndTransfer(method, ...params);
|
|
1019
1019
|
},
|
|
@@ -1048,6 +1048,12 @@ const sendMessagePortToExtensionHostWorker$1 = async (port, rpcId = 0) => {
|
|
|
1048
1048
|
const command = 'HandleMessagePort.handleMessagePort2';
|
|
1049
1049
|
await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToExtensionHostWorker', port, command, rpcId);
|
|
1050
1050
|
};
|
|
1051
|
+
const getPreference = async key => {
|
|
1052
|
+
return await invoke('Preferences.get', key);
|
|
1053
|
+
};
|
|
1054
|
+
const openExternal = async uri => {
|
|
1055
|
+
await invoke('Open.openExternal', uri);
|
|
1056
|
+
};
|
|
1051
1057
|
|
|
1052
1058
|
const toCommandId = key => {
|
|
1053
1059
|
const dotIndex = key.indexOf('.');
|
|
@@ -1188,25 +1194,122 @@ const i18nString = (key, placeholders = emptyObject) => {
|
|
|
1188
1194
|
return key.replaceAll(RE_PLACEHOLDER, replacer);
|
|
1189
1195
|
};
|
|
1190
1196
|
|
|
1191
|
-
const chatTitle =
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
const
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
const
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
const
|
|
1207
|
-
|
|
1208
|
-
|
|
1197
|
+
const chatTitle = () => {
|
|
1198
|
+
return i18nString('Chat');
|
|
1199
|
+
};
|
|
1200
|
+
const chats = () => {
|
|
1201
|
+
return i18nString('Chats');
|
|
1202
|
+
};
|
|
1203
|
+
const newChat = () => {
|
|
1204
|
+
return i18nString('New Chat');
|
|
1205
|
+
};
|
|
1206
|
+
const backToChats = () => {
|
|
1207
|
+
return i18nString('Back to chats');
|
|
1208
|
+
};
|
|
1209
|
+
const settings = () => {
|
|
1210
|
+
return i18nString('Settings');
|
|
1211
|
+
};
|
|
1212
|
+
const closeChat = () => {
|
|
1213
|
+
return i18nString('Close Chat');
|
|
1214
|
+
};
|
|
1215
|
+
const clickToOpenNewChat = () => {
|
|
1216
|
+
return i18nString('Click the + button to open a new chat.');
|
|
1217
|
+
};
|
|
1218
|
+
const startConversation = () => {
|
|
1219
|
+
return i18nString('Start a conversation by typing below.');
|
|
1220
|
+
};
|
|
1221
|
+
const composePlaceholder = () => {
|
|
1222
|
+
return i18nString('Type your message. Enter to send, Shift+Enter for newline.');
|
|
1223
|
+
};
|
|
1224
|
+
const openRouterApiKeyPlaceholder = () => {
|
|
1225
|
+
return i18nString('Enter OpenRouter API key');
|
|
1226
|
+
};
|
|
1227
|
+
const sendMessage = () => {
|
|
1228
|
+
return i18nString('Send message');
|
|
1229
|
+
};
|
|
1230
|
+
const save = () => {
|
|
1231
|
+
return i18nString('Save');
|
|
1232
|
+
};
|
|
1233
|
+
const getOpenRouterApiKey = () => {
|
|
1234
|
+
return i18nString('Get API Key');
|
|
1235
|
+
};
|
|
1236
|
+
const deleteChatSession$1 = () => {
|
|
1237
|
+
return i18nString('Delete chat session');
|
|
1238
|
+
};
|
|
1239
|
+
const defaultSessionTitle = () => {
|
|
1240
|
+
return i18nString('Chat 1');
|
|
1241
|
+
};
|
|
1242
|
+
const dummyChatA = () => {
|
|
1243
|
+
return i18nString('Dummy Chat A');
|
|
1244
|
+
};
|
|
1245
|
+
const dummyChatB = () => {
|
|
1246
|
+
return i18nString('Dummy Chat B');
|
|
1247
|
+
};
|
|
1248
|
+
const dummyChatC = () => {
|
|
1249
|
+
return i18nString('Dummy Chat C');
|
|
1250
|
+
};
|
|
1251
|
+
const unknownViewMode = () => {
|
|
1252
|
+
return i18nString('Unknown view mode');
|
|
1253
|
+
};
|
|
1209
1254
|
|
|
1255
|
+
/* eslint-disable @cspell/spellchecker */
|
|
1256
|
+
|
|
1257
|
+
const getDefaultModels = () => {
|
|
1258
|
+
const defaultModelId = 'test';
|
|
1259
|
+
return [{
|
|
1260
|
+
id: defaultModelId,
|
|
1261
|
+
name: 'test',
|
|
1262
|
+
provider: 'test'
|
|
1263
|
+
}, {
|
|
1264
|
+
id: 'codex-5.3',
|
|
1265
|
+
name: 'Codex 5.3',
|
|
1266
|
+
provider: 'openRouter'
|
|
1267
|
+
}, {
|
|
1268
|
+
id: 'claude-code',
|
|
1269
|
+
name: 'Claude Code',
|
|
1270
|
+
provider: 'openRouter'
|
|
1271
|
+
}, {
|
|
1272
|
+
id: 'claude-haiku',
|
|
1273
|
+
name: 'Claude Haiku',
|
|
1274
|
+
provider: 'openRouter'
|
|
1275
|
+
}, {
|
|
1276
|
+
id: 'openRouter/openai/gpt-4o-mini',
|
|
1277
|
+
name: 'GPT-4o Mini',
|
|
1278
|
+
provider: 'openRouter'
|
|
1279
|
+
}, {
|
|
1280
|
+
id: 'openRouter/anthropic/claude-3.5-haiku',
|
|
1281
|
+
name: 'Claude 3.5 Haiku',
|
|
1282
|
+
provider: 'openRouter'
|
|
1283
|
+
}, {
|
|
1284
|
+
id: 'openRouter/google/gemini-2.0-flash-001',
|
|
1285
|
+
name: 'Gemini 2.0 Flash',
|
|
1286
|
+
provider: 'openRouter'
|
|
1287
|
+
}, {
|
|
1288
|
+
id: 'openRouter/openai/gpt-oss-20b:free',
|
|
1289
|
+
name: 'GPT OSS 20B (Free)',
|
|
1290
|
+
provider: 'openRouter'
|
|
1291
|
+
}, {
|
|
1292
|
+
id: 'openRouter/openai/gpt-oss-120b:free',
|
|
1293
|
+
name: 'GPT OSS 120B (Free)',
|
|
1294
|
+
provider: 'openRouter'
|
|
1295
|
+
}, {
|
|
1296
|
+
id: 'openRouter/meta-llama/llama-3.3-70b-instruct:free',
|
|
1297
|
+
name: 'Llama 3.3 70B Instruct (Free)',
|
|
1298
|
+
provider: 'openRouter'
|
|
1299
|
+
}, {
|
|
1300
|
+
id: 'openRouter/google/gemma-3-27b-it:free',
|
|
1301
|
+
name: 'Gemma 3 27B IT (Free)',
|
|
1302
|
+
provider: 'openRouter'
|
|
1303
|
+
}, {
|
|
1304
|
+
id: 'openRouter/qwen/qwen3-coder:free',
|
|
1305
|
+
name: 'Qwen3 Coder (Free)',
|
|
1306
|
+
provider: 'openRouter'
|
|
1307
|
+
}, {
|
|
1308
|
+
id: 'openRouter/mistralai/mistral-small-3.1-24b-instruct:free',
|
|
1309
|
+
name: 'Mistral Small 3.1 24B Instruct (Free)',
|
|
1310
|
+
provider: 'openRouter'
|
|
1311
|
+
}];
|
|
1312
|
+
};
|
|
1210
1313
|
const createDefaultState = () => {
|
|
1211
1314
|
const defaultSessionId = 'session-1';
|
|
1212
1315
|
const defaultModelId = 'test';
|
|
@@ -1222,20 +1325,12 @@ const createDefaultState = () => {
|
|
|
1222
1325
|
inputSource: 'script',
|
|
1223
1326
|
lastSubmittedSessionId: '',
|
|
1224
1327
|
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
|
-
}],
|
|
1328
|
+
models: getDefaultModels(),
|
|
1238
1329
|
nextMessageId: 1,
|
|
1330
|
+
openRouterApiBaseUrl: 'https://openrouter.ai/api/v1',
|
|
1331
|
+
openRouterApiKey: '',
|
|
1332
|
+
openRouterApiKeyInput: '',
|
|
1333
|
+
openRouterApiKeysSettingsUrl: 'https://openrouter.ai/settings/keys',
|
|
1239
1334
|
platform: 0,
|
|
1240
1335
|
renamingSessionId: '',
|
|
1241
1336
|
selectedModelId: defaultModelId,
|
|
@@ -1243,7 +1338,7 @@ const createDefaultState = () => {
|
|
|
1243
1338
|
sessions: [{
|
|
1244
1339
|
id: defaultSessionId,
|
|
1245
1340
|
messages: [],
|
|
1246
|
-
title: defaultSessionTitle
|
|
1341
|
+
title: defaultSessionTitle()
|
|
1247
1342
|
}],
|
|
1248
1343
|
tokensMax: 0,
|
|
1249
1344
|
tokensUsed: 0,
|
|
@@ -1258,7 +1353,7 @@ const createDefaultState = () => {
|
|
|
1258
1353
|
};
|
|
1259
1354
|
|
|
1260
1355
|
const {
|
|
1261
|
-
get,
|
|
1356
|
+
get: get$1,
|
|
1262
1357
|
getCommandIds,
|
|
1263
1358
|
registerCommands,
|
|
1264
1359
|
set,
|
|
@@ -1266,7 +1361,7 @@ const {
|
|
|
1266
1361
|
wrapGetter
|
|
1267
1362
|
} = create$1();
|
|
1268
1363
|
|
|
1269
|
-
const create = (uid,
|
|
1364
|
+
const create = (uid, x, y, width, height, platform, assetDir) => {
|
|
1270
1365
|
const state = {
|
|
1271
1366
|
...createDefaultState(),
|
|
1272
1367
|
assetDir,
|
|
@@ -1327,7 +1422,7 @@ const diff2 = uid => {
|
|
|
1327
1422
|
const {
|
|
1328
1423
|
newState,
|
|
1329
1424
|
oldState
|
|
1330
|
-
} = get(uid);
|
|
1425
|
+
} = get$1(uid);
|
|
1331
1426
|
const result = diff(oldState, newState);
|
|
1332
1427
|
return result;
|
|
1333
1428
|
};
|
|
@@ -1352,8 +1447,11 @@ const Button$2 = 'button';
|
|
|
1352
1447
|
|
|
1353
1448
|
const Button$1 = 1;
|
|
1354
1449
|
const Div = 4;
|
|
1450
|
+
const Input = 6;
|
|
1355
1451
|
const Span = 8;
|
|
1356
1452
|
const Text = 12;
|
|
1453
|
+
const Li = 48;
|
|
1454
|
+
const Ol = 49;
|
|
1357
1455
|
const P = 50;
|
|
1358
1456
|
const TextArea = 62;
|
|
1359
1457
|
const Select$1 = 63;
|
|
@@ -1676,107 +1774,184 @@ const getKeyBindings = () => {
|
|
|
1676
1774
|
}];
|
|
1677
1775
|
};
|
|
1678
1776
|
|
|
1679
|
-
const
|
|
1777
|
+
const getSelectedSessionId = state => {
|
|
1778
|
+
return state.selectedSessionId;
|
|
1779
|
+
};
|
|
1780
|
+
|
|
1781
|
+
const getListIndex = (state, eventX, eventY) => {
|
|
1782
|
+
const {
|
|
1783
|
+
headerHeight,
|
|
1784
|
+
height,
|
|
1785
|
+
listItemHeight,
|
|
1786
|
+
width,
|
|
1787
|
+
x,
|
|
1788
|
+
y
|
|
1789
|
+
} = state;
|
|
1790
|
+
if (eventX < x || eventY < y) {
|
|
1791
|
+
return -1;
|
|
1792
|
+
}
|
|
1793
|
+
if (eventX >= x + width || eventY >= y + height) {
|
|
1794
|
+
return -1;
|
|
1795
|
+
}
|
|
1796
|
+
const listY = eventY - y - headerHeight;
|
|
1797
|
+
if (listY < 0) {
|
|
1798
|
+
return -1;
|
|
1799
|
+
}
|
|
1800
|
+
const itemHeight = listItemHeight > 0 ? listItemHeight : 40;
|
|
1801
|
+
return Math.floor(listY / itemHeight);
|
|
1802
|
+
};
|
|
1803
|
+
|
|
1680
1804
|
const CHAT_LIST_ITEM_CONTEXT_MENU = 'ChatListItemContextMenu';
|
|
1681
|
-
const handleChatListContextMenu = async (
|
|
1682
|
-
|
|
1683
|
-
|
|
1805
|
+
const handleChatListContextMenu = async (state, eventX, eventY) => {
|
|
1806
|
+
const index = getListIndex(state, eventX, eventY);
|
|
1807
|
+
if (index === -1) {
|
|
1808
|
+
return state;
|
|
1684
1809
|
}
|
|
1685
|
-
const
|
|
1686
|
-
|
|
1687
|
-
|
|
1810
|
+
const item = state.sessions[index];
|
|
1811
|
+
if (!item) {
|
|
1812
|
+
return state;
|
|
1813
|
+
}
|
|
1814
|
+
await invoke('ContextMenu.show', eventX, eventY, CHAT_LIST_ITEM_CONTEXT_MENU, item.id);
|
|
1815
|
+
return state;
|
|
1688
1816
|
};
|
|
1689
1817
|
|
|
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
1818
|
const toError = error => {
|
|
1694
1819
|
if (error instanceof Error) {
|
|
1695
1820
|
return error;
|
|
1696
1821
|
}
|
|
1697
1822
|
return new Error('IndexedDB request failed');
|
|
1698
1823
|
};
|
|
1824
|
+
|
|
1699
1825
|
const requestToPromise = async createRequest => {
|
|
1700
1826
|
const request = createRequest();
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1827
|
+
const {
|
|
1828
|
+
promise,
|
|
1829
|
+
reject,
|
|
1830
|
+
resolve
|
|
1831
|
+
} = Promise.withResolvers();
|
|
1832
|
+
request.addEventListener('success', () => {
|
|
1833
|
+
resolve(request.result);
|
|
1708
1834
|
});
|
|
1709
|
-
|
|
1710
|
-
|
|
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
|
-
});
|
|
1835
|
+
request.addEventListener('error', () => {
|
|
1836
|
+
reject(toError(request.error));
|
|
1722
1837
|
});
|
|
1838
|
+
return promise;
|
|
1723
1839
|
};
|
|
1724
|
-
|
|
1725
|
-
|
|
1840
|
+
|
|
1841
|
+
const openSessionsDatabase = async (databaseName, databaseVersion, storeName) => {
|
|
1842
|
+
const request = indexedDB.open(databaseName, databaseVersion);
|
|
1726
1843
|
request.addEventListener('upgradeneeded', () => {
|
|
1727
1844
|
const database = request.result;
|
|
1728
|
-
if (!database.objectStoreNames.contains(
|
|
1729
|
-
database.createObjectStore(
|
|
1845
|
+
if (!database.objectStoreNames.contains(storeName)) {
|
|
1846
|
+
database.createObjectStore(storeName, {
|
|
1730
1847
|
keyPath: 'id'
|
|
1731
1848
|
});
|
|
1732
1849
|
}
|
|
1733
1850
|
});
|
|
1734
1851
|
return requestToPromise(() => request);
|
|
1735
1852
|
};
|
|
1853
|
+
|
|
1854
|
+
const getDatabase = async (getDatabasePromise, setDatabasePromise, databaseName, databaseVersion, storeName) => {
|
|
1855
|
+
const existingDatabasePromise = getDatabasePromise();
|
|
1856
|
+
if (existingDatabasePromise) {
|
|
1857
|
+
return existingDatabasePromise;
|
|
1858
|
+
}
|
|
1859
|
+
const nextDatabasePromise = openSessionsDatabase(databaseName, databaseVersion, storeName);
|
|
1860
|
+
setDatabasePromise(nextDatabasePromise);
|
|
1861
|
+
return nextDatabasePromise;
|
|
1862
|
+
};
|
|
1863
|
+
|
|
1864
|
+
const transactionToPromise = async createTransaction => {
|
|
1865
|
+
const transaction = createTransaction();
|
|
1866
|
+
const {
|
|
1867
|
+
promise,
|
|
1868
|
+
reject,
|
|
1869
|
+
resolve
|
|
1870
|
+
} = Promise.withResolvers();
|
|
1871
|
+
transaction.addEventListener('complete', () => {
|
|
1872
|
+
resolve();
|
|
1873
|
+
});
|
|
1874
|
+
transaction.addEventListener('error', () => {
|
|
1875
|
+
reject(toError(transaction.error));
|
|
1876
|
+
});
|
|
1877
|
+
transaction.addEventListener('abort', () => {
|
|
1878
|
+
reject(toError(transaction.error));
|
|
1879
|
+
});
|
|
1880
|
+
return promise;
|
|
1881
|
+
};
|
|
1882
|
+
|
|
1883
|
+
const clear = async (getDatabasePromise, setDatabasePromise, databaseName, databaseVersion, storeName) => {
|
|
1884
|
+
const database = await getDatabase(getDatabasePromise, setDatabasePromise, databaseName, databaseVersion, storeName);
|
|
1885
|
+
const transaction = database.transaction(storeName, 'readwrite');
|
|
1886
|
+
const createTransaction = () => transaction;
|
|
1887
|
+
const store = transaction.objectStore(storeName);
|
|
1888
|
+
store.clear();
|
|
1889
|
+
await transactionToPromise(createTransaction);
|
|
1890
|
+
};
|
|
1891
|
+
|
|
1892
|
+
const deleteSession$1 = async (getDatabasePromise, setDatabasePromise, databaseName, databaseVersion, storeName, id) => {
|
|
1893
|
+
const database = await getDatabase(getDatabasePromise, setDatabasePromise, databaseName, databaseVersion, storeName);
|
|
1894
|
+
const transaction = database.transaction(storeName, 'readwrite');
|
|
1895
|
+
const createTransaction = () => transaction;
|
|
1896
|
+
const store = transaction.objectStore(storeName);
|
|
1897
|
+
store.delete(id);
|
|
1898
|
+
await transactionToPromise(createTransaction);
|
|
1899
|
+
};
|
|
1900
|
+
|
|
1901
|
+
const getSession = async (getDatabasePromise, setDatabasePromise, databaseName, databaseVersion, storeName, id) => {
|
|
1902
|
+
const database = await getDatabase(getDatabasePromise, setDatabasePromise, databaseName, databaseVersion, storeName);
|
|
1903
|
+
const transaction = database.transaction(storeName, 'readonly');
|
|
1904
|
+
const store = transaction.objectStore(storeName);
|
|
1905
|
+
const result = await requestToPromise(() => store.get(id));
|
|
1906
|
+
return result;
|
|
1907
|
+
};
|
|
1908
|
+
|
|
1909
|
+
const listSessions = async (getDatabasePromise, setDatabasePromise, databaseName, databaseVersion, storeName) => {
|
|
1910
|
+
const database = await getDatabase(getDatabasePromise, setDatabasePromise, databaseName, databaseVersion, storeName);
|
|
1911
|
+
const transaction = database.transaction(storeName, 'readonly');
|
|
1912
|
+
const store = transaction.objectStore(storeName);
|
|
1913
|
+
const result = await requestToPromise(() => store.getAll());
|
|
1914
|
+
return result;
|
|
1915
|
+
};
|
|
1916
|
+
|
|
1917
|
+
const setSession = async (getDatabasePromise, setDatabasePromise, databaseName, databaseVersion, storeName, session) => {
|
|
1918
|
+
const database = await getDatabase(getDatabasePromise, setDatabasePromise, databaseName, databaseVersion, storeName);
|
|
1919
|
+
const transaction = database.transaction(storeName, 'readwrite');
|
|
1920
|
+
const createTransaction = () => transaction;
|
|
1921
|
+
const store = transaction.objectStore(storeName);
|
|
1922
|
+
store.put(session);
|
|
1923
|
+
await transactionToPromise(createTransaction);
|
|
1924
|
+
};
|
|
1925
|
+
|
|
1736
1926
|
class IndexedDbChatSessionStorage {
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1927
|
+
constructor(options = {}) {
|
|
1928
|
+
this.state = {
|
|
1929
|
+
databaseName: options.databaseName || 'lvce-chat-view-sessions',
|
|
1930
|
+
databasePromise: undefined,
|
|
1931
|
+
databaseVersion: options.databaseVersion || 1,
|
|
1932
|
+
storeName: options.storeName || 'chat-sessions'
|
|
1933
|
+
};
|
|
1742
1934
|
}
|
|
1935
|
+
getDatabasePromise = () => {
|
|
1936
|
+
return this.state.databasePromise;
|
|
1937
|
+
};
|
|
1938
|
+
setDatabasePromise = databasePromise => {
|
|
1939
|
+
this.state.databasePromise = databasePromise;
|
|
1940
|
+
};
|
|
1743
1941
|
async clear() {
|
|
1744
|
-
|
|
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);
|
|
1942
|
+
return clear(this.getDatabasePromise, this.setDatabasePromise, this.state.databaseName, this.state.databaseVersion, this.state.storeName);
|
|
1750
1943
|
}
|
|
1751
1944
|
async deleteSession(id) {
|
|
1752
|
-
|
|
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);
|
|
1945
|
+
return deleteSession$1(this.getDatabasePromise, this.setDatabasePromise, this.state.databaseName, this.state.databaseVersion, this.state.storeName, id);
|
|
1758
1946
|
}
|
|
1759
1947
|
async getSession(id) {
|
|
1760
|
-
|
|
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;
|
|
1948
|
+
return getSession(this.getDatabasePromise, this.setDatabasePromise, this.state.databaseName, this.state.databaseVersion, this.state.storeName, id);
|
|
1765
1949
|
}
|
|
1766
1950
|
async listSessions() {
|
|
1767
|
-
|
|
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;
|
|
1951
|
+
return listSessions(this.getDatabasePromise, this.setDatabasePromise, this.state.databaseName, this.state.databaseVersion, this.state.storeName);
|
|
1772
1952
|
}
|
|
1773
1953
|
async setSession(session) {
|
|
1774
|
-
|
|
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);
|
|
1954
|
+
return setSession(this.getDatabasePromise, this.setDatabasePromise, this.state.databaseName, this.state.databaseVersion, this.state.storeName, session);
|
|
1780
1955
|
}
|
|
1781
1956
|
}
|
|
1782
1957
|
|
|
@@ -1909,22 +2084,135 @@ const deleteSession = async (state, id) => {
|
|
|
1909
2084
|
};
|
|
1910
2085
|
};
|
|
1911
2086
|
|
|
1912
|
-
const
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
focus: 'composer',
|
|
1916
|
-
focused: true
|
|
1917
|
-
};
|
|
2087
|
+
const handleClickOpenRouterApiKeySettings = async state => {
|
|
2088
|
+
await openExternal(state.openRouterApiKeysSettingsUrl);
|
|
2089
|
+
return state;
|
|
1918
2090
|
};
|
|
1919
2091
|
|
|
2092
|
+
const openRouterApiKeyRequiredMessage = 'OpenRouter API key is not configured. Enter your OpenRouter API key below and click Save.';
|
|
2093
|
+
const openRouterRequestFailedMessage = 'OpenRouter request failed. Possible reasons:';
|
|
2094
|
+
const openRouterRequestFailureReasons = ['ContentSecurityPolicyViolation: Check DevTools for details.', 'OpenRouter server offline: Check DevTools for details.', 'Check your internet connection.'];
|
|
2095
|
+
|
|
1920
2096
|
const delay = async ms => {
|
|
1921
2097
|
await new Promise(resolve => setTimeout(resolve, ms));
|
|
1922
2098
|
};
|
|
1923
|
-
|
|
2099
|
+
|
|
2100
|
+
const getMockAiResponse = async userMessage => {
|
|
2101
|
+
await delay(800);
|
|
1924
2102
|
return `Mock AI response: I received "${userMessage}".`;
|
|
1925
2103
|
};
|
|
1926
|
-
|
|
1927
|
-
|
|
2104
|
+
|
|
2105
|
+
const defaultOpenRouterApiBaseUrl = 'https://openrouter.ai/api/v1';
|
|
2106
|
+
const getOpenRouterApiEndpoint = openRouterApiBaseUrl => {
|
|
2107
|
+
const trimmedBaseUrl = (openRouterApiBaseUrl || defaultOpenRouterApiBaseUrl).replace(/\/+$/, '');
|
|
2108
|
+
return `${trimmedBaseUrl}/chat/completions`;
|
|
2109
|
+
};
|
|
2110
|
+
|
|
2111
|
+
const getTextContent = content => {
|
|
2112
|
+
if (typeof content === 'string') {
|
|
2113
|
+
return content;
|
|
2114
|
+
}
|
|
2115
|
+
if (!Array.isArray(content)) {
|
|
2116
|
+
return '';
|
|
2117
|
+
}
|
|
2118
|
+
const textParts = [];
|
|
2119
|
+
for (const part of content) {
|
|
2120
|
+
if (!part || typeof part !== 'object') {
|
|
2121
|
+
continue;
|
|
2122
|
+
}
|
|
2123
|
+
const maybeType = Reflect.get(part, 'type');
|
|
2124
|
+
const maybeText = Reflect.get(part, 'text');
|
|
2125
|
+
if (maybeType === 'text' && typeof maybeText === 'string') {
|
|
2126
|
+
textParts.push(maybeText);
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
return textParts.join('\n');
|
|
2130
|
+
};
|
|
2131
|
+
|
|
2132
|
+
const getOpenRouterAssistantText = async (userText, modelId, openRouterApiKey, openRouterApiBaseUrl) => {
|
|
2133
|
+
let response;
|
|
2134
|
+
try {
|
|
2135
|
+
response = await fetch(getOpenRouterApiEndpoint(openRouterApiBaseUrl), {
|
|
2136
|
+
body: JSON.stringify({
|
|
2137
|
+
messages: [{
|
|
2138
|
+
content: userText,
|
|
2139
|
+
role: 'user'
|
|
2140
|
+
}],
|
|
2141
|
+
model: modelId
|
|
2142
|
+
}),
|
|
2143
|
+
headers: {
|
|
2144
|
+
Authorization: `Bearer ${openRouterApiKey}`,
|
|
2145
|
+
'Content-Type': 'application/json'
|
|
2146
|
+
},
|
|
2147
|
+
method: 'POST'
|
|
2148
|
+
});
|
|
2149
|
+
} catch {
|
|
2150
|
+
throw new Error(openRouterRequestFailedMessage);
|
|
2151
|
+
}
|
|
2152
|
+
if (!response.ok) {
|
|
2153
|
+
throw new Error(`Failed to get OpenRouter response: ${response.status}`);
|
|
2154
|
+
}
|
|
2155
|
+
const parsed = await response.json();
|
|
2156
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
2157
|
+
return '';
|
|
2158
|
+
}
|
|
2159
|
+
const choices = Reflect.get(parsed, 'choices');
|
|
2160
|
+
if (!Array.isArray(choices)) {
|
|
2161
|
+
return '';
|
|
2162
|
+
}
|
|
2163
|
+
const firstChoice = choices[0];
|
|
2164
|
+
if (!firstChoice || typeof firstChoice !== 'object') {
|
|
2165
|
+
return '';
|
|
2166
|
+
}
|
|
2167
|
+
const message = Reflect.get(firstChoice, 'message');
|
|
2168
|
+
if (!message || typeof message !== 'object') {
|
|
2169
|
+
return '';
|
|
2170
|
+
}
|
|
2171
|
+
const content = Reflect.get(message, 'content');
|
|
2172
|
+
return getTextContent(content);
|
|
2173
|
+
};
|
|
2174
|
+
|
|
2175
|
+
/* eslint-disable @cspell/spellchecker */
|
|
2176
|
+
const getOpenRouterModelId = selectedModelId => {
|
|
2177
|
+
const openRouterPrefix = 'openrouter/';
|
|
2178
|
+
if (selectedModelId.toLowerCase().startsWith(openRouterPrefix)) {
|
|
2179
|
+
return selectedModelId.slice(openRouterPrefix.length);
|
|
2180
|
+
}
|
|
2181
|
+
return selectedModelId;
|
|
2182
|
+
};
|
|
2183
|
+
|
|
2184
|
+
/* eslint-disable @cspell/spellchecker */
|
|
2185
|
+
|
|
2186
|
+
const isOpenRouterModel = (selectedModelId, models) => {
|
|
2187
|
+
const selectedModel = models.find(model => model.id === selectedModelId);
|
|
2188
|
+
const normalizedProvider = selectedModel?.provider?.toLowerCase();
|
|
2189
|
+
if (normalizedProvider === 'openrouter' || normalizedProvider === 'open-router') {
|
|
2190
|
+
return true;
|
|
2191
|
+
}
|
|
2192
|
+
return selectedModelId.toLowerCase().startsWith('openrouter/');
|
|
2193
|
+
};
|
|
2194
|
+
|
|
2195
|
+
const getAiResponse = async (userText, nextMessageId, selectedModelId, models, openRouterApiKey, openRouterApiBaseUrl) => {
|
|
2196
|
+
let text = '';
|
|
2197
|
+
const usesOpenRouterModel = isOpenRouterModel(selectedModelId, models);
|
|
2198
|
+
if (usesOpenRouterModel) {
|
|
2199
|
+
if (openRouterApiKey) {
|
|
2200
|
+
try {
|
|
2201
|
+
text = await getOpenRouterAssistantText(userText, getOpenRouterModelId(selectedModelId), openRouterApiKey, openRouterApiBaseUrl);
|
|
2202
|
+
} catch (error) {
|
|
2203
|
+
if (error instanceof Error && error.message) {
|
|
2204
|
+
text = error.message;
|
|
2205
|
+
} else {
|
|
2206
|
+
text = openRouterRequestFailedMessage;
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
} else {
|
|
2210
|
+
text = openRouterApiKeyRequiredMessage;
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
if (!text && !usesOpenRouterModel) {
|
|
2214
|
+
text = await getMockAiResponse(userText);
|
|
2215
|
+
}
|
|
1928
2216
|
const assistantTime = new Date().toLocaleTimeString([], {
|
|
1929
2217
|
hour: '2-digit',
|
|
1930
2218
|
minute: '2-digit'
|
|
@@ -1932,15 +2220,88 @@ const getAiResponse = async (userText, nextMessageId) => {
|
|
|
1932
2220
|
return {
|
|
1933
2221
|
id: `message-${nextMessageId}`,
|
|
1934
2222
|
role: 'assistant',
|
|
1935
|
-
text
|
|
2223
|
+
text,
|
|
1936
2224
|
time: assistantTime
|
|
1937
2225
|
};
|
|
1938
2226
|
};
|
|
1939
2227
|
|
|
2228
|
+
const get = async key => {
|
|
2229
|
+
return getPreference(key);
|
|
2230
|
+
};
|
|
2231
|
+
const update = async settings => {
|
|
2232
|
+
await invoke('Preferences.update', settings);
|
|
2233
|
+
};
|
|
2234
|
+
|
|
2235
|
+
const setOpenRouterApiKey = async (state, openRouterApiKey, persist = true) => {
|
|
2236
|
+
if (persist) {
|
|
2237
|
+
await update({
|
|
2238
|
+
'secrets.openRouterApiKey': openRouterApiKey
|
|
2239
|
+
});
|
|
2240
|
+
}
|
|
2241
|
+
return {
|
|
2242
|
+
...state,
|
|
2243
|
+
openRouterApiKey,
|
|
2244
|
+
openRouterApiKeyInput: openRouterApiKey
|
|
2245
|
+
};
|
|
2246
|
+
};
|
|
2247
|
+
|
|
2248
|
+
const handleClickSaveOpenRouterApiKey = async state => {
|
|
2249
|
+
const {
|
|
2250
|
+
openRouterApiKeyInput
|
|
2251
|
+
} = state;
|
|
2252
|
+
const openRouterApiKey = openRouterApiKeyInput.trim();
|
|
2253
|
+
if (!openRouterApiKey) {
|
|
2254
|
+
return state;
|
|
2255
|
+
}
|
|
2256
|
+
const updatedState = await setOpenRouterApiKey(state, openRouterApiKey);
|
|
2257
|
+
const session = updatedState.sessions.find(item => item.id === updatedState.selectedSessionId);
|
|
2258
|
+
if (!session) {
|
|
2259
|
+
return updatedState;
|
|
2260
|
+
}
|
|
2261
|
+
const lastMessage = session.messages.at(-1);
|
|
2262
|
+
const shouldRetryOpenRouter = lastMessage?.role === 'assistant' && lastMessage.text === openRouterApiKeyRequiredMessage;
|
|
2263
|
+
if (!shouldRetryOpenRouter) {
|
|
2264
|
+
return updatedState;
|
|
2265
|
+
}
|
|
2266
|
+
const previousUserMessage = session.messages.toReversed().find(item => item.role === 'user');
|
|
2267
|
+
if (!previousUserMessage) {
|
|
2268
|
+
return updatedState;
|
|
2269
|
+
}
|
|
2270
|
+
const assistantMessage = await getAiResponse(previousUserMessage.text, updatedState.nextMessageId, updatedState.selectedModelId, updatedState.models, openRouterApiKey, updatedState.openRouterApiBaseUrl);
|
|
2271
|
+
const updatedSession = {
|
|
2272
|
+
...session,
|
|
2273
|
+
messages: [...session.messages.slice(0, -1), assistantMessage]
|
|
2274
|
+
};
|
|
2275
|
+
await saveChatSession(updatedSession);
|
|
2276
|
+
const updatedSessions = updatedState.sessions.map(item => {
|
|
2277
|
+
if (item.id !== updatedState.selectedSessionId) {
|
|
2278
|
+
return item;
|
|
2279
|
+
}
|
|
2280
|
+
return updatedSession;
|
|
2281
|
+
});
|
|
2282
|
+
return {
|
|
2283
|
+
...updatedState,
|
|
2284
|
+
nextMessageId: updatedState.nextMessageId + 1,
|
|
2285
|
+
sessions: updatedSessions
|
|
2286
|
+
};
|
|
2287
|
+
};
|
|
2288
|
+
|
|
2289
|
+
const focusInput = state => {
|
|
2290
|
+
return {
|
|
2291
|
+
...state,
|
|
2292
|
+
focus: 'composer',
|
|
2293
|
+
focused: true
|
|
2294
|
+
};
|
|
2295
|
+
};
|
|
2296
|
+
|
|
1940
2297
|
const handleSubmit = async state => {
|
|
1941
2298
|
const {
|
|
1942
2299
|
composerValue,
|
|
2300
|
+
models,
|
|
1943
2301
|
nextMessageId,
|
|
2302
|
+
openRouterApiBaseUrl,
|
|
2303
|
+
openRouterApiKey,
|
|
2304
|
+
selectedModelId,
|
|
1944
2305
|
selectedSessionId,
|
|
1945
2306
|
sessions,
|
|
1946
2307
|
viewMode
|
|
@@ -2016,7 +2377,7 @@ const handleSubmit = async state => {
|
|
|
2016
2377
|
set(state.uid, state, optimisticState);
|
|
2017
2378
|
// @ts-ignore
|
|
2018
2379
|
await invoke('Chat.rerender');
|
|
2019
|
-
const assistantMessage = await getAiResponse(userText, optimisticState.nextMessageId);
|
|
2380
|
+
const assistantMessage = await getAiResponse(userText, optimisticState.nextMessageId, selectedModelId, models, openRouterApiKey, openRouterApiBaseUrl);
|
|
2020
2381
|
const updatedSessions = optimisticState.sessions.map(session => {
|
|
2021
2382
|
if (session.id !== optimisticState.selectedSessionId) {
|
|
2022
2383
|
return session;
|
|
@@ -2051,6 +2412,37 @@ const handleClickSend = async state => {
|
|
|
2051
2412
|
return handleSubmit(submitState);
|
|
2052
2413
|
};
|
|
2053
2414
|
|
|
2415
|
+
const Composer = 'composer';
|
|
2416
|
+
const Send = 'send';
|
|
2417
|
+
const Back = 'back';
|
|
2418
|
+
const Model = 'model';
|
|
2419
|
+
const CreateSession = 'create-session';
|
|
2420
|
+
const Settings = 'settings';
|
|
2421
|
+
const CloseChat = 'close-chat';
|
|
2422
|
+
const SessionDelete = 'SessionDelete';
|
|
2423
|
+
const SessionPrefix = 'session:';
|
|
2424
|
+
const RenamePrefix = 'session-rename:';
|
|
2425
|
+
const getSessionInputName = sessionId => {
|
|
2426
|
+
return `${SessionPrefix}${sessionId}`;
|
|
2427
|
+
};
|
|
2428
|
+
const isSessionInputName = name => {
|
|
2429
|
+
return name.startsWith(SessionPrefix);
|
|
2430
|
+
};
|
|
2431
|
+
const getSessionIdFromInputName = name => {
|
|
2432
|
+
return name.slice(SessionPrefix.length);
|
|
2433
|
+
};
|
|
2434
|
+
const isRenameInputName = name => {
|
|
2435
|
+
return name.startsWith(RenamePrefix);
|
|
2436
|
+
};
|
|
2437
|
+
const getRenameIdFromInputName = name => {
|
|
2438
|
+
return name.slice(RenamePrefix.length);
|
|
2439
|
+
};
|
|
2440
|
+
|
|
2441
|
+
/* eslint-disable @cspell/spellchecker */
|
|
2442
|
+
const OpenRouterApiKeyInput = 'open-router-api-key';
|
|
2443
|
+
const SaveOpenRouterApiKey = 'save-openrouter-api-key';
|
|
2444
|
+
const OpenOpenRouterApiKeySettings = 'open-openrouter-api-key-settings';
|
|
2445
|
+
|
|
2054
2446
|
const selectSession = async (state, id) => {
|
|
2055
2447
|
const exists = state.sessions.some(session => session.id === id);
|
|
2056
2448
|
if (!exists) {
|
|
@@ -2092,29 +2484,6 @@ const startRename = (state, id) => {
|
|
|
2092
2484
|
};
|
|
2093
2485
|
};
|
|
2094
2486
|
|
|
2095
|
-
const getListIndex = (state, eventX, eventY) => {
|
|
2096
|
-
const {
|
|
2097
|
-
headerHeight,
|
|
2098
|
-
height,
|
|
2099
|
-
listItemHeight,
|
|
2100
|
-
width,
|
|
2101
|
-
x,
|
|
2102
|
-
y
|
|
2103
|
-
} = state;
|
|
2104
|
-
if (eventX < x || eventY < y) {
|
|
2105
|
-
return -1;
|
|
2106
|
-
}
|
|
2107
|
-
if (eventX >= x + width || eventY >= y + height) {
|
|
2108
|
-
return -1;
|
|
2109
|
-
}
|
|
2110
|
-
const listY = eventY - y - headerHeight;
|
|
2111
|
-
if (listY < 0) {
|
|
2112
|
-
return -1;
|
|
2113
|
-
}
|
|
2114
|
-
const itemHeight = listItemHeight > 0 ? listItemHeight : 40;
|
|
2115
|
-
return Math.floor(listY / itemHeight);
|
|
2116
|
-
};
|
|
2117
|
-
|
|
2118
2487
|
const selectListIndex = async (state, index) => {
|
|
2119
2488
|
const {
|
|
2120
2489
|
sessions
|
|
@@ -2131,32 +2500,33 @@ const handleClickList = async (state, eventX, eventY) => {
|
|
|
2131
2500
|
return selectListIndex(state, index);
|
|
2132
2501
|
};
|
|
2133
2502
|
|
|
2134
|
-
const CREATE_SESSION = 'create-session';
|
|
2135
|
-
const SESSION_PREFIX = 'session:';
|
|
2136
|
-
const RENAME_PREFIX = 'session-rename:';
|
|
2137
|
-
const SESSION_DELETE = 'SessionDelete';
|
|
2138
|
-
const SEND = 'send';
|
|
2139
2503
|
const handleClick = async (state, name, id = '') => {
|
|
2140
2504
|
if (!name) {
|
|
2141
2505
|
return state;
|
|
2142
2506
|
}
|
|
2143
|
-
if (name ===
|
|
2507
|
+
if (name === CreateSession) {
|
|
2144
2508
|
return createSession(state);
|
|
2145
2509
|
}
|
|
2146
|
-
if (name
|
|
2147
|
-
const
|
|
2148
|
-
return selectSession(state,
|
|
2510
|
+
if (isSessionInputName(name)) {
|
|
2511
|
+
const sessionId = getSessionIdFromInputName(name);
|
|
2512
|
+
return selectSession(state, sessionId);
|
|
2149
2513
|
}
|
|
2150
|
-
if (name
|
|
2151
|
-
const
|
|
2152
|
-
return startRename(state,
|
|
2514
|
+
if (isRenameInputName(name)) {
|
|
2515
|
+
const sessionId = getRenameIdFromInputName(name);
|
|
2516
|
+
return startRename(state, sessionId);
|
|
2153
2517
|
}
|
|
2154
|
-
if (name ===
|
|
2518
|
+
if (name === SessionDelete) {
|
|
2155
2519
|
return deleteSession(state, id);
|
|
2156
2520
|
}
|
|
2157
|
-
if (name ===
|
|
2521
|
+
if (name === Send) {
|
|
2158
2522
|
return handleClickSend(state);
|
|
2159
2523
|
}
|
|
2524
|
+
if (name === SaveOpenRouterApiKey) {
|
|
2525
|
+
return handleClickSaveOpenRouterApiKey(state);
|
|
2526
|
+
}
|
|
2527
|
+
if (name === OpenOpenRouterApiKeySettings) {
|
|
2528
|
+
return handleClickOpenRouterApiKeySettings(state);
|
|
2529
|
+
}
|
|
2160
2530
|
return state;
|
|
2161
2531
|
};
|
|
2162
2532
|
|
|
@@ -2186,7 +2556,16 @@ const handleClickSettings = async () => {
|
|
|
2186
2556
|
await invoke('Main.openUri', 'app://settings.json');
|
|
2187
2557
|
};
|
|
2188
2558
|
|
|
2189
|
-
const handleInput = async (state, value, inputSource = 'user') => {
|
|
2559
|
+
const handleInput = async (state, name, value, inputSource = 'user') => {
|
|
2560
|
+
if (name === OpenRouterApiKeyInput) {
|
|
2561
|
+
return {
|
|
2562
|
+
...state,
|
|
2563
|
+
openRouterApiKeyInput: value
|
|
2564
|
+
};
|
|
2565
|
+
}
|
|
2566
|
+
if (name !== Composer) {
|
|
2567
|
+
return state;
|
|
2568
|
+
}
|
|
2190
2569
|
return {
|
|
2191
2570
|
...state,
|
|
2192
2571
|
composerValue: value,
|
|
@@ -2195,24 +2574,24 @@ const handleInput = async (state, value, inputSource = 'user') => {
|
|
|
2195
2574
|
};
|
|
2196
2575
|
|
|
2197
2576
|
const handleInputFocus = async (state, name) => {
|
|
2198
|
-
if (name ===
|
|
2577
|
+
if (name === Composer) {
|
|
2199
2578
|
return focusInput(state);
|
|
2200
2579
|
}
|
|
2201
|
-
if (name ===
|
|
2580
|
+
if (name === Send) {
|
|
2202
2581
|
return {
|
|
2203
2582
|
...state,
|
|
2204
2583
|
focus: 'send-button',
|
|
2205
2584
|
focused: true
|
|
2206
2585
|
};
|
|
2207
2586
|
}
|
|
2208
|
-
if (name
|
|
2587
|
+
if (isSessionInputName(name) || name === SessionDelete) {
|
|
2209
2588
|
return {
|
|
2210
2589
|
...state,
|
|
2211
2590
|
focus: 'list',
|
|
2212
2591
|
focused: true
|
|
2213
2592
|
};
|
|
2214
2593
|
}
|
|
2215
|
-
if (name ===
|
|
2594
|
+
if (name === CreateSession || name === Settings || name === CloseChat || name === Back) {
|
|
2216
2595
|
return {
|
|
2217
2596
|
...state,
|
|
2218
2597
|
focus: 'header',
|
|
@@ -2291,7 +2670,7 @@ const handleModelChange = async (state, value) => {
|
|
|
2291
2670
|
};
|
|
2292
2671
|
|
|
2293
2672
|
const handleNewline = async state => {
|
|
2294
|
-
return handleInput(state, `${state.composerValue}\n`);
|
|
2673
|
+
return handleInput(state, Composer, `${state.composerValue}\n`);
|
|
2295
2674
|
};
|
|
2296
2675
|
|
|
2297
2676
|
const id = 7201;
|
|
@@ -2320,36 +2699,6 @@ const isObject = value => {
|
|
|
2320
2699
|
return typeof value === 'object' && value !== null;
|
|
2321
2700
|
};
|
|
2322
2701
|
|
|
2323
|
-
const getSavedBounds = savedState => {
|
|
2324
|
-
if (!isObject(savedState)) {
|
|
2325
|
-
return undefined;
|
|
2326
|
-
}
|
|
2327
|
-
const {
|
|
2328
|
-
height,
|
|
2329
|
-
width,
|
|
2330
|
-
x,
|
|
2331
|
-
y
|
|
2332
|
-
} = savedState;
|
|
2333
|
-
if (typeof x !== 'number') {
|
|
2334
|
-
return undefined;
|
|
2335
|
-
}
|
|
2336
|
-
if (typeof y !== 'number') {
|
|
2337
|
-
return undefined;
|
|
2338
|
-
}
|
|
2339
|
-
if (typeof width !== 'number') {
|
|
2340
|
-
return undefined;
|
|
2341
|
-
}
|
|
2342
|
-
if (typeof height !== 'number') {
|
|
2343
|
-
return undefined;
|
|
2344
|
-
}
|
|
2345
|
-
return {
|
|
2346
|
-
height,
|
|
2347
|
-
width,
|
|
2348
|
-
x,
|
|
2349
|
-
y
|
|
2350
|
-
};
|
|
2351
|
-
};
|
|
2352
|
-
|
|
2353
2702
|
const getSavedSelectedModelId = savedState => {
|
|
2354
2703
|
if (!isObject(savedState)) {
|
|
2355
2704
|
return undefined;
|
|
@@ -2425,9 +2774,15 @@ const loadSelectedSessionMessages = async (sessions, selectedSessionId) => {
|
|
|
2425
2774
|
});
|
|
2426
2775
|
};
|
|
2427
2776
|
const loadContent = async (state, savedState) => {
|
|
2428
|
-
const savedBounds = getSavedBounds(savedState);
|
|
2429
2777
|
const savedSelectedModelId = getSavedSelectedModelId(savedState);
|
|
2430
2778
|
const savedViewMode = getSavedViewMode(savedState);
|
|
2779
|
+
let openRouterApiKey = '';
|
|
2780
|
+
try {
|
|
2781
|
+
const savedOpenRouterApiKey = await get('secrets.openRouterApiKey');
|
|
2782
|
+
openRouterApiKey = typeof savedOpenRouterApiKey === 'string' ? savedOpenRouterApiKey : '';
|
|
2783
|
+
} catch {
|
|
2784
|
+
openRouterApiKey = '';
|
|
2785
|
+
}
|
|
2431
2786
|
const legacySavedSessions = getSavedSessions(savedState);
|
|
2432
2787
|
const storedSessions = await listChatSessions();
|
|
2433
2788
|
let sessions = storedSessions;
|
|
@@ -2452,8 +2807,9 @@ const loadContent = async (state, savedState) => {
|
|
|
2452
2807
|
const viewMode = sessions.length === 0 || !selectedSessionId ? 'list' : preferredViewMode === 'detail' ? 'detail' : 'list';
|
|
2453
2808
|
return {
|
|
2454
2809
|
...state,
|
|
2455
|
-
...savedBounds,
|
|
2456
2810
|
initial: false,
|
|
2811
|
+
openRouterApiKey,
|
|
2812
|
+
openRouterApiKeyInput: openRouterApiKey,
|
|
2457
2813
|
selectedModelId,
|
|
2458
2814
|
selectedSessionId,
|
|
2459
2815
|
sessions,
|
|
@@ -2462,11 +2818,14 @@ const loadContent = async (state, savedState) => {
|
|
|
2462
2818
|
};
|
|
2463
2819
|
|
|
2464
2820
|
const openMockSession = async (state, mockSessionId, mockChatMessages) => {
|
|
2821
|
+
const {
|
|
2822
|
+
sessions: currentSessions
|
|
2823
|
+
} = state;
|
|
2465
2824
|
if (!mockSessionId) {
|
|
2466
2825
|
return state;
|
|
2467
2826
|
}
|
|
2468
|
-
const existingSession =
|
|
2469
|
-
const sessions = existingSession ?
|
|
2827
|
+
const existingSession = currentSessions.find(session => session.id === mockSessionId);
|
|
2828
|
+
const sessions = existingSession ? currentSessions.map(session => {
|
|
2470
2829
|
if (session.id !== mockSessionId) {
|
|
2471
2830
|
return session;
|
|
2472
2831
|
}
|
|
@@ -2474,7 +2833,7 @@ const openMockSession = async (state, mockSessionId, mockChatMessages) => {
|
|
|
2474
2833
|
...session,
|
|
2475
2834
|
messages: mockChatMessages
|
|
2476
2835
|
};
|
|
2477
|
-
}) : [...
|
|
2836
|
+
}) : [...currentSessions, {
|
|
2478
2837
|
id: mockSessionId,
|
|
2479
2838
|
messages: mockChatMessages,
|
|
2480
2839
|
title: mockSessionId
|
|
@@ -2524,17 +2883,20 @@ const renderFocusContext = (oldState, newState) => {
|
|
|
2524
2883
|
return [SetFocusContext, newState.uid, FocusChatInput];
|
|
2525
2884
|
};
|
|
2526
2885
|
|
|
2886
|
+
const Actions = 'Actions';
|
|
2527
2887
|
const ChatActions = 'ChatActions';
|
|
2528
2888
|
const ChatName = 'ChatName';
|
|
2529
2889
|
const ChatSendArea = 'ChatSendArea';
|
|
2890
|
+
const SendButtonDisabled = 'SendButtonDisabled';
|
|
2530
2891
|
const ChatSendAreaBottom = 'ChatSendAreaBottom';
|
|
2531
2892
|
const ChatSendAreaContent = 'ChatSendAreaContent';
|
|
2532
2893
|
const Chat = 'Chat';
|
|
2533
2894
|
const ChatHeader = 'ChatHeader';
|
|
2534
2895
|
const Button = 'Button';
|
|
2535
|
-
const ButtonDisabled = 'ButtonDisabled';
|
|
2536
2896
|
const ButtonPrimary = 'ButtonPrimary';
|
|
2897
|
+
const ButtonSecondary = 'ButtonSecondary';
|
|
2537
2898
|
const IconButton = 'IconButton';
|
|
2899
|
+
const InputBox = 'InputBox';
|
|
2538
2900
|
const Label = 'Label';
|
|
2539
2901
|
const LabelDetail = 'LabelDetail';
|
|
2540
2902
|
const ChatList = 'ChatList';
|
|
@@ -2569,6 +2931,12 @@ const HandleClickDelete = 18;
|
|
|
2569
2931
|
const HandleSubmit = 19;
|
|
2570
2932
|
const HandleModelChange = 20;
|
|
2571
2933
|
|
|
2934
|
+
const getModelLabel = model => {
|
|
2935
|
+
if (model.provider === 'openRouter') {
|
|
2936
|
+
return `${model.name} (OpenRouter)`;
|
|
2937
|
+
}
|
|
2938
|
+
return model.name;
|
|
2939
|
+
};
|
|
2572
2940
|
const getModelOptionDOm = (model, selectedModelId) => {
|
|
2573
2941
|
return [{
|
|
2574
2942
|
childCount: 1,
|
|
@@ -2576,11 +2944,11 @@ const getModelOptionDOm = (model, selectedModelId) => {
|
|
|
2576
2944
|
selected: model.id === selectedModelId,
|
|
2577
2945
|
type: Option$1,
|
|
2578
2946
|
value: model.id
|
|
2579
|
-
}, text(model
|
|
2947
|
+
}, text(getModelLabel(model))];
|
|
2580
2948
|
};
|
|
2581
2949
|
|
|
2582
2950
|
const getSendButtonClassName = isSendDisabled => {
|
|
2583
|
-
return isSendDisabled ? `${
|
|
2951
|
+
return isSendDisabled ? `${IconButton} ${SendButtonDisabled}` : `${IconButton}`;
|
|
2584
2952
|
};
|
|
2585
2953
|
|
|
2586
2954
|
const getSendButtonDom = isSendDisabled => {
|
|
@@ -2589,14 +2957,14 @@ const getSendButtonDom = isSendDisabled => {
|
|
|
2589
2957
|
childCount: 1,
|
|
2590
2958
|
className: sendButtonClassName,
|
|
2591
2959
|
disabled: isSendDisabled,
|
|
2592
|
-
name:
|
|
2960
|
+
name: Send,
|
|
2593
2961
|
onClick: HandleSubmit,
|
|
2594
2962
|
role: Button$2,
|
|
2595
|
-
title: sendMessage,
|
|
2963
|
+
title: sendMessage(),
|
|
2596
2964
|
type: Button$1
|
|
2597
2965
|
}, {
|
|
2598
2966
|
childCount: 0,
|
|
2599
|
-
|
|
2967
|
+
className: 'MaskIcon MaskIconSend',
|
|
2600
2968
|
type: Div
|
|
2601
2969
|
}];
|
|
2602
2970
|
};
|
|
@@ -2650,10 +3018,10 @@ const getChatSendAreaDom = (composerValue, models, selectedModelId, usageOvervie
|
|
|
2650
3018
|
}, {
|
|
2651
3019
|
childCount: 0,
|
|
2652
3020
|
className: MultilineInputBox,
|
|
2653
|
-
name:
|
|
3021
|
+
name: Composer,
|
|
2654
3022
|
onFocus: HandleFocus,
|
|
2655
3023
|
onInput: HandleInput,
|
|
2656
|
-
placeholder: composePlaceholder,
|
|
3024
|
+
placeholder: composePlaceholder(),
|
|
2657
3025
|
rows: 4,
|
|
2658
3026
|
type: TextArea,
|
|
2659
3027
|
value: composerValue
|
|
@@ -2664,7 +3032,7 @@ const getChatSendAreaDom = (composerValue, models, selectedModelId, usageOvervie
|
|
|
2664
3032
|
}, {
|
|
2665
3033
|
childCount: models.length,
|
|
2666
3034
|
className: Select,
|
|
2667
|
-
name:
|
|
3035
|
+
name: Model,
|
|
2668
3036
|
onInput: HandleModelChange,
|
|
2669
3037
|
type: Select$1,
|
|
2670
3038
|
value: selectedModelId
|
|
@@ -2675,10 +3043,10 @@ const getBackButtonVirtualDom = () => {
|
|
|
2675
3043
|
return [{
|
|
2676
3044
|
childCount: 1,
|
|
2677
3045
|
className: IconButton,
|
|
2678
|
-
name:
|
|
3046
|
+
name: Back,
|
|
2679
3047
|
onClick: HandleClickBack,
|
|
2680
3048
|
role: Button$2,
|
|
2681
|
-
title: backToChats,
|
|
3049
|
+
title: backToChats(),
|
|
2682
3050
|
type: Button$1
|
|
2683
3051
|
}, {
|
|
2684
3052
|
childCount: 0,
|
|
@@ -2706,19 +3074,19 @@ const getHeaderActionVirtualDom = item => {
|
|
|
2706
3074
|
const getChatHeaderActionsDom = () => {
|
|
2707
3075
|
const items = [{
|
|
2708
3076
|
icon: 'MaskIcon MaskIconAdd',
|
|
2709
|
-
name:
|
|
3077
|
+
name: CreateSession,
|
|
2710
3078
|
onClick: HandleClickNew,
|
|
2711
|
-
title: newChat
|
|
3079
|
+
title: newChat()
|
|
2712
3080
|
}, {
|
|
2713
3081
|
icon: 'MaskIcon MaskIconSettingsGear',
|
|
2714
|
-
name:
|
|
3082
|
+
name: Settings,
|
|
2715
3083
|
onClick: HandleClickSettings,
|
|
2716
|
-
title: settings
|
|
3084
|
+
title: settings()
|
|
2717
3085
|
}, {
|
|
2718
3086
|
icon: 'MaskIcon MaskIconClose',
|
|
2719
|
-
name:
|
|
3087
|
+
name: CloseChat,
|
|
2720
3088
|
onClick: HandleClickClose,
|
|
2721
|
-
title: closeChat
|
|
3089
|
+
title: closeChat()
|
|
2722
3090
|
}];
|
|
2723
3091
|
return [{
|
|
2724
3092
|
childCount: items.length,
|
|
@@ -2743,47 +3111,96 @@ const getChatHeaderDomDetailMode = selectedSessionTitle => {
|
|
|
2743
3111
|
}, text(selectedSessionTitle), ...getChatHeaderActionsDom()];
|
|
2744
3112
|
};
|
|
2745
3113
|
|
|
2746
|
-
const
|
|
3114
|
+
const getMissingOpenRouterApiKeyDom = openRouterApiKeyInput => {
|
|
3115
|
+
return [{
|
|
3116
|
+
childCount: 2,
|
|
3117
|
+
type: Div
|
|
3118
|
+
}, {
|
|
3119
|
+
childCount: 0,
|
|
3120
|
+
className: InputBox,
|
|
3121
|
+
name: OpenRouterApiKeyInput,
|
|
3122
|
+
onInput: HandleInput,
|
|
3123
|
+
placeholder: openRouterApiKeyPlaceholder(),
|
|
3124
|
+
type: Input,
|
|
3125
|
+
value: openRouterApiKeyInput
|
|
3126
|
+
}, {
|
|
3127
|
+
childCount: 2,
|
|
3128
|
+
className: Actions,
|
|
3129
|
+
type: Div
|
|
3130
|
+
}, {
|
|
3131
|
+
childCount: 1,
|
|
3132
|
+
className: mergeClassNames(Button, ButtonPrimary),
|
|
3133
|
+
name: SaveOpenRouterApiKey,
|
|
3134
|
+
onClick: HandleClick,
|
|
3135
|
+
type: Button$1
|
|
3136
|
+
}, text(save()), {
|
|
3137
|
+
childCount: 1,
|
|
3138
|
+
className: mergeClassNames(Button, ButtonSecondary),
|
|
3139
|
+
name: OpenOpenRouterApiKeySettings,
|
|
3140
|
+
onClick: HandleClick,
|
|
3141
|
+
type: Button$1
|
|
3142
|
+
}, text(getOpenRouterApiKey())];
|
|
3143
|
+
};
|
|
3144
|
+
|
|
3145
|
+
const getOpenRouterRequestFailedDom = () => {
|
|
3146
|
+
return [{
|
|
3147
|
+
childCount: openRouterRequestFailureReasons.length,
|
|
3148
|
+
className: Markdown,
|
|
3149
|
+
type: Ol
|
|
3150
|
+
}, ...openRouterRequestFailureReasons.flatMap(reason => {
|
|
3151
|
+
return [{
|
|
3152
|
+
childCount: 1,
|
|
3153
|
+
type: Li
|
|
3154
|
+
}, text(reason)];
|
|
3155
|
+
})];
|
|
3156
|
+
};
|
|
3157
|
+
const getChatMessageDom = (message, openRouterApiKeyInput) => {
|
|
2747
3158
|
const roleClassName = message.role === 'user' ? MessageUser : MessageAssistant;
|
|
3159
|
+
const isOpenRouterApiKeyMissingMessage = message.role === 'assistant' && message.text === openRouterApiKeyRequiredMessage;
|
|
3160
|
+
const isOpenRouterRequestFailedMessage = message.role === 'assistant' && message.text === openRouterRequestFailedMessage;
|
|
3161
|
+
const extraChildCount = isOpenRouterApiKeyMissingMessage || isOpenRouterRequestFailedMessage ? 2 : 1;
|
|
2748
3162
|
return [{
|
|
2749
3163
|
childCount: 1,
|
|
2750
3164
|
className: mergeClassNames(Message, roleClassName),
|
|
2751
3165
|
type: Div
|
|
2752
3166
|
}, {
|
|
2753
|
-
childCount:
|
|
3167
|
+
childCount: extraChildCount,
|
|
2754
3168
|
className: ChatMessageContent,
|
|
2755
3169
|
type: Div
|
|
2756
3170
|
}, {
|
|
2757
3171
|
childCount: 1,
|
|
2758
3172
|
className: Markdown,
|
|
2759
3173
|
type: P
|
|
2760
|
-
}, text(message.text)];
|
|
3174
|
+
}, text(message.text), ...(isOpenRouterApiKeyMissingMessage ? getMissingOpenRouterApiKeyDom(openRouterApiKeyInput) : []), ...(isOpenRouterRequestFailedMessage ? getOpenRouterRequestFailedDom() : [])];
|
|
2761
3175
|
};
|
|
2762
3176
|
|
|
2763
|
-
const
|
|
3177
|
+
const getEmptyMessagesDom = () => {
|
|
3178
|
+
return [{
|
|
3179
|
+
childCount: 1,
|
|
3180
|
+
className: ChatWelcomeMessage,
|
|
3181
|
+
type: Div
|
|
3182
|
+
}, text(startConversation())];
|
|
3183
|
+
};
|
|
3184
|
+
const getMessagesDom = (messages, openRouterApiKeyInput) => {
|
|
2764
3185
|
if (messages.length === 0) {
|
|
2765
|
-
return
|
|
2766
|
-
childCount: 1,
|
|
2767
|
-
className: ChatWelcomeMessage,
|
|
2768
|
-
type: Div
|
|
2769
|
-
}, text(startConversation)];
|
|
3186
|
+
return getEmptyMessagesDom();
|
|
2770
3187
|
}
|
|
2771
3188
|
return [{
|
|
2772
3189
|
childCount: messages.length,
|
|
2773
3190
|
className: 'ChatMessages',
|
|
2774
3191
|
type: Div
|
|
2775
|
-
}, ...messages.flatMap(getChatMessageDom)];
|
|
3192
|
+
}, ...messages.flatMap(message => getChatMessageDom(message, openRouterApiKeyInput))];
|
|
2776
3193
|
};
|
|
2777
3194
|
|
|
2778
|
-
const getChatModeDetailVirtualDom = (sessions, selectedSessionId, composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax) => {
|
|
3195
|
+
const getChatModeDetailVirtualDom = (sessions, selectedSessionId, composerValue, openRouterApiKeyInput, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax) => {
|
|
2779
3196
|
const selectedSession = sessions.find(session => session.id === selectedSessionId);
|
|
2780
|
-
const selectedSessionTitle = selectedSession?.title || chatTitle;
|
|
3197
|
+
const selectedSessionTitle = selectedSession?.title || chatTitle();
|
|
2781
3198
|
const messages = selectedSession ? selectedSession.messages : [];
|
|
2782
3199
|
return [{
|
|
2783
3200
|
childCount: 3,
|
|
2784
3201
|
className: mergeClassNames(Viewlet, Chat),
|
|
2785
3202
|
type: Div
|
|
2786
|
-
}, ...getChatHeaderDomDetailMode(selectedSessionTitle), ...getMessagesDom(messages), ...getChatSendAreaDom(composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax)];
|
|
3203
|
+
}, ...getChatHeaderDomDetailMode(selectedSessionTitle), ...getMessagesDom(messages, openRouterApiKeyInput), ...getChatSendAreaDom(composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax)];
|
|
2787
3204
|
};
|
|
2788
3205
|
|
|
2789
3206
|
const getChatHeaderListModeDom = () => {
|
|
@@ -2795,7 +3212,7 @@ const getChatHeaderListModeDom = () => {
|
|
|
2795
3212
|
childCount: 1,
|
|
2796
3213
|
className: Label,
|
|
2797
3214
|
type: Span
|
|
2798
|
-
}, text(chats), ...getChatHeaderActionsDom()];
|
|
3215
|
+
}, text(chats()), ...getChatHeaderActionsDom()];
|
|
2799
3216
|
};
|
|
2800
3217
|
|
|
2801
3218
|
const getEmptyChatSessionsDom = () => {
|
|
@@ -2807,7 +3224,7 @@ const getEmptyChatSessionsDom = () => {
|
|
|
2807
3224
|
childCount: 1,
|
|
2808
3225
|
className: Label,
|
|
2809
3226
|
type: Div
|
|
2810
|
-
}, text(clickToOpenNewChat)];
|
|
3227
|
+
}, text(clickToOpenNewChat())];
|
|
2811
3228
|
};
|
|
2812
3229
|
|
|
2813
3230
|
const getSessionDom = session => {
|
|
@@ -2819,7 +3236,7 @@ const getSessionDom = session => {
|
|
|
2819
3236
|
}, {
|
|
2820
3237
|
childCount: 1,
|
|
2821
3238
|
className: ChatListItemLabel,
|
|
2822
|
-
name:
|
|
3239
|
+
name: getSessionInputName(session.id),
|
|
2823
3240
|
onContextMenu: HandleListContextMenu,
|
|
2824
3241
|
tabIndex: 0,
|
|
2825
3242
|
type: Div
|
|
@@ -2831,11 +3248,11 @@ const getSessionDom = session => {
|
|
|
2831
3248
|
childCount: 1,
|
|
2832
3249
|
className: IconButton,
|
|
2833
3250
|
'data-id': session.id,
|
|
2834
|
-
name:
|
|
3251
|
+
name: SessionDelete,
|
|
2835
3252
|
onClick: HandleClickDelete,
|
|
2836
3253
|
role: Button$2,
|
|
2837
3254
|
tabIndex: 0,
|
|
2838
|
-
title: deleteChatSession$1,
|
|
3255
|
+
title: deleteChatSession$1(),
|
|
2839
3256
|
type: Button$1
|
|
2840
3257
|
}, text('🗑')];
|
|
2841
3258
|
};
|
|
@@ -2864,13 +3281,13 @@ const getChatModeUnsupportedVirtualDom = () => {
|
|
|
2864
3281
|
return [{
|
|
2865
3282
|
childCount: 1,
|
|
2866
3283
|
type: Div
|
|
2867
|
-
}, text(
|
|
3284
|
+
}, text(unknownViewMode())];
|
|
2868
3285
|
};
|
|
2869
3286
|
|
|
2870
|
-
const getChatVirtualDom = (sessions, selectedSessionId, composerValue, viewMode, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax) => {
|
|
3287
|
+
const getChatVirtualDom = (sessions, selectedSessionId, composerValue, openRouterApiKeyInput, viewMode, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax) => {
|
|
2871
3288
|
switch (viewMode) {
|
|
2872
3289
|
case 'detail':
|
|
2873
|
-
return getChatModeDetailVirtualDom(sessions, selectedSessionId, composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax);
|
|
3290
|
+
return getChatModeDetailVirtualDom(sessions, selectedSessionId, composerValue, openRouterApiKeyInput, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax);
|
|
2874
3291
|
case 'list':
|
|
2875
3292
|
return getChatModeListVirtualDom(sessions, selectedSessionId, composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax);
|
|
2876
3293
|
default:
|
|
@@ -2883,6 +3300,7 @@ const renderItems = (oldState, newState) => {
|
|
|
2883
3300
|
composerValue,
|
|
2884
3301
|
initial,
|
|
2885
3302
|
models,
|
|
3303
|
+
openRouterApiKeyInput,
|
|
2886
3304
|
selectedModelId,
|
|
2887
3305
|
selectedSessionId,
|
|
2888
3306
|
sessions,
|
|
@@ -2895,7 +3313,7 @@ const renderItems = (oldState, newState) => {
|
|
|
2895
3313
|
if (initial) {
|
|
2896
3314
|
return [SetDom2, uid, []];
|
|
2897
3315
|
}
|
|
2898
|
-
const dom = getChatVirtualDom(sessions, selectedSessionId, composerValue, viewMode, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax);
|
|
3316
|
+
const dom = getChatVirtualDom(sessions, selectedSessionId, composerValue, openRouterApiKeyInput, viewMode, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax);
|
|
2899
3317
|
return [SetDom2, uid, dom];
|
|
2900
3318
|
};
|
|
2901
3319
|
|
|
@@ -2910,7 +3328,7 @@ const renderValue = (oldState, newState) => {
|
|
|
2910
3328
|
const {
|
|
2911
3329
|
composerValue
|
|
2912
3330
|
} = newState;
|
|
2913
|
-
return [SetValueByName, newState.uid,
|
|
3331
|
+
return [SetValueByName, newState.uid, Composer, composerValue];
|
|
2914
3332
|
};
|
|
2915
3333
|
|
|
2916
3334
|
const getRenderer = diffType => {
|
|
@@ -2948,7 +3366,7 @@ const render2 = (uid, diffResult) => {
|
|
|
2948
3366
|
const {
|
|
2949
3367
|
newState,
|
|
2950
3368
|
oldState
|
|
2951
|
-
} = get(uid);
|
|
3369
|
+
} = get$1(uid);
|
|
2952
3370
|
set(uid, newState, newState);
|
|
2953
3371
|
const commands = applyRender(oldState, newState, diffResult);
|
|
2954
3372
|
return commands;
|
|
@@ -2982,7 +3400,7 @@ const renderEventListeners = () => {
|
|
|
2982
3400
|
params: ['handleClickList', ClientX, ClientY]
|
|
2983
3401
|
}, {
|
|
2984
3402
|
name: HandleInput,
|
|
2985
|
-
params: ['handleInput', TargetValue]
|
|
3403
|
+
params: ['handleInput', TargetName, TargetValue]
|
|
2986
3404
|
}, {
|
|
2987
3405
|
name: HandleModelChange,
|
|
2988
3406
|
params: ['handleModelChange', TargetValue]
|
|
@@ -3007,6 +3425,7 @@ const reset = async state => {
|
|
|
3007
3425
|
return {
|
|
3008
3426
|
...state,
|
|
3009
3427
|
composerValue: '',
|
|
3428
|
+
openRouterApiKey: '',
|
|
3010
3429
|
selectedSessionId: '',
|
|
3011
3430
|
sessions: [],
|
|
3012
3431
|
viewMode: 'list'
|
|
@@ -3050,15 +3469,15 @@ const saveState = state => {
|
|
|
3050
3469
|
const dummySessions = [{
|
|
3051
3470
|
id: 'session-a',
|
|
3052
3471
|
messages: [],
|
|
3053
|
-
title: dummyChatA
|
|
3472
|
+
title: dummyChatA()
|
|
3054
3473
|
}, {
|
|
3055
3474
|
id: 'session-b',
|
|
3056
3475
|
messages: [],
|
|
3057
|
-
title: dummyChatB
|
|
3476
|
+
title: dummyChatB()
|
|
3058
3477
|
}, {
|
|
3059
3478
|
id: 'session-c',
|
|
3060
3479
|
messages: [],
|
|
3061
|
-
title: dummyChatC
|
|
3480
|
+
title: dummyChatC()
|
|
3062
3481
|
}];
|
|
3063
3482
|
const setChatList = state => {
|
|
3064
3483
|
return {
|
|
@@ -3076,6 +3495,7 @@ const commandMap = {
|
|
|
3076
3495
|
'Chat.enterNewLine': wrapCommand(handleNewline),
|
|
3077
3496
|
'Chat.getCommandIds': getCommandIds,
|
|
3078
3497
|
'Chat.getKeyBindings': getKeyBindings,
|
|
3498
|
+
'Chat.getSelectedSessionId': wrapGetter(getSelectedSessionId),
|
|
3079
3499
|
'Chat.handleChatListContextMenu': handleChatListContextMenu,
|
|
3080
3500
|
'Chat.handleClick': wrapCommand(handleClick),
|
|
3081
3501
|
'Chat.handleClickBack': wrapCommand(handleClickBack),
|
|
@@ -3100,6 +3520,7 @@ const commandMap = {
|
|
|
3100
3520
|
'Chat.resize': wrapCommand(resize),
|
|
3101
3521
|
'Chat.saveState': wrapGetter(saveState),
|
|
3102
3522
|
'Chat.setChatList': wrapCommand(setChatList),
|
|
3523
|
+
'Chat.setOpenRouterApiKey': wrapCommand(setOpenRouterApiKey),
|
|
3103
3524
|
'Chat.terminate': terminate
|
|
3104
3525
|
};
|
|
3105
3526
|
|