@lvce-editor/chat-view 1.19.0 → 1.20.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.
@@ -1386,11 +1386,16 @@ const getDefaultModels = () => {
1386
1386
  const createDefaultState = () => {
1387
1387
  const defaultSessionId = 'session-1';
1388
1388
  const defaultModelId = 'test';
1389
+ const chatMessageFontSize = 13;
1390
+ const chatMessageLineHeight = 20;
1389
1391
  const composerFontSize = 13;
1390
1392
  const composerLineHeight = 20;
1391
1393
  return {
1392
1394
  assetDir: '',
1393
1395
  chatListScrollTop: 0,
1396
+ chatMessageFontFamily: 'system-ui',
1397
+ chatMessageFontSize,
1398
+ chatMessageLineHeight,
1394
1399
  composerFontFamily: 'system-ui',
1395
1400
  composerFontSize,
1396
1401
  composerHeight: composerLineHeight + 8,
@@ -1419,6 +1424,7 @@ const createDefaultState = () => {
1419
1424
  openRouterApiKeyInput: '',
1420
1425
  openRouterApiKeysSettingsUrl: 'https://openrouter.ai/settings/keys',
1421
1426
  openRouterApiKeyState: 'idle',
1427
+ passIncludeObfuscation: false,
1422
1428
  platform: 0,
1423
1429
  renamingSessionId: '',
1424
1430
  selectedModelId: defaultModelId,
@@ -1466,7 +1472,7 @@ const create = (uid, x, y, width, height, platform, assetDir) => {
1466
1472
  };
1467
1473
 
1468
1474
  const isEqual$1 = (oldState, newState) => {
1469
- return oldState.initial === newState.initial && oldState.composerHeight === newState.composerHeight && oldState.composerLineHeight === newState.composerLineHeight && oldState.composerFontFamily === newState.composerFontFamily && oldState.composerFontSize === newState.composerFontSize;
1475
+ return oldState.initial === newState.initial && oldState.chatMessageFontFamily === newState.chatMessageFontFamily && oldState.chatMessageFontSize === newState.chatMessageFontSize && oldState.chatMessageLineHeight === newState.chatMessageLineHeight && oldState.composerHeight === newState.composerHeight && oldState.composerLineHeight === newState.composerLineHeight && oldState.composerFontFamily === newState.composerFontFamily && oldState.composerFontSize === newState.composerFontSize && oldState.listItemHeight === newState.listItemHeight;
1470
1476
  };
1471
1477
 
1472
1478
  const diffFocus = (oldState, newState) => {
@@ -1883,18 +1889,12 @@ const getListIndex = (state, eventX, eventY) => {
1883
1889
  x,
1884
1890
  y
1885
1891
  } = state;
1886
- if (eventX < x || eventY < y) {
1892
+ const relativeX = eventX - x;
1893
+ const relativeY = eventY - y - headerHeight;
1894
+ if (relativeX < 0 || relativeY < 0 || relativeX >= width || relativeY >= height - headerHeight) {
1887
1895
  return -1;
1888
1896
  }
1889
- if (eventX >= x + width || eventY >= y + height) {
1890
- return -1;
1891
- }
1892
- const listY = eventY - y - headerHeight;
1893
- if (listY < 0) {
1894
- return -1;
1895
- }
1896
- const itemHeight = listItemHeight > 0 ? listItemHeight : 40;
1897
- return Math.floor(listY / itemHeight);
1897
+ return Math.floor(relativeY / listItemHeight);
1898
1898
  };
1899
1899
 
1900
1900
  const CHAT_LIST_ITEM_CONTEXT_MENU = 'ChatListItemContextMenu';
@@ -2207,6 +2207,63 @@ const getMockAiResponse = async userMessage => {
2207
2207
  return `Mock AI response: I received "${userMessage}".`;
2208
2208
  };
2209
2209
 
2210
+ let queue = [];
2211
+ let waiters = [];
2212
+ let finished = false;
2213
+ const reset$1 = () => {
2214
+ queue = [];
2215
+ waiters = [];
2216
+ finished = false;
2217
+ };
2218
+ const pushChunk = chunk => {
2219
+ if (waiters.length > 0) {
2220
+ const resolve = waiters.shift();
2221
+ resolve?.(chunk);
2222
+ return;
2223
+ }
2224
+ queue.push(chunk);
2225
+ };
2226
+ const finish = () => {
2227
+ finished = true;
2228
+ if (waiters.length === 0) {
2229
+ return;
2230
+ }
2231
+ const activeWaiters = waiters;
2232
+ waiters = [];
2233
+ for (const resolve of activeWaiters) {
2234
+ resolve(undefined);
2235
+ }
2236
+ };
2237
+ const readNextChunk = async () => {
2238
+ if (queue.length > 0) {
2239
+ return queue.shift();
2240
+ }
2241
+ if (finished) {
2242
+ return undefined;
2243
+ }
2244
+ return new Promise(resolve => {
2245
+ waiters.push(resolve);
2246
+ });
2247
+ };
2248
+
2249
+ const getMockOpenApiAssistantText = async (stream, onTextChunk) => {
2250
+ let text = '';
2251
+ while (true) {
2252
+ const chunk = await readNextChunk();
2253
+ if (typeof chunk !== 'string') {
2254
+ break;
2255
+ }
2256
+ text += chunk;
2257
+ if (stream && onTextChunk) {
2258
+ await onTextChunk(chunk);
2259
+ }
2260
+ }
2261
+ return {
2262
+ text,
2263
+ type: 'success'
2264
+ };
2265
+ };
2266
+
2210
2267
  const activateByEvent = (event, assetDir, platform) => {
2211
2268
  // @ts-ignore
2212
2269
  return activateByEvent$1(event, assetDir, platform);
@@ -2711,6 +2768,7 @@ const getOpenApiErrorDetails = async response => {
2711
2768
  };
2712
2769
  const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApiApiBaseUrl, assetDir, platform, options) => {
2713
2770
  const {
2771
+ includeObfuscation = false,
2714
2772
  onTextChunk,
2715
2773
  stream
2716
2774
  } = options ?? {
@@ -2732,6 +2790,9 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
2732
2790
  ...(stream ? {
2733
2791
  stream: true
2734
2792
  } : {}),
2793
+ ...(includeObfuscation ? {
2794
+ include_obfuscation: true
2795
+ } : {}),
2735
2796
  tool_choice: 'auto',
2736
2797
  tools
2737
2798
  }),
@@ -3187,6 +3248,7 @@ const getAiResponse = async ({
3187
3248
  openApiApiKey,
3188
3249
  openRouterApiBaseUrl,
3189
3250
  openRouterApiKey,
3251
+ passIncludeObfuscation = false,
3190
3252
  platform,
3191
3253
  selectedModelId,
3192
3254
  streamingEnabled = false,
@@ -3197,8 +3259,19 @@ const getAiResponse = async ({
3197
3259
  const usesOpenApiModel = isOpenApiModel(selectedModelId, models);
3198
3260
  const usesOpenRouterModel = isOpenRouterModel(selectedModelId, models);
3199
3261
  if (usesOpenApiModel) {
3200
- if (openApiApiKey) {
3262
+ if (useMockApi) {
3263
+ const result = await getMockOpenApiAssistantText(streamingEnabled, onTextChunk);
3264
+ if (result.type === 'success') {
3265
+ const {
3266
+ text: assistantText
3267
+ } = result;
3268
+ text = assistantText;
3269
+ } else {
3270
+ text = getOpenApiErrorMessage(result);
3271
+ }
3272
+ } else if (openApiApiKey) {
3201
3273
  const result = await getOpenApiAssistantText(messages, getOpenApiModelId(selectedModelId), openApiApiKey, openApiApiBaseUrl, assetDir, platform, {
3274
+ includeObfuscation: passIncludeObfuscation,
3202
3275
  onTextChunk,
3203
3276
  stream: streamingEnabled
3204
3277
  });
@@ -3419,17 +3492,6 @@ const focusInput = state => {
3419
3492
  };
3420
3493
  };
3421
3494
 
3422
- const appendMessageToSelectedSession = (sessions, selectedSessionId, message) => {
3423
- return sessions.map(session => {
3424
- if (session.id !== selectedSessionId) {
3425
- return session;
3426
- }
3427
- return {
3428
- ...session,
3429
- messages: [...session.messages, message]
3430
- };
3431
- });
3432
- };
3433
3495
  const updateMessageTextInSelectedSession = (sessions, selectedSessionId, messageId, text, inProgress) => {
3434
3496
  return sessions.map(session => {
3435
3497
  if (session.id !== selectedSessionId) {
@@ -3450,6 +3512,47 @@ const updateMessageTextInSelectedSession = (sessions, selectedSessionId, message
3450
3512
  };
3451
3513
  });
3452
3514
  };
3515
+ const handleTextChunkFunction = async (uid, assistantMessageId, chunk, handleTextChunkState) => {
3516
+ const selectedSession = handleTextChunkState.latestState.sessions.find(session => session.id === handleTextChunkState.latestState.selectedSessionId);
3517
+ if (!selectedSession) {
3518
+ return {
3519
+ latestState: handleTextChunkState.latestState,
3520
+ previousState: handleTextChunkState.previousState
3521
+ };
3522
+ }
3523
+ const assistantMessage = selectedSession.messages.find(message => message.id === assistantMessageId);
3524
+ if (!assistantMessage) {
3525
+ return {
3526
+ latestState: handleTextChunkState.latestState,
3527
+ previousState: handleTextChunkState.previousState
3528
+ };
3529
+ }
3530
+ const updatedText = assistantMessage.text + chunk;
3531
+ const updatedSessions = updateMessageTextInSelectedSession(handleTextChunkState.latestState.sessions, handleTextChunkState.latestState.selectedSessionId, assistantMessageId, updatedText, true);
3532
+ const nextState = {
3533
+ ...handleTextChunkState.latestState,
3534
+ sessions: updatedSessions
3535
+ };
3536
+ set(uid, handleTextChunkState.previousState, nextState);
3537
+ // @ts-ignore
3538
+ await invoke('Chat.rerender');
3539
+ return {
3540
+ latestState: nextState,
3541
+ previousState: nextState
3542
+ };
3543
+ };
3544
+
3545
+ const appendMessageToSelectedSession = (sessions, selectedSessionId, message) => {
3546
+ return sessions.map(session => {
3547
+ if (session.id !== selectedSessionId) {
3548
+ return session;
3549
+ }
3550
+ return {
3551
+ ...session,
3552
+ messages: [...session.messages, message]
3553
+ };
3554
+ });
3555
+ };
3453
3556
  const handleSubmit = async state => {
3454
3557
  const {
3455
3558
  assetDir,
@@ -3461,6 +3564,7 @@ const handleSubmit = async state => {
3461
3564
  openApiApiKey,
3462
3565
  openRouterApiBaseUrl,
3463
3566
  openRouterApiKey,
3567
+ passIncludeObfuscation,
3464
3568
  platform,
3465
3569
  selectedModelId,
3466
3570
  selectedSessionId,
@@ -3547,30 +3651,14 @@ const handleSubmit = async state => {
3547
3651
  set(state.uid, state, optimisticState);
3548
3652
  // @ts-ignore
3549
3653
  await invoke('Chat.rerender');
3550
- let latestState = optimisticState;
3551
- let previousState = optimisticState;
3552
- const selectedOptimisticSession = latestState.sessions.find(session => session.id === latestState.selectedSessionId);
3654
+ let handleTextChunkState = {
3655
+ latestState: optimisticState,
3656
+ previousState: optimisticState
3657
+ };
3658
+ const selectedOptimisticSession = optimisticState.sessions.find(session => session.id === optimisticState.selectedSessionId);
3553
3659
  const messages = (selectedOptimisticSession?.messages ?? []).filter(message => !message.inProgress);
3554
- const onTextChunk = streamingEnabled ? async chunk => {
3555
- const selectedSession = latestState.sessions.find(session => session.id === latestState.selectedSessionId);
3556
- if (!selectedSession) {
3557
- return;
3558
- }
3559
- const assistantMessage = selectedSession.messages.find(message => message.id === assistantMessageId);
3560
- if (!assistantMessage) {
3561
- return;
3562
- }
3563
- const updatedText = assistantMessage.text + chunk;
3564
- const updatedSessions = updateMessageTextInSelectedSession(latestState.sessions, latestState.selectedSessionId, assistantMessageId, updatedText, true);
3565
- const nextState = {
3566
- ...latestState,
3567
- sessions: updatedSessions
3568
- };
3569
- set(state.uid, previousState, nextState);
3570
- previousState = nextState;
3571
- latestState = nextState;
3572
- // @ts-ignore
3573
- await invoke('Chat.rerender');
3660
+ const handleTextChunkFunctionRef = streamingEnabled ? async chunk => {
3661
+ handleTextChunkState = await handleTextChunkFunction(state.uid, assistantMessageId, chunk, handleTextChunkState);
3574
3662
  } : undefined;
3575
3663
  const assistantMessage = await getAiResponse({
3576
3664
  assetDir,
@@ -3578,17 +3666,21 @@ const handleSubmit = async state => {
3578
3666
  mockApiCommandId,
3579
3667
  models,
3580
3668
  nextMessageId: optimisticState.nextMessageId,
3581
- onTextChunk,
3669
+ onTextChunk: handleTextChunkFunctionRef,
3582
3670
  openApiApiBaseUrl,
3583
3671
  openApiApiKey,
3584
3672
  openRouterApiBaseUrl,
3585
3673
  openRouterApiKey,
3674
+ passIncludeObfuscation,
3586
3675
  platform,
3587
3676
  selectedModelId,
3588
3677
  streamingEnabled,
3589
3678
  useMockApi,
3590
3679
  userText
3591
3680
  });
3681
+ const {
3682
+ latestState
3683
+ } = handleTextChunkState;
3592
3684
  const updatedSessions = streamingEnabled ? updateMessageTextInSelectedSession(latestState.sessions, latestState.selectedSessionId, assistantMessageId, assistantMessage.text, false) : appendMessageToSelectedSession(latestState.sessions, latestState.selectedSessionId, assistantMessage);
3593
3685
  const selectedSession = updatedSessions.find(session => session.id === latestState.selectedSessionId);
3594
3686
  if (selectedSession) {
@@ -4037,6 +4129,7 @@ const loadOpenApiApiKey = async () => {
4037
4129
  return '';
4038
4130
  }
4039
4131
  };
4132
+
4040
4133
  const loadOpenRouterApiKey = async () => {
4041
4134
  try {
4042
4135
  const savedOpenRouterApiKey = await get('secrets.openRouterApiKey');
@@ -4045,12 +4138,35 @@ const loadOpenRouterApiKey = async () => {
4045
4138
  return '';
4046
4139
  }
4047
4140
  };
4141
+
4142
+ const loadPassIncludeObfuscation = async () => {
4143
+ try {
4144
+ const savedPassIncludeObfuscation = await get('chatView.passIncludeObfuscation');
4145
+ return typeof savedPassIncludeObfuscation === 'boolean' ? savedPassIncludeObfuscation : false;
4146
+ } catch {
4147
+ return false;
4148
+ }
4149
+ };
4150
+
4151
+ const loadStreamingEnabled = async () => {
4152
+ try {
4153
+ const savedStreamingEnabled = await get('chatView.streamingEnabled');
4154
+ return typeof savedStreamingEnabled === 'boolean' ? savedStreamingEnabled : false;
4155
+ } catch {
4156
+ return false;
4157
+ }
4158
+ };
4159
+
4048
4160
  const loadPreferences = async () => {
4049
4161
  const openApiApiKey = await loadOpenApiApiKey();
4050
4162
  const openRouterApiKey = await loadOpenRouterApiKey();
4163
+ const streamingEnabled = await loadStreamingEnabled();
4164
+ const passIncludeObfuscation = await loadPassIncludeObfuscation();
4051
4165
  return {
4052
4166
  openApiApiKey,
4053
- openRouterApiKey
4167
+ openRouterApiKey,
4168
+ passIncludeObfuscation,
4169
+ streamingEnabled
4054
4170
  };
4055
4171
  };
4056
4172
 
@@ -4081,7 +4197,9 @@ const loadContent = async (state, savedState) => {
4081
4197
  const savedViewMode = getSavedViewMode(savedState);
4082
4198
  const {
4083
4199
  openApiApiKey,
4084
- openRouterApiKey
4200
+ openRouterApiKey,
4201
+ passIncludeObfuscation,
4202
+ streamingEnabled
4085
4203
  } = await loadPreferences();
4086
4204
  const legacySavedSessions = getSavedSessions(savedState);
4087
4205
  const storedSessions = await listChatSessions();
@@ -4116,13 +4234,30 @@ const loadContent = async (state, savedState) => {
4116
4234
  openApiApiKeyInput: openApiApiKey,
4117
4235
  openRouterApiKey,
4118
4236
  openRouterApiKeyInput: openRouterApiKey,
4237
+ passIncludeObfuscation,
4119
4238
  selectedModelId,
4120
4239
  selectedSessionId,
4121
4240
  sessions,
4241
+ streamingEnabled,
4122
4242
  viewMode
4123
4243
  };
4124
4244
  };
4125
4245
 
4246
+ const mockOpenApiStreamFinish = state => {
4247
+ finish();
4248
+ return state;
4249
+ };
4250
+
4251
+ const mockOpenApiStreamPushChunk = (state, chunk) => {
4252
+ pushChunk(chunk);
4253
+ return state;
4254
+ };
4255
+
4256
+ const mockOpenApiStreamReset = state => {
4257
+ reset$1();
4258
+ return state;
4259
+ };
4260
+
4126
4261
  const openMockSession = async (state, mockSessionId, mockChatMessages) => {
4127
4262
  const {
4128
4263
  sessions: currentSessions
@@ -4157,9 +4292,13 @@ const openMockSession = async (state, mockSessionId, mockChatMessages) => {
4157
4292
  };
4158
4293
  };
4159
4294
 
4160
- const getCss = composerHeight => {
4295
+ const getCss = (composerHeight, listItemHeight, chatMessageFontSize, chatMessageLineHeight, chatMessageFontFamily) => {
4161
4296
  return `:root {
4162
4297
  --ChatInputBoxHeight: ${composerHeight}px;
4298
+ --ChatListItemHeight: ${listItemHeight}px;
4299
+ --ChatMessageFontSize: ${chatMessageFontSize}px;
4300
+ --ChatMessageLineHeight: ${chatMessageLineHeight}px;
4301
+ --ChatMessageFontFamily: ${chatMessageFontFamily};
4163
4302
  }`;
4164
4303
  };
4165
4304
 
@@ -4168,10 +4307,14 @@ const getCss = composerHeight => {
4168
4307
 
4169
4308
  const renderCss = (oldState, newState) => {
4170
4309
  const {
4310
+ chatMessageFontFamily,
4311
+ chatMessageFontSize,
4312
+ chatMessageLineHeight,
4171
4313
  composerHeight,
4314
+ listItemHeight,
4172
4315
  uid
4173
4316
  } = newState;
4174
- const css = getCss(composerHeight);
4317
+ const css = getCss(composerHeight, listItemHeight, chatMessageFontSize, chatMessageLineHeight, chatMessageFontFamily);
4175
4318
  return [SetCss, uid, css];
4176
4319
  };
4177
4320
 
@@ -4831,8 +4974,10 @@ const reset = async state => {
4831
4974
  composerHeight: getMinComposerHeightForState(state),
4832
4975
  composerValue: '',
4833
4976
  openRouterApiKey: '',
4977
+ selectedModelId: 'test',
4834
4978
  selectedSessionId: '',
4835
4979
  sessions: [],
4980
+ streamingEnabled: false,
4836
4981
  viewMode: 'list'
4837
4982
  };
4838
4983
  };
@@ -4897,6 +5042,13 @@ const setChatList = state => {
4897
5042
  };
4898
5043
  };
4899
5044
 
5045
+ const setStreamingEnabled = (state, streamingEnabled) => {
5046
+ return {
5047
+ ...state,
5048
+ streamingEnabled
5049
+ };
5050
+ };
5051
+
4900
5052
  const defaultMockApiCommandId = 'ChatE2e.mockApi';
4901
5053
  const useMockApi = (state, value, mockApiCommandId = defaultMockApiCommandId) => {
4902
5054
  if (!value) {
@@ -4938,6 +5090,9 @@ const commandMap = {
4938
5090
  'Chat.initialize': initialize,
4939
5091
  'Chat.loadContent': wrapCommand(loadContent),
4940
5092
  'Chat.loadContent2': wrapCommand(loadContent),
5093
+ 'Chat.mockOpenApiStreamFinish': wrapCommand(mockOpenApiStreamFinish),
5094
+ 'Chat.mockOpenApiStreamPushChunk': wrapCommand(mockOpenApiStreamPushChunk),
5095
+ 'Chat.mockOpenApiStreamReset': wrapCommand(mockOpenApiStreamReset),
4941
5096
  'Chat.openMockSession': wrapCommand(openMockSession),
4942
5097
  'Chat.render2': render2,
4943
5098
  'Chat.renderEventListeners': renderEventListeners,
@@ -4947,6 +5102,7 @@ const commandMap = {
4947
5102
  'Chat.saveState': wrapGetter(saveState),
4948
5103
  'Chat.setChatList': wrapCommand(setChatList),
4949
5104
  'Chat.setOpenRouterApiKey': wrapCommand(setOpenRouterApiKey),
5105
+ 'Chat.setStreamingEnabled': wrapCommand(setStreamingEnabled),
4950
5106
  'Chat.terminate': terminate,
4951
5107
  'Chat.useMockApi': wrapCommand(useMockApi)
4952
5108
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/chat-view",
3
- "version": "1.19.0",
3
+ "version": "1.20.0",
4
4
  "description": "Chat View Worker",
5
5
  "repository": {
6
6
  "type": "git",