@lvce-editor/chat-view 6.66.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,7 +1870,7 @@ 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';
@@ -1927,6 +1930,9 @@ const settings = () => {
1927
1930
  const search = () => {
1928
1931
  return i18nString(Search$1);
1929
1932
  };
1933
+ const scrollDown = () => {
1934
+ return i18nString(ScrollDown$1);
1935
+ };
1930
1936
  const searchModels = () => {
1931
1937
  return i18nString(SearchModels);
1932
1938
  };
@@ -2079,16 +2085,19 @@ const getDefaultModelsOpenAi = () => {
2079
2085
  id: 'openapi/gpt-4o-mini',
2080
2086
  name: 'GPT-4o Mini',
2081
2087
  provider: 'openApi',
2088
+ supportsImages: true,
2082
2089
  usageCost: 1
2083
2090
  }, {
2084
2091
  id: 'openapi/gpt-4o',
2085
2092
  name: 'GPT-4o',
2086
2093
  provider: 'openApi',
2094
+ supportsImages: true,
2087
2095
  usageCost: 3
2088
2096
  }, {
2089
2097
  id: 'openapi/gpt-4.1-mini',
2090
2098
  name: 'GPT-4.1 Mini',
2091
2099
  provider: 'openApi',
2100
+ supportsImages: true,
2092
2101
  usageCost: 1
2093
2102
  }];
2094
2103
  };
@@ -2326,6 +2335,8 @@ const createDefaultState = () => {
2326
2335
  chatSendAreaPaddingLeft: 8,
2327
2336
  chatSendAreaPaddingRight: 8,
2328
2337
  chatSendAreaPaddingTop: 10,
2338
+ composerAttachmentPreviewOverlayAttachmentId: '',
2339
+ composerAttachmentPreviewOverlayError: false,
2329
2340
  composerAttachments: [],
2330
2341
  composerAttachmentsHeight: 0,
2331
2342
  composerDropActive: false,
@@ -2356,12 +2367,14 @@ const createDefaultState = () => {
2356
2367
  messagesScrollTop: 0,
2357
2368
  mockAiResponseDelay: 800,
2358
2369
  mockApiCommandId: '',
2370
+ mockOpenApiRequests: [],
2359
2371
  modelPickerHeaderHeight,
2360
2372
  modelPickerHeight: getModelPickerHeight(modelPickerHeaderHeight, visibleModels.length),
2361
2373
  modelPickerListScrollTop: 0,
2362
2374
  modelPickerOpen: false,
2363
2375
  modelPickerSearchValue: '',
2364
2376
  models,
2377
+ nextAttachmentId: 1,
2365
2378
  nextMessageId: 1,
2366
2379
  openApiApiBaseUrl: 'https://api.openai.com/v1',
2367
2380
  openApiApiKey: '',
@@ -2392,6 +2405,7 @@ const createDefaultState = () => {
2392
2405
  responsivePickerVisibilityEnabled,
2393
2406
  runMode: 'local',
2394
2407
  runModePickerOpen: false,
2408
+ scrollDownButtonEnabled: false,
2395
2409
  searchEnabled: false,
2396
2410
  searchFieldVisible: false,
2397
2411
  searchValue: '',
@@ -2622,6 +2636,13 @@ const getComposerAttachmentPreviewSrc = async (blob, displayType, mimeType) => {
2622
2636
  return `data:${mimeType || 'application/octet-stream'};base64,${base64}`;
2623
2637
  };
2624
2638
 
2639
+ const getComposerAttachmentTextContent = async (blob, displayType) => {
2640
+ if (displayType !== 'text-file') {
2641
+ return undefined;
2642
+ }
2643
+ return blob.text();
2644
+ };
2645
+
2625
2646
  const isChatAttachmentAddedEvent = event => {
2626
2647
  return event.type === 'chat-attachment-added';
2627
2648
  };
@@ -2644,6 +2665,7 @@ const getComposerAttachments = async sessionId => {
2644
2665
  }
2645
2666
  const displayType = await getComposerAttachmentDisplayType(event.blob, event.name, event.mimeType);
2646
2667
  const previewSrc = await getComposerAttachmentPreviewSrc(event.blob, displayType, event.mimeType);
2668
+ const textContent = await getComposerAttachmentTextContent(event.blob, displayType);
2647
2669
  attachments.set(event.attachmentId, {
2648
2670
  attachmentId: event.attachmentId,
2649
2671
  displayType,
@@ -2652,7 +2674,10 @@ const getComposerAttachments = async sessionId => {
2652
2674
  ...(previewSrc ? {
2653
2675
  previewSrc
2654
2676
  } : {}),
2655
- size: event.size
2677
+ size: event.size,
2678
+ ...(typeof textContent === 'string' ? {
2679
+ textContent
2680
+ } : {})
2656
2681
  });
2657
2682
  }
2658
2683
  return [...attachments.values()];
@@ -3214,7 +3239,8 @@ const openModelPicker = state => {
3214
3239
  return {
3215
3240
  ...state,
3216
3241
  agentModePickerOpen: false,
3217
- focus: 'model-picker-input',
3242
+ focus: modelPickerOpen ? 'model-picker-input' : state.focus,
3243
+ focused: modelPickerOpen ? true : state.focused,
3218
3244
  modelPickerHeight: getModelPickerHeight(state.modelPickerHeaderHeight, visibleModels.length),
3219
3245
  modelPickerListScrollTop: 0,
3220
3246
  modelPickerOpen,
@@ -3250,6 +3276,10 @@ const getKeyBindings = () => {
3250
3276
  command: 'Chat.chatListFocusPrevious',
3251
3277
  key: UpArrow,
3252
3278
  when: FocusChatList
3279
+ }, {
3280
+ command: 'Chat.handleClickNew',
3281
+ key: CtrlCmd | KeyN,
3282
+ when: FocusChatInput
3253
3283
  }, {
3254
3284
  command: 'Chat.handleSubmit',
3255
3285
  key: Enter,
@@ -3348,6 +3378,7 @@ const ComposerDropTarget = 'composer-drop-target';
3348
3378
  const ComposerAttachmentPrefix = 'composer-attachment:';
3349
3379
  const ComposerAttachmentPreviewPrefix = 'composer-attachment-preview:';
3350
3380
  const ComposerAttachmentRemovePrefix = 'composer-attachment-remove:';
3381
+ const ComposerAttachmentPreviewOverlay = 'composer-attachment-preview-overlay';
3351
3382
  const AddContext = 'add-context';
3352
3383
  const Dictate = 'dictate';
3353
3384
  const CreatePullRequest$1 = 'create-pull-request';
@@ -3357,10 +3388,12 @@ const FocusCommit = 'focus-commit';
3357
3388
  const FocusOpenTerminal = 'focus-open-terminal';
3358
3389
  const FocusShowDiff = 'focus-show-diff';
3359
3390
  const Send = 'send';
3391
+ const ScrollDown = 'scroll-down';
3360
3392
  const Back = 'back';
3361
3393
  const ModelPickerToggle = 'model-picker-toggle';
3362
3394
  const ModelPickerSearch = 'model-picker-search';
3363
3395
  const ModelPickerList = 'model-picker-list';
3396
+ const PickerList = 'picker-list';
3364
3397
  const AgentModePickerToggle = 'agent-mode-picker-toggle';
3365
3398
  const AgentModePickerItemPrefix = 'agent-mode-picker-item:';
3366
3399
  const ReasoningEffortPickerToggle = 'reasoning-effort-picker-toggle';
@@ -3422,6 +3455,12 @@ const getModelPickerItemInputName = modelId => {
3422
3455
  const getComposerAttachmentInputName = attachmentId => {
3423
3456
  return `${ComposerAttachmentPrefix}${attachmentId}`;
3424
3457
  };
3458
+ const isComposerAttachmentInputName = name => {
3459
+ return name.startsWith(ComposerAttachmentPrefix);
3460
+ };
3461
+ const getAttachmentIdFromComposerAttachmentInputName = name => {
3462
+ return name.slice(ComposerAttachmentPrefix.length);
3463
+ };
3425
3464
  const getComposerAttachmentRemoveInputName = attachmentId => {
3426
3465
  return `${ComposerAttachmentRemovePrefix}${attachmentId}`;
3427
3466
  };
@@ -3437,6 +3476,9 @@ const isComposerAttachmentRemoveInputName = name => {
3437
3476
  const getAttachmentIdFromComposerAttachmentRemoveInputName = name => {
3438
3477
  return name.slice(ComposerAttachmentRemovePrefix.length);
3439
3478
  };
3479
+ const isComposerAttachmentPreviewOverlayInputName = name => {
3480
+ return name === ComposerAttachmentPreviewOverlay;
3481
+ };
3440
3482
  const isModelPickerItemInputName = name => {
3441
3483
  return name.startsWith(ModelPickerItemPrefix);
3442
3484
  };
@@ -3530,6 +3572,10 @@ const getMenuEntries = (menuId, props) => {
3530
3572
  }
3531
3573
  };
3532
3574
 
3575
+ const getMockOpenApiRequests = state => {
3576
+ return state.mockOpenApiRequests;
3577
+ };
3578
+
3533
3579
  const getQuickPickMenuEntries = () => {
3534
3580
  return [];
3535
3581
  };
@@ -3655,6 +3701,12 @@ const getModelPickerClickIndex = (y, height, eventY, modelPickerBottomOffset, mo
3655
3701
  return Math.floor(relativeY / modelPickerItemHeight);
3656
3702
  };
3657
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
+
3658
3710
  const openFolder = async () => {
3659
3711
  try {
3660
3712
  return await invoke('FilePicker.showDirectoryPicker');
@@ -4232,6 +4284,54 @@ const getBasicChatTools = async (agentMode = defaultAgentMode, questionToolEnabl
4232
4284
  }
4233
4285
  };
4234
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
+
4235
4335
  const getClientRequestIdHeader = () => {
4236
4336
  return {
4237
4337
  'x-client-request-id': crypto.randomUUID()
@@ -4247,11 +4347,22 @@ const getMockAiResponse = async (userMessage, delayInMs) => {
4247
4347
  return `Mock AI response: I received "${userMessage}".`;
4248
4348
  };
4249
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
+
4250
4361
  let queue = [];
4251
4362
  let waiters = [];
4252
4363
  let finished = false;
4253
4364
  let errorResult;
4254
- const reset$2 = () => {
4365
+ const reset$1 = () => {
4255
4366
  queue = [];
4256
4367
  waiters = [];
4257
4368
  finished = false;
@@ -4325,6 +4436,47 @@ const readNextChunk = async () => {
4325
4436
  return promise;
4326
4437
  };
4327
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
+ };
4328
4480
  const getResponseFunctionCalls$1 = value => {
4329
4481
  if (!value || typeof value !== 'object') {
4330
4482
  return [];
@@ -4522,9 +4674,10 @@ const getMockOpenApiAssistantText = async (stream, onTextChunk, onToolCallsChunk
4522
4674
  }
4523
4675
  continue;
4524
4676
  }
4525
- text += chunk;
4677
+ const resolvedChunk = chunk === lastRequestSummaryToken ? getLastRequestSummary() : chunk;
4678
+ text += resolvedChunk;
4526
4679
  if (stream && onTextChunk) {
4527
- await onTextChunk(chunk);
4680
+ await onTextChunk(resolvedChunk);
4528
4681
  }
4529
4682
  }
4530
4683
  if (!requestDone && remainder) {
@@ -5361,7 +5514,7 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
5361
5514
  stream: false
5362
5515
  };
5363
5516
  const openAiInput = messages.map(message => ({
5364
- content: message.text,
5517
+ content: getChatMessageOpenAiContent(message),
5365
5518
  role: message.role
5366
5519
  }));
5367
5520
  const tools = await getBasicChatTools(agentMode, questionToolEnabled, toolEnablement);
@@ -5754,12 +5907,22 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
5754
5907
  };
5755
5908
  };
5756
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;
5757
5912
  const isOffline = () => {
5758
5913
  if (!globalThis.navigator) {
5759
5914
  return false;
5760
5915
  }
5761
5916
  return globalThis.navigator.onLine === false;
5762
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
+ };
5763
5926
  const getOpenApiErrorMessage = errorResult => {
5764
5927
  switch (errorResult.details) {
5765
5928
  case 'http-error':
@@ -5773,6 +5936,9 @@ const getOpenApiErrorMessage = errorResult => {
5773
5936
  const status = typeof errorResult.statusCode === 'number' ? errorResult.statusCode : 401;
5774
5937
  return `OpenAI request failed (Status ${status}): Invalid API key. Please verify your OpenAI API key in Chat Settings.`;
5775
5938
  }
5939
+ if (isImageNotSupportedError(errorResult)) {
5940
+ return getImageNotSupportedMessage();
5941
+ }
5776
5942
  if (errorResult.statusCode === 429) {
5777
5943
  let prefix = 'OpenAI rate limit exceeded (429)';
5778
5944
  if (hasErrorCode) {
@@ -6221,17 +6387,6 @@ const isOpenRouterModel = (selectedModelId, models) => {
6221
6387
  return selectedModelId.toLowerCase().startsWith('openrouter/');
6222
6388
  };
6223
6389
 
6224
- let requests = [];
6225
- const reset$1 = () => {
6226
- requests = [];
6227
- };
6228
- const capture = request => {
6229
- requests = [...requests, request];
6230
- };
6231
- const getAll = () => {
6232
- return requests;
6233
- };
6234
-
6235
6390
  /* eslint-disable prefer-destructuring */
6236
6391
 
6237
6392
  const trailingSlashesRegex = /\/+$/;
@@ -6246,6 +6401,9 @@ const getEffectiveBackendModelId = selectedModelId => {
6246
6401
  }
6247
6402
  return selectedModelId.slice(separatorIndex + 1);
6248
6403
  };
6404
+ const hasImageAttachments = messages => {
6405
+ return messages.some(message => message.attachments?.some(attachment => attachment.displayType === 'image'));
6406
+ };
6249
6407
  const getBackendAssistantText = async (messages, selectedModelId, backendUrl, authAccessToken, systemPrompt) => {
6250
6408
  let response;
6251
6409
  try {
@@ -6293,6 +6451,7 @@ const getAiResponse = async ({
6293
6451
  nextMessageId,
6294
6452
  onDataEvent,
6295
6453
  onEventStreamFinished,
6454
+ onMockOpenApiRequestCaptured,
6296
6455
  onTextChunk,
6297
6456
  onToolCallsChunk,
6298
6457
  openApiApiBaseUrl,
@@ -6368,7 +6527,15 @@ const getAiResponse = async ({
6368
6527
  }
6369
6528
  }
6370
6529
  let text = '';
6371
- 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) {
6372
6539
  if (!backendUrl) {
6373
6540
  text = backendUrlRequiredMessage;
6374
6541
  } else if (authAccessToken) {
@@ -6377,15 +6544,11 @@ const getAiResponse = async ({
6377
6544
  text = backendAccessTokenRequiredMessage;
6378
6545
  }
6379
6546
  }
6380
- const usesOpenApiModel = isOpenApiModel(selectedModelId, models);
6381
- const usesOpenRouterModel = isOpenRouterModel(selectedModelId, models);
6382
- const selectedModel = models.find(model => model.id === selectedModelId);
6383
- const supportsReasoningEffort = selectedModel?.supportsReasoningEffort ?? false;
6384
6547
  if (!text && usesOpenApiModel) {
6385
6548
  const safeMaxToolCalls = Math.max(1, maxToolCalls);
6386
6549
  if (useMockApi) {
6387
6550
  const openAiInput = messages.map(message => ({
6388
- content: message.text,
6551
+ content: getChatMessageOpenAiContent(message),
6389
6552
  role: message.role
6390
6553
  }));
6391
6554
  const modelId = getOpenApiModelId(selectedModelId);
@@ -6397,12 +6560,16 @@ const getAiResponse = async ({
6397
6560
  const maxToolIterations = safeMaxToolCalls - 1;
6398
6561
  let previousResponseId;
6399
6562
  for (let i = 0; i <= maxToolIterations; i++) {
6400
- capture({
6563
+ const request = {
6401
6564
  headers,
6402
6565
  method: 'POST',
6403
6566
  payload: getOpenAiParams(openAiInput, modelId, streamingEnabled, passIncludeObfuscation, await getBasicChatTools(agentMode, questionToolEnabled, toolEnablement), agentMode === 'plan' ? false : webSearchEnabled, safeMaxToolCalls, systemPrompt, previousResponseId, reasoningEffort, supportsReasoningEffort),
6404
6567
  url: getOpenApiApiEndpoint(openApiApiBaseUrl)
6405
- });
6568
+ };
6569
+ capture(request);
6570
+ if (onMockOpenApiRequestCaptured) {
6571
+ await Promise.resolve(onMockOpenApiRequestCaptured(request));
6572
+ }
6406
6573
  const result = await getMockOpenApiAssistantText(streamingEnabled, onTextChunk, onToolCallsChunk, onDataEvent, onEventStreamFinished);
6407
6574
  if (result.type !== 'success') {
6408
6575
  text = getOpenApiErrorMessage(result);
@@ -6595,6 +6762,9 @@ const ChatComposerAttachmentImage = 'ChatComposerAttachmentImage';
6595
6762
  const ChatComposerAttachmentInvalidImage = 'ChatComposerAttachmentInvalidImage';
6596
6763
  const ChatComposerAttachmentLabel = 'ChatComposerAttachmentLabel';
6597
6764
  const ChatComposerAttachmentPreview = 'ChatComposerAttachmentPreview';
6765
+ const ChatComposerAttachmentPreviewOverlay = 'ChatComposerAttachmentPreviewOverlay';
6766
+ const ChatComposerAttachmentPreviewOverlayError = 'ChatComposerAttachmentPreviewOverlayError';
6767
+ const ChatComposerAttachmentPreviewOverlayImage = 'ChatComposerAttachmentPreviewOverlayImage';
6598
6768
  const ChatComposerAttachmentRemoveButton = 'ChatComposerAttachmentRemoveButton';
6599
6769
  const ChatComposerAttachments = 'ChatComposerAttachments';
6600
6770
  const ChatComposerAttachmentTextFile = 'ChatComposerAttachmentTextFile';
@@ -6633,6 +6803,7 @@ const FileIcon = 'FileIcon';
6633
6803
  const IconButton = 'IconButton';
6634
6804
  const IconButtonDisabled = 'IconButtonDisabled';
6635
6805
  const ImageElement = 'ImageElement';
6806
+ const ImageErrorMessage = 'ImageErrorMessage';
6636
6807
  const InputBox = 'InputBox';
6637
6808
  const Insertion = 'Insertion';
6638
6809
  const Label = 'Label';
@@ -8407,12 +8578,6 @@ const getMentionContextMessage = async value => {
8407
8578
  };
8408
8579
  };
8409
8580
 
8410
- const AutoScrollTopA = Number.MAX_SAFE_INTEGER;
8411
- const AutoScrollTopB = Number.MAX_SAFE_INTEGER - 1;
8412
- const getNextAutoScrollTop = currentScrollTop => {
8413
- return currentScrollTop === AutoScrollTopA ? AutoScrollTopB : AutoScrollTopA;
8414
- };
8415
-
8416
8581
  const slashCommandRegex = /^\/([a-z][a-z0-9-]*)(?:\s+.*)?$/i;
8417
8582
  const getSlashCommand = value => {
8418
8583
  const trimmed = value.trim();
@@ -8690,6 +8855,19 @@ const withUpdatedMessageScrollTop = state => {
8690
8855
  };
8691
8856
  };
8692
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
+ };
8693
8871
  const getCurrentDate = () => {
8694
8872
  return new Date().toISOString().slice(0, 10);
8695
8873
  };
@@ -8782,7 +8960,12 @@ const handleSubmit = async state => {
8782
8960
  minute: '2-digit'
8783
8961
  });
8784
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));
8785
8965
  const userMessage = {
8966
+ ...(composerAttachments.length > 0 ? {
8967
+ attachments: composerAttachments
8968
+ } : {}),
8786
8969
  id: userMessageId,
8787
8970
  role: 'user',
8788
8971
  text: userText,
@@ -8897,6 +9080,9 @@ const handleSubmit = async state => {
8897
9080
  latestState: optimisticState,
8898
9081
  previousState: optimisticState
8899
9082
  };
9083
+ let {
9084
+ mockOpenApiRequests
9085
+ } = optimisticState;
8900
9086
  const selectedOptimisticSession = optimisticState.sessions.find(session => session.id === optimisticState.selectedSessionId);
8901
9087
  const systemPrompt = getEffectiveSystemPrompt(optimisticState, selectedOptimisticSession);
8902
9088
  const workspaceUri = getWorkspaceUri(optimisticState, selectedOptimisticSession);
@@ -8939,6 +9125,9 @@ const handleSubmit = async state => {
8939
9125
  value: '[DONE]'
8940
9126
  });
8941
9127
  },
9128
+ onMockOpenApiRequestCaptured: async request => {
9129
+ mockOpenApiRequests = [...mockOpenApiRequests, request];
9130
+ },
8942
9131
  ...(handleTextChunkFunctionRef ? {
8943
9132
  onTextChunk: handleTextChunkFunctionRef
8944
9133
  } : {}),
@@ -8995,6 +9184,7 @@ const handleSubmit = async state => {
8995
9184
  }
8996
9185
  return withUpdatedMessageScrollTop(focusInput({
8997
9186
  ...latestState,
9187
+ mockOpenApiRequests,
8998
9188
  nextMessageId: latestState.nextMessageId + 1,
8999
9189
  parsedMessages: finalParsedMessages,
9000
9190
  sessions: updatedSessions
@@ -9056,6 +9246,8 @@ const openAgentModePicker = state => {
9056
9246
  return {
9057
9247
  ...state,
9058
9248
  agentModePickerOpen,
9249
+ focus: agentModePickerOpen ? 'picker-list' : state.focus,
9250
+ focused: agentModePickerOpen ? true : state.focused,
9059
9251
  modelPickerOpen: false,
9060
9252
  modelPickerSearchValue: '',
9061
9253
  reasoningEffortPickerOpen: false,
@@ -9074,6 +9266,8 @@ const openReasoningEffortPicker = state => {
9074
9266
  return {
9075
9267
  ...state,
9076
9268
  agentModePickerOpen: false,
9269
+ focus: reasoningEffortPickerOpen ? 'picker-list' : state.focus,
9270
+ focused: reasoningEffortPickerOpen ? true : state.focused,
9077
9271
  modelPickerOpen: false,
9078
9272
  modelPickerSearchValue: '',
9079
9273
  reasoningEffortPickerOpen,
@@ -9099,6 +9293,8 @@ const openRunModePicker = state => {
9099
9293
  return {
9100
9294
  ...state,
9101
9295
  agentModePickerOpen: false,
9296
+ focus: runModePickerOpen ? 'picker-list' : state.focus,
9297
+ focused: runModePickerOpen ? true : state.focused,
9102
9298
  modelPickerOpen: false,
9103
9299
  modelPickerSearchValue: '',
9104
9300
  reasoningEffortPickerOpen: false,
@@ -9362,6 +9558,12 @@ const handleClick = async (state, name, id = '', eventX = 0, eventY = 0) => {
9362
9558
  return deleteSession(state, id);
9363
9559
  case name === Send:
9364
9560
  return handleClickSend(state);
9561
+ case name === ScrollDown:
9562
+ return {
9563
+ ...state,
9564
+ messagesAutoScrollEnabled: true,
9565
+ messagesScrollTop: getNextAutoScrollTop(state.messagesScrollTop)
9566
+ };
9365
9567
  case name === SaveOpenRouterApiKey:
9366
9568
  return handleClickSaveOpenRouterApiKey(state);
9367
9569
  case name === SaveOpenApiApiKey:
@@ -9592,6 +9794,7 @@ const handleDropFiles = async (state, name, fileHandles = []) => {
9592
9794
  const {
9593
9795
  composerDropActive,
9594
9796
  composerDropEnabled,
9797
+ nextAttachmentId,
9595
9798
  selectedSessionId,
9596
9799
  width
9597
9800
  } = state;
@@ -9614,9 +9817,10 @@ const handleDropFiles = async (state, name, fileHandles = []) => {
9614
9817
  const nextAttachments = [];
9615
9818
  for (const droppedFileHandle of droppedFileHandles) {
9616
9819
  const file = await droppedFileHandle.getFile();
9617
- const attachmentId = crypto.randomUUID();
9820
+ const attachmentId = `attachment-${nextAttachmentId + nextAttachments.length}`;
9618
9821
  const displayType = await getComposerAttachmentDisplayType(file, file.name, file.type);
9619
9822
  const previewSrc = await getComposerAttachmentPreviewSrc(file, displayType, file.type);
9823
+ const textContent = await getComposerAttachmentTextContent(file, displayType);
9620
9824
  await appendChatViewEvent({
9621
9825
  attachmentId,
9622
9826
  blob: file,
@@ -9635,13 +9839,27 @@ const handleDropFiles = async (state, name, fileHandles = []) => {
9635
9839
  ...(previewSrc ? {
9636
9840
  previewSrc
9637
9841
  } : {}),
9638
- size: file.size
9842
+ size: file.size,
9843
+ ...(typeof textContent === 'string' ? {
9844
+ textContent
9845
+ } : {})
9639
9846
  });
9640
9847
  }
9641
9848
  return {
9642
9849
  ...nextState,
9643
9850
  composerAttachments: [...nextState.composerAttachments, ...nextAttachments],
9644
- 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
9645
9863
  };
9646
9864
  };
9647
9865
 
@@ -9845,6 +10063,60 @@ const handleModelChange = async (state, value) => {
9845
10063
  };
9846
10064
  };
9847
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
+
9848
10120
  const handleNewline = async state => {
9849
10121
  const {
9850
10122
  composerValue
@@ -10038,6 +10310,23 @@ const getSavedChatListScrollTop = savedState => {
10038
10310
  return chatListScrollTop;
10039
10311
  };
10040
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
+
10041
10330
  const getSavedComposerValue = savedState => {
10042
10331
  if (!isObject$1(savedState)) {
10043
10332
  return undefined;
@@ -10339,6 +10628,15 @@ const loadReasoningPickerEnabled = async () => {
10339
10628
  }
10340
10629
  };
10341
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
+
10342
10640
  const loadSearchEnabled = async () => {
10343
10641
  try {
10344
10642
  const savedSearchEnabled = await get('chatView.searchEnabled');
@@ -10430,7 +10728,7 @@ const loadVoiceDictationEnabled = async () => {
10430
10728
  };
10431
10729
 
10432
10730
  const loadPreferences = async () => {
10433
- 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()]);
10434
10732
  return {
10435
10733
  aiSessionTitleGenerationEnabled,
10436
10734
  authAccessToken,
@@ -10444,6 +10742,7 @@ const loadPreferences = async () => {
10444
10742
  openRouterApiKey,
10445
10743
  passIncludeObfuscation,
10446
10744
  reasoningPickerEnabled,
10745
+ scrollDownButtonEnabled,
10447
10746
  searchEnabled,
10448
10747
  streamingEnabled,
10449
10748
  todoListToolEnabled,
@@ -10501,6 +10800,9 @@ const loadContent = async (state, savedState) => {
10501
10800
  const savedSelectedModelId = getSavedSelectedModelId(savedState);
10502
10801
  const savedViewMode = getSavedViewMode(savedState);
10503
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];
10504
10806
  const {
10505
10807
  aiSessionTitleGenerationEnabled,
10506
10808
  authAccessToken,
@@ -10514,6 +10816,7 @@ const loadContent = async (state, savedState) => {
10514
10816
  openRouterApiKey,
10515
10817
  passIncludeObfuscation,
10516
10818
  reasoningPickerEnabled,
10819
+ scrollDownButtonEnabled,
10517
10820
  searchEnabled,
10518
10821
  streamingEnabled,
10519
10822
  todoListToolEnabled,
@@ -10592,7 +10895,9 @@ const loadContent = async (state, savedState) => {
10592
10895
  composerAttachmentsHeight: getComposerAttachmentsHeight(composerAttachments, state.width),
10593
10896
  composerDropActive: false,
10594
10897
  composerDropEnabled,
10595
- composerValue: savedComposerValue ?? state.composerValue,
10898
+ composerSelectionEnd,
10899
+ composerSelectionStart,
10900
+ composerValue,
10596
10901
  emitStreamingFunctionCallEvents,
10597
10902
  initial: false,
10598
10903
  lastNormalViewMode,
@@ -10614,6 +10919,7 @@ const loadContent = async (state, savedState) => {
10614
10919
  reasoningEffortPickerOpen: false,
10615
10920
  reasoningPickerEnabled,
10616
10921
  runModePickerOpen: false,
10922
+ scrollDownButtonEnabled,
10617
10923
  searchEnabled,
10618
10924
  searchFieldVisible: false,
10619
10925
  searchValue: '',
@@ -10671,7 +10977,7 @@ const mockOpenApiRequestGetAll = _state => {
10671
10977
  };
10672
10978
 
10673
10979
  const mockOpenApiRequestReset = state => {
10674
- reset$1();
10980
+ reset$2();
10675
10981
  return state;
10676
10982
  };
10677
10983
 
@@ -10696,7 +11002,7 @@ const mockOpenApiStreamPushChunk = (state, chunk) => {
10696
11002
  };
10697
11003
 
10698
11004
  const mockOpenApiStreamReset = state => {
10699
- reset$2();
11005
+ reset$1();
10700
11006
  return state;
10701
11007
  };
10702
11008
 
@@ -10744,7 +11050,7 @@ const pasteInput = async state => {
10744
11050
  };
10745
11051
 
10746
11052
  const registerMockResponse = (state, mockResponse) => {
10747
- reset$2();
11053
+ reset$1();
10748
11054
  pushChunk(mockResponse.text);
10749
11055
  finish();
10750
11056
  return state;
@@ -10852,6 +11158,43 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
10852
11158
  border-color: var(--vscode-charts-green, var(--vscode-widget-border, var(--vscode-panel-border)));
10853
11159
  }
10854
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
+
10855
11198
  .CustomSelectContainer{
10856
11199
  position: relative;
10857
11200
  min-width: 0;
@@ -10931,6 +11274,18 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
10931
11274
  min-width: 0;
10932
11275
  }
10933
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
+
10934
11289
  .ChatFocus .ChatMessages > .Message{
10935
11290
  inline-size: fit-content;
10936
11291
  max-inline-size: min(100%, var(--ChatFocusContentMaxWidth));
@@ -10977,6 +11332,7 @@ const renderCss = (oldState, newState) => {
10977
11332
  return [SetCss, uid, css];
10978
11333
  };
10979
11334
 
11335
+ const pickerListSelector = '.ChatOverlays .ChatModelPickerList';
10980
11336
  const getFocusSelector = state => {
10981
11337
  const {
10982
11338
  focus,
@@ -11002,6 +11358,8 @@ const getFocusSelector = state => {
11002
11358
  }
11003
11359
  case 'model-picker-input':
11004
11360
  return `[name="${ModelPickerSearch}"]`;
11361
+ case 'picker-list':
11362
+ return pickerListSelector;
11005
11363
  case 'send-button':
11006
11364
  return '[name="send"]';
11007
11365
  default:
@@ -11012,6 +11370,9 @@ const renderFocus = (oldState, newState) => {
11012
11370
  if (newState.modelPickerOpen && !oldState.modelPickerOpen) {
11013
11371
  return [FocusSelector, `[name="${ModelPickerSearch}"]`];
11014
11372
  }
11373
+ if (newState.agentModePickerOpen && !oldState.agentModePickerOpen || newState.runModePickerOpen && !oldState.runModePickerOpen || newState.reasoningEffortPickerOpen && !oldState.reasoningEffortPickerOpen) {
11374
+ return [FocusSelector, pickerListSelector];
11375
+ }
11015
11376
  const selector = getFocusSelector(newState);
11016
11377
  return [FocusSelector, selector];
11017
11378
  };
@@ -11029,6 +11390,8 @@ const renderFocusContext = (oldState, newState) => {
11029
11390
  const HandleListContextMenu = 2;
11030
11391
  const HandleFocus = 3;
11031
11392
  const HandleInput = 4;
11393
+ const HandleMouseOut = 5;
11394
+ const HandleMouseOver = 7;
11032
11395
  const HandleClick = 11;
11033
11396
  const HandleKeyDown = 12;
11034
11397
  const HandleClickClose = 13;
@@ -11076,6 +11439,7 @@ const HandlePointerUpModelPickerList = 55;
11076
11439
  const HandleClickReasoningEffortPickerToggle = 56;
11077
11440
  const HandleClickAgentModePickerToggle = 57;
11078
11441
  const HandleContextMenuChatImageAttachment = 58;
11442
+ const HandleErrorComposerAttachmentPreviewOverlay = 59;
11079
11443
 
11080
11444
  const getAddContextButtonDom = () => {
11081
11445
  return [{
@@ -11192,6 +11556,8 @@ const getReasoningEffortPickerVirtualDom = (selectedReasoningEffort, reasoningEf
11192
11556
  }, {
11193
11557
  childCount: reasoningEfforts.length,
11194
11558
  className: ChatModelPickerList,
11559
+ name: PickerList,
11560
+ tabIndex: -1,
11195
11561
  type: Ul
11196
11562
  }, ...getReasoningEffortOptionsVirtualDom(selectedReasoningEffort)] : [])];
11197
11563
  };
@@ -11200,6 +11566,21 @@ const getRunModePickerVirtualDom = (selectedRunMode, runModePickerOpen) => {
11200
11566
  return getCustomSelectPickerToggleVirtualDom(selectedRunMode, RunModePickerToggle, runModePickerOpen, HandleClickRunModePickerToggle);
11201
11567
  };
11202
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
+
11203
11584
  const getSendButtonClassName = isSendDisabled => {
11204
11585
  return mergeClassNames(IconButton, isSendDisabled ? SendButtonDisabled : '');
11205
11586
  };
@@ -11375,10 +11756,15 @@ const getComposerAttachmentsDom = composerAttachments => {
11375
11756
  childCount: 1 + (removeButtonDom.length > 0 ? 1 : 0) + previewDom.length,
11376
11757
  className: mergeClassNames(ChatComposerAttachment, getComposerAttachmentClassName(attachment.displayType)),
11377
11758
  name: getComposerAttachmentInputName(attachment.attachmentId),
11759
+ onMouseOut: HandleMouseOut,
11760
+ onMouseOver: HandleMouseOver,
11761
+ onPointerOut: HandleMouseOut,
11762
+ onPointerOver: HandleMouseOver,
11378
11763
  type: Div
11379
11764
  }, ...removeButtonDom, ...previewDom, {
11380
11765
  childCount: 1,
11381
11766
  className: ChatComposerAttachmentLabel,
11767
+ name: getComposerAttachmentInputName(attachment.attachmentId),
11382
11768
  type: Span
11383
11769
  }, {
11384
11770
  text: `${getComposerAttachmentLabel(attachment.displayType)} · ${attachment.name}`,
@@ -11400,11 +11786,12 @@ const getComposerTextAreaDom = () => {
11400
11786
  type: TextArea
11401
11787
  };
11402
11788
  };
11403
- 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) => {
11404
11790
  const isSendDisabled = composerValue.trim() === '';
11405
11791
  const showAgentModePicker = hasSpaceForAgentModePicker;
11406
11792
  const showResponsiveRunModePicker = showRunMode && hasSpaceForRunModePicker;
11407
- 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);
11408
11795
  const primaryControlsCount = 1 + (showAgentModePicker ? 1 : 0) + (reasoningPickerEnabled ? 1 : 0) + (showResponsiveRunModePicker ? 1 : 0);
11409
11796
  const hasTodoList = todoListToolEnabled && todoListItems.length > 0;
11410
11797
  const hasComposerAttachments = composerAttachments.length > 0;
@@ -11428,7 +11815,7 @@ const getChatSendAreaDom = (composerValue, composerAttachments, agentMode, agent
11428
11815
  className: ChatSendAreaPrimaryControls,
11429
11816
  role: 'toolbar',
11430
11817
  type: Div
11431
- }, ...(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)];
11432
11819
  };
11433
11820
 
11434
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;';
@@ -11495,6 +11882,8 @@ const getCustomSelectPopOverVirtualDom = (optionCount, height, optionNodes, cont
11495
11882
  }, {
11496
11883
  childCount: optionCount,
11497
11884
  className: ChatModelPickerList,
11885
+ name: PickerList,
11886
+ tabIndex: -1,
11498
11887
  type: Ul
11499
11888
  }, ...optionNodes];
11500
11889
  };
@@ -11573,6 +11962,42 @@ const getChatModelPickerPopOverVirtualDom = (models, selectedModelId, modelPicke
11573
11962
  }, ...getModelPickerHeaderDom(modelPickerSearchValue), ...getChatModelListVirtualDom(visibleModels, selectedModelId)];
11574
11963
  };
11575
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
+
11576
12001
  const runModes = ['local', 'background', 'cloud'];
11577
12002
  const runModePickerHeight = runModes.length * 28;
11578
12003
  const getRunModeOptionsVirtualDom = selectedRunMode => {
@@ -11599,6 +12024,10 @@ const getDropOverlayVirtualDom = () => {
11599
12024
  const getChatOverlaysVirtualDom = ({
11600
12025
  agentMode,
11601
12026
  agentModePickerVisible,
12027
+ composerAttachmentPreviewOverlayAttachmentId,
12028
+ composerAttachmentPreviewOverlayError,
12029
+ composerAttachmentPreviewOverlayVisible,
12030
+ composerAttachments,
11602
12031
  dropOverlayVisible,
11603
12032
  modelPickerSearchValue,
11604
12033
  modelPickerVisible,
@@ -11607,7 +12036,7 @@ const getChatOverlaysVirtualDom = ({
11607
12036
  selectedModelId,
11608
12037
  visibleModels
11609
12038
  }) => {
11610
- 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);
11611
12040
  if (!overlayChildCount) {
11612
12041
  return [];
11613
12042
  }
@@ -11615,7 +12044,7 @@ const getChatOverlaysVirtualDom = ({
11615
12044
  childCount: overlayChildCount,
11616
12045
  className: ChatOverlays,
11617
12046
  type: Div
11618
- }, ...(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) : [])];
11619
12048
  };
11620
12049
 
11621
12050
  const getBoldInlineNodeDom = (inlineNode, useChatMathWorker, renderInlineNodeDom) => {
@@ -13167,6 +13596,8 @@ const getChatModeChatFocusVirtualDom = ({
13167
13596
  authEnabled = false,
13168
13597
  authErrorMessage = '',
13169
13598
  authStatus = 'signed-out',
13599
+ composerAttachmentPreviewOverlayAttachmentId,
13600
+ composerAttachmentPreviewOverlayError = false,
13170
13601
  composerAttachments,
13171
13602
  composerDropActive = false,
13172
13603
  composerDropEnabled = true,
@@ -13177,6 +13608,7 @@ const getChatModeChatFocusVirtualDom = ({
13177
13608
  composerValue,
13178
13609
  hasSpaceForAgentModePicker,
13179
13610
  hasSpaceForRunModePicker,
13611
+ messagesAutoScrollEnabled,
13180
13612
  messagesScrollTop = 0,
13181
13613
  modelPickerOpen = false,
13182
13614
  modelPickerSearchValue = '',
@@ -13196,6 +13628,7 @@ const getChatModeChatFocusVirtualDom = ({
13196
13628
  reasoningPickerEnabled,
13197
13629
  runMode,
13198
13630
  runModePickerOpen = false,
13631
+ scrollDownButtonEnabled,
13199
13632
  selectedModelId,
13200
13633
  selectedProjectId = '',
13201
13634
  selectedSessionId,
@@ -13217,10 +13650,11 @@ const getChatModeChatFocusVirtualDom = ({
13217
13650
  const selectedProjectName = selectedProject?.name || '';
13218
13651
  const showCreatePullRequestButton = canCreatePullRequest(selectedSession);
13219
13652
  const isDropOverlayVisible = composerDropEnabled && composerDropActive;
13653
+ const isComposerAttachmentPreviewOverlayVisible = !!composerAttachmentPreviewOverlayAttachmentId;
13220
13654
  const isAgentModePickerVisible = hasSpaceForAgentModePicker && agentModePickerOpen;
13221
13655
  const isNewModelPickerVisible = modelPickerOpen;
13222
13656
  const isRunModePickerVisible = showRunMode && hasSpaceForRunModePicker && runModePickerOpen;
13223
- const hasVisibleOverlays = isDropOverlayVisible || isAgentModePickerVisible || isNewModelPickerVisible || isRunModePickerVisible;
13657
+ const hasVisibleOverlays = isDropOverlayVisible || isComposerAttachmentPreviewOverlayVisible || isAgentModePickerVisible || isNewModelPickerVisible || isRunModePickerVisible;
13224
13658
  const chatRootChildCount = 4 + (hasVisibleOverlays ? 1 : 0);
13225
13659
  return [{
13226
13660
  childCount: chatRootChildCount,
@@ -13228,9 +13662,13 @@ const getChatModeChatFocusVirtualDom = ({
13228
13662
  onDragEnter: HandleDragEnterChatView,
13229
13663
  onDragOver: HandleDragOverChatView,
13230
13664
  type: Div
13231
- }, ...getProjectListDom(projects, sessions, projectExpandedIds, selectedProjectId, selectedSessionId, projectListScrollTop, true), ...getChatHeaderDomFocusMode(selectedSessionTitle, selectedProjectName), ...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({
13232
13666
  agentMode,
13233
13667
  agentModePickerVisible: isAgentModePickerVisible,
13668
+ composerAttachmentPreviewOverlayAttachmentId,
13669
+ composerAttachmentPreviewOverlayError,
13670
+ composerAttachmentPreviewOverlayVisible: isComposerAttachmentPreviewOverlayVisible,
13671
+ composerAttachments,
13234
13672
  dropOverlayVisible: isDropOverlayVisible,
13235
13673
  modelPickerSearchValue,
13236
13674
  modelPickerVisible: isNewModelPickerVisible,
@@ -13368,6 +13806,8 @@ const getChatModeDetailVirtualDom = ({
13368
13806
  authEnabled = false,
13369
13807
  authErrorMessage = '',
13370
13808
  authStatus = 'signed-out',
13809
+ composerAttachmentPreviewOverlayAttachmentId,
13810
+ composerAttachmentPreviewOverlayError = false,
13371
13811
  composerAttachments,
13372
13812
  composerDropActive = false,
13373
13813
  composerDropEnabled = true,
@@ -13378,6 +13818,7 @@ const getChatModeDetailVirtualDom = ({
13378
13818
  composerValue,
13379
13819
  hasSpaceForAgentModePicker,
13380
13820
  hasSpaceForRunModePicker,
13821
+ messagesAutoScrollEnabled,
13381
13822
  messagesScrollTop = 0,
13382
13823
  modelPickerOpen = false,
13383
13824
  modelPickerSearchValue = '',
@@ -13394,6 +13835,7 @@ const getChatModeDetailVirtualDom = ({
13394
13835
  reasoningPickerEnabled,
13395
13836
  runMode,
13396
13837
  runModePickerOpen = false,
13838
+ scrollDownButtonEnabled,
13397
13839
  selectedModelId,
13398
13840
  selectedSessionId,
13399
13841
  sessions,
@@ -13412,10 +13854,11 @@ const getChatModeDetailVirtualDom = ({
13412
13854
  const messages = selectedSession ? selectedSession.messages : [];
13413
13855
  const showCreatePullRequestButton = canCreatePullRequest(selectedSession);
13414
13856
  const isDropOverlayVisible = composerDropEnabled && composerDropActive;
13857
+ const isComposerAttachmentPreviewOverlayVisible = !!composerAttachmentPreviewOverlayAttachmentId;
13415
13858
  const isAgentModePickerVisible = hasSpaceForAgentModePicker && agentModePickerOpen;
13416
13859
  const isNewModelPickerVisible = modelPickerOpen;
13417
13860
  const isRunModePickerVisible = showRunMode && hasSpaceForRunModePicker && runModePickerOpen;
13418
- const hasVisibleOverlays = isDropOverlayVisible || isAgentModePickerVisible || isNewModelPickerVisible || isRunModePickerVisible;
13861
+ const hasVisibleOverlays = isDropOverlayVisible || isComposerAttachmentPreviewOverlayVisible || isAgentModePickerVisible || isNewModelPickerVisible || isRunModePickerVisible;
13419
13862
  const chatRootChildCount = 3 + (hasVisibleOverlays ? 1 : 0);
13420
13863
  return [{
13421
13864
  childCount: chatRootChildCount,
@@ -13423,9 +13866,13 @@ const getChatModeDetailVirtualDom = ({
13423
13866
  onDragEnter: HandleDragEnterChatView,
13424
13867
  onDragOver: HandleDragOverChatView,
13425
13868
  type: Div
13426
- }, ...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({
13427
13870
  agentMode,
13428
13871
  agentModePickerVisible: isAgentModePickerVisible,
13872
+ composerAttachmentPreviewOverlayAttachmentId,
13873
+ composerAttachmentPreviewOverlayError,
13874
+ composerAttachmentPreviewOverlayVisible: isComposerAttachmentPreviewOverlayVisible,
13875
+ composerAttachments,
13429
13876
  dropOverlayVisible: isDropOverlayVisible,
13430
13877
  modelPickerSearchValue,
13431
13878
  modelPickerVisible: isNewModelPickerVisible,
@@ -13570,6 +14017,8 @@ const getChatModeListVirtualDom = ({
13570
14017
  authErrorMessage = '',
13571
14018
  authStatus = 'signed-out',
13572
14019
  chatListScrollTop = 0,
14020
+ composerAttachmentPreviewOverlayAttachmentId,
14021
+ composerAttachmentPreviewOverlayError = false,
13573
14022
  composerAttachments,
13574
14023
  composerDropActive = false,
13575
14024
  composerDropEnabled = true,
@@ -13605,10 +14054,11 @@ const getChatModeListVirtualDom = ({
13605
14054
  voiceDictationEnabled = false
13606
14055
  }) => {
13607
14056
  const isDropOverlayVisible = composerDropEnabled && composerDropActive;
14057
+ const isComposerAttachmentPreviewOverlayVisible = !!composerAttachmentPreviewOverlayAttachmentId;
13608
14058
  const isAgentModePickerVisible = hasSpaceForAgentModePicker && agentModePickerOpen;
13609
14059
  const isNewModelPickerVisible = modelPickerOpen;
13610
14060
  const isRunModePickerVisible = showRunMode && hasSpaceForRunModePicker && runModePickerOpen;
13611
- const hasVisibleOverlays = isDropOverlayVisible || isAgentModePickerVisible || isNewModelPickerVisible || isRunModePickerVisible;
14061
+ const hasVisibleOverlays = isDropOverlayVisible || isComposerAttachmentPreviewOverlayVisible || isAgentModePickerVisible || isNewModelPickerVisible || isRunModePickerVisible;
13612
14062
  const chatRootChildCount = 3 + (hasVisibleOverlays ? 1 : 0);
13613
14063
  const searchValueTrimmed = searchValue.trim().toLowerCase();
13614
14064
  const visibleSessions = searchEnabled && searchValueTrimmed ? sessions.filter(session => session.title.toLowerCase().includes(searchValueTrimmed)) : sessions;
@@ -13621,6 +14071,10 @@ const getChatModeListVirtualDom = ({
13621
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({
13622
14072
  agentMode,
13623
14073
  agentModePickerVisible: isAgentModePickerVisible,
14074
+ composerAttachmentPreviewOverlayAttachmentId,
14075
+ composerAttachmentPreviewOverlayError,
14076
+ composerAttachmentPreviewOverlayVisible: isComposerAttachmentPreviewOverlayVisible,
14077
+ composerAttachments,
13624
14078
  dropOverlayVisible: isDropOverlayVisible,
13625
14079
  modelPickerSearchValue,
13626
14080
  modelPickerVisible: isNewModelPickerVisible,
@@ -13719,6 +14173,8 @@ const getChatVirtualDom = options => {
13719
14173
  authErrorMessage = '',
13720
14174
  authStatus = 'signed-out',
13721
14175
  chatListScrollTop,
14176
+ composerAttachmentPreviewOverlayAttachmentId,
14177
+ composerAttachmentPreviewOverlayError = false,
13722
14178
  composerAttachments,
13723
14179
  composerDropActive = false,
13724
14180
  composerDropEnabled = true,
@@ -13730,6 +14186,7 @@ const getChatVirtualDom = options => {
13730
14186
  hasSpaceForAgentModePicker,
13731
14187
  hasSpaceForRunModePicker,
13732
14188
  listFocusedIndex = -1,
14189
+ messagesAutoScrollEnabled,
13733
14190
  messagesScrollTop,
13734
14191
  modelPickerOpen = false,
13735
14192
  modelPickerSearchValue = '',
@@ -13749,6 +14206,7 @@ const getChatVirtualDom = options => {
13749
14206
  reasoningPickerEnabled,
13750
14207
  runMode,
13751
14208
  runModePickerOpen = false,
14209
+ scrollDownButtonEnabled,
13752
14210
  searchEnabled = false,
13753
14211
  searchFieldVisible = false,
13754
14212
  searchValue = '',
@@ -13777,6 +14235,8 @@ const getChatVirtualDom = options => {
13777
14235
  authEnabled,
13778
14236
  authErrorMessage,
13779
14237
  authStatus,
14238
+ composerAttachmentPreviewOverlayAttachmentId,
14239
+ composerAttachmentPreviewOverlayError,
13780
14240
  composerAttachments,
13781
14241
  composerDropActive,
13782
14242
  composerDropEnabled,
@@ -13787,6 +14247,7 @@ const getChatVirtualDom = options => {
13787
14247
  composerValue,
13788
14248
  hasSpaceForAgentModePicker,
13789
14249
  hasSpaceForRunModePicker,
14250
+ messagesAutoScrollEnabled,
13790
14251
  messagesScrollTop,
13791
14252
  modelPickerOpen,
13792
14253
  modelPickerSearchValue,
@@ -13806,6 +14267,7 @@ const getChatVirtualDom = options => {
13806
14267
  reasoningPickerEnabled,
13807
14268
  runMode,
13808
14269
  runModePickerOpen,
14270
+ scrollDownButtonEnabled,
13809
14271
  selectedModelId,
13810
14272
  selectedProjectId,
13811
14273
  selectedSessionId,
@@ -13828,6 +14290,8 @@ const getChatVirtualDom = options => {
13828
14290
  authEnabled,
13829
14291
  authErrorMessage,
13830
14292
  authStatus,
14293
+ composerAttachmentPreviewOverlayAttachmentId,
14294
+ composerAttachmentPreviewOverlayError,
13831
14295
  composerAttachments,
13832
14296
  composerDropActive,
13833
14297
  composerDropEnabled,
@@ -13838,6 +14302,7 @@ const getChatVirtualDom = options => {
13838
14302
  composerValue,
13839
14303
  hasSpaceForAgentModePicker,
13840
14304
  hasSpaceForRunModePicker,
14305
+ messagesAutoScrollEnabled,
13841
14306
  messagesScrollTop,
13842
14307
  modelPickerOpen,
13843
14308
  modelPickerSearchValue,
@@ -13854,6 +14319,7 @@ const getChatVirtualDom = options => {
13854
14319
  reasoningPickerEnabled,
13855
14320
  runMode,
13856
14321
  runModePickerOpen,
14322
+ scrollDownButtonEnabled,
13857
14323
  selectedModelId,
13858
14324
  selectedSessionId,
13859
14325
  sessions,
@@ -13876,6 +14342,8 @@ const getChatVirtualDom = options => {
13876
14342
  authErrorMessage,
13877
14343
  authStatus,
13878
14344
  chatListScrollTop,
14345
+ composerAttachmentPreviewOverlayAttachmentId,
14346
+ composerAttachmentPreviewOverlayError,
13879
14347
  composerAttachments,
13880
14348
  composerDropActive,
13881
14349
  composerDropEnabled,
@@ -13924,6 +14392,8 @@ const renderItems = (oldState, newState) => {
13924
14392
  authErrorMessage,
13925
14393
  authStatus,
13926
14394
  chatListScrollTop,
14395
+ composerAttachmentPreviewOverlayAttachmentId,
14396
+ composerAttachmentPreviewOverlayError,
13927
14397
  composerAttachments,
13928
14398
  composerDropActive,
13929
14399
  composerDropEnabled,
@@ -13936,6 +14406,7 @@ const renderItems = (oldState, newState) => {
13936
14406
  hasSpaceForRunModePicker,
13937
14407
  initial,
13938
14408
  listFocusedIndex,
14409
+ messagesAutoScrollEnabled,
13939
14410
  messagesScrollTop,
13940
14411
  modelPickerOpen,
13941
14412
  modelPickerSearchValue,
@@ -13955,6 +14426,7 @@ const renderItems = (oldState, newState) => {
13955
14426
  reasoningPickerEnabled,
13956
14427
  runMode,
13957
14428
  runModePickerOpen,
14429
+ scrollDownButtonEnabled,
13958
14430
  searchEnabled,
13959
14431
  searchFieldVisible,
13960
14432
  searchValue,
@@ -13984,6 +14456,8 @@ const renderItems = (oldState, newState) => {
13984
14456
  authErrorMessage,
13985
14457
  authStatus,
13986
14458
  chatListScrollTop,
14459
+ composerAttachmentPreviewOverlayAttachmentId,
14460
+ composerAttachmentPreviewOverlayError,
13987
14461
  composerAttachments,
13988
14462
  composerDropActive,
13989
14463
  composerDropEnabled,
@@ -13995,6 +14469,7 @@ const renderItems = (oldState, newState) => {
13995
14469
  hasSpaceForAgentModePicker,
13996
14470
  hasSpaceForRunModePicker,
13997
14471
  listFocusedIndex,
14472
+ messagesAutoScrollEnabled,
13998
14473
  messagesScrollTop,
13999
14474
  modelPickerOpen,
14000
14475
  modelPickerSearchValue,
@@ -14014,6 +14489,7 @@ const renderItems = (oldState, newState) => {
14014
14489
  reasoningPickerEnabled,
14015
14490
  runMode,
14016
14491
  runModePickerOpen,
14492
+ scrollDownButtonEnabled,
14017
14493
  searchEnabled,
14018
14494
  searchFieldVisible,
14019
14495
  searchValue,
@@ -14132,6 +14608,12 @@ const renderEventListeners = () => {
14132
14608
  }, {
14133
14609
  name: HandleClick,
14134
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") : ""']
14135
14617
  }, {
14136
14618
  name: HandleClickDictationButton,
14137
14619
  params: ['handleClickDictationButton']
@@ -14298,6 +14780,9 @@ const renderEventListeners = () => {
14298
14780
  name: HandleContextMenuChatImageAttachment,
14299
14781
  params: ['handleContextMenuChatImageAttachment', TargetName, ClientX, ClientY],
14300
14782
  preventDefault: true
14783
+ }, {
14784
+ name: HandleErrorComposerAttachmentPreviewOverlay,
14785
+ params: ['handleErrorComposerAttachmentPreviewOverlay']
14301
14786
  }];
14302
14787
  };
14303
14788
 
@@ -14379,6 +14864,8 @@ const saveState = state => {
14379
14864
  const {
14380
14865
  agentMode,
14381
14866
  chatListScrollTop,
14867
+ composerSelectionEnd,
14868
+ composerSelectionStart,
14382
14869
  composerValue,
14383
14870
  lastNormalViewMode,
14384
14871
  maxToolCalls,
@@ -14400,6 +14887,8 @@ const saveState = state => {
14400
14887
  return {
14401
14888
  agentMode,
14402
14889
  chatListScrollTop,
14890
+ composerSelectionEnd,
14891
+ composerSelectionStart,
14403
14892
  composerValue,
14404
14893
  lastNormalViewMode,
14405
14894
  maxToolCalls,
@@ -14496,6 +14985,13 @@ const setResponsivePickerVisibilityEnabled = (state, responsivePickerVisibilityE
14496
14985
  };
14497
14986
  };
14498
14987
 
14988
+ const setScrollDownButtonEnabled = (state, scrollDownButtonEnabled) => {
14989
+ return {
14990
+ ...state,
14991
+ scrollDownButtonEnabled
14992
+ };
14993
+ };
14994
+
14499
14995
  const setSearchEnabled = (state, searchEnabled) => {
14500
14996
  return {
14501
14997
  ...state,
@@ -14628,6 +15124,7 @@ const commandMap = {
14628
15124
  'Chat.getKeyBindings': getKeyBindings,
14629
15125
  'Chat.getMenuEntries': getMenuEntries,
14630
15126
  'Chat.getMenuEntryIds': getMenuEntryIds,
15127
+ 'Chat.getMockOpenApiRequests': wrapGetter(getMockOpenApiRequests),
14631
15128
  'Chat.getQuickPickMenuEntries': getQuickPickMenuEntries,
14632
15129
  'Chat.getSelectedSessionId': wrapGetter(getSelectedSessionId),
14633
15130
  'Chat.getSystemPrompt': wrapGetter(getSystemPrompt),
@@ -14660,6 +15157,7 @@ const commandMap = {
14660
15157
  'Chat.handleDragLeave': wrapCommand(handleDragLeave),
14661
15158
  'Chat.handleDragOver': wrapCommand(handleDragOver),
14662
15159
  'Chat.handleDropFiles': wrapCommand(handleDropFiles),
15160
+ 'Chat.handleErrorComposerAttachmentPreviewOverlay': wrapCommand(handleErrorComposerAttachmentPreviewOverlay),
14663
15161
  'Chat.handleInput': wrapCommand(handleInput),
14664
15162
  'Chat.handleInputFocus': wrapCommand(handleInputFocus),
14665
15163
  'Chat.handleKeyDown': wrapCommand(handleKeyDown),
@@ -14670,6 +15168,8 @@ const commandMap = {
14670
15168
  'Chat.handleModelChange': wrapCommand(handleModelChange),
14671
15169
  'Chat.handleModelInputBlur': wrapCommand(handleModelInputBlur),
14672
15170
  'Chat.handleModelPickerListScroll': wrapCommand(handleModelPickerListScroll),
15171
+ 'Chat.handleMouseOut': wrapCommand(handleMouseOut),
15172
+ 'Chat.handleMouseOver': wrapCommand(handleMouseOver),
14673
15173
  'Chat.handlePointerDownModelPickerList': wrapCommand(handlePointerDownModelPickerList),
14674
15174
  'Chat.handlePointerUpModelPickerList': wrapCommand(handlePointerUpModelPickerList),
14675
15175
  'Chat.handleProjectAddButtonContextMenu': wrapCommand(handleProjectAddButtonContextMenu),
@@ -14679,6 +15179,7 @@ const commandMap = {
14679
15179
  'Chat.handleRunModeChange': wrapCommand(handleRunModeChange),
14680
15180
  'Chat.handleSearchValueChange': wrapCommand(handleSearchValueChange),
14681
15181
  'Chat.handleSubmit': wrapCommand(handleSubmit),
15182
+ 'Chat.hideComposerAttachmentPreviewOverlay': wrapCommand(hideComposerAttachmentPreviewOverlay),
14682
15183
  'Chat.initialize': initialize,
14683
15184
  'Chat.loadContent': wrapCommand(loadContent),
14684
15185
  'Chat.loadContent2': wrapCommand(loadContent),
@@ -14715,6 +15216,7 @@ const commandMap = {
14715
15216
  'Chat.setReasoningEffort': wrapCommand(setReasoningEffort),
14716
15217
  'Chat.setReasoningPickerEnabled': wrapCommand(setReasoningPickerEnabled),
14717
15218
  'Chat.setResponsivePickerVisibilityEnabled': wrapCommand(setResponsivePickerVisibilityEnabled),
15219
+ 'Chat.setScrollDownButtonEnabled': wrapCommand(setScrollDownButtonEnabled),
14718
15220
  'Chat.setSearchEnabled': wrapCommand(setSearchEnabled),
14719
15221
  'Chat.setShowRunMode': wrapCommand(setShowRunMode),
14720
15222
  'Chat.setStreamingEnabled': wrapCommand(setStreamingEnabled),
@@ -14725,6 +15227,7 @@ const commandMap = {
14725
15227
  'Chat.setUseChatMathWorker': wrapCommand(setUseChatMathWorker),
14726
15228
  'Chat.setUseChatMessageParsingWorker': wrapCommand(setUseChatMessageParsingWorker),
14727
15229
  'Chat.setUseChatNetworkWorkerForRequests': wrapCommand(setUseChatNetworkWorkerForRequests),
15230
+ 'Chat.showComposerAttachmentPreviewOverlay': wrapCommand(showComposerAttachmentPreviewOverlay),
14728
15231
  'Chat.terminate': terminate,
14729
15232
  'Chat.useMockApi': wrapCommand(useMockApi)
14730
15233
  };