@lvce-editor/chat-view 6.65.0 → 6.67.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.
@@ -1213,7 +1213,9 @@ const End = 255;
1213
1213
  const Home = 12;
1214
1214
  const UpArrow = 14;
1215
1215
  const DownArrow = 16;
1216
+ const KeyN = 42;
1216
1217
 
1218
+ const CtrlCmd = 1 << 11 >>> 0;
1217
1219
  const Shift = 1 << 10 >>> 0;
1218
1220
 
1219
1221
  const Chat$2 = 97;
@@ -1847,6 +1849,7 @@ const Debug = 'Debug';
1847
1849
  const BackToChats = 'Back to chats';
1848
1850
  const BackToChatList = 'Back to chat list';
1849
1851
  const Search$1 = 'Search';
1852
+ const ScrollDown$1 = 'Scroll down';
1850
1853
  const SearchModels = 'Search models';
1851
1854
  const SearchChats = 'Search chats';
1852
1855
  const Paste = 'Paste';
@@ -1867,12 +1870,17 @@ const ComposePlaceholder = 'Type your message. Enter to send.';
1867
1870
  const AttachImageAsContext = 'Attach Image as Context';
1868
1871
  const OpenImageInNewTab = 'Open image in new tab';
1869
1872
  const RemoveAttachment = 'Remove attachment';
1870
- const ImageCouldNotBeLoaded = 'image could not be loaded';
1873
+ const ImageCouldNotBeLoaded = 'Image preview could not be loaded';
1871
1874
  const OpenRouterApiKeyPlaceholder = 'Enter OpenRouter API key';
1872
1875
  const OpenApiApiKeyPlaceholder = 'Enter OpenAI API key';
1873
1876
  const SendMessage = 'Send message';
1874
1877
  const StartVoiceDictation = 'Start voice dictation';
1875
1878
  const AddContext$1 = 'Add Context';
1879
+ const AddAction = 'Add Action';
1880
+ const OpenInVsCode = 'Open in VSCode';
1881
+ const Commit = 'Commit';
1882
+ const OpenTerminal = 'Open Terminal';
1883
+ const ShowDiff = 'Show Diff';
1876
1884
  const CreatePullRequest$2 = 'Create PR';
1877
1885
  const Save = 'Save';
1878
1886
  const Saving = 'Saving...';
@@ -1922,6 +1930,9 @@ const settings = () => {
1922
1930
  const search = () => {
1923
1931
  return i18nString(Search$1);
1924
1932
  };
1933
+ const scrollDown = () => {
1934
+ return i18nString(ScrollDown$1);
1935
+ };
1925
1936
  const searchModels = () => {
1926
1937
  return i18nString(SearchModels);
1927
1938
  };
@@ -1985,6 +1996,21 @@ const startVoiceDictation = () => {
1985
1996
  const addContext = () => {
1986
1997
  return i18nString(AddContext$1);
1987
1998
  };
1999
+ const addAction = () => {
2000
+ return i18nString(AddAction);
2001
+ };
2002
+ const openInVsCode = () => {
2003
+ return i18nString(OpenInVsCode);
2004
+ };
2005
+ const commit = () => {
2006
+ return i18nString(Commit);
2007
+ };
2008
+ const openTerminal = () => {
2009
+ return i18nString(OpenTerminal);
2010
+ };
2011
+ const showDiff = () => {
2012
+ return i18nString(ShowDiff);
2013
+ };
1988
2014
  const createPullRequest = () => {
1989
2015
  return i18nString(CreatePullRequest$2);
1990
2016
  };
@@ -2059,16 +2085,19 @@ const getDefaultModelsOpenAi = () => {
2059
2085
  id: 'openapi/gpt-4o-mini',
2060
2086
  name: 'GPT-4o Mini',
2061
2087
  provider: 'openApi',
2088
+ supportsImages: true,
2062
2089
  usageCost: 1
2063
2090
  }, {
2064
2091
  id: 'openapi/gpt-4o',
2065
2092
  name: 'GPT-4o',
2066
2093
  provider: 'openApi',
2094
+ supportsImages: true,
2067
2095
  usageCost: 3
2068
2096
  }, {
2069
2097
  id: 'openapi/gpt-4.1-mini',
2070
2098
  name: 'GPT-4.1 Mini',
2071
2099
  provider: 'openApi',
2100
+ supportsImages: true,
2072
2101
  usageCost: 1
2073
2102
  }];
2074
2103
  };
@@ -2306,6 +2335,8 @@ const createDefaultState = () => {
2306
2335
  chatSendAreaPaddingLeft: 8,
2307
2336
  chatSendAreaPaddingRight: 8,
2308
2337
  chatSendAreaPaddingTop: 10,
2338
+ composerAttachmentPreviewOverlayAttachmentId: '',
2339
+ composerAttachmentPreviewOverlayError: false,
2309
2340
  composerAttachments: [],
2310
2341
  composerAttachmentsHeight: 0,
2311
2342
  composerDropActive: false,
@@ -2336,12 +2367,14 @@ const createDefaultState = () => {
2336
2367
  messagesScrollTop: 0,
2337
2368
  mockAiResponseDelay: 800,
2338
2369
  mockApiCommandId: '',
2370
+ mockOpenApiRequests: [],
2339
2371
  modelPickerHeaderHeight,
2340
2372
  modelPickerHeight: getModelPickerHeight(modelPickerHeaderHeight, visibleModels.length),
2341
2373
  modelPickerListScrollTop: 0,
2342
2374
  modelPickerOpen: false,
2343
2375
  modelPickerSearchValue: '',
2344
2376
  models,
2377
+ nextAttachmentId: 1,
2345
2378
  nextMessageId: 1,
2346
2379
  openApiApiBaseUrl: 'https://api.openai.com/v1',
2347
2380
  openApiApiKey: '',
@@ -2372,6 +2405,7 @@ const createDefaultState = () => {
2372
2405
  responsivePickerVisibilityEnabled,
2373
2406
  runMode: 'local',
2374
2407
  runModePickerOpen: false,
2408
+ scrollDownButtonEnabled: false,
2375
2409
  searchEnabled: false,
2376
2410
  searchFieldVisible: false,
2377
2411
  searchValue: '',
@@ -2602,6 +2636,13 @@ const getComposerAttachmentPreviewSrc = async (blob, displayType, mimeType) => {
2602
2636
  return `data:${mimeType || 'application/octet-stream'};base64,${base64}`;
2603
2637
  };
2604
2638
 
2639
+ const getComposerAttachmentTextContent = async (blob, displayType) => {
2640
+ if (displayType !== 'text-file') {
2641
+ return undefined;
2642
+ }
2643
+ return blob.text();
2644
+ };
2645
+
2605
2646
  const isChatAttachmentAddedEvent = event => {
2606
2647
  return event.type === 'chat-attachment-added';
2607
2648
  };
@@ -2624,6 +2665,7 @@ const getComposerAttachments = async sessionId => {
2624
2665
  }
2625
2666
  const displayType = await getComposerAttachmentDisplayType(event.blob, event.name, event.mimeType);
2626
2667
  const previewSrc = await getComposerAttachmentPreviewSrc(event.blob, displayType, event.mimeType);
2668
+ const textContent = await getComposerAttachmentTextContent(event.blob, displayType);
2627
2669
  attachments.set(event.attachmentId, {
2628
2670
  attachmentId: event.attachmentId,
2629
2671
  displayType,
@@ -2632,7 +2674,10 @@ const getComposerAttachments = async sessionId => {
2632
2674
  ...(previewSrc ? {
2633
2675
  previewSrc
2634
2676
  } : {}),
2635
- size: event.size
2677
+ size: event.size,
2678
+ ...(typeof textContent === 'string' ? {
2679
+ textContent
2680
+ } : {})
2636
2681
  });
2637
2682
  }
2638
2683
  return [...attachments.values()];
@@ -3194,7 +3239,8 @@ const openModelPicker = state => {
3194
3239
  return {
3195
3240
  ...state,
3196
3241
  agentModePickerOpen: false,
3197
- focus: 'model-picker-input',
3242
+ focus: modelPickerOpen ? 'model-picker-input' : state.focus,
3243
+ focused: modelPickerOpen ? true : state.focused,
3198
3244
  modelPickerHeight: getModelPickerHeight(state.modelPickerHeaderHeight, visibleModels.length),
3199
3245
  modelPickerListScrollTop: 0,
3200
3246
  modelPickerOpen,
@@ -3230,6 +3276,10 @@ const getKeyBindings = () => {
3230
3276
  command: 'Chat.chatListFocusPrevious',
3231
3277
  key: UpArrow,
3232
3278
  when: FocusChatList
3279
+ }, {
3280
+ command: 'Chat.handleClickNew',
3281
+ key: CtrlCmd | KeyN,
3282
+ when: FocusChatInput
3233
3283
  }, {
3234
3284
  command: 'Chat.handleSubmit',
3235
3285
  key: Enter,
@@ -3328,14 +3378,22 @@ const ComposerDropTarget = 'composer-drop-target';
3328
3378
  const ComposerAttachmentPrefix = 'composer-attachment:';
3329
3379
  const ComposerAttachmentPreviewPrefix = 'composer-attachment-preview:';
3330
3380
  const ComposerAttachmentRemovePrefix = 'composer-attachment-remove:';
3381
+ const ComposerAttachmentPreviewOverlay = 'composer-attachment-preview-overlay';
3331
3382
  const AddContext = 'add-context';
3332
3383
  const Dictate = 'dictate';
3333
3384
  const CreatePullRequest$1 = 'create-pull-request';
3385
+ const FocusAddAction = 'focus-add-action';
3386
+ const FocusOpenInVsCode = 'focus-open-in-vscode';
3387
+ const FocusCommit = 'focus-commit';
3388
+ const FocusOpenTerminal = 'focus-open-terminal';
3389
+ const FocusShowDiff = 'focus-show-diff';
3334
3390
  const Send = 'send';
3391
+ const ScrollDown = 'scroll-down';
3335
3392
  const Back = 'back';
3336
3393
  const ModelPickerToggle = 'model-picker-toggle';
3337
3394
  const ModelPickerSearch = 'model-picker-search';
3338
3395
  const ModelPickerList = 'model-picker-list';
3396
+ const PickerList = 'picker-list';
3339
3397
  const AgentModePickerToggle = 'agent-mode-picker-toggle';
3340
3398
  const AgentModePickerItemPrefix = 'agent-mode-picker-item:';
3341
3399
  const ReasoningEffortPickerToggle = 'reasoning-effort-picker-toggle';
@@ -3397,6 +3455,12 @@ const getModelPickerItemInputName = modelId => {
3397
3455
  const getComposerAttachmentInputName = attachmentId => {
3398
3456
  return `${ComposerAttachmentPrefix}${attachmentId}`;
3399
3457
  };
3458
+ const isComposerAttachmentInputName = name => {
3459
+ return name.startsWith(ComposerAttachmentPrefix);
3460
+ };
3461
+ const getAttachmentIdFromComposerAttachmentInputName = name => {
3462
+ return name.slice(ComposerAttachmentPrefix.length);
3463
+ };
3400
3464
  const getComposerAttachmentRemoveInputName = attachmentId => {
3401
3465
  return `${ComposerAttachmentRemovePrefix}${attachmentId}`;
3402
3466
  };
@@ -3412,6 +3476,9 @@ const isComposerAttachmentRemoveInputName = name => {
3412
3476
  const getAttachmentIdFromComposerAttachmentRemoveInputName = name => {
3413
3477
  return name.slice(ComposerAttachmentRemovePrefix.length);
3414
3478
  };
3479
+ const isComposerAttachmentPreviewOverlayInputName = name => {
3480
+ return name === ComposerAttachmentPreviewOverlay;
3481
+ };
3415
3482
  const isModelPickerItemInputName = name => {
3416
3483
  return name.startsWith(ModelPickerItemPrefix);
3417
3484
  };
@@ -3505,6 +3572,10 @@ const getMenuEntries = (menuId, props) => {
3505
3572
  }
3506
3573
  };
3507
3574
 
3575
+ const getMockOpenApiRequests = state => {
3576
+ return state.mockOpenApiRequests;
3577
+ };
3578
+
3508
3579
  const getQuickPickMenuEntries = () => {
3509
3580
  return [];
3510
3581
  };
@@ -3630,6 +3701,12 @@ const getModelPickerClickIndex = (y, height, eventY, modelPickerBottomOffset, mo
3630
3701
  return Math.floor(relativeY / modelPickerItemHeight);
3631
3702
  };
3632
3703
 
3704
+ const AutoScrollTopA = Number.MAX_SAFE_INTEGER;
3705
+ const AutoScrollTopB = Number.MAX_SAFE_INTEGER - 1;
3706
+ const getNextAutoScrollTop = currentScrollTop => {
3707
+ return currentScrollTop === AutoScrollTopA ? AutoScrollTopB : AutoScrollTopA;
3708
+ };
3709
+
3633
3710
  const openFolder = async () => {
3634
3711
  try {
3635
3712
  return await invoke('FilePicker.showDirectoryPicker');
@@ -4207,6 +4284,54 @@ const getBasicChatTools = async (agentMode = defaultAgentMode, questionToolEnabl
4207
4284
  }
4208
4285
  };
4209
4286
 
4287
+ const getAttachmentTextPart = attachment => {
4288
+ switch (attachment.displayType) {
4289
+ case 'file':
4290
+ return {
4291
+ text: `Attached file "${attachment.name}" (${attachment.mimeType || 'application/octet-stream'}, ${attachment.size} bytes).`,
4292
+ type: 'input_text'
4293
+ };
4294
+ case 'image':
4295
+ return {
4296
+ text: `Attached image "${attachment.name}" could not be encoded for the AI request.`,
4297
+ type: 'input_text'
4298
+ };
4299
+ case 'invalid-image':
4300
+ return {
4301
+ text: `Attached file "${attachment.name}" could not be processed as a valid image.`,
4302
+ type: 'input_text'
4303
+ };
4304
+ case 'text-file':
4305
+ return {
4306
+ text: attachment.textContent ? `Attached text file "${attachment.name}" (${attachment.mimeType || 'text/plain'}):\n\n${attachment.textContent}` : `Attached text file "${attachment.name}" (${attachment.mimeType || 'text/plain'}).`,
4307
+ type: 'input_text'
4308
+ };
4309
+ }
4310
+ };
4311
+ const getChatMessageOpenAiContent = message => {
4312
+ if (!message.attachments || message.attachments.length === 0) {
4313
+ return message.text;
4314
+ }
4315
+ const parts = [];
4316
+ if (message.text) {
4317
+ parts.push({
4318
+ text: message.text,
4319
+ type: 'input_text'
4320
+ });
4321
+ }
4322
+ for (const attachment of message.attachments) {
4323
+ if (attachment.displayType === 'image' && attachment.previewSrc) {
4324
+ parts.push({
4325
+ image_url: attachment.previewSrc,
4326
+ type: 'input_image'
4327
+ });
4328
+ continue;
4329
+ }
4330
+ parts.push(getAttachmentTextPart(attachment));
4331
+ }
4332
+ return parts;
4333
+ };
4334
+
4210
4335
  const getClientRequestIdHeader = () => {
4211
4336
  return {
4212
4337
  'x-client-request-id': crypto.randomUUID()
@@ -4222,11 +4347,22 @@ const getMockAiResponse = async (userMessage, delayInMs) => {
4222
4347
  return `Mock AI response: I received "${userMessage}".`;
4223
4348
  };
4224
4349
 
4350
+ let requests = [];
4351
+ const reset$2 = () => {
4352
+ requests = [];
4353
+ };
4354
+ const capture = request => {
4355
+ requests = [...requests, request];
4356
+ };
4357
+ const getAll = () => {
4358
+ return requests;
4359
+ };
4360
+
4225
4361
  let queue = [];
4226
4362
  let waiters = [];
4227
4363
  let finished = false;
4228
4364
  let errorResult;
4229
- const reset$2 = () => {
4365
+ const reset$1 = () => {
4230
4366
  queue = [];
4231
4367
  waiters = [];
4232
4368
  finished = false;
@@ -4300,6 +4436,47 @@ const readNextChunk = async () => {
4300
4436
  return promise;
4301
4437
  };
4302
4438
 
4439
+ const lastRequestSummaryToken = '__MOCK_OPENAPI_LAST_REQUEST_SUMMARY__';
4440
+ const getLastRequestSummary = () => {
4441
+ const requests = getAll();
4442
+ const request = requests.at(-1);
4443
+ if (!request || !request.payload || typeof request.payload !== 'object') {
4444
+ return 'mock-request-summary images=0 text-files=0';
4445
+ }
4446
+ const input = Reflect.get(request.payload, 'input');
4447
+ if (!Array.isArray(input) || input.length === 0) {
4448
+ return 'mock-request-summary images=0 text-files=0';
4449
+ }
4450
+ let imageCount = 0;
4451
+ let textFileCount = 0;
4452
+ for (const item of input) {
4453
+ if (!item || typeof item !== 'object') {
4454
+ continue;
4455
+ }
4456
+ const content = Reflect.get(item, 'content');
4457
+ if (!Array.isArray(content)) {
4458
+ continue;
4459
+ }
4460
+ for (const part of content) {
4461
+ if (!part || typeof part !== 'object') {
4462
+ continue;
4463
+ }
4464
+ const type = Reflect.get(part, 'type');
4465
+ if (type === 'input_image') {
4466
+ imageCount++;
4467
+ continue;
4468
+ }
4469
+ if (type !== 'input_text') {
4470
+ continue;
4471
+ }
4472
+ const text = Reflect.get(part, 'text');
4473
+ if (typeof text === 'string' && text.startsWith('Attached text file "')) {
4474
+ textFileCount++;
4475
+ }
4476
+ }
4477
+ }
4478
+ return `mock-request-summary images=${imageCount} text-files=${textFileCount}`;
4479
+ };
4303
4480
  const getResponseFunctionCalls$1 = value => {
4304
4481
  if (!value || typeof value !== 'object') {
4305
4482
  return [];
@@ -4497,9 +4674,10 @@ const getMockOpenApiAssistantText = async (stream, onTextChunk, onToolCallsChunk
4497
4674
  }
4498
4675
  continue;
4499
4676
  }
4500
- text += chunk;
4677
+ const resolvedChunk = chunk === lastRequestSummaryToken ? getLastRequestSummary() : chunk;
4678
+ text += resolvedChunk;
4501
4679
  if (stream && onTextChunk) {
4502
- await onTextChunk(chunk);
4680
+ await onTextChunk(resolvedChunk);
4503
4681
  }
4504
4682
  }
4505
4683
  if (!requestDone && remainder) {
@@ -5336,7 +5514,7 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
5336
5514
  stream: false
5337
5515
  };
5338
5516
  const openAiInput = messages.map(message => ({
5339
- content: message.text,
5517
+ content: getChatMessageOpenAiContent(message),
5340
5518
  role: message.role
5341
5519
  }));
5342
5520
  const tools = await getBasicChatTools(agentMode, questionToolEnabled, toolEnablement);
@@ -5729,12 +5907,22 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
5729
5907
  };
5730
5908
  };
5731
5909
 
5910
+ const imageRegex = /\b(?:image|vision|multimodal)\b/i;
5911
+ const unsupportedRegex = /\bdoes(?:\s+not|n't)\s+support|not\s+support(?:ed)?|unsupported\b/i;
5732
5912
  const isOffline = () => {
5733
5913
  if (!globalThis.navigator) {
5734
5914
  return false;
5735
5915
  }
5736
5916
  return globalThis.navigator.onLine === false;
5737
5917
  };
5918
+ const isImageNotSupportedError = errorResult => {
5919
+ const haystack = [errorResult.errorCode, errorResult.errorMessage, errorResult.errorType].filter(Boolean).join(' ');
5920
+ return imageRegex.test(haystack) && unsupportedRegex.test(haystack);
5921
+ };
5922
+ const getImageNotSupportedMessage = modelName => {
5923
+ const subject = modelName ? `${modelName} does not support image attachments.` : 'This model does not support image attachments.';
5924
+ return `${subject} Choose a vision-capable model like GPT-4o Mini or GPT-4o, or remove the image and try again.`;
5925
+ };
5738
5926
  const getOpenApiErrorMessage = errorResult => {
5739
5927
  switch (errorResult.details) {
5740
5928
  case 'http-error':
@@ -5748,6 +5936,9 @@ const getOpenApiErrorMessage = errorResult => {
5748
5936
  const status = typeof errorResult.statusCode === 'number' ? errorResult.statusCode : 401;
5749
5937
  return `OpenAI request failed (Status ${status}): Invalid API key. Please verify your OpenAI API key in Chat Settings.`;
5750
5938
  }
5939
+ if (isImageNotSupportedError(errorResult)) {
5940
+ return getImageNotSupportedMessage();
5941
+ }
5751
5942
  if (errorResult.statusCode === 429) {
5752
5943
  let prefix = 'OpenAI rate limit exceeded (429)';
5753
5944
  if (hasErrorCode) {
@@ -6196,17 +6387,6 @@ const isOpenRouterModel = (selectedModelId, models) => {
6196
6387
  return selectedModelId.toLowerCase().startsWith('openrouter/');
6197
6388
  };
6198
6389
 
6199
- let requests = [];
6200
- const reset$1 = () => {
6201
- requests = [];
6202
- };
6203
- const capture = request => {
6204
- requests = [...requests, request];
6205
- };
6206
- const getAll = () => {
6207
- return requests;
6208
- };
6209
-
6210
6390
  /* eslint-disable prefer-destructuring */
6211
6391
 
6212
6392
  const trailingSlashesRegex = /\/+$/;
@@ -6221,6 +6401,9 @@ const getEffectiveBackendModelId = selectedModelId => {
6221
6401
  }
6222
6402
  return selectedModelId.slice(separatorIndex + 1);
6223
6403
  };
6404
+ const hasImageAttachments = messages => {
6405
+ return messages.some(message => message.attachments?.some(attachment => attachment.displayType === 'image'));
6406
+ };
6224
6407
  const getBackendAssistantText = async (messages, selectedModelId, backendUrl, authAccessToken, systemPrompt) => {
6225
6408
  let response;
6226
6409
  try {
@@ -6268,6 +6451,7 @@ const getAiResponse = async ({
6268
6451
  nextMessageId,
6269
6452
  onDataEvent,
6270
6453
  onEventStreamFinished,
6454
+ onMockOpenApiRequestCaptured,
6271
6455
  onTextChunk,
6272
6456
  onToolCallsChunk,
6273
6457
  openApiApiBaseUrl,
@@ -6343,7 +6527,15 @@ const getAiResponse = async ({
6343
6527
  }
6344
6528
  }
6345
6529
  let text = '';
6346
- if (authEnabled) {
6530
+ const usesOpenApiModel = isOpenApiModel(selectedModelId, models);
6531
+ const usesOpenRouterModel = isOpenRouterModel(selectedModelId, models);
6532
+ const selectedModel = models.find(model => model.id === selectedModelId);
6533
+ const supportsImages = selectedModel?.supportsImages ?? false;
6534
+ const supportsReasoningEffort = selectedModel?.supportsReasoningEffort ?? false;
6535
+ if (hasImageAttachments(messages) && !supportsImages) {
6536
+ text = getImageNotSupportedMessage(selectedModel?.name);
6537
+ }
6538
+ if (!text && authEnabled) {
6347
6539
  if (!backendUrl) {
6348
6540
  text = backendUrlRequiredMessage;
6349
6541
  } else if (authAccessToken) {
@@ -6352,15 +6544,11 @@ const getAiResponse = async ({
6352
6544
  text = backendAccessTokenRequiredMessage;
6353
6545
  }
6354
6546
  }
6355
- const usesOpenApiModel = isOpenApiModel(selectedModelId, models);
6356
- const usesOpenRouterModel = isOpenRouterModel(selectedModelId, models);
6357
- const selectedModel = models.find(model => model.id === selectedModelId);
6358
- const supportsReasoningEffort = selectedModel?.supportsReasoningEffort ?? false;
6359
6547
  if (!text && usesOpenApiModel) {
6360
6548
  const safeMaxToolCalls = Math.max(1, maxToolCalls);
6361
6549
  if (useMockApi) {
6362
6550
  const openAiInput = messages.map(message => ({
6363
- content: message.text,
6551
+ content: getChatMessageOpenAiContent(message),
6364
6552
  role: message.role
6365
6553
  }));
6366
6554
  const modelId = getOpenApiModelId(selectedModelId);
@@ -6372,12 +6560,16 @@ const getAiResponse = async ({
6372
6560
  const maxToolIterations = safeMaxToolCalls - 1;
6373
6561
  let previousResponseId;
6374
6562
  for (let i = 0; i <= maxToolIterations; i++) {
6375
- capture({
6563
+ const request = {
6376
6564
  headers,
6377
6565
  method: 'POST',
6378
6566
  payload: getOpenAiParams(openAiInput, modelId, streamingEnabled, passIncludeObfuscation, await getBasicChatTools(agentMode, questionToolEnabled, toolEnablement), agentMode === 'plan' ? false : webSearchEnabled, safeMaxToolCalls, systemPrompt, previousResponseId, reasoningEffort, supportsReasoningEffort),
6379
6567
  url: getOpenApiApiEndpoint(openApiApiBaseUrl)
6380
- });
6568
+ };
6569
+ capture(request);
6570
+ if (onMockOpenApiRequestCaptured) {
6571
+ await Promise.resolve(onMockOpenApiRequestCaptured(request));
6572
+ }
6381
6573
  const result = await getMockOpenApiAssistantText(streamingEnabled, onTextChunk, onToolCallsChunk, onDataEvent, onEventStreamFinished);
6382
6574
  if (result.type !== 'success') {
6383
6575
  text = getOpenApiErrorMessage(result);
@@ -6532,6 +6724,9 @@ const Chat = 'Chat';
6532
6724
  const ChatActions = 'ChatActions';
6533
6725
  const ChatAuthError = 'ChatAuthError';
6534
6726
  const ChatFocus = 'ChatFocus';
6727
+ const ChatFocusActions = 'ChatFocusActions';
6728
+ const ChatFocusHeader = 'ChatFocusHeader';
6729
+ const ChatFocusProject = 'ChatFocusProject';
6535
6730
  const ChatHeader = 'ChatHeader';
6536
6731
  const ChatHeaderLabel = 'ChatHeaderLabel';
6537
6732
  const ChatInputBox = 'ChatInputBox';
@@ -6567,6 +6762,9 @@ const ChatComposerAttachmentImage = 'ChatComposerAttachmentImage';
6567
6762
  const ChatComposerAttachmentInvalidImage = 'ChatComposerAttachmentInvalidImage';
6568
6763
  const ChatComposerAttachmentLabel = 'ChatComposerAttachmentLabel';
6569
6764
  const ChatComposerAttachmentPreview = 'ChatComposerAttachmentPreview';
6765
+ const ChatComposerAttachmentPreviewOverlay = 'ChatComposerAttachmentPreviewOverlay';
6766
+ const ChatComposerAttachmentPreviewOverlayError = 'ChatComposerAttachmentPreviewOverlayError';
6767
+ const ChatComposerAttachmentPreviewOverlayImage = 'ChatComposerAttachmentPreviewOverlayImage';
6570
6768
  const ChatComposerAttachmentRemoveButton = 'ChatComposerAttachmentRemoveButton';
6571
6769
  const ChatComposerAttachments = 'ChatComposerAttachments';
6572
6770
  const ChatComposerAttachmentTextFile = 'ChatComposerAttachmentTextFile';
@@ -6605,6 +6803,7 @@ const FileIcon = 'FileIcon';
6605
6803
  const IconButton = 'IconButton';
6606
6804
  const IconButtonDisabled = 'IconButtonDisabled';
6607
6805
  const ImageElement = 'ImageElement';
6806
+ const ImageErrorMessage = 'ImageErrorMessage';
6608
6807
  const InputBox = 'InputBox';
6609
6808
  const Insertion = 'Insertion';
6610
6809
  const Label = 'Label';
@@ -8379,12 +8578,6 @@ const getMentionContextMessage = async value => {
8379
8578
  };
8380
8579
  };
8381
8580
 
8382
- const AutoScrollTopA = Number.MAX_SAFE_INTEGER;
8383
- const AutoScrollTopB = Number.MAX_SAFE_INTEGER - 1;
8384
- const getNextAutoScrollTop = currentScrollTop => {
8385
- return currentScrollTop === AutoScrollTopA ? AutoScrollTopB : AutoScrollTopA;
8386
- };
8387
-
8388
8581
  const slashCommandRegex = /^\/([a-z][a-z0-9-]*)(?:\s+.*)?$/i;
8389
8582
  const getSlashCommand = value => {
8390
8583
  const trimmed = value.trim();
@@ -8662,6 +8855,19 @@ const withUpdatedMessageScrollTop = state => {
8662
8855
  };
8663
8856
  };
8664
8857
  const workspaceUriPlaceholder = '{{workspaceUri}}';
8858
+ const clearComposerAttachments = async (sessionId, attachmentIds) => {
8859
+ if (!sessionId) {
8860
+ return;
8861
+ }
8862
+ for (const attachmentId of attachmentIds) {
8863
+ await appendChatViewEvent({
8864
+ attachmentId,
8865
+ sessionId,
8866
+ timestamp: new Date().toISOString(),
8867
+ type: 'chat-attachment-removed'
8868
+ });
8869
+ }
8870
+ };
8665
8871
  const getCurrentDate = () => {
8666
8872
  return new Date().toISOString().slice(0, 10);
8667
8873
  };
@@ -8754,7 +8960,12 @@ const handleSubmit = async state => {
8754
8960
  minute: '2-digit'
8755
8961
  });
8756
8962
  const userMessageId = crypto.randomUUID();
8963
+ const composerAttachments = state.composerAttachments.length > 0 ? state.composerAttachments : await getComposerAttachments(state.selectedSessionId);
8964
+ await clearComposerAttachments(state.selectedSessionId, composerAttachments.map(attachment => attachment.attachmentId));
8757
8965
  const userMessage = {
8966
+ ...(composerAttachments.length > 0 ? {
8967
+ attachments: composerAttachments
8968
+ } : {}),
8758
8969
  id: userMessageId,
8759
8970
  role: 'user',
8760
8971
  text: userText,
@@ -8869,6 +9080,9 @@ const handleSubmit = async state => {
8869
9080
  latestState: optimisticState,
8870
9081
  previousState: optimisticState
8871
9082
  };
9083
+ let {
9084
+ mockOpenApiRequests
9085
+ } = optimisticState;
8872
9086
  const selectedOptimisticSession = optimisticState.sessions.find(session => session.id === optimisticState.selectedSessionId);
8873
9087
  const systemPrompt = getEffectiveSystemPrompt(optimisticState, selectedOptimisticSession);
8874
9088
  const workspaceUri = getWorkspaceUri(optimisticState, selectedOptimisticSession);
@@ -8911,6 +9125,9 @@ const handleSubmit = async state => {
8911
9125
  value: '[DONE]'
8912
9126
  });
8913
9127
  },
9128
+ onMockOpenApiRequestCaptured: async request => {
9129
+ mockOpenApiRequests = [...mockOpenApiRequests, request];
9130
+ },
8914
9131
  ...(handleTextChunkFunctionRef ? {
8915
9132
  onTextChunk: handleTextChunkFunctionRef
8916
9133
  } : {}),
@@ -8967,6 +9184,7 @@ const handleSubmit = async state => {
8967
9184
  }
8968
9185
  return withUpdatedMessageScrollTop(focusInput({
8969
9186
  ...latestState,
9187
+ mockOpenApiRequests,
8970
9188
  nextMessageId: latestState.nextMessageId + 1,
8971
9189
  parsedMessages: finalParsedMessages,
8972
9190
  sessions: updatedSessions
@@ -9028,6 +9246,8 @@ const openAgentModePicker = state => {
9028
9246
  return {
9029
9247
  ...state,
9030
9248
  agentModePickerOpen,
9249
+ focus: agentModePickerOpen ? 'picker-list' : state.focus,
9250
+ focused: agentModePickerOpen ? true : state.focused,
9031
9251
  modelPickerOpen: false,
9032
9252
  modelPickerSearchValue: '',
9033
9253
  reasoningEffortPickerOpen: false,
@@ -9046,6 +9266,8 @@ const openReasoningEffortPicker = state => {
9046
9266
  return {
9047
9267
  ...state,
9048
9268
  agentModePickerOpen: false,
9269
+ focus: reasoningEffortPickerOpen ? 'picker-list' : state.focus,
9270
+ focused: reasoningEffortPickerOpen ? true : state.focused,
9049
9271
  modelPickerOpen: false,
9050
9272
  modelPickerSearchValue: '',
9051
9273
  reasoningEffortPickerOpen,
@@ -9071,6 +9293,8 @@ const openRunModePicker = state => {
9071
9293
  return {
9072
9294
  ...state,
9073
9295
  agentModePickerOpen: false,
9296
+ focus: runModePickerOpen ? 'picker-list' : state.focus,
9297
+ focused: runModePickerOpen ? true : state.focused,
9074
9298
  modelPickerOpen: false,
9075
9299
  modelPickerSearchValue: '',
9076
9300
  reasoningEffortPickerOpen: false,
@@ -9334,6 +9558,12 @@ const handleClick = async (state, name, id = '', eventX = 0, eventY = 0) => {
9334
9558
  return deleteSession(state, id);
9335
9559
  case name === Send:
9336
9560
  return handleClickSend(state);
9561
+ case name === ScrollDown:
9562
+ return {
9563
+ ...state,
9564
+ messagesAutoScrollEnabled: true,
9565
+ messagesScrollTop: getNextAutoScrollTop(state.messagesScrollTop)
9566
+ };
9337
9567
  case name === SaveOpenRouterApiKey:
9338
9568
  return handleClickSaveOpenRouterApiKey(state);
9339
9569
  case name === SaveOpenApiApiKey:
@@ -9564,6 +9794,7 @@ const handleDropFiles = async (state, name, fileHandles = []) => {
9564
9794
  const {
9565
9795
  composerDropActive,
9566
9796
  composerDropEnabled,
9797
+ nextAttachmentId,
9567
9798
  selectedSessionId,
9568
9799
  width
9569
9800
  } = state;
@@ -9586,9 +9817,10 @@ const handleDropFiles = async (state, name, fileHandles = []) => {
9586
9817
  const nextAttachments = [];
9587
9818
  for (const droppedFileHandle of droppedFileHandles) {
9588
9819
  const file = await droppedFileHandle.getFile();
9589
- const attachmentId = crypto.randomUUID();
9820
+ const attachmentId = `attachment-${nextAttachmentId + nextAttachments.length}`;
9590
9821
  const displayType = await getComposerAttachmentDisplayType(file, file.name, file.type);
9591
9822
  const previewSrc = await getComposerAttachmentPreviewSrc(file, displayType, file.type);
9823
+ const textContent = await getComposerAttachmentTextContent(file, displayType);
9592
9824
  await appendChatViewEvent({
9593
9825
  attachmentId,
9594
9826
  blob: file,
@@ -9607,13 +9839,27 @@ const handleDropFiles = async (state, name, fileHandles = []) => {
9607
9839
  ...(previewSrc ? {
9608
9840
  previewSrc
9609
9841
  } : {}),
9610
- size: file.size
9842
+ size: file.size,
9843
+ ...(typeof textContent === 'string' ? {
9844
+ textContent
9845
+ } : {})
9611
9846
  });
9612
9847
  }
9613
9848
  return {
9614
9849
  ...nextState,
9615
9850
  composerAttachments: [...nextState.composerAttachments, ...nextAttachments],
9616
- composerAttachmentsHeight: getComposerAttachmentsHeight([...nextState.composerAttachments, ...nextAttachments], width)
9851
+ composerAttachmentsHeight: getComposerAttachmentsHeight([...nextState.composerAttachments, ...nextAttachments], width),
9852
+ nextAttachmentId: nextAttachmentId + nextAttachments.length
9853
+ };
9854
+ };
9855
+
9856
+ const handleErrorComposerAttachmentPreviewOverlay = async state => {
9857
+ if (!state.composerAttachmentPreviewOverlayAttachmentId || state.composerAttachmentPreviewOverlayError) {
9858
+ return state;
9859
+ }
9860
+ return {
9861
+ ...state,
9862
+ composerAttachmentPreviewOverlayError: true
9617
9863
  };
9618
9864
  };
9619
9865
 
@@ -9817,6 +10063,60 @@ const handleModelChange = async (state, value) => {
9817
10063
  };
9818
10064
  };
9819
10065
 
10066
+ const hideComposerAttachmentPreviewOverlay = async state => {
10067
+ if (!state.composerAttachmentPreviewOverlayAttachmentId && !state.composerAttachmentPreviewOverlayError) {
10068
+ return state;
10069
+ }
10070
+ return {
10071
+ ...state,
10072
+ composerAttachmentPreviewOverlayAttachmentId: '',
10073
+ composerAttachmentPreviewOverlayError: false
10074
+ };
10075
+ };
10076
+
10077
+ const isCurrentAttachmentTarget = (name, attachmentId) => {
10078
+ return name === getComposerAttachmentInputName(attachmentId) || isComposerAttachmentRemoveInputName(name) && getAttachmentIdFromComposerAttachmentRemoveInputName(name) === attachmentId;
10079
+ };
10080
+ const handleMouseOut = async (state, name, relatedName = '') => {
10081
+ const {
10082
+ composerAttachmentPreviewOverlayAttachmentId
10083
+ } = state;
10084
+ if (!composerAttachmentPreviewOverlayAttachmentId) {
10085
+ return state;
10086
+ }
10087
+ const isLeavingOverlay = isComposerAttachmentPreviewOverlayInputName(name);
10088
+ if (!isLeavingOverlay) {
10089
+ return state;
10090
+ }
10091
+ if (isComposerAttachmentPreviewOverlayInputName(relatedName) || isCurrentAttachmentTarget(relatedName, composerAttachmentPreviewOverlayAttachmentId)) {
10092
+ return state;
10093
+ }
10094
+ return hideComposerAttachmentPreviewOverlay(state);
10095
+ };
10096
+
10097
+ const showComposerAttachmentPreviewOverlay = async (state, attachmentId) => {
10098
+ const attachment = state.composerAttachments.find(item => item.attachmentId === attachmentId) ?? state.composerAttachments.find(item => item.displayType === 'image' && !!item.previewSrc);
10099
+ if (!attachment || attachment.displayType !== 'image' || !attachment.previewSrc) {
10100
+ return state;
10101
+ }
10102
+ if (state.composerAttachmentPreviewOverlayAttachmentId === attachmentId && !state.composerAttachmentPreviewOverlayError) {
10103
+ return state;
10104
+ }
10105
+ return {
10106
+ ...state,
10107
+ composerAttachmentPreviewOverlayAttachmentId: attachmentId,
10108
+ composerAttachmentPreviewOverlayError: false
10109
+ };
10110
+ };
10111
+
10112
+ const handleMouseOver = async (state, name) => {
10113
+ if (!isComposerAttachmentInputName(name)) {
10114
+ return state;
10115
+ }
10116
+ const attachmentId = getAttachmentIdFromComposerAttachmentInputName(name);
10117
+ return showComposerAttachmentPreviewOverlay(state, attachmentId);
10118
+ };
10119
+
9820
10120
  const handleNewline = async state => {
9821
10121
  const {
9822
10122
  composerValue
@@ -10010,6 +10310,23 @@ const getSavedChatListScrollTop = savedState => {
10010
10310
  return chatListScrollTop;
10011
10311
  };
10012
10312
 
10313
+ const getSavedComposerSelection = (savedState, composerValue) => {
10314
+ if (!isObject$1(savedState)) {
10315
+ return undefined;
10316
+ }
10317
+ const {
10318
+ composerSelectionEnd,
10319
+ composerSelectionStart
10320
+ } = savedState;
10321
+ if (typeof composerSelectionStart !== 'number') {
10322
+ return undefined;
10323
+ }
10324
+ if (typeof composerSelectionEnd !== 'number') {
10325
+ return undefined;
10326
+ }
10327
+ return getNormalizedComposerSelection(composerValue, composerSelectionStart, composerSelectionEnd);
10328
+ };
10329
+
10013
10330
  const getSavedComposerValue = savedState => {
10014
10331
  if (!isObject$1(savedState)) {
10015
10332
  return undefined;
@@ -10311,6 +10628,15 @@ const loadReasoningPickerEnabled = async () => {
10311
10628
  }
10312
10629
  };
10313
10630
 
10631
+ const loadScrollDownButtonEnabled = async () => {
10632
+ try {
10633
+ const savedScrollDownButtonEnabled = await get('chatView.scrollDownButtonEnabled');
10634
+ return typeof savedScrollDownButtonEnabled === 'boolean' ? savedScrollDownButtonEnabled : false;
10635
+ } catch {
10636
+ return false;
10637
+ }
10638
+ };
10639
+
10314
10640
  const loadSearchEnabled = async () => {
10315
10641
  try {
10316
10642
  const savedSearchEnabled = await get('chatView.searchEnabled');
@@ -10402,7 +10728,7 @@ const loadVoiceDictationEnabled = async () => {
10402
10728
  };
10403
10729
 
10404
10730
  const loadPreferences = async () => {
10405
- const [aiSessionTitleGenerationEnabled, authAccessToken, authEnabled, authRefreshToken, backendUrl, chatHistoryEnabled, composerDropEnabled, openApiApiKey, openRouterApiKey, emitStreamingFunctionCallEvents, reasoningPickerEnabled, searchEnabled, streamingEnabled, todoListToolEnabled, toolEnablement, passIncludeObfuscation, useChatCoordinatorWorker, useChatMathWorker, useChatMessageParsingWorker, useChatNetworkWorkerForRequests, useChatToolWorker, voiceDictationEnabled] = await Promise.all([loadAiSessionTitleGenerationEnabled(), loadBackendAccessToken(), loadAuthEnabled(), loadBackendRefreshToken(), loadBackendUrl(), loadChatHistoryEnabled(), loadComposerDropEnabled(), loadOpenApiApiKey(), loadOpenRouterApiKey(), loadEmitStreamingFunctionCallEvents(), loadReasoningPickerEnabled(), loadSearchEnabled(), loadStreamingEnabled(), loadTodoListToolEnabled(), loadToolEnablement(), loadPassIncludeObfuscation(), loadUseChatCoordinatorWorker(), loadUseChatMathWorker(), loadUseChatMessageParsingWorker(), loadUseChatNetworkWorkerForRequests(), loadUseChatToolWorker(), loadVoiceDictationEnabled()]);
10731
+ const [aiSessionTitleGenerationEnabled, authAccessToken, authEnabled, authRefreshToken, backendUrl, chatHistoryEnabled, composerDropEnabled, openApiApiKey, openRouterApiKey, emitStreamingFunctionCallEvents, reasoningPickerEnabled, scrollDownButtonEnabled, searchEnabled, streamingEnabled, todoListToolEnabled, toolEnablement, passIncludeObfuscation, useChatCoordinatorWorker, useChatMathWorker, useChatMessageParsingWorker, useChatNetworkWorkerForRequests, useChatToolWorker, voiceDictationEnabled] = await Promise.all([loadAiSessionTitleGenerationEnabled(), loadBackendAccessToken(), loadAuthEnabled(), loadBackendRefreshToken(), loadBackendUrl(), loadChatHistoryEnabled(), loadComposerDropEnabled(), loadOpenApiApiKey(), loadOpenRouterApiKey(), loadEmitStreamingFunctionCallEvents(), loadReasoningPickerEnabled(), loadScrollDownButtonEnabled(), loadSearchEnabled(), loadStreamingEnabled(), loadTodoListToolEnabled(), loadToolEnablement(), loadPassIncludeObfuscation(), loadUseChatCoordinatorWorker(), loadUseChatMathWorker(), loadUseChatMessageParsingWorker(), loadUseChatNetworkWorkerForRequests(), loadUseChatToolWorker(), loadVoiceDictationEnabled()]);
10406
10732
  return {
10407
10733
  aiSessionTitleGenerationEnabled,
10408
10734
  authAccessToken,
@@ -10416,6 +10742,7 @@ const loadPreferences = async () => {
10416
10742
  openRouterApiKey,
10417
10743
  passIncludeObfuscation,
10418
10744
  reasoningPickerEnabled,
10745
+ scrollDownButtonEnabled,
10419
10746
  searchEnabled,
10420
10747
  streamingEnabled,
10421
10748
  todoListToolEnabled,
@@ -10473,6 +10800,9 @@ const loadContent = async (state, savedState) => {
10473
10800
  const savedSelectedModelId = getSavedSelectedModelId(savedState);
10474
10801
  const savedViewMode = getSavedViewMode(savedState);
10475
10802
  const savedComposerValue = getSavedComposerValue(savedState);
10803
+ const composerValue = savedComposerValue ?? state.composerValue;
10804
+ const savedComposerSelection = getSavedComposerSelection(savedState, composerValue);
10805
+ const [composerSelectionStart, composerSelectionEnd] = savedComposerSelection ?? [state.composerSelectionStart, state.composerSelectionEnd];
10476
10806
  const {
10477
10807
  aiSessionTitleGenerationEnabled,
10478
10808
  authAccessToken,
@@ -10486,6 +10816,7 @@ const loadContent = async (state, savedState) => {
10486
10816
  openRouterApiKey,
10487
10817
  passIncludeObfuscation,
10488
10818
  reasoningPickerEnabled,
10819
+ scrollDownButtonEnabled,
10489
10820
  searchEnabled,
10490
10821
  streamingEnabled,
10491
10822
  todoListToolEnabled,
@@ -10564,7 +10895,9 @@ const loadContent = async (state, savedState) => {
10564
10895
  composerAttachmentsHeight: getComposerAttachmentsHeight(composerAttachments, state.width),
10565
10896
  composerDropActive: false,
10566
10897
  composerDropEnabled,
10567
- composerValue: savedComposerValue ?? state.composerValue,
10898
+ composerSelectionEnd,
10899
+ composerSelectionStart,
10900
+ composerValue,
10568
10901
  emitStreamingFunctionCallEvents,
10569
10902
  initial: false,
10570
10903
  lastNormalViewMode,
@@ -10586,6 +10919,7 @@ const loadContent = async (state, savedState) => {
10586
10919
  reasoningEffortPickerOpen: false,
10587
10920
  reasoningPickerEnabled,
10588
10921
  runModePickerOpen: false,
10922
+ scrollDownButtonEnabled,
10589
10923
  searchEnabled,
10590
10924
  searchFieldVisible: false,
10591
10925
  searchValue: '',
@@ -10643,7 +10977,7 @@ const mockOpenApiRequestGetAll = _state => {
10643
10977
  };
10644
10978
 
10645
10979
  const mockOpenApiRequestReset = state => {
10646
- reset$1();
10980
+ reset$2();
10647
10981
  return state;
10648
10982
  };
10649
10983
 
@@ -10668,7 +11002,7 @@ const mockOpenApiStreamPushChunk = (state, chunk) => {
10668
11002
  };
10669
11003
 
10670
11004
  const mockOpenApiStreamReset = state => {
10671
- reset$2();
11005
+ reset$1();
10672
11006
  return state;
10673
11007
  };
10674
11008
 
@@ -10716,7 +11050,7 @@ const pasteInput = async state => {
10716
11050
  };
10717
11051
 
10718
11052
  const registerMockResponse = (state, mockResponse) => {
10719
- reset$2();
11053
+ reset$1();
10720
11054
  pushChunk(mockResponse.text);
10721
11055
  finish();
10722
11056
  return state;
@@ -10824,6 +11158,43 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
10824
11158
  border-color: var(--vscode-charts-green, var(--vscode-widget-border, var(--vscode-panel-border)));
10825
11159
  }
10826
11160
 
11161
+ .Chat{
11162
+ position: relative;
11163
+ }
11164
+
11165
+ .ChatComposerAttachmentPreviewOverlay{
11166
+ position: absolute;
11167
+ left: calc(var(--ChatSendAreaPaddingLeft) + 8px);
11168
+ bottom: calc(var(--ChatSendAreaHeight) - 12px);
11169
+ z-index: 6;
11170
+ width: 240px;
11171
+ min-width: 160px;
11172
+ min-height: 160px;
11173
+ max-width: calc(100% - var(--ChatSendAreaPaddingLeft) - var(--ChatSendAreaPaddingRight) - 16px);
11174
+ display: flex;
11175
+ align-items: center;
11176
+ justify-content: center;
11177
+ border: 1px solid var(--vscode-widget-border, var(--vscode-panel-border));
11178
+ border-radius: 12px;
11179
+ background: var(--vscode-editorWidget-background, var(--vscode-editor-background));
11180
+ box-shadow: 0 12px 28px color-mix(in srgb, var(--vscode-editor-background) 45%, black);
11181
+ overflow: hidden;
11182
+ }
11183
+
11184
+ .ChatComposerAttachmentPreviewOverlayImage{
11185
+ display: block;
11186
+ width: 100%;
11187
+ max-width: 100%;
11188
+ max-height: min(320px, calc(100vh - 200px));
11189
+ object-fit: contain;
11190
+ background: color-mix(in srgb, var(--vscode-editor-background) 88%, black);
11191
+ }
11192
+
11193
+ .ChatComposerAttachmentPreviewOverlayError{
11194
+ padding: 12px;
11195
+ color: var(--vscode-errorForeground, var(--vscode-foreground));
11196
+ }
11197
+
10827
11198
  .CustomSelectContainer{
10828
11199
  position: relative;
10829
11200
  min-width: 0;
@@ -10845,6 +11216,10 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
10845
11216
  pointer-events: auto;
10846
11217
  }
10847
11218
 
11219
+ .ChatViewDropOverlayActive{
11220
+ background: rgba(255, 255, 255, 0.1);
11221
+ }
11222
+
10848
11223
  .RunModePickerPopOver{
10849
11224
  overflow: hidden;
10850
11225
  border: 1px solid var(--vscode-widget-border, var(--vscode-panel-border));
@@ -10899,6 +11274,27 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
10899
11274
  min-width: 0;
10900
11275
  }
10901
11276
 
11277
+ .ChatMessages > .Message{
11278
+ display: flex;
11279
+ }
11280
+
11281
+ .ChatMessages > .MessageUser{
11282
+ justify-content: flex-end;
11283
+ }
11284
+
11285
+ .ChatMessages > .MessageAssistant{
11286
+ justify-content: flex-start;
11287
+ }
11288
+
11289
+ .ChatFocus .ChatMessages > .Message{
11290
+ inline-size: fit-content;
11291
+ max-inline-size: min(100%, var(--ChatFocusContentMaxWidth));
11292
+ }
11293
+
11294
+ .ChatFocus .ChatMessages > .Message > .ChatMessageContent{
11295
+ max-inline-size: 100%;
11296
+ }
11297
+
10902
11298
  `;
10903
11299
  return `${baseCss}
10904
11300
 
@@ -10936,6 +11332,7 @@ const renderCss = (oldState, newState) => {
10936
11332
  return [SetCss, uid, css];
10937
11333
  };
10938
11334
 
11335
+ const pickerListSelector = '.ChatOverlays .ChatModelPickerList';
10939
11336
  const getFocusSelector = state => {
10940
11337
  const {
10941
11338
  focus,
@@ -10961,6 +11358,8 @@ const getFocusSelector = state => {
10961
11358
  }
10962
11359
  case 'model-picker-input':
10963
11360
  return `[name="${ModelPickerSearch}"]`;
11361
+ case 'picker-list':
11362
+ return pickerListSelector;
10964
11363
  case 'send-button':
10965
11364
  return '[name="send"]';
10966
11365
  default:
@@ -10971,6 +11370,9 @@ const renderFocus = (oldState, newState) => {
10971
11370
  if (newState.modelPickerOpen && !oldState.modelPickerOpen) {
10972
11371
  return [FocusSelector, `[name="${ModelPickerSearch}"]`];
10973
11372
  }
11373
+ if (newState.agentModePickerOpen && !oldState.agentModePickerOpen || newState.runModePickerOpen && !oldState.runModePickerOpen || newState.reasoningEffortPickerOpen && !oldState.reasoningEffortPickerOpen) {
11374
+ return [FocusSelector, pickerListSelector];
11375
+ }
10974
11376
  const selector = getFocusSelector(newState);
10975
11377
  return [FocusSelector, selector];
10976
11378
  };
@@ -10988,6 +11390,8 @@ const renderFocusContext = (oldState, newState) => {
10988
11390
  const HandleListContextMenu = 2;
10989
11391
  const HandleFocus = 3;
10990
11392
  const HandleInput = 4;
11393
+ const HandleMouseOut = 5;
11394
+ const HandleMouseOver = 7;
10991
11395
  const HandleClick = 11;
10992
11396
  const HandleKeyDown = 12;
10993
11397
  const HandleClickClose = 13;
@@ -11035,6 +11439,7 @@ const HandlePointerUpModelPickerList = 55;
11035
11439
  const HandleClickReasoningEffortPickerToggle = 56;
11036
11440
  const HandleClickAgentModePickerToggle = 57;
11037
11441
  const HandleContextMenuChatImageAttachment = 58;
11442
+ const HandleErrorComposerAttachmentPreviewOverlay = 59;
11038
11443
 
11039
11444
  const getAddContextButtonDom = () => {
11040
11445
  return [{
@@ -11151,6 +11556,8 @@ const getReasoningEffortPickerVirtualDom = (selectedReasoningEffort, reasoningEf
11151
11556
  }, {
11152
11557
  childCount: reasoningEfforts.length,
11153
11558
  className: ChatModelPickerList,
11559
+ name: PickerList,
11560
+ tabIndex: -1,
11154
11561
  type: Ul
11155
11562
  }, ...getReasoningEffortOptionsVirtualDom(selectedReasoningEffort)] : [])];
11156
11563
  };
@@ -11159,6 +11566,21 @@ const getRunModePickerVirtualDom = (selectedRunMode, runModePickerOpen) => {
11159
11566
  return getCustomSelectPickerToggleVirtualDom(selectedRunMode, RunModePickerToggle, runModePickerOpen, HandleClickRunModePickerToggle);
11160
11567
  };
11161
11568
 
11569
+ const getScrollDownButtonDom = () => {
11570
+ return [{
11571
+ childCount: 1,
11572
+ className: mergeClassNames(Button, ButtonSecondary),
11573
+ inputType: 'button',
11574
+ name: ScrollDown,
11575
+ onClick: HandleClick,
11576
+ title: scrollDown(),
11577
+ type: Button$1
11578
+ }, {
11579
+ text: scrollDown(),
11580
+ type: Text
11581
+ }];
11582
+ };
11583
+
11162
11584
  const getSendButtonClassName = isSendDisabled => {
11163
11585
  return mergeClassNames(IconButton, isSendDisabled ? SendButtonDisabled : '');
11164
11586
  };
@@ -11334,10 +11756,15 @@ const getComposerAttachmentsDom = composerAttachments => {
11334
11756
  childCount: 1 + (removeButtonDom.length > 0 ? 1 : 0) + previewDom.length,
11335
11757
  className: mergeClassNames(ChatComposerAttachment, getComposerAttachmentClassName(attachment.displayType)),
11336
11758
  name: getComposerAttachmentInputName(attachment.attachmentId),
11759
+ onMouseOut: HandleMouseOut,
11760
+ onMouseOver: HandleMouseOver,
11761
+ onPointerOut: HandleMouseOut,
11762
+ onPointerOver: HandleMouseOver,
11337
11763
  type: Div
11338
11764
  }, ...removeButtonDom, ...previewDom, {
11339
11765
  childCount: 1,
11340
11766
  className: ChatComposerAttachmentLabel,
11767
+ name: getComposerAttachmentInputName(attachment.attachmentId),
11341
11768
  type: Span
11342
11769
  }, {
11343
11770
  text: `${getComposerAttachmentLabel(attachment.displayType)} · ${attachment.name}`,
@@ -11359,11 +11786,12 @@ const getComposerTextAreaDom = () => {
11359
11786
  type: TextArea
11360
11787
  };
11361
11788
  };
11362
- const getChatSendAreaDom = (composerValue, composerAttachments, agentMode, agentModePickerOpen, hasSpaceForAgentModePicker, modelPickerOpen, models, selectedModelId, reasoningPickerEnabled, reasoningEffort, reasoningEffortPickerOpen, usageOverviewEnabled, tokensUsed, tokensMax, addContextButtonEnabled, showRunMode, hasSpaceForRunModePicker, runMode, runModePickerOpen, todoListToolEnabled, todoListItems, showCreatePullRequestButton = false, voiceDictationEnabled = false) => {
11789
+ const getChatSendAreaDom = (composerValue, composerAttachments, agentMode, agentModePickerOpen, hasSpaceForAgentModePicker, modelPickerOpen, models, selectedModelId, reasoningPickerEnabled, reasoningEffort, reasoningEffortPickerOpen, usageOverviewEnabled, tokensUsed, tokensMax, addContextButtonEnabled, showRunMode, hasSpaceForRunModePicker, runMode, runModePickerOpen, todoListToolEnabled, todoListItems, showCreatePullRequestButton = false, voiceDictationEnabled = false, scrollDownButtonEnabled = false, messagesAutoScrollEnabled = true) => {
11363
11790
  const isSendDisabled = composerValue.trim() === '';
11364
11791
  const showAgentModePicker = hasSpaceForAgentModePicker;
11365
11792
  const showResponsiveRunModePicker = showRunMode && hasSpaceForRunModePicker;
11366
- const bottomControlsCount = 2 + (usageOverviewEnabled ? 1 : 0) + (addContextButtonEnabled ? 1 : 0) + (showCreatePullRequestButton ? 1 : 0) + (voiceDictationEnabled ? 1 : 0);
11793
+ const showScrollDownButton = scrollDownButtonEnabled && !messagesAutoScrollEnabled;
11794
+ const bottomControlsCount = 2 + (usageOverviewEnabled ? 1 : 0) + (addContextButtonEnabled ? 1 : 0) + (showCreatePullRequestButton ? 1 : 0) + (voiceDictationEnabled ? 1 : 0) + (showScrollDownButton ? 1 : 0);
11367
11795
  const primaryControlsCount = 1 + (showAgentModePicker ? 1 : 0) + (reasoningPickerEnabled ? 1 : 0) + (showResponsiveRunModePicker ? 1 : 0);
11368
11796
  const hasTodoList = todoListToolEnabled && todoListItems.length > 0;
11369
11797
  const hasComposerAttachments = composerAttachments.length > 0;
@@ -11387,7 +11815,58 @@ const getChatSendAreaDom = (composerValue, composerAttachments, agentMode, agent
11387
11815
  className: ChatSendAreaPrimaryControls,
11388
11816
  role: 'toolbar',
11389
11817
  type: Div
11390
- }, ...(showAgentModePicker ? getAgentModePickerVirtualDom(agentMode, agentModePickerOpen) : []), ...getChatModelPickerToggleVirtualDom(models, selectedModelId, modelPickerOpen), ...(reasoningPickerEnabled ? getReasoningEffortPickerVirtualDom(reasoningEffort, reasoningEffortPickerOpen) : []), ...(showResponsiveRunModePicker ? getRunModePickerVirtualDom(runMode, runModePickerOpen) : []), ...(usageOverviewEnabled ? getUsageOverviewDom(tokensUsed, tokensMax) : []), ...(addContextButtonEnabled ? getAddContextButtonDom() : []), ...(showCreatePullRequestButton ? getCreatePullRequestButtonDom() : []), ...getSendButtonDom(isSendDisabled, voiceDictationEnabled)];
11818
+ }, ...(showAgentModePicker ? getAgentModePickerVirtualDom(agentMode, agentModePickerOpen) : []), ...getChatModelPickerToggleVirtualDom(models, selectedModelId, modelPickerOpen), ...(reasoningPickerEnabled ? getReasoningEffortPickerVirtualDom(reasoningEffort, reasoningEffortPickerOpen) : []), ...(showResponsiveRunModePicker ? getRunModePickerVirtualDom(runMode, runModePickerOpen) : []), ...(usageOverviewEnabled ? getUsageOverviewDom(tokensUsed, tokensMax) : []), ...(addContextButtonEnabled ? getAddContextButtonDom() : []), ...(showCreatePullRequestButton ? getCreatePullRequestButtonDom() : []), ...(showScrollDownButton ? getScrollDownButtonDom() : []), ...getSendButtonDom(isSendDisabled, voiceDictationEnabled)];
11819
+ };
11820
+
11821
+ const focusHeaderStyle = 'align-items:center;border-bottom:1px solid var(--vscode-panel-border, transparent);display:flex;gap:12px;justify-content:space-between;padding:8px 12px;';
11822
+ const focusHeaderMetaStyle = 'align-items:baseline;display:flex;gap:8px;min-width:0;overflow:hidden;';
11823
+ const focusHeaderTitleStyle = 'margin:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;';
11824
+ const focusHeaderProjectStyle = 'overflow:hidden;text-overflow:ellipsis;white-space:nowrap;';
11825
+ const focusHeaderActionsStyle = 'display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end;';
11826
+ const focusHeaderButtonStyle = 'white-space:nowrap;';
11827
+ const getFocusHeaderActionButtonDom = (label, name) => {
11828
+ return [{
11829
+ childCount: 1,
11830
+ className: mergeClassNames(Button, ButtonSecondary),
11831
+ inputType: 'button',
11832
+ name,
11833
+ onClick: HandleClick,
11834
+ style: focusHeaderButtonStyle,
11835
+ title: label,
11836
+ type: Button$1
11837
+ }, text(label)];
11838
+ };
11839
+ const getChatHeaderDomFocusMode = (selectedSessionTitle, selectedProjectName) => {
11840
+ const items = [[addAction(), FocusAddAction], [openInVsCode(), FocusOpenInVsCode], [commit(), FocusCommit], [openTerminal(), FocusOpenTerminal], [showDiff(), FocusShowDiff]];
11841
+ const hasProjectName = !!selectedProjectName;
11842
+ return [{
11843
+ childCount: 2,
11844
+ className: ChatFocusHeader,
11845
+ style: focusHeaderStyle,
11846
+ type: Header
11847
+ }, {
11848
+ childCount: hasProjectName ? 2 : 1,
11849
+ className: ChatName,
11850
+ style: focusHeaderMetaStyle,
11851
+ type: Div
11852
+ }, {
11853
+ childCount: 1,
11854
+ className: ChatHeaderLabel,
11855
+ style: focusHeaderTitleStyle,
11856
+ type: H2
11857
+ }, text(selectedSessionTitle), ...(hasProjectName ? [{
11858
+ childCount: 1,
11859
+ className: mergeClassNames(LabelDetail, ChatFocusProject),
11860
+ style: focusHeaderProjectStyle,
11861
+ type: Span
11862
+ }, text(selectedProjectName)] : []), {
11863
+ 'aria-label': 'focus header actions',
11864
+ childCount: items.length,
11865
+ className: ChatFocusActions,
11866
+ role: ToolBar,
11867
+ style: focusHeaderActionsStyle,
11868
+ type: Div
11869
+ }, ...items.flatMap(([label, name]) => getFocusHeaderActionButtonDom(label, name))];
11391
11870
  };
11392
11871
 
11393
11872
  const getCustomSelectPopOverVirtualDom = (optionCount, height, optionNodes, containerClassName = '', popOverClassName = '') => {
@@ -11403,6 +11882,8 @@ const getCustomSelectPopOverVirtualDom = (optionCount, height, optionNodes, cont
11403
11882
  }, {
11404
11883
  childCount: optionCount,
11405
11884
  className: ChatModelPickerList,
11885
+ name: PickerList,
11886
+ tabIndex: -1,
11406
11887
  type: Ul
11407
11888
  }, ...optionNodes];
11408
11889
  };
@@ -11481,6 +11962,42 @@ const getChatModelPickerPopOverVirtualDom = (models, selectedModelId, modelPicke
11481
11962
  }, ...getModelPickerHeaderDom(modelPickerSearchValue), ...getChatModelListVirtualDom(visibleModels, selectedModelId)];
11482
11963
  };
11483
11964
 
11965
+ const getComposerAttachmentPreviewOverlayVirtualDom = (composerAttachments, attachmentId, hasError) => {
11966
+ if (!attachmentId) {
11967
+ return [];
11968
+ }
11969
+ const attachment = composerAttachments.find(item => item.attachmentId === attachmentId);
11970
+ if (!attachment || attachment.displayType !== 'image' || !attachment.previewSrc) {
11971
+ return [];
11972
+ }
11973
+ return [{
11974
+ childCount: 1,
11975
+ className: ChatComposerAttachmentPreviewOverlay,
11976
+ name: ComposerAttachmentPreviewOverlay,
11977
+ onMouseOut: HandleMouseOut,
11978
+ onMouseOver: HandleMouseOver,
11979
+ onPointerOut: HandleMouseOut,
11980
+ onPointerOver: HandleMouseOver,
11981
+ type: Div
11982
+ }, ...(hasError ? [{
11983
+ childCount: 1,
11984
+ className: mergeClassNames(ChatComposerAttachmentPreviewOverlayError, ImageErrorMessage),
11985
+ name: ComposerAttachmentPreviewOverlay,
11986
+ type: Div
11987
+ }, {
11988
+ text: imageCouldNotBeLoaded(),
11989
+ type: Text
11990
+ }] : [{
11991
+ alt: `Large image preview for ${attachment.name}`,
11992
+ childCount: 0,
11993
+ className: ChatComposerAttachmentPreviewOverlayImage,
11994
+ name: ComposerAttachmentPreviewOverlay,
11995
+ onError: HandleErrorComposerAttachmentPreviewOverlay,
11996
+ src: attachment.previewSrc,
11997
+ type: Img
11998
+ }])];
11999
+ };
12000
+
11484
12001
  const runModes = ['local', 'background', 'cloud'];
11485
12002
  const runModePickerHeight = runModes.length * 28;
11486
12003
  const getRunModeOptionsVirtualDom = selectedRunMode => {
@@ -11507,6 +12024,10 @@ const getDropOverlayVirtualDom = () => {
11507
12024
  const getChatOverlaysVirtualDom = ({
11508
12025
  agentMode,
11509
12026
  agentModePickerVisible,
12027
+ composerAttachmentPreviewOverlayAttachmentId,
12028
+ composerAttachmentPreviewOverlayError,
12029
+ composerAttachmentPreviewOverlayVisible,
12030
+ composerAttachments,
11510
12031
  dropOverlayVisible,
11511
12032
  modelPickerSearchValue,
11512
12033
  modelPickerVisible,
@@ -11515,7 +12036,7 @@ const getChatOverlaysVirtualDom = ({
11515
12036
  selectedModelId,
11516
12037
  visibleModels
11517
12038
  }) => {
11518
- const overlayChildCount = (dropOverlayVisible ? 1 : 0) + (agentModePickerVisible ? 1 : 0) + (modelPickerVisible ? 1 : 0) + (runModePickerVisible ? 1 : 0);
12039
+ const overlayChildCount = (dropOverlayVisible ? 1 : 0) + (composerAttachmentPreviewOverlayVisible ? 1 : 0) + (agentModePickerVisible ? 1 : 0) + (modelPickerVisible ? 1 : 0) + (runModePickerVisible ? 1 : 0);
11519
12040
  if (!overlayChildCount) {
11520
12041
  return [];
11521
12042
  }
@@ -11523,7 +12044,7 @@ const getChatOverlaysVirtualDom = ({
11523
12044
  childCount: overlayChildCount,
11524
12045
  className: ChatOverlays,
11525
12046
  type: Div
11526
- }, ...(dropOverlayVisible ? getDropOverlayVirtualDom() : []), ...(agentModePickerVisible ? getAgentModePickerPopOverVirtualDom(agentMode) : []), ...(modelPickerVisible ? getChatModelPickerPopOverVirtualDom(visibleModels, selectedModelId, modelPickerSearchValue) : []), ...(runModePickerVisible ? getRunModePickerPopOverVirtualDom(runMode) : [])];
12047
+ }, ...(dropOverlayVisible ? getDropOverlayVirtualDom() : []), ...getComposerAttachmentPreviewOverlayVirtualDom(composerAttachments, composerAttachmentPreviewOverlayAttachmentId, composerAttachmentPreviewOverlayError), ...(agentModePickerVisible ? getAgentModePickerPopOverVirtualDom(agentMode) : []), ...(modelPickerVisible ? getChatModelPickerPopOverVirtualDom(visibleModels, selectedModelId, modelPickerSearchValue) : []), ...(runModePickerVisible ? getRunModePickerPopOverVirtualDom(runMode) : [])];
11527
12048
  };
11528
12049
 
11529
12050
  const getBoldInlineNodeDom = (inlineNode, useChatMathWorker, renderInlineNodeDom) => {
@@ -12963,19 +13484,20 @@ const getMessagesDom = (messages, parsedMessages, openRouterApiKeyInput, openApi
12963
13484
  }, ...displayMessages.flatMap(item => getChatMessageDom(item.message, item.parsedContent, openRouterApiKeyInput, openApiApiKeyInput, openApiApiKeyState, openApiApiKeysSettingsUrl, openApiApiKeyInputPattern, openRouterApiKeyState, useChatMathWorker))];
12964
13485
  };
12965
13486
 
13487
+ const arrowLeft$1 = {
13488
+ childCount: 0,
13489
+ className: mergeClassNames(MaskIcon, MaskIconArrowLeft),
13490
+ type: Div
13491
+ };
12966
13492
  const getBackToChatsButtonDom = () => {
12967
13493
  return [{
12968
13494
  childCount: 1,
12969
- className: mergeClassNames(Button, ButtonSecondary),
12970
- inputType: 'button',
13495
+ className: IconButton,
12971
13496
  name: Back,
12972
13497
  onClick: HandleClickBack,
12973
13498
  title: backToChatList(),
12974
13499
  type: Button$1
12975
- }, {
12976
- text: backToChatList(),
12977
- type: Text
12978
- }];
13500
+ }, arrowLeft$1];
12979
13501
  };
12980
13502
 
12981
13503
  const getProjectSessionDom = (session, selectedSessionId) => {
@@ -13074,6 +13596,8 @@ const getChatModeChatFocusVirtualDom = ({
13074
13596
  authEnabled = false,
13075
13597
  authErrorMessage = '',
13076
13598
  authStatus = 'signed-out',
13599
+ composerAttachmentPreviewOverlayAttachmentId,
13600
+ composerAttachmentPreviewOverlayError = false,
13077
13601
  composerAttachments,
13078
13602
  composerDropActive = false,
13079
13603
  composerDropEnabled = true,
@@ -13084,6 +13608,7 @@ const getChatModeChatFocusVirtualDom = ({
13084
13608
  composerValue,
13085
13609
  hasSpaceForAgentModePicker,
13086
13610
  hasSpaceForRunModePicker,
13611
+ messagesAutoScrollEnabled,
13087
13612
  messagesScrollTop = 0,
13088
13613
  modelPickerOpen = false,
13089
13614
  modelPickerSearchValue = '',
@@ -13103,6 +13628,7 @@ const getChatModeChatFocusVirtualDom = ({
13103
13628
  reasoningPickerEnabled,
13104
13629
  runMode,
13105
13630
  runModePickerOpen = false,
13631
+ scrollDownButtonEnabled,
13106
13632
  selectedModelId,
13107
13633
  selectedProjectId = '',
13108
13634
  selectedSessionId,
@@ -13118,23 +13644,31 @@ const getChatModeChatFocusVirtualDom = ({
13118
13644
  voiceDictationEnabled = false
13119
13645
  }) => {
13120
13646
  const selectedSession = sessions.find(session => session.id === selectedSessionId);
13647
+ const selectedProject = projects.find(project => project.id === selectedProjectId);
13121
13648
  const messages = selectedSession ? selectedSession.messages : [];
13649
+ const selectedSessionTitle = selectedSession?.title || chatTitle();
13650
+ const selectedProjectName = selectedProject?.name || '';
13122
13651
  const showCreatePullRequestButton = canCreatePullRequest(selectedSession);
13123
13652
  const isDropOverlayVisible = composerDropEnabled && composerDropActive;
13653
+ const isComposerAttachmentPreviewOverlayVisible = !!composerAttachmentPreviewOverlayAttachmentId;
13124
13654
  const isAgentModePickerVisible = hasSpaceForAgentModePicker && agentModePickerOpen;
13125
13655
  const isNewModelPickerVisible = modelPickerOpen;
13126
13656
  const isRunModePickerVisible = showRunMode && hasSpaceForRunModePicker && runModePickerOpen;
13127
- const hasVisibleOverlays = isDropOverlayVisible || isAgentModePickerVisible || isNewModelPickerVisible || isRunModePickerVisible;
13128
- const chatRootChildCount = 3 + (hasVisibleOverlays ? 1 : 0);
13657
+ const hasVisibleOverlays = isDropOverlayVisible || isComposerAttachmentPreviewOverlayVisible || isAgentModePickerVisible || isNewModelPickerVisible || isRunModePickerVisible;
13658
+ const chatRootChildCount = 4 + (hasVisibleOverlays ? 1 : 0);
13129
13659
  return [{
13130
13660
  childCount: chatRootChildCount,
13131
13661
  className: mergeClassNames(Viewlet, Chat, ChatFocus),
13132
13662
  onDragEnter: HandleDragEnterChatView,
13133
13663
  onDragOver: HandleDragOverChatView,
13134
13664
  type: Div
13135
- }, ...getProjectListDom(projects, sessions, projectExpandedIds, selectedProjectId, selectedSessionId, projectListScrollTop, true), ...getMessagesDom(messages, parsedMessages, openRouterApiKeyInput, openApiApiKeyInput, openApiApiKeyState, openApiApiKeysSettingsUrl, openApiApiKeyInputPattern, openRouterApiKeyState, messagesScrollTop, useChatMathWorker, true), ...getChatSendAreaDom(composerValue, composerAttachments, agentMode, agentModePickerOpen, hasSpaceForAgentModePicker, modelPickerOpen, models, selectedModelId, reasoningPickerEnabled, reasoningEffort, reasoningEffortPickerOpen, usageOverviewEnabled, tokensUsed, tokensMax, addContextButtonEnabled, showRunMode, hasSpaceForRunModePicker, runMode, runModePickerOpen, todoListToolEnabled, todoListItems, showCreatePullRequestButton, voiceDictationEnabled), ...getChatOverlaysVirtualDom({
13665
+ }, ...getChatHeaderDomFocusMode(selectedSessionTitle, selectedProjectName), ...getMessagesDom(messages, parsedMessages, openRouterApiKeyInput, openApiApiKeyInput, openApiApiKeyState, openApiApiKeysSettingsUrl, openApiApiKeyInputPattern, openRouterApiKeyState, messagesScrollTop, useChatMathWorker, true), ...getProjectListDom(projects, sessions, projectExpandedIds, selectedProjectId, selectedSessionId, projectListScrollTop, true), ...getChatSendAreaDom(composerValue, composerAttachments, agentMode, agentModePickerOpen, hasSpaceForAgentModePicker, modelPickerOpen, models, selectedModelId, reasoningPickerEnabled, reasoningEffort, reasoningEffortPickerOpen, usageOverviewEnabled, tokensUsed, tokensMax, addContextButtonEnabled, showRunMode, hasSpaceForRunModePicker, runMode, runModePickerOpen, todoListToolEnabled, todoListItems, showCreatePullRequestButton, voiceDictationEnabled, scrollDownButtonEnabled, messagesAutoScrollEnabled), ...getChatOverlaysVirtualDom({
13136
13666
  agentMode,
13137
13667
  agentModePickerVisible: isAgentModePickerVisible,
13668
+ composerAttachmentPreviewOverlayAttachmentId,
13669
+ composerAttachmentPreviewOverlayError,
13670
+ composerAttachmentPreviewOverlayVisible: isComposerAttachmentPreviewOverlayVisible,
13671
+ composerAttachments,
13138
13672
  dropOverlayVisible: isDropOverlayVisible,
13139
13673
  modelPickerSearchValue,
13140
13674
  modelPickerVisible: isNewModelPickerVisible,
@@ -13272,6 +13806,8 @@ const getChatModeDetailVirtualDom = ({
13272
13806
  authEnabled = false,
13273
13807
  authErrorMessage = '',
13274
13808
  authStatus = 'signed-out',
13809
+ composerAttachmentPreviewOverlayAttachmentId,
13810
+ composerAttachmentPreviewOverlayError = false,
13275
13811
  composerAttachments,
13276
13812
  composerDropActive = false,
13277
13813
  composerDropEnabled = true,
@@ -13282,6 +13818,7 @@ const getChatModeDetailVirtualDom = ({
13282
13818
  composerValue,
13283
13819
  hasSpaceForAgentModePicker,
13284
13820
  hasSpaceForRunModePicker,
13821
+ messagesAutoScrollEnabled,
13285
13822
  messagesScrollTop = 0,
13286
13823
  modelPickerOpen = false,
13287
13824
  modelPickerSearchValue = '',
@@ -13298,6 +13835,7 @@ const getChatModeDetailVirtualDom = ({
13298
13835
  reasoningPickerEnabled,
13299
13836
  runMode,
13300
13837
  runModePickerOpen = false,
13838
+ scrollDownButtonEnabled,
13301
13839
  selectedModelId,
13302
13840
  selectedSessionId,
13303
13841
  sessions,
@@ -13316,10 +13854,11 @@ const getChatModeDetailVirtualDom = ({
13316
13854
  const messages = selectedSession ? selectedSession.messages : [];
13317
13855
  const showCreatePullRequestButton = canCreatePullRequest(selectedSession);
13318
13856
  const isDropOverlayVisible = composerDropEnabled && composerDropActive;
13857
+ const isComposerAttachmentPreviewOverlayVisible = !!composerAttachmentPreviewOverlayAttachmentId;
13319
13858
  const isAgentModePickerVisible = hasSpaceForAgentModePicker && agentModePickerOpen;
13320
13859
  const isNewModelPickerVisible = modelPickerOpen;
13321
13860
  const isRunModePickerVisible = showRunMode && hasSpaceForRunModePicker && runModePickerOpen;
13322
- const hasVisibleOverlays = isDropOverlayVisible || isAgentModePickerVisible || isNewModelPickerVisible || isRunModePickerVisible;
13861
+ const hasVisibleOverlays = isDropOverlayVisible || isComposerAttachmentPreviewOverlayVisible || isAgentModePickerVisible || isNewModelPickerVisible || isRunModePickerVisible;
13323
13862
  const chatRootChildCount = 3 + (hasVisibleOverlays ? 1 : 0);
13324
13863
  return [{
13325
13864
  childCount: chatRootChildCount,
@@ -13327,9 +13866,13 @@ const getChatModeDetailVirtualDom = ({
13327
13866
  onDragEnter: HandleDragEnterChatView,
13328
13867
  onDragOver: HandleDragOverChatView,
13329
13868
  type: Div
13330
- }, ...getChatHeaderDomDetailMode(selectedSessionTitle, authEnabled, authStatus, authErrorMessage), ...getMessagesDom(messages, parsedMessages, openRouterApiKeyInput, openApiApiKeyInput, openApiApiKeyState, openApiApiKeysSettingsUrl, openApiApiKeyInputPattern, openRouterApiKeyState, messagesScrollTop, useChatMathWorker), ...getChatSendAreaDom(composerValue, composerAttachments, agentMode, agentModePickerOpen, hasSpaceForAgentModePicker, modelPickerOpen, models, selectedModelId, reasoningPickerEnabled, reasoningEffort, reasoningEffortPickerOpen, usageOverviewEnabled, tokensUsed, tokensMax, addContextButtonEnabled, showRunMode, hasSpaceForRunModePicker, runMode, runModePickerOpen, todoListToolEnabled, todoListItems, showCreatePullRequestButton, voiceDictationEnabled), ...getChatOverlaysVirtualDom({
13869
+ }, ...getChatHeaderDomDetailMode(selectedSessionTitle, authEnabled, authStatus, authErrorMessage), ...getMessagesDom(messages, parsedMessages, openRouterApiKeyInput, openApiApiKeyInput, openApiApiKeyState, openApiApiKeysSettingsUrl, openApiApiKeyInputPattern, openRouterApiKeyState, messagesScrollTop, useChatMathWorker), ...getChatSendAreaDom(composerValue, composerAttachments, agentMode, agentModePickerOpen, hasSpaceForAgentModePicker, modelPickerOpen, models, selectedModelId, reasoningPickerEnabled, reasoningEffort, reasoningEffortPickerOpen, usageOverviewEnabled, tokensUsed, tokensMax, addContextButtonEnabled, showRunMode, hasSpaceForRunModePicker, runMode, runModePickerOpen, todoListToolEnabled, todoListItems, showCreatePullRequestButton, voiceDictationEnabled, scrollDownButtonEnabled, messagesAutoScrollEnabled), ...getChatOverlaysVirtualDom({
13331
13870
  agentMode,
13332
13871
  agentModePickerVisible: isAgentModePickerVisible,
13872
+ composerAttachmentPreviewOverlayAttachmentId,
13873
+ composerAttachmentPreviewOverlayError,
13874
+ composerAttachmentPreviewOverlayVisible: isComposerAttachmentPreviewOverlayVisible,
13875
+ composerAttachments,
13333
13876
  dropOverlayVisible: isDropOverlayVisible,
13334
13877
  modelPickerSearchValue,
13335
13878
  modelPickerVisible: isNewModelPickerVisible,
@@ -13474,6 +14017,8 @@ const getChatModeListVirtualDom = ({
13474
14017
  authErrorMessage = '',
13475
14018
  authStatus = 'signed-out',
13476
14019
  chatListScrollTop = 0,
14020
+ composerAttachmentPreviewOverlayAttachmentId,
14021
+ composerAttachmentPreviewOverlayError = false,
13477
14022
  composerAttachments,
13478
14023
  composerDropActive = false,
13479
14024
  composerDropEnabled = true,
@@ -13509,10 +14054,11 @@ const getChatModeListVirtualDom = ({
13509
14054
  voiceDictationEnabled = false
13510
14055
  }) => {
13511
14056
  const isDropOverlayVisible = composerDropEnabled && composerDropActive;
14057
+ const isComposerAttachmentPreviewOverlayVisible = !!composerAttachmentPreviewOverlayAttachmentId;
13512
14058
  const isAgentModePickerVisible = hasSpaceForAgentModePicker && agentModePickerOpen;
13513
14059
  const isNewModelPickerVisible = modelPickerOpen;
13514
14060
  const isRunModePickerVisible = showRunMode && hasSpaceForRunModePicker && runModePickerOpen;
13515
- const hasVisibleOverlays = isDropOverlayVisible || isAgentModePickerVisible || isNewModelPickerVisible || isRunModePickerVisible;
14061
+ const hasVisibleOverlays = isDropOverlayVisible || isComposerAttachmentPreviewOverlayVisible || isAgentModePickerVisible || isNewModelPickerVisible || isRunModePickerVisible;
13516
14062
  const chatRootChildCount = 3 + (hasVisibleOverlays ? 1 : 0);
13517
14063
  const searchValueTrimmed = searchValue.trim().toLowerCase();
13518
14064
  const visibleSessions = searchEnabled && searchValueTrimmed ? sessions.filter(session => session.title.toLowerCase().includes(searchValueTrimmed)) : sessions;
@@ -13525,6 +14071,10 @@ const getChatModeListVirtualDom = ({
13525
14071
  }, ...getChatHeaderListModeDom(authEnabled, authStatus, authErrorMessage, searchEnabled, searchFieldVisible, searchValue), ...getChatListDom(visibleSessions, selectedSessionId, listFocusedIndex, chatListScrollTop), ...getChatSendAreaDom(composerValue, composerAttachments, agentMode, agentModePickerOpen, hasSpaceForAgentModePicker, modelPickerOpen, models, selectedModelId, reasoningPickerEnabled, reasoningEffort, reasoningEffortPickerOpen, usageOverviewEnabled, tokensUsed, tokensMax, addContextButtonEnabled, showRunMode, hasSpaceForRunModePicker, runMode, runModePickerOpen, todoListToolEnabled, todoListItems, false, voiceDictationEnabled), ...getChatOverlaysVirtualDom({
13526
14072
  agentMode,
13527
14073
  agentModePickerVisible: isAgentModePickerVisible,
14074
+ composerAttachmentPreviewOverlayAttachmentId,
14075
+ composerAttachmentPreviewOverlayError,
14076
+ composerAttachmentPreviewOverlayVisible: isComposerAttachmentPreviewOverlayVisible,
14077
+ composerAttachments,
13528
14078
  dropOverlayVisible: isDropOverlayVisible,
13529
14079
  modelPickerSearchValue,
13530
14080
  modelPickerVisible: isNewModelPickerVisible,
@@ -13623,6 +14173,8 @@ const getChatVirtualDom = options => {
13623
14173
  authErrorMessage = '',
13624
14174
  authStatus = 'signed-out',
13625
14175
  chatListScrollTop,
14176
+ composerAttachmentPreviewOverlayAttachmentId,
14177
+ composerAttachmentPreviewOverlayError = false,
13626
14178
  composerAttachments,
13627
14179
  composerDropActive = false,
13628
14180
  composerDropEnabled = true,
@@ -13634,6 +14186,7 @@ const getChatVirtualDom = options => {
13634
14186
  hasSpaceForAgentModePicker,
13635
14187
  hasSpaceForRunModePicker,
13636
14188
  listFocusedIndex = -1,
14189
+ messagesAutoScrollEnabled,
13637
14190
  messagesScrollTop,
13638
14191
  modelPickerOpen = false,
13639
14192
  modelPickerSearchValue = '',
@@ -13653,6 +14206,7 @@ const getChatVirtualDom = options => {
13653
14206
  reasoningPickerEnabled,
13654
14207
  runMode,
13655
14208
  runModePickerOpen = false,
14209
+ scrollDownButtonEnabled,
13656
14210
  searchEnabled = false,
13657
14211
  searchFieldVisible = false,
13658
14212
  searchValue = '',
@@ -13681,6 +14235,8 @@ const getChatVirtualDom = options => {
13681
14235
  authEnabled,
13682
14236
  authErrorMessage,
13683
14237
  authStatus,
14238
+ composerAttachmentPreviewOverlayAttachmentId,
14239
+ composerAttachmentPreviewOverlayError,
13684
14240
  composerAttachments,
13685
14241
  composerDropActive,
13686
14242
  composerDropEnabled,
@@ -13691,6 +14247,7 @@ const getChatVirtualDom = options => {
13691
14247
  composerValue,
13692
14248
  hasSpaceForAgentModePicker,
13693
14249
  hasSpaceForRunModePicker,
14250
+ messagesAutoScrollEnabled,
13694
14251
  messagesScrollTop,
13695
14252
  modelPickerOpen,
13696
14253
  modelPickerSearchValue,
@@ -13710,6 +14267,7 @@ const getChatVirtualDom = options => {
13710
14267
  reasoningPickerEnabled,
13711
14268
  runMode,
13712
14269
  runModePickerOpen,
14270
+ scrollDownButtonEnabled,
13713
14271
  selectedModelId,
13714
14272
  selectedProjectId,
13715
14273
  selectedSessionId,
@@ -13732,6 +14290,8 @@ const getChatVirtualDom = options => {
13732
14290
  authEnabled,
13733
14291
  authErrorMessage,
13734
14292
  authStatus,
14293
+ composerAttachmentPreviewOverlayAttachmentId,
14294
+ composerAttachmentPreviewOverlayError,
13735
14295
  composerAttachments,
13736
14296
  composerDropActive,
13737
14297
  composerDropEnabled,
@@ -13742,6 +14302,7 @@ const getChatVirtualDom = options => {
13742
14302
  composerValue,
13743
14303
  hasSpaceForAgentModePicker,
13744
14304
  hasSpaceForRunModePicker,
14305
+ messagesAutoScrollEnabled,
13745
14306
  messagesScrollTop,
13746
14307
  modelPickerOpen,
13747
14308
  modelPickerSearchValue,
@@ -13758,6 +14319,7 @@ const getChatVirtualDom = options => {
13758
14319
  reasoningPickerEnabled,
13759
14320
  runMode,
13760
14321
  runModePickerOpen,
14322
+ scrollDownButtonEnabled,
13761
14323
  selectedModelId,
13762
14324
  selectedSessionId,
13763
14325
  sessions,
@@ -13780,6 +14342,8 @@ const getChatVirtualDom = options => {
13780
14342
  authErrorMessage,
13781
14343
  authStatus,
13782
14344
  chatListScrollTop,
14345
+ composerAttachmentPreviewOverlayAttachmentId,
14346
+ composerAttachmentPreviewOverlayError,
13783
14347
  composerAttachments,
13784
14348
  composerDropActive,
13785
14349
  composerDropEnabled,
@@ -13828,6 +14392,8 @@ const renderItems = (oldState, newState) => {
13828
14392
  authErrorMessage,
13829
14393
  authStatus,
13830
14394
  chatListScrollTop,
14395
+ composerAttachmentPreviewOverlayAttachmentId,
14396
+ composerAttachmentPreviewOverlayError,
13831
14397
  composerAttachments,
13832
14398
  composerDropActive,
13833
14399
  composerDropEnabled,
@@ -13840,6 +14406,7 @@ const renderItems = (oldState, newState) => {
13840
14406
  hasSpaceForRunModePicker,
13841
14407
  initial,
13842
14408
  listFocusedIndex,
14409
+ messagesAutoScrollEnabled,
13843
14410
  messagesScrollTop,
13844
14411
  modelPickerOpen,
13845
14412
  modelPickerSearchValue,
@@ -13859,6 +14426,7 @@ const renderItems = (oldState, newState) => {
13859
14426
  reasoningPickerEnabled,
13860
14427
  runMode,
13861
14428
  runModePickerOpen,
14429
+ scrollDownButtonEnabled,
13862
14430
  searchEnabled,
13863
14431
  searchFieldVisible,
13864
14432
  searchValue,
@@ -13888,6 +14456,8 @@ const renderItems = (oldState, newState) => {
13888
14456
  authErrorMessage,
13889
14457
  authStatus,
13890
14458
  chatListScrollTop,
14459
+ composerAttachmentPreviewOverlayAttachmentId,
14460
+ composerAttachmentPreviewOverlayError,
13891
14461
  composerAttachments,
13892
14462
  composerDropActive,
13893
14463
  composerDropEnabled,
@@ -13899,6 +14469,7 @@ const renderItems = (oldState, newState) => {
13899
14469
  hasSpaceForAgentModePicker,
13900
14470
  hasSpaceForRunModePicker,
13901
14471
  listFocusedIndex,
14472
+ messagesAutoScrollEnabled,
13902
14473
  messagesScrollTop,
13903
14474
  modelPickerOpen,
13904
14475
  modelPickerSearchValue,
@@ -13918,6 +14489,7 @@ const renderItems = (oldState, newState) => {
13918
14489
  reasoningPickerEnabled,
13919
14490
  runMode,
13920
14491
  runModePickerOpen,
14492
+ scrollDownButtonEnabled,
13921
14493
  searchEnabled,
13922
14494
  searchFieldVisible,
13923
14495
  searchValue,
@@ -14036,6 +14608,12 @@ const renderEventListeners = () => {
14036
14608
  }, {
14037
14609
  name: HandleClick,
14038
14610
  params: ['handleClick', TargetName, 'event.target.dataset.id', ClientX, ClientY]
14611
+ }, {
14612
+ name: HandleMouseOver,
14613
+ params: ['handleMouseOver', TargetName]
14614
+ }, {
14615
+ name: HandleMouseOut,
14616
+ params: ['handleMouseOut', TargetName, 'event.relatedTarget && event.relatedTarget.getAttribute ? event.relatedTarget.getAttribute("name") : ""']
14039
14617
  }, {
14040
14618
  name: HandleClickDictationButton,
14041
14619
  params: ['handleClickDictationButton']
@@ -14202,6 +14780,9 @@ const renderEventListeners = () => {
14202
14780
  name: HandleContextMenuChatImageAttachment,
14203
14781
  params: ['handleContextMenuChatImageAttachment', TargetName, ClientX, ClientY],
14204
14782
  preventDefault: true
14783
+ }, {
14784
+ name: HandleErrorComposerAttachmentPreviewOverlay,
14785
+ params: ['handleErrorComposerAttachmentPreviewOverlay']
14205
14786
  }];
14206
14787
  };
14207
14788
 
@@ -14283,6 +14864,8 @@ const saveState = state => {
14283
14864
  const {
14284
14865
  agentMode,
14285
14866
  chatListScrollTop,
14867
+ composerSelectionEnd,
14868
+ composerSelectionStart,
14286
14869
  composerValue,
14287
14870
  lastNormalViewMode,
14288
14871
  maxToolCalls,
@@ -14304,6 +14887,8 @@ const saveState = state => {
14304
14887
  return {
14305
14888
  agentMode,
14306
14889
  chatListScrollTop,
14890
+ composerSelectionEnd,
14891
+ composerSelectionStart,
14307
14892
  composerValue,
14308
14893
  lastNormalViewMode,
14309
14894
  maxToolCalls,
@@ -14400,6 +14985,13 @@ const setResponsivePickerVisibilityEnabled = (state, responsivePickerVisibilityE
14400
14985
  };
14401
14986
  };
14402
14987
 
14988
+ const setScrollDownButtonEnabled = (state, scrollDownButtonEnabled) => {
14989
+ return {
14990
+ ...state,
14991
+ scrollDownButtonEnabled
14992
+ };
14993
+ };
14994
+
14403
14995
  const setSearchEnabled = (state, searchEnabled) => {
14404
14996
  return {
14405
14997
  ...state,
@@ -14532,6 +15124,7 @@ const commandMap = {
14532
15124
  'Chat.getKeyBindings': getKeyBindings,
14533
15125
  'Chat.getMenuEntries': getMenuEntries,
14534
15126
  'Chat.getMenuEntryIds': getMenuEntryIds,
15127
+ 'Chat.getMockOpenApiRequests': wrapGetter(getMockOpenApiRequests),
14535
15128
  'Chat.getQuickPickMenuEntries': getQuickPickMenuEntries,
14536
15129
  'Chat.getSelectedSessionId': wrapGetter(getSelectedSessionId),
14537
15130
  'Chat.getSystemPrompt': wrapGetter(getSystemPrompt),
@@ -14564,6 +15157,7 @@ const commandMap = {
14564
15157
  'Chat.handleDragLeave': wrapCommand(handleDragLeave),
14565
15158
  'Chat.handleDragOver': wrapCommand(handleDragOver),
14566
15159
  'Chat.handleDropFiles': wrapCommand(handleDropFiles),
15160
+ 'Chat.handleErrorComposerAttachmentPreviewOverlay': wrapCommand(handleErrorComposerAttachmentPreviewOverlay),
14567
15161
  'Chat.handleInput': wrapCommand(handleInput),
14568
15162
  'Chat.handleInputFocus': wrapCommand(handleInputFocus),
14569
15163
  'Chat.handleKeyDown': wrapCommand(handleKeyDown),
@@ -14574,6 +15168,8 @@ const commandMap = {
14574
15168
  'Chat.handleModelChange': wrapCommand(handleModelChange),
14575
15169
  'Chat.handleModelInputBlur': wrapCommand(handleModelInputBlur),
14576
15170
  'Chat.handleModelPickerListScroll': wrapCommand(handleModelPickerListScroll),
15171
+ 'Chat.handleMouseOut': wrapCommand(handleMouseOut),
15172
+ 'Chat.handleMouseOver': wrapCommand(handleMouseOver),
14577
15173
  'Chat.handlePointerDownModelPickerList': wrapCommand(handlePointerDownModelPickerList),
14578
15174
  'Chat.handlePointerUpModelPickerList': wrapCommand(handlePointerUpModelPickerList),
14579
15175
  'Chat.handleProjectAddButtonContextMenu': wrapCommand(handleProjectAddButtonContextMenu),
@@ -14583,6 +15179,7 @@ const commandMap = {
14583
15179
  'Chat.handleRunModeChange': wrapCommand(handleRunModeChange),
14584
15180
  'Chat.handleSearchValueChange': wrapCommand(handleSearchValueChange),
14585
15181
  'Chat.handleSubmit': wrapCommand(handleSubmit),
15182
+ 'Chat.hideComposerAttachmentPreviewOverlay': wrapCommand(hideComposerAttachmentPreviewOverlay),
14586
15183
  'Chat.initialize': initialize,
14587
15184
  'Chat.loadContent': wrapCommand(loadContent),
14588
15185
  'Chat.loadContent2': wrapCommand(loadContent),
@@ -14619,6 +15216,7 @@ const commandMap = {
14619
15216
  'Chat.setReasoningEffort': wrapCommand(setReasoningEffort),
14620
15217
  'Chat.setReasoningPickerEnabled': wrapCommand(setReasoningPickerEnabled),
14621
15218
  'Chat.setResponsivePickerVisibilityEnabled': wrapCommand(setResponsivePickerVisibilityEnabled),
15219
+ 'Chat.setScrollDownButtonEnabled': wrapCommand(setScrollDownButtonEnabled),
14622
15220
  'Chat.setSearchEnabled': wrapCommand(setSearchEnabled),
14623
15221
  'Chat.setShowRunMode': wrapCommand(setShowRunMode),
14624
15222
  'Chat.setStreamingEnabled': wrapCommand(setStreamingEnabled),
@@ -14629,6 +15227,7 @@ const commandMap = {
14629
15227
  'Chat.setUseChatMathWorker': wrapCommand(setUseChatMathWorker),
14630
15228
  'Chat.setUseChatMessageParsingWorker': wrapCommand(setUseChatMessageParsingWorker),
14631
15229
  'Chat.setUseChatNetworkWorkerForRequests': wrapCommand(setUseChatNetworkWorkerForRequests),
15230
+ 'Chat.showComposerAttachmentPreviewOverlay': wrapCommand(showComposerAttachmentPreviewOverlay),
14632
15231
  'Chat.terminate': terminate,
14633
15232
  'Chat.useMockApi': wrapCommand(useMockApi)
14634
15233
  };