@lvce-editor/chat-view 6.66.0 → 6.68.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,12 +1849,14 @@ 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';
1853
1856
  const Rename = 'Rename';
1854
1857
  const Archive = 'Archive';
1855
1858
  const AddProjectChat = 'Add Project';
1859
+ const RemoveProject = 'Remove Project';
1856
1860
  const Settings$1 = 'Settings';
1857
1861
  const LoginToBackend = 'Login to backend';
1858
1862
  const LogoutFromBackend = 'Logout from backend';
@@ -1867,7 +1871,7 @@ const ComposePlaceholder = 'Type your message. Enter to send.';
1867
1871
  const AttachImageAsContext = 'Attach Image as Context';
1868
1872
  const OpenImageInNewTab = 'Open image in new tab';
1869
1873
  const RemoveAttachment = 'Remove attachment';
1870
- const ImageCouldNotBeLoaded = 'image could not be loaded';
1874
+ const ImageCouldNotBeLoaded = 'Image preview could not be loaded';
1871
1875
  const OpenRouterApiKeyPlaceholder = 'Enter OpenRouter API key';
1872
1876
  const OpenApiApiKeyPlaceholder = 'Enter OpenAI API key';
1873
1877
  const SendMessage = 'Send message';
@@ -1912,6 +1916,9 @@ const newChat = () => {
1912
1916
  const addProject = () => {
1913
1917
  return i18nString(AddProjectChat);
1914
1918
  };
1919
+ const removeProject = () => {
1920
+ return i18nString(RemoveProject);
1921
+ };
1915
1922
  const debug = () => {
1916
1923
  return i18nString(Debug);
1917
1924
  };
@@ -1927,6 +1934,9 @@ const settings = () => {
1927
1934
  const search = () => {
1928
1935
  return i18nString(Search$1);
1929
1936
  };
1937
+ const scrollDown = () => {
1938
+ return i18nString(ScrollDown$1);
1939
+ };
1930
1940
  const searchModels = () => {
1931
1941
  return i18nString(SearchModels);
1932
1942
  };
@@ -2079,16 +2089,19 @@ const getDefaultModelsOpenAi = () => {
2079
2089
  id: 'openapi/gpt-4o-mini',
2080
2090
  name: 'GPT-4o Mini',
2081
2091
  provider: 'openApi',
2092
+ supportsImages: true,
2082
2093
  usageCost: 1
2083
2094
  }, {
2084
2095
  id: 'openapi/gpt-4o',
2085
2096
  name: 'GPT-4o',
2086
2097
  provider: 'openApi',
2098
+ supportsImages: true,
2087
2099
  usageCost: 3
2088
2100
  }, {
2089
2101
  id: 'openapi/gpt-4.1-mini',
2090
2102
  name: 'GPT-4.1 Mini',
2091
2103
  provider: 'openApi',
2104
+ supportsImages: true,
2092
2105
  usageCost: 1
2093
2106
  }];
2094
2107
  };
@@ -2326,6 +2339,8 @@ const createDefaultState = () => {
2326
2339
  chatSendAreaPaddingLeft: 8,
2327
2340
  chatSendAreaPaddingRight: 8,
2328
2341
  chatSendAreaPaddingTop: 10,
2342
+ composerAttachmentPreviewOverlayAttachmentId: '',
2343
+ composerAttachmentPreviewOverlayError: false,
2329
2344
  composerAttachments: [],
2330
2345
  composerAttachmentsHeight: 0,
2331
2346
  composerDropActive: false,
@@ -2356,12 +2371,14 @@ const createDefaultState = () => {
2356
2371
  messagesScrollTop: 0,
2357
2372
  mockAiResponseDelay: 800,
2358
2373
  mockApiCommandId: '',
2374
+ mockOpenApiRequests: [],
2359
2375
  modelPickerHeaderHeight,
2360
2376
  modelPickerHeight: getModelPickerHeight(modelPickerHeaderHeight, visibleModels.length),
2361
2377
  modelPickerListScrollTop: 0,
2362
2378
  modelPickerOpen: false,
2363
2379
  modelPickerSearchValue: '',
2364
2380
  models,
2381
+ nextAttachmentId: 1,
2365
2382
  nextMessageId: 1,
2366
2383
  openApiApiBaseUrl: 'https://api.openai.com/v1',
2367
2384
  openApiApiKey: '',
@@ -2392,6 +2409,7 @@ const createDefaultState = () => {
2392
2409
  responsivePickerVisibilityEnabled,
2393
2410
  runMode: 'local',
2394
2411
  runModePickerOpen: false,
2412
+ scrollDownButtonEnabled: false,
2395
2413
  searchEnabled: false,
2396
2414
  searchFieldVisible: false,
2397
2415
  searchValue: '',
@@ -2622,6 +2640,13 @@ const getComposerAttachmentPreviewSrc = async (blob, displayType, mimeType) => {
2622
2640
  return `data:${mimeType || 'application/octet-stream'};base64,${base64}`;
2623
2641
  };
2624
2642
 
2643
+ const getComposerAttachmentTextContent = async (blob, displayType) => {
2644
+ if (displayType !== 'text-file') {
2645
+ return undefined;
2646
+ }
2647
+ return blob.text();
2648
+ };
2649
+
2625
2650
  const isChatAttachmentAddedEvent = event => {
2626
2651
  return event.type === 'chat-attachment-added';
2627
2652
  };
@@ -2644,6 +2669,7 @@ const getComposerAttachments = async sessionId => {
2644
2669
  }
2645
2670
  const displayType = await getComposerAttachmentDisplayType(event.blob, event.name, event.mimeType);
2646
2671
  const previewSrc = await getComposerAttachmentPreviewSrc(event.blob, displayType, event.mimeType);
2672
+ const textContent = await getComposerAttachmentTextContent(event.blob, displayType);
2647
2673
  attachments.set(event.attachmentId, {
2648
2674
  attachmentId: event.attachmentId,
2649
2675
  displayType,
@@ -2652,7 +2678,10 @@ const getComposerAttachments = async sessionId => {
2652
2678
  ...(previewSrc ? {
2653
2679
  previewSrc
2654
2680
  } : {}),
2655
- size: event.size
2681
+ size: event.size,
2682
+ ...(typeof textContent === 'string' ? {
2683
+ textContent
2684
+ } : {})
2656
2685
  });
2657
2686
  }
2658
2687
  return [...attachments.values()];
@@ -3214,7 +3243,8 @@ const openModelPicker = state => {
3214
3243
  return {
3215
3244
  ...state,
3216
3245
  agentModePickerOpen: false,
3217
- focus: 'model-picker-input',
3246
+ focus: modelPickerOpen ? 'model-picker-input' : state.focus,
3247
+ focused: modelPickerOpen ? true : state.focused,
3218
3248
  modelPickerHeight: getModelPickerHeight(state.modelPickerHeaderHeight, visibleModels.length),
3219
3249
  modelPickerListScrollTop: 0,
3220
3250
  modelPickerOpen,
@@ -3250,6 +3280,10 @@ const getKeyBindings = () => {
3250
3280
  command: 'Chat.chatListFocusPrevious',
3251
3281
  key: UpArrow,
3252
3282
  when: FocusChatList
3283
+ }, {
3284
+ command: 'Chat.handleClickNew',
3285
+ key: CtrlCmd | KeyN,
3286
+ when: FocusChatInput
3253
3287
  }, {
3254
3288
  command: 'Chat.handleSubmit',
3255
3289
  key: Enter,
@@ -3348,6 +3382,7 @@ const ComposerDropTarget = 'composer-drop-target';
3348
3382
  const ComposerAttachmentPrefix = 'composer-attachment:';
3349
3383
  const ComposerAttachmentPreviewPrefix = 'composer-attachment-preview:';
3350
3384
  const ComposerAttachmentRemovePrefix = 'composer-attachment-remove:';
3385
+ const ComposerAttachmentPreviewOverlay = 'composer-attachment-preview-overlay';
3351
3386
  const AddContext = 'add-context';
3352
3387
  const Dictate = 'dictate';
3353
3388
  const CreatePullRequest$1 = 'create-pull-request';
@@ -3357,10 +3392,12 @@ const FocusCommit = 'focus-commit';
3357
3392
  const FocusOpenTerminal = 'focus-open-terminal';
3358
3393
  const FocusShowDiff = 'focus-show-diff';
3359
3394
  const Send = 'send';
3395
+ const ScrollDown = 'scroll-down';
3360
3396
  const Back = 'back';
3361
3397
  const ModelPickerToggle = 'model-picker-toggle';
3362
3398
  const ModelPickerSearch = 'model-picker-search';
3363
3399
  const ModelPickerList = 'model-picker-list';
3400
+ const PickerList = 'picker-list';
3364
3401
  const AgentModePickerToggle = 'agent-mode-picker-toggle';
3365
3402
  const AgentModePickerItemPrefix = 'agent-mode-picker-item:';
3366
3403
  const ReasoningEffortPickerToggle = 'reasoning-effort-picker-toggle';
@@ -3379,6 +3416,7 @@ const Login = 'login';
3379
3416
  const Logout = 'logout';
3380
3417
  const CloseChat = 'close-chat';
3381
3418
  const SessionDelete = 'SessionDelete';
3419
+ const ProjectDelete = 'ProjectDelete';
3382
3420
  const ProjectPrefix = 'project:';
3383
3421
  const SessionPrefix = 'session:';
3384
3422
  const RenamePrefix = 'session-rename:';
@@ -3422,6 +3460,12 @@ const getModelPickerItemInputName = modelId => {
3422
3460
  const getComposerAttachmentInputName = attachmentId => {
3423
3461
  return `${ComposerAttachmentPrefix}${attachmentId}`;
3424
3462
  };
3463
+ const isComposerAttachmentInputName = name => {
3464
+ return name.startsWith(ComposerAttachmentPrefix);
3465
+ };
3466
+ const getAttachmentIdFromComposerAttachmentInputName = name => {
3467
+ return name.slice(ComposerAttachmentPrefix.length);
3468
+ };
3425
3469
  const getComposerAttachmentRemoveInputName = attachmentId => {
3426
3470
  return `${ComposerAttachmentRemovePrefix}${attachmentId}`;
3427
3471
  };
@@ -3437,6 +3481,9 @@ const isComposerAttachmentRemoveInputName = name => {
3437
3481
  const getAttachmentIdFromComposerAttachmentRemoveInputName = name => {
3438
3482
  return name.slice(ComposerAttachmentRemovePrefix.length);
3439
3483
  };
3484
+ const isComposerAttachmentPreviewOverlayInputName = name => {
3485
+ return name === ComposerAttachmentPreviewOverlay;
3486
+ };
3440
3487
  const isModelPickerItemInputName = name => {
3441
3488
  return name.startsWith(ModelPickerItemPrefix);
3442
3489
  };
@@ -3478,17 +3525,27 @@ const menuEntryAddProject = {
3478
3525
  id: 'addProject',
3479
3526
  label: addProject()
3480
3527
  };
3481
- const getMenuEntriesChatProjectList = (projectId = '') => {
3528
+ const getMenuEntriesChatProjectList = (projectId = '', canRemoveProject = true) => {
3482
3529
  if (!projectId) {
3483
3530
  return [menuEntryAddProject];
3484
3531
  }
3485
- return [{
3532
+ const entries = [{
3486
3533
  args: [getCreateSessionInProjectInputName(projectId)],
3487
3534
  command: 'Chat.handleClick',
3488
3535
  flags: None,
3489
3536
  id: 'newChat',
3490
3537
  label: newChat()
3491
3538
  }, menuEntryAddProject];
3539
+ if (canRemoveProject) {
3540
+ entries.push({
3541
+ args: [ProjectDelete, projectId],
3542
+ command: 'Chat.handleClick',
3543
+ flags: None,
3544
+ id: 'removeProject',
3545
+ label: removeProject()
3546
+ });
3547
+ }
3548
+ return entries;
3492
3549
  };
3493
3550
 
3494
3551
  const getMenuEntriesProjectAddButton = () => {
@@ -3522,7 +3579,7 @@ const getMenuEntries = (menuId, props) => {
3522
3579
  case MenuChatList:
3523
3580
  return getMenuEntriesChatList();
3524
3581
  case MenuChatProjectList:
3525
- return getMenuEntriesChatProjectList(props.projectId);
3582
+ return getMenuEntriesChatProjectList(props.projectId, props.canRemoveProject);
3526
3583
  case MenuProjectAddButton:
3527
3584
  return getMenuEntriesProjectAddButton();
3528
3585
  default:
@@ -3530,6 +3587,10 @@ const getMenuEntries = (menuId, props) => {
3530
3587
  }
3531
3588
  };
3532
3589
 
3590
+ const getMockOpenApiRequests = state => {
3591
+ return state.mockOpenApiRequests;
3592
+ };
3593
+
3533
3594
  const getQuickPickMenuEntries = () => {
3534
3595
  return [];
3535
3596
  };
@@ -3619,6 +3680,15 @@ const handleChatListContextMenu = async (state, eventX, eventY) => {
3619
3680
  };
3620
3681
  };
3621
3682
 
3683
+ const focusInput = state => {
3684
+ return {
3685
+ ...state,
3686
+ focus: 'composer',
3687
+ focused: true,
3688
+ listFocusedIndex: -1
3689
+ };
3690
+ };
3691
+
3622
3692
  const generateSessionId = () => {
3623
3693
  return crypto.randomUUID();
3624
3694
  };
@@ -3634,7 +3704,7 @@ const createSession = async (state, projectIdOverride = '') => {
3634
3704
  title: `Chat ${state.sessions.length + 1}`
3635
3705
  };
3636
3706
  await saveChatSession(session);
3637
- return {
3707
+ return focusInput({
3638
3708
  ...state,
3639
3709
  composerAttachments: [],
3640
3710
  composerAttachmentsHeight: 0,
@@ -3643,6 +3713,84 @@ const createSession = async (state, projectIdOverride = '') => {
3643
3713
  selectedProjectId: projectId,
3644
3714
  selectedSessionId: id,
3645
3715
  sessions: [...state.sessions, session]
3716
+ });
3717
+ };
3718
+
3719
+ const getBlankProjectId = (state, removedProjectId) => {
3720
+ return state.projects.find(project => project.id !== removedProjectId && project.name === '_blank')?.id || '';
3721
+ };
3722
+ const getNextViewMode = (state, hasSelectedSession) => {
3723
+ if (state.viewMode === 'chat-focus') {
3724
+ return 'chat-focus';
3725
+ }
3726
+ if (!hasSelectedSession) {
3727
+ return 'list';
3728
+ }
3729
+ return state.viewMode === 'detail' ? 'detail' : 'list';
3730
+ };
3731
+ const ensureExpandedProject = (state, projectExpandedIds, selectedProjectId, visibleSessions) => {
3732
+ if (state.viewMode !== 'chat-focus' || !selectedProjectId || visibleSessions.length === 0 || projectExpandedIds.includes(selectedProjectId)) {
3733
+ return projectExpandedIds;
3734
+ }
3735
+ return [...projectExpandedIds, selectedProjectId];
3736
+ };
3737
+ const deleteProject = async (state, projectId) => {
3738
+ const project = state.projects.find(candidate => candidate.id === projectId);
3739
+ if (!project || project.name === '_blank') {
3740
+ return state;
3741
+ }
3742
+ const blankProjectId = getBlankProjectId(state, projectId);
3743
+ if (!blankProjectId) {
3744
+ return state;
3745
+ }
3746
+ const projects = state.projects.filter(candidate => candidate.id !== projectId);
3747
+ const sessions = await Promise.all(state.sessions.map(async session => {
3748
+ if (session.projectId !== projectId) {
3749
+ return session;
3750
+ }
3751
+ const updatedSession = {
3752
+ ...session,
3753
+ projectId: blankProjectId
3754
+ };
3755
+ await saveChatSession(updatedSession);
3756
+ return updatedSession;
3757
+ }));
3758
+ const selectedProjectId = !state.selectedProjectId || state.selectedProjectId === projectId || !projects.some(candidate => candidate.id === state.selectedProjectId) ? blankProjectId : state.selectedProjectId;
3759
+ const projectExpandedIds = state.projectExpandedIds.filter(expandedProjectId => expandedProjectId !== projectId && projects.some(candidate => candidate.id === expandedProjectId));
3760
+ const visibleSessions = getVisibleSessions(sessions, selectedProjectId);
3761
+ const nextProjectExpandedIds = ensureExpandedProject(state, projectExpandedIds, selectedProjectId, visibleSessions);
3762
+ if (visibleSessions.length === 0) {
3763
+ return {
3764
+ ...state,
3765
+ composerAttachments: [],
3766
+ composerAttachmentsHeight: 0,
3767
+ projectExpandedIds: nextProjectExpandedIds,
3768
+ projects,
3769
+ selectedProjectId,
3770
+ selectedSessionId: '',
3771
+ sessions,
3772
+ viewMode: getNextViewMode(state, false)
3773
+ };
3774
+ }
3775
+ const selectedSessionId = visibleSessions.some(session => session.id === state.selectedSessionId) ? state.selectedSessionId : visibleSessions[0].id;
3776
+ const loadedSession = await getChatSession(selectedSessionId);
3777
+ const composerAttachments = await getComposerAttachments(selectedSessionId);
3778
+ const hydratedSessions = sessions.map(session => {
3779
+ if (session.id !== selectedSessionId || !loadedSession) {
3780
+ return session;
3781
+ }
3782
+ return loadedSession;
3783
+ });
3784
+ return {
3785
+ ...state,
3786
+ composerAttachments,
3787
+ composerAttachmentsHeight: getComposerAttachmentsHeight(composerAttachments, state.width),
3788
+ projectExpandedIds: nextProjectExpandedIds,
3789
+ projects,
3790
+ selectedProjectId,
3791
+ selectedSessionId,
3792
+ sessions: hydratedSessions,
3793
+ viewMode: getNextViewMode(state, true)
3646
3794
  };
3647
3795
  };
3648
3796
 
@@ -3655,6 +3803,12 @@ const getModelPickerClickIndex = (y, height, eventY, modelPickerBottomOffset, mo
3655
3803
  return Math.floor(relativeY / modelPickerItemHeight);
3656
3804
  };
3657
3805
 
3806
+ const AutoScrollTopA = Number.MAX_SAFE_INTEGER;
3807
+ const AutoScrollTopB = Number.MAX_SAFE_INTEGER - 1;
3808
+ const getNextAutoScrollTop = currentScrollTop => {
3809
+ return currentScrollTop === AutoScrollTopA ? AutoScrollTopB : AutoScrollTopA;
3810
+ };
3811
+
3658
3812
  const openFolder = async () => {
3659
3813
  try {
3660
3814
  return await invoke('FilePicker.showDirectoryPicker');
@@ -4232,6 +4386,54 @@ const getBasicChatTools = async (agentMode = defaultAgentMode, questionToolEnabl
4232
4386
  }
4233
4387
  };
4234
4388
 
4389
+ const getAttachmentTextPart = attachment => {
4390
+ switch (attachment.displayType) {
4391
+ case 'file':
4392
+ return {
4393
+ text: `Attached file "${attachment.name}" (${attachment.mimeType || 'application/octet-stream'}, ${attachment.size} bytes).`,
4394
+ type: 'input_text'
4395
+ };
4396
+ case 'image':
4397
+ return {
4398
+ text: `Attached image "${attachment.name}" could not be encoded for the AI request.`,
4399
+ type: 'input_text'
4400
+ };
4401
+ case 'invalid-image':
4402
+ return {
4403
+ text: `Attached file "${attachment.name}" could not be processed as a valid image.`,
4404
+ type: 'input_text'
4405
+ };
4406
+ case 'text-file':
4407
+ return {
4408
+ 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'}).`,
4409
+ type: 'input_text'
4410
+ };
4411
+ }
4412
+ };
4413
+ const getChatMessageOpenAiContent = message => {
4414
+ if (!message.attachments || message.attachments.length === 0) {
4415
+ return message.text;
4416
+ }
4417
+ const parts = [];
4418
+ if (message.text) {
4419
+ parts.push({
4420
+ text: message.text,
4421
+ type: 'input_text'
4422
+ });
4423
+ }
4424
+ for (const attachment of message.attachments) {
4425
+ if (attachment.displayType === 'image' && attachment.previewSrc) {
4426
+ parts.push({
4427
+ image_url: attachment.previewSrc,
4428
+ type: 'input_image'
4429
+ });
4430
+ continue;
4431
+ }
4432
+ parts.push(getAttachmentTextPart(attachment));
4433
+ }
4434
+ return parts;
4435
+ };
4436
+
4235
4437
  const getClientRequestIdHeader = () => {
4236
4438
  return {
4237
4439
  'x-client-request-id': crypto.randomUUID()
@@ -4247,11 +4449,22 @@ const getMockAiResponse = async (userMessage, delayInMs) => {
4247
4449
  return `Mock AI response: I received "${userMessage}".`;
4248
4450
  };
4249
4451
 
4452
+ let requests = [];
4453
+ const reset$2 = () => {
4454
+ requests = [];
4455
+ };
4456
+ const capture = request => {
4457
+ requests = [...requests, request];
4458
+ };
4459
+ const getAll = () => {
4460
+ return requests;
4461
+ };
4462
+
4250
4463
  let queue = [];
4251
4464
  let waiters = [];
4252
4465
  let finished = false;
4253
4466
  let errorResult;
4254
- const reset$2 = () => {
4467
+ const reset$1 = () => {
4255
4468
  queue = [];
4256
4469
  waiters = [];
4257
4470
  finished = false;
@@ -4325,6 +4538,47 @@ const readNextChunk = async () => {
4325
4538
  return promise;
4326
4539
  };
4327
4540
 
4541
+ const lastRequestSummaryToken = '__MOCK_OPENAPI_LAST_REQUEST_SUMMARY__';
4542
+ const getLastRequestSummary = () => {
4543
+ const requests = getAll();
4544
+ const request = requests.at(-1);
4545
+ if (!request || !request.payload || typeof request.payload !== 'object') {
4546
+ return 'mock-request-summary images=0 text-files=0';
4547
+ }
4548
+ const input = Reflect.get(request.payload, 'input');
4549
+ if (!Array.isArray(input) || input.length === 0) {
4550
+ return 'mock-request-summary images=0 text-files=0';
4551
+ }
4552
+ let imageCount = 0;
4553
+ let textFileCount = 0;
4554
+ for (const item of input) {
4555
+ if (!item || typeof item !== 'object') {
4556
+ continue;
4557
+ }
4558
+ const content = Reflect.get(item, 'content');
4559
+ if (!Array.isArray(content)) {
4560
+ continue;
4561
+ }
4562
+ for (const part of content) {
4563
+ if (!part || typeof part !== 'object') {
4564
+ continue;
4565
+ }
4566
+ const type = Reflect.get(part, 'type');
4567
+ if (type === 'input_image') {
4568
+ imageCount++;
4569
+ continue;
4570
+ }
4571
+ if (type !== 'input_text') {
4572
+ continue;
4573
+ }
4574
+ const text = Reflect.get(part, 'text');
4575
+ if (typeof text === 'string' && text.startsWith('Attached text file "')) {
4576
+ textFileCount++;
4577
+ }
4578
+ }
4579
+ }
4580
+ return `mock-request-summary images=${imageCount} text-files=${textFileCount}`;
4581
+ };
4328
4582
  const getResponseFunctionCalls$1 = value => {
4329
4583
  if (!value || typeof value !== 'object') {
4330
4584
  return [];
@@ -4522,9 +4776,10 @@ const getMockOpenApiAssistantText = async (stream, onTextChunk, onToolCallsChunk
4522
4776
  }
4523
4777
  continue;
4524
4778
  }
4525
- text += chunk;
4779
+ const resolvedChunk = chunk === lastRequestSummaryToken ? getLastRequestSummary() : chunk;
4780
+ text += resolvedChunk;
4526
4781
  if (stream && onTextChunk) {
4527
- await onTextChunk(chunk);
4782
+ await onTextChunk(resolvedChunk);
4528
4783
  }
4529
4784
  }
4530
4785
  if (!requestDone && remainder) {
@@ -5361,7 +5616,7 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
5361
5616
  stream: false
5362
5617
  };
5363
5618
  const openAiInput = messages.map(message => ({
5364
- content: message.text,
5619
+ content: getChatMessageOpenAiContent(message),
5365
5620
  role: message.role
5366
5621
  }));
5367
5622
  const tools = await getBasicChatTools(agentMode, questionToolEnabled, toolEnablement);
@@ -5754,12 +6009,22 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
5754
6009
  };
5755
6010
  };
5756
6011
 
6012
+ const imageRegex = /\b(?:image|vision|multimodal)\b/i;
6013
+ const unsupportedRegex = /\bdoes(?:\s+not|n't)\s+support|not\s+support(?:ed)?|unsupported\b/i;
5757
6014
  const isOffline = () => {
5758
6015
  if (!globalThis.navigator) {
5759
6016
  return false;
5760
6017
  }
5761
6018
  return globalThis.navigator.onLine === false;
5762
6019
  };
6020
+ const isImageNotSupportedError = errorResult => {
6021
+ const haystack = [errorResult.errorCode, errorResult.errorMessage, errorResult.errorType].filter(Boolean).join(' ');
6022
+ return imageRegex.test(haystack) && unsupportedRegex.test(haystack);
6023
+ };
6024
+ const getImageNotSupportedMessage = modelName => {
6025
+ const subject = modelName ? `${modelName} does not support image attachments.` : 'This model does not support image attachments.';
6026
+ return `${subject} Choose a vision-capable model like GPT-4o Mini or GPT-4o, or remove the image and try again.`;
6027
+ };
5763
6028
  const getOpenApiErrorMessage = errorResult => {
5764
6029
  switch (errorResult.details) {
5765
6030
  case 'http-error':
@@ -5773,6 +6038,9 @@ const getOpenApiErrorMessage = errorResult => {
5773
6038
  const status = typeof errorResult.statusCode === 'number' ? errorResult.statusCode : 401;
5774
6039
  return `OpenAI request failed (Status ${status}): Invalid API key. Please verify your OpenAI API key in Chat Settings.`;
5775
6040
  }
6041
+ if (isImageNotSupportedError(errorResult)) {
6042
+ return getImageNotSupportedMessage();
6043
+ }
5776
6044
  if (errorResult.statusCode === 429) {
5777
6045
  let prefix = 'OpenAI rate limit exceeded (429)';
5778
6046
  if (hasErrorCode) {
@@ -6221,17 +6489,6 @@ const isOpenRouterModel = (selectedModelId, models) => {
6221
6489
  return selectedModelId.toLowerCase().startsWith('openrouter/');
6222
6490
  };
6223
6491
 
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
6492
  /* eslint-disable prefer-destructuring */
6236
6493
 
6237
6494
  const trailingSlashesRegex = /\/+$/;
@@ -6246,6 +6503,9 @@ const getEffectiveBackendModelId = selectedModelId => {
6246
6503
  }
6247
6504
  return selectedModelId.slice(separatorIndex + 1);
6248
6505
  };
6506
+ const hasImageAttachments = messages => {
6507
+ return messages.some(message => message.attachments?.some(attachment => attachment.displayType === 'image'));
6508
+ };
6249
6509
  const getBackendAssistantText = async (messages, selectedModelId, backendUrl, authAccessToken, systemPrompt) => {
6250
6510
  let response;
6251
6511
  try {
@@ -6293,6 +6553,7 @@ const getAiResponse = async ({
6293
6553
  nextMessageId,
6294
6554
  onDataEvent,
6295
6555
  onEventStreamFinished,
6556
+ onMockOpenApiRequestCaptured,
6296
6557
  onTextChunk,
6297
6558
  onToolCallsChunk,
6298
6559
  openApiApiBaseUrl,
@@ -6368,7 +6629,15 @@ const getAiResponse = async ({
6368
6629
  }
6369
6630
  }
6370
6631
  let text = '';
6371
- if (authEnabled) {
6632
+ const usesOpenApiModel = isOpenApiModel(selectedModelId, models);
6633
+ const usesOpenRouterModel = isOpenRouterModel(selectedModelId, models);
6634
+ const selectedModel = models.find(model => model.id === selectedModelId);
6635
+ const supportsImages = selectedModel?.supportsImages ?? false;
6636
+ const supportsReasoningEffort = selectedModel?.supportsReasoningEffort ?? false;
6637
+ if (hasImageAttachments(messages) && !supportsImages) {
6638
+ text = getImageNotSupportedMessage(selectedModel?.name);
6639
+ }
6640
+ if (!text && authEnabled) {
6372
6641
  if (!backendUrl) {
6373
6642
  text = backendUrlRequiredMessage;
6374
6643
  } else if (authAccessToken) {
@@ -6377,15 +6646,11 @@ const getAiResponse = async ({
6377
6646
  text = backendAccessTokenRequiredMessage;
6378
6647
  }
6379
6648
  }
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
6649
  if (!text && usesOpenApiModel) {
6385
6650
  const safeMaxToolCalls = Math.max(1, maxToolCalls);
6386
6651
  if (useMockApi) {
6387
6652
  const openAiInput = messages.map(message => ({
6388
- content: message.text,
6653
+ content: getChatMessageOpenAiContent(message),
6389
6654
  role: message.role
6390
6655
  }));
6391
6656
  const modelId = getOpenApiModelId(selectedModelId);
@@ -6397,12 +6662,16 @@ const getAiResponse = async ({
6397
6662
  const maxToolIterations = safeMaxToolCalls - 1;
6398
6663
  let previousResponseId;
6399
6664
  for (let i = 0; i <= maxToolIterations; i++) {
6400
- capture({
6665
+ const request = {
6401
6666
  headers,
6402
6667
  method: 'POST',
6403
6668
  payload: getOpenAiParams(openAiInput, modelId, streamingEnabled, passIncludeObfuscation, await getBasicChatTools(agentMode, questionToolEnabled, toolEnablement), agentMode === 'plan' ? false : webSearchEnabled, safeMaxToolCalls, systemPrompt, previousResponseId, reasoningEffort, supportsReasoningEffort),
6404
6669
  url: getOpenApiApiEndpoint(openApiApiBaseUrl)
6405
- });
6670
+ };
6671
+ capture(request);
6672
+ if (onMockOpenApiRequestCaptured) {
6673
+ await Promise.resolve(onMockOpenApiRequestCaptured(request));
6674
+ }
6406
6675
  const result = await getMockOpenApiAssistantText(streamingEnabled, onTextChunk, onToolCallsChunk, onDataEvent, onEventStreamFinished);
6407
6676
  if (result.type !== 'success') {
6408
6677
  text = getOpenApiErrorMessage(result);
@@ -6559,6 +6828,7 @@ const ChatAuthError = 'ChatAuthError';
6559
6828
  const ChatFocus = 'ChatFocus';
6560
6829
  const ChatFocusActions = 'ChatFocusActions';
6561
6830
  const ChatFocusHeader = 'ChatFocusHeader';
6831
+ const ChatFocusMainArea = 'ChatFocusMainArea';
6562
6832
  const ChatFocusProject = 'ChatFocusProject';
6563
6833
  const ChatHeader = 'ChatHeader';
6564
6834
  const ChatHeaderLabel = 'ChatHeaderLabel';
@@ -6595,9 +6865,19 @@ const ChatComposerAttachmentImage = 'ChatComposerAttachmentImage';
6595
6865
  const ChatComposerAttachmentInvalidImage = 'ChatComposerAttachmentInvalidImage';
6596
6866
  const ChatComposerAttachmentLabel = 'ChatComposerAttachmentLabel';
6597
6867
  const ChatComposerAttachmentPreview = 'ChatComposerAttachmentPreview';
6868
+ const ChatComposerAttachmentPreviewOverlay = 'ChatComposerAttachmentPreviewOverlay';
6869
+ const ChatComposerAttachmentPreviewOverlayError = 'ChatComposerAttachmentPreviewOverlayError';
6870
+ const ChatComposerAttachmentPreviewOverlayImage = 'ChatComposerAttachmentPreviewOverlayImage';
6598
6871
  const ChatComposerAttachmentRemoveButton = 'ChatComposerAttachmentRemoveButton';
6599
6872
  const ChatComposerAttachments = 'ChatComposerAttachments';
6600
6873
  const ChatComposerAttachmentTextFile = 'ChatComposerAttachmentTextFile';
6874
+ const ChatAttachment = 'ChatAttachment';
6875
+ const ChatAttachmentImage = 'ChatAttachmentImage';
6876
+ const ChatAttachmentInvalidImage = 'ChatAttachmentInvalidImage';
6877
+ const ChatAttachmentLabel = 'ChatAttachmentLabel';
6878
+ const ChatAttachmentPreview = 'ChatAttachmentPreview';
6879
+ const ChatAttachments = 'ChatAttachments';
6880
+ const ChatAttachmentTextFile = 'ChatAttachmentTextFile';
6601
6881
  const ChatSendArea = 'ChatSendArea';
6602
6882
  const ChatSendAreaBottom = 'ChatSendAreaBottom';
6603
6883
  const ChatSendAreaContent = 'ChatSendAreaContent';
@@ -6633,6 +6913,7 @@ const FileIcon = 'FileIcon';
6633
6913
  const IconButton = 'IconButton';
6634
6914
  const IconButtonDisabled = 'IconButtonDisabled';
6635
6915
  const ImageElement = 'ImageElement';
6916
+ const ImageErrorMessage = 'ImageErrorMessage';
6636
6917
  const InputBox = 'InputBox';
6637
6918
  const Insertion = 'Insertion';
6638
6919
  const Label = 'Label';
@@ -6646,6 +6927,7 @@ const MaskIconAccount = 'MaskIconAccount';
6646
6927
  const MaskIconAdd = 'MaskIconAdd';
6647
6928
  const MaskIconArrowLeft = 'MaskIconArrowLeft';
6648
6929
  const MaskIconChevronDown = 'MaskIconChevronDown';
6930
+ const MaskIconChevronRight = 'MaskIconChevronRight';
6649
6931
  const MaskIconChevronUp = 'MaskIconChevronUp';
6650
6932
  const MaskIconClose = 'MaskIconClose';
6651
6933
  const MaskIconDebugPause = 'MaskIconDebugPause';
@@ -8245,15 +8527,6 @@ const getSlashCommandHandler = command => {
8245
8527
  return slashCommandRegistry[command];
8246
8528
  };
8247
8529
 
8248
- const focusInput = state => {
8249
- return {
8250
- ...state,
8251
- focus: 'composer',
8252
- focused: true,
8253
- listFocusedIndex: -1
8254
- };
8255
- };
8256
-
8257
8530
  const withClearedComposer = state => {
8258
8531
  return focusInput({
8259
8532
  ...state,
@@ -8407,12 +8680,6 @@ const getMentionContextMessage = async value => {
8407
8680
  };
8408
8681
  };
8409
8682
 
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
8683
  const slashCommandRegex = /^\/([a-z][a-z0-9-]*)(?:\s+.*)?$/i;
8417
8684
  const getSlashCommand = value => {
8418
8685
  const trimmed = value.trim();
@@ -8690,6 +8957,19 @@ const withUpdatedMessageScrollTop = state => {
8690
8957
  };
8691
8958
  };
8692
8959
  const workspaceUriPlaceholder = '{{workspaceUri}}';
8960
+ const clearComposerAttachments = async (sessionId, attachmentIds) => {
8961
+ if (!sessionId) {
8962
+ return;
8963
+ }
8964
+ for (const attachmentId of attachmentIds) {
8965
+ await appendChatViewEvent({
8966
+ attachmentId,
8967
+ sessionId,
8968
+ timestamp: new Date().toISOString(),
8969
+ type: 'chat-attachment-removed'
8970
+ });
8971
+ }
8972
+ };
8693
8973
  const getCurrentDate = () => {
8694
8974
  return new Date().toISOString().slice(0, 10);
8695
8975
  };
@@ -8782,7 +9062,12 @@ const handleSubmit = async state => {
8782
9062
  minute: '2-digit'
8783
9063
  });
8784
9064
  const userMessageId = crypto.randomUUID();
9065
+ const composerAttachments = state.composerAttachments.length > 0 ? state.composerAttachments : await getComposerAttachments(state.selectedSessionId);
9066
+ await clearComposerAttachments(state.selectedSessionId, composerAttachments.map(attachment => attachment.attachmentId));
8785
9067
  const userMessage = {
9068
+ ...(composerAttachments.length > 0 ? {
9069
+ attachments: composerAttachments
9070
+ } : {}),
8786
9071
  id: userMessageId,
8787
9072
  role: 'user',
8788
9073
  text: userText,
@@ -8897,6 +9182,9 @@ const handleSubmit = async state => {
8897
9182
  latestState: optimisticState,
8898
9183
  previousState: optimisticState
8899
9184
  };
9185
+ let {
9186
+ mockOpenApiRequests
9187
+ } = optimisticState;
8900
9188
  const selectedOptimisticSession = optimisticState.sessions.find(session => session.id === optimisticState.selectedSessionId);
8901
9189
  const systemPrompt = getEffectiveSystemPrompt(optimisticState, selectedOptimisticSession);
8902
9190
  const workspaceUri = getWorkspaceUri(optimisticState, selectedOptimisticSession);
@@ -8939,6 +9227,9 @@ const handleSubmit = async state => {
8939
9227
  value: '[DONE]'
8940
9228
  });
8941
9229
  },
9230
+ onMockOpenApiRequestCaptured: async request => {
9231
+ mockOpenApiRequests = [...mockOpenApiRequests, request];
9232
+ },
8942
9233
  ...(handleTextChunkFunctionRef ? {
8943
9234
  onTextChunk: handleTextChunkFunctionRef
8944
9235
  } : {}),
@@ -8995,6 +9286,7 @@ const handleSubmit = async state => {
8995
9286
  }
8996
9287
  return withUpdatedMessageScrollTop(focusInput({
8997
9288
  ...latestState,
9289
+ mockOpenApiRequests,
8998
9290
  nextMessageId: latestState.nextMessageId + 1,
8999
9291
  parsedMessages: finalParsedMessages,
9000
9292
  sessions: updatedSessions
@@ -9056,6 +9348,8 @@ const openAgentModePicker = state => {
9056
9348
  return {
9057
9349
  ...state,
9058
9350
  agentModePickerOpen,
9351
+ focus: agentModePickerOpen ? 'picker-list' : state.focus,
9352
+ focused: agentModePickerOpen ? true : state.focused,
9059
9353
  modelPickerOpen: false,
9060
9354
  modelPickerSearchValue: '',
9061
9355
  reasoningEffortPickerOpen: false,
@@ -9074,6 +9368,8 @@ const openReasoningEffortPicker = state => {
9074
9368
  return {
9075
9369
  ...state,
9076
9370
  agentModePickerOpen: false,
9371
+ focus: reasoningEffortPickerOpen ? 'picker-list' : state.focus,
9372
+ focused: reasoningEffortPickerOpen ? true : state.focused,
9077
9373
  modelPickerOpen: false,
9078
9374
  modelPickerSearchValue: '',
9079
9375
  reasoningEffortPickerOpen,
@@ -9099,6 +9395,8 @@ const openRunModePicker = state => {
9099
9395
  return {
9100
9396
  ...state,
9101
9397
  agentModePickerOpen: false,
9398
+ focus: runModePickerOpen ? 'picker-list' : state.focus,
9399
+ focused: runModePickerOpen ? true : state.focused,
9102
9400
  modelPickerOpen: false,
9103
9401
  modelPickerSearchValue: '',
9104
9402
  reasoningEffortPickerOpen: false,
@@ -9360,8 +9658,16 @@ const handleClick = async (state, name, id = '', eventX = 0, eventY = 0) => {
9360
9658
  }
9361
9659
  case name === SessionDelete:
9362
9660
  return deleteSession(state, id);
9661
+ case name === ProjectDelete:
9662
+ return deleteProject(state, id);
9363
9663
  case name === Send:
9364
9664
  return handleClickSend(state);
9665
+ case name === ScrollDown:
9666
+ return {
9667
+ ...state,
9668
+ messagesAutoScrollEnabled: true,
9669
+ messagesScrollTop: getNextAutoScrollTop(state.messagesScrollTop)
9670
+ };
9365
9671
  case name === SaveOpenRouterApiKey:
9366
9672
  return handleClickSaveOpenRouterApiKey(state);
9367
9673
  case name === SaveOpenApiApiKey:
@@ -9592,6 +9898,7 @@ const handleDropFiles = async (state, name, fileHandles = []) => {
9592
9898
  const {
9593
9899
  composerDropActive,
9594
9900
  composerDropEnabled,
9901
+ nextAttachmentId,
9595
9902
  selectedSessionId,
9596
9903
  width
9597
9904
  } = state;
@@ -9614,9 +9921,10 @@ const handleDropFiles = async (state, name, fileHandles = []) => {
9614
9921
  const nextAttachments = [];
9615
9922
  for (const droppedFileHandle of droppedFileHandles) {
9616
9923
  const file = await droppedFileHandle.getFile();
9617
- const attachmentId = crypto.randomUUID();
9924
+ const attachmentId = `attachment-${nextAttachmentId + nextAttachments.length}`;
9618
9925
  const displayType = await getComposerAttachmentDisplayType(file, file.name, file.type);
9619
9926
  const previewSrc = await getComposerAttachmentPreviewSrc(file, displayType, file.type);
9927
+ const textContent = await getComposerAttachmentTextContent(file, displayType);
9620
9928
  await appendChatViewEvent({
9621
9929
  attachmentId,
9622
9930
  blob: file,
@@ -9635,13 +9943,27 @@ const handleDropFiles = async (state, name, fileHandles = []) => {
9635
9943
  ...(previewSrc ? {
9636
9944
  previewSrc
9637
9945
  } : {}),
9638
- size: file.size
9946
+ size: file.size,
9947
+ ...(typeof textContent === 'string' ? {
9948
+ textContent
9949
+ } : {})
9639
9950
  });
9640
9951
  }
9641
9952
  return {
9642
9953
  ...nextState,
9643
9954
  composerAttachments: [...nextState.composerAttachments, ...nextAttachments],
9644
- composerAttachmentsHeight: getComposerAttachmentsHeight([...nextState.composerAttachments, ...nextAttachments], width)
9955
+ composerAttachmentsHeight: getComposerAttachmentsHeight([...nextState.composerAttachments, ...nextAttachments], width),
9956
+ nextAttachmentId: nextAttachmentId + nextAttachments.length
9957
+ };
9958
+ };
9959
+
9960
+ const handleErrorComposerAttachmentPreviewOverlay = async state => {
9961
+ if (!state.composerAttachmentPreviewOverlayAttachmentId || state.composerAttachmentPreviewOverlayError) {
9962
+ return state;
9963
+ }
9964
+ return {
9965
+ ...state,
9966
+ composerAttachmentPreviewOverlayError: true
9645
9967
  };
9646
9968
  };
9647
9969
 
@@ -9845,6 +10167,60 @@ const handleModelChange = async (state, value) => {
9845
10167
  };
9846
10168
  };
9847
10169
 
10170
+ const hideComposerAttachmentPreviewOverlay = async state => {
10171
+ if (!state.composerAttachmentPreviewOverlayAttachmentId && !state.composerAttachmentPreviewOverlayError) {
10172
+ return state;
10173
+ }
10174
+ return {
10175
+ ...state,
10176
+ composerAttachmentPreviewOverlayAttachmentId: '',
10177
+ composerAttachmentPreviewOverlayError: false
10178
+ };
10179
+ };
10180
+
10181
+ const isCurrentAttachmentTarget = (name, attachmentId) => {
10182
+ return name === getComposerAttachmentInputName(attachmentId) || isComposerAttachmentRemoveInputName(name) && getAttachmentIdFromComposerAttachmentRemoveInputName(name) === attachmentId;
10183
+ };
10184
+ const handleMouseOut = async (state, name, relatedName = '') => {
10185
+ const {
10186
+ composerAttachmentPreviewOverlayAttachmentId
10187
+ } = state;
10188
+ if (!composerAttachmentPreviewOverlayAttachmentId) {
10189
+ return state;
10190
+ }
10191
+ const isLeavingOverlay = isComposerAttachmentPreviewOverlayInputName(name);
10192
+ if (!isLeavingOverlay) {
10193
+ return state;
10194
+ }
10195
+ if (isComposerAttachmentPreviewOverlayInputName(relatedName) || isCurrentAttachmentTarget(relatedName, composerAttachmentPreviewOverlayAttachmentId)) {
10196
+ return state;
10197
+ }
10198
+ return hideComposerAttachmentPreviewOverlay(state);
10199
+ };
10200
+
10201
+ const showComposerAttachmentPreviewOverlay = async (state, attachmentId) => {
10202
+ const attachment = state.composerAttachments.find(item => item.attachmentId === attachmentId) ?? state.composerAttachments.find(item => item.displayType === 'image' && !!item.previewSrc);
10203
+ if (!attachment || attachment.displayType !== 'image' || !attachment.previewSrc) {
10204
+ return state;
10205
+ }
10206
+ if (state.composerAttachmentPreviewOverlayAttachmentId === attachmentId && !state.composerAttachmentPreviewOverlayError) {
10207
+ return state;
10208
+ }
10209
+ return {
10210
+ ...state,
10211
+ composerAttachmentPreviewOverlayAttachmentId: attachmentId,
10212
+ composerAttachmentPreviewOverlayError: false
10213
+ };
10214
+ };
10215
+
10216
+ const handleMouseOver = async (state, name) => {
10217
+ if (!isComposerAttachmentInputName(name)) {
10218
+ return state;
10219
+ }
10220
+ const attachmentId = getAttachmentIdFromComposerAttachmentInputName(name);
10221
+ return showComposerAttachmentPreviewOverlay(state, attachmentId);
10222
+ };
10223
+
9848
10224
  const handleNewline = async state => {
9849
10225
  const {
9850
10226
  composerValue
@@ -9870,7 +10246,7 @@ const handleProjectAddButtonContextMenu = async (state, button, x, y) => {
9870
10246
  return state;
9871
10247
  };
9872
10248
 
9873
- const getProjectIdAtIndex = (state, index) => {
10249
+ const getProjectAtIndex = (state, index) => {
9874
10250
  const {
9875
10251
  projectExpandedIds,
9876
10252
  projects,
@@ -9880,7 +10256,7 @@ const getProjectIdAtIndex = (state, index) => {
9880
10256
  let currentIndex = 0;
9881
10257
  for (const project of projects) {
9882
10258
  if (currentIndex === index) {
9883
- return project.id;
10259
+ return project;
9884
10260
  }
9885
10261
  currentIndex++;
9886
10262
  if (projectExpandedIds.includes(project.id)) {
@@ -9890,13 +10266,13 @@ const getProjectIdAtIndex = (state, index) => {
9890
10266
  continue;
9891
10267
  }
9892
10268
  if (currentIndex === index) {
9893
- return project.id;
10269
+ return project;
9894
10270
  }
9895
10271
  currentIndex++;
9896
10272
  }
9897
10273
  }
9898
10274
  }
9899
- return '';
10275
+ return undefined;
9900
10276
  };
9901
10277
  const handleProjectListContextMenu = async (state, button, x, y) => {
9902
10278
  const {
@@ -9909,13 +10285,14 @@ const handleProjectListContextMenu = async (state, button, x, y) => {
9909
10285
  if (index < 0) {
9910
10286
  return state;
9911
10287
  }
9912
- const projectId = getProjectIdAtIndex(state, index);
9913
- if (!projectId) {
10288
+ const project = getProjectAtIndex(state, index);
10289
+ if (!project) {
9914
10290
  return state;
9915
10291
  }
9916
10292
  await showContextMenu2(uid, MenuChatProjectList, x, y, {
10293
+ canRemoveProject: project.name !== '_blank',
9917
10294
  menuId: MenuChatProjectList,
9918
- projectId
10295
+ projectId: project.id
9919
10296
  });
9920
10297
  return state;
9921
10298
  };
@@ -10038,6 +10415,23 @@ const getSavedChatListScrollTop = savedState => {
10038
10415
  return chatListScrollTop;
10039
10416
  };
10040
10417
 
10418
+ const getSavedComposerSelection = (savedState, composerValue) => {
10419
+ if (!isObject$1(savedState)) {
10420
+ return undefined;
10421
+ }
10422
+ const {
10423
+ composerSelectionEnd,
10424
+ composerSelectionStart
10425
+ } = savedState;
10426
+ if (typeof composerSelectionStart !== 'number') {
10427
+ return undefined;
10428
+ }
10429
+ if (typeof composerSelectionEnd !== 'number') {
10430
+ return undefined;
10431
+ }
10432
+ return getNormalizedComposerSelection(composerValue, composerSelectionStart, composerSelectionEnd);
10433
+ };
10434
+
10041
10435
  const getSavedComposerValue = savedState => {
10042
10436
  if (!isObject$1(savedState)) {
10043
10437
  return undefined;
@@ -10339,16 +10733,25 @@ const loadReasoningPickerEnabled = async () => {
10339
10733
  }
10340
10734
  };
10341
10735
 
10342
- const loadSearchEnabled = async () => {
10736
+ const loadScrollDownButtonEnabled = async () => {
10343
10737
  try {
10344
- const savedSearchEnabled = await get('chatView.searchEnabled');
10345
- return typeof savedSearchEnabled === 'boolean' ? savedSearchEnabled : false;
10738
+ const savedScrollDownButtonEnabled = await get('chatView.scrollDownButtonEnabled');
10739
+ return typeof savedScrollDownButtonEnabled === 'boolean' ? savedScrollDownButtonEnabled : false;
10346
10740
  } catch {
10347
10741
  return false;
10348
10742
  }
10349
10743
  };
10350
10744
 
10351
- const loadStreamingEnabled = async () => {
10745
+ const loadSearchEnabled = async () => {
10746
+ try {
10747
+ const savedSearchEnabled = await get('chatView.searchEnabled');
10748
+ return typeof savedSearchEnabled === 'boolean' ? savedSearchEnabled : false;
10749
+ } catch {
10750
+ return false;
10751
+ }
10752
+ };
10753
+
10754
+ const loadStreamingEnabled = async () => {
10352
10755
  try {
10353
10756
  const savedStreamingEnabled = await get('chatView.streamingEnabled');
10354
10757
  return typeof savedStreamingEnabled === 'boolean' ? savedStreamingEnabled : true;
@@ -10430,7 +10833,7 @@ const loadVoiceDictationEnabled = async () => {
10430
10833
  };
10431
10834
 
10432
10835
  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()]);
10836
+ 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
10837
  return {
10435
10838
  aiSessionTitleGenerationEnabled,
10436
10839
  authAccessToken,
@@ -10444,6 +10847,7 @@ const loadPreferences = async () => {
10444
10847
  openRouterApiKey,
10445
10848
  passIncludeObfuscation,
10446
10849
  reasoningPickerEnabled,
10850
+ scrollDownButtonEnabled,
10447
10851
  searchEnabled,
10448
10852
  streamingEnabled,
10449
10853
  todoListToolEnabled,
@@ -10501,6 +10905,9 @@ const loadContent = async (state, savedState) => {
10501
10905
  const savedSelectedModelId = getSavedSelectedModelId(savedState);
10502
10906
  const savedViewMode = getSavedViewMode(savedState);
10503
10907
  const savedComposerValue = getSavedComposerValue(savedState);
10908
+ const composerValue = savedComposerValue ?? state.composerValue;
10909
+ const savedComposerSelection = getSavedComposerSelection(savedState, composerValue);
10910
+ const [composerSelectionStart, composerSelectionEnd] = savedComposerSelection ?? [state.composerSelectionStart, state.composerSelectionEnd];
10504
10911
  const {
10505
10912
  aiSessionTitleGenerationEnabled,
10506
10913
  authAccessToken,
@@ -10514,6 +10921,7 @@ const loadContent = async (state, savedState) => {
10514
10921
  openRouterApiKey,
10515
10922
  passIncludeObfuscation,
10516
10923
  reasoningPickerEnabled,
10924
+ scrollDownButtonEnabled,
10517
10925
  searchEnabled,
10518
10926
  streamingEnabled,
10519
10927
  todoListToolEnabled,
@@ -10592,7 +11000,9 @@ const loadContent = async (state, savedState) => {
10592
11000
  composerAttachmentsHeight: getComposerAttachmentsHeight(composerAttachments, state.width),
10593
11001
  composerDropActive: false,
10594
11002
  composerDropEnabled,
10595
- composerValue: savedComposerValue ?? state.composerValue,
11003
+ composerSelectionEnd,
11004
+ composerSelectionStart,
11005
+ composerValue,
10596
11006
  emitStreamingFunctionCallEvents,
10597
11007
  initial: false,
10598
11008
  lastNormalViewMode,
@@ -10614,6 +11024,7 @@ const loadContent = async (state, savedState) => {
10614
11024
  reasoningEffortPickerOpen: false,
10615
11025
  reasoningPickerEnabled,
10616
11026
  runModePickerOpen: false,
11027
+ scrollDownButtonEnabled,
10617
11028
  searchEnabled,
10618
11029
  searchFieldVisible: false,
10619
11030
  searchValue: '',
@@ -10671,7 +11082,7 @@ const mockOpenApiRequestGetAll = _state => {
10671
11082
  };
10672
11083
 
10673
11084
  const mockOpenApiRequestReset = state => {
10674
- reset$1();
11085
+ reset$2();
10675
11086
  return state;
10676
11087
  };
10677
11088
 
@@ -10696,10 +11107,33 @@ const mockOpenApiStreamPushChunk = (state, chunk) => {
10696
11107
  };
10697
11108
 
10698
11109
  const mockOpenApiStreamReset = state => {
10699
- reset$2();
11110
+ reset$1();
10700
11111
  return state;
10701
11112
  };
10702
11113
 
11114
+ const openMockProject = async (state, projectId, projectName, projectUri) => {
11115
+ if (!projectId || !projectName) {
11116
+ return state;
11117
+ }
11118
+ const project = {
11119
+ id: projectId,
11120
+ name: projectName,
11121
+ uri: projectUri
11122
+ };
11123
+ const projects = state.projects.some(candidate => candidate.id === projectId) ? state.projects.map(candidate => {
11124
+ if (candidate.id !== projectId) {
11125
+ return candidate;
11126
+ }
11127
+ return project;
11128
+ }) : [...state.projects, project];
11129
+ return {
11130
+ ...state,
11131
+ projectExpandedIds: state.projectExpandedIds.includes(projectId) ? state.projectExpandedIds : [...state.projectExpandedIds, projectId],
11132
+ projects,
11133
+ selectedProjectId: projectId
11134
+ };
11135
+ };
11136
+
10703
11137
  const openMockSession = async (state, mockSessionId, mockChatMessages) => {
10704
11138
  const {
10705
11139
  sessions: currentSessions
@@ -10744,7 +11178,7 @@ const pasteInput = async state => {
10744
11178
  };
10745
11179
 
10746
11180
  const registerMockResponse = (state, mockResponse) => {
10747
- reset$2();
11181
+ reset$1();
10748
11182
  pushChunk(mockResponse.text);
10749
11183
  finish();
10750
11184
  return state;
@@ -10852,6 +11286,93 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
10852
11286
  border-color: var(--vscode-charts-green, var(--vscode-widget-border, var(--vscode-panel-border)));
10853
11287
  }
10854
11288
 
11289
+ .ChatAttachments{
11290
+ display: flex;
11291
+ flex-wrap: wrap;
11292
+ gap: 8px;
11293
+ margin-top: 8px;
11294
+ }
11295
+
11296
+ .ChatAttachment{
11297
+ align-items: center;
11298
+ display: inline-flex;
11299
+ gap: 6px;
11300
+ max-width: 100%;
11301
+ min-width: 0;
11302
+ overflow: hidden;
11303
+ border-radius: 999px;
11304
+ border: 1px solid var(--vscode-widget-border, var(--vscode-panel-border));
11305
+ padding: 4px 10px;
11306
+ background: var(--vscode-badge-background, color-mix(in srgb, var(--vscode-editor-background) 88%, white));
11307
+ color: var(--vscode-badge-foreground, var(--vscode-foreground));
11308
+ font-size: 12px;
11309
+ }
11310
+
11311
+ .ChatAttachmentLabel{
11312
+ min-width: 0;
11313
+ overflow: hidden;
11314
+ text-overflow: ellipsis;
11315
+ white-space: nowrap;
11316
+ }
11317
+
11318
+ .ChatAttachmentPreview{
11319
+ width: 20px;
11320
+ height: 20px;
11321
+ flex: none;
11322
+ border-radius: 4px;
11323
+ object-fit: cover;
11324
+ }
11325
+
11326
+ .ChatAttachmentImage{
11327
+ border-color: var(--vscode-charts-blue);
11328
+ }
11329
+
11330
+ .ChatAttachmentInvalidImage{
11331
+ border-color: var(--vscode-inputValidation-errorBorder, var(--vscode-errorForeground));
11332
+ color: var(--vscode-errorForeground, var(--vscode-foreground));
11333
+ }
11334
+
11335
+ .ChatAttachmentTextFile{
11336
+ border-color: var(--vscode-charts-green, var(--vscode-widget-border, var(--vscode-panel-border)));
11337
+ }
11338
+
11339
+ .Chat{
11340
+ position: relative;
11341
+ }
11342
+
11343
+ .ChatComposerAttachmentPreviewOverlay{
11344
+ position: absolute;
11345
+ left: calc(var(--ChatSendAreaPaddingLeft) + 8px);
11346
+ bottom: calc(var(--ChatSendAreaHeight) - 12px);
11347
+ z-index: 6;
11348
+ width: 240px;
11349
+ min-width: 160px;
11350
+ min-height: 160px;
11351
+ max-width: calc(100% - var(--ChatSendAreaPaddingLeft) - var(--ChatSendAreaPaddingRight) - 16px);
11352
+ display: flex;
11353
+ align-items: center;
11354
+ justify-content: center;
11355
+ border: 1px solid var(--vscode-widget-border, var(--vscode-panel-border));
11356
+ border-radius: 12px;
11357
+ background: var(--vscode-editorWidget-background, var(--vscode-editor-background));
11358
+ box-shadow: 0 12px 28px color-mix(in srgb, var(--vscode-editor-background) 45%, black);
11359
+ overflow: hidden;
11360
+ }
11361
+
11362
+ .ChatComposerAttachmentPreviewOverlayImage{
11363
+ display: block;
11364
+ width: 100%;
11365
+ max-width: 100%;
11366
+ max-height: min(320px, calc(100vh - 200px));
11367
+ object-fit: contain;
11368
+ background: color-mix(in srgb, var(--vscode-editor-background) 88%, black);
11369
+ }
11370
+
11371
+ .ChatComposerAttachmentPreviewOverlayError{
11372
+ padding: 12px;
11373
+ color: var(--vscode-errorForeground, var(--vscode-foreground));
11374
+ }
11375
+
10855
11376
  .CustomSelectContainer{
10856
11377
  position: relative;
10857
11378
  min-width: 0;
@@ -10889,6 +11410,13 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
10889
11410
  color: var(--vscode-textLink-foreground);
10890
11411
  }
10891
11412
 
11413
+ .ProjectListChevron{
11414
+ display: inline-block;
11415
+ flex: none;
11416
+ height: 16px;
11417
+ width: 16px;
11418
+ }
11419
+
10892
11420
  .ChatOrderedListItem{
10893
11421
  align-items: flex-start;
10894
11422
  display: flex;
@@ -10931,6 +11459,22 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
10931
11459
  min-width: 0;
10932
11460
  }
10933
11461
 
11462
+ .ChatMessages > .Message{
11463
+ display: flex;
11464
+ }
11465
+
11466
+ .ChatMessages > .MessageUser{
11467
+ justify-content: flex-end;
11468
+ }
11469
+
11470
+ .ChatMessages > .MessageAssistant{
11471
+ justify-content: flex-start;
11472
+ }
11473
+
11474
+ .ChatMessages > .MessageUser .ChatAttachments{
11475
+ justify-content: flex-end;
11476
+ }
11477
+
10934
11478
  .ChatFocus .ChatMessages > .Message{
10935
11479
  inline-size: fit-content;
10936
11480
  max-inline-size: min(100%, var(--ChatFocusContentMaxWidth));
@@ -10940,6 +11484,24 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
10940
11484
  max-inline-size: 100%;
10941
11485
  }
10942
11486
 
11487
+
11488
+ .Viewlet.Chat.ChatFocus{
11489
+ display: flex !important;
11490
+ min-width: 0;
11491
+ }
11492
+
11493
+ .ChatFocusMainArea{
11494
+ display: flex;
11495
+ flex: 1;
11496
+ flex-direction: column;
11497
+ min-width: 0;
11498
+ }
11499
+
11500
+ .ChatFocusMainArea > .ChatMessages{
11501
+ flex: 1;
11502
+ min-height: 0;
11503
+ }
11504
+
10943
11505
  `;
10944
11506
  return `${baseCss}
10945
11507
 
@@ -10977,6 +11539,7 @@ const renderCss = (oldState, newState) => {
10977
11539
  return [SetCss, uid, css];
10978
11540
  };
10979
11541
 
11542
+ const pickerListSelector = '.ChatOverlays .ChatModelPickerList';
10980
11543
  const getFocusSelector = state => {
10981
11544
  const {
10982
11545
  focus,
@@ -11002,6 +11565,8 @@ const getFocusSelector = state => {
11002
11565
  }
11003
11566
  case 'model-picker-input':
11004
11567
  return `[name="${ModelPickerSearch}"]`;
11568
+ case 'picker-list':
11569
+ return pickerListSelector;
11005
11570
  case 'send-button':
11006
11571
  return '[name="send"]';
11007
11572
  default:
@@ -11012,6 +11577,9 @@ const renderFocus = (oldState, newState) => {
11012
11577
  if (newState.modelPickerOpen && !oldState.modelPickerOpen) {
11013
11578
  return [FocusSelector, `[name="${ModelPickerSearch}"]`];
11014
11579
  }
11580
+ if (newState.agentModePickerOpen && !oldState.agentModePickerOpen || newState.runModePickerOpen && !oldState.runModePickerOpen || newState.reasoningEffortPickerOpen && !oldState.reasoningEffortPickerOpen) {
11581
+ return [FocusSelector, pickerListSelector];
11582
+ }
11015
11583
  const selector = getFocusSelector(newState);
11016
11584
  return [FocusSelector, selector];
11017
11585
  };
@@ -11029,6 +11597,8 @@ const renderFocusContext = (oldState, newState) => {
11029
11597
  const HandleListContextMenu = 2;
11030
11598
  const HandleFocus = 3;
11031
11599
  const HandleInput = 4;
11600
+ const HandleMouseOut = 5;
11601
+ const HandleMouseOver = 7;
11032
11602
  const HandleClick = 11;
11033
11603
  const HandleKeyDown = 12;
11034
11604
  const HandleClickClose = 13;
@@ -11076,6 +11646,7 @@ const HandlePointerUpModelPickerList = 55;
11076
11646
  const HandleClickReasoningEffortPickerToggle = 56;
11077
11647
  const HandleClickAgentModePickerToggle = 57;
11078
11648
  const HandleContextMenuChatImageAttachment = 58;
11649
+ const HandleErrorComposerAttachmentPreviewOverlay = 59;
11079
11650
 
11080
11651
  const getAddContextButtonDom = () => {
11081
11652
  return [{
@@ -11192,6 +11763,8 @@ const getReasoningEffortPickerVirtualDom = (selectedReasoningEffort, reasoningEf
11192
11763
  }, {
11193
11764
  childCount: reasoningEfforts.length,
11194
11765
  className: ChatModelPickerList,
11766
+ name: PickerList,
11767
+ tabIndex: -1,
11195
11768
  type: Ul
11196
11769
  }, ...getReasoningEffortOptionsVirtualDom(selectedReasoningEffort)] : [])];
11197
11770
  };
@@ -11200,6 +11773,21 @@ const getRunModePickerVirtualDom = (selectedRunMode, runModePickerOpen) => {
11200
11773
  return getCustomSelectPickerToggleVirtualDom(selectedRunMode, RunModePickerToggle, runModePickerOpen, HandleClickRunModePickerToggle);
11201
11774
  };
11202
11775
 
11776
+ const getScrollDownButtonDom = () => {
11777
+ return [{
11778
+ childCount: 1,
11779
+ className: mergeClassNames(Button, ButtonSecondary),
11780
+ inputType: 'button',
11781
+ name: ScrollDown,
11782
+ onClick: HandleClick,
11783
+ title: scrollDown(),
11784
+ type: Button$1
11785
+ }, {
11786
+ text: scrollDown(),
11787
+ type: Text
11788
+ }];
11789
+ };
11790
+
11203
11791
  const getSendButtonClassName = isSendDisabled => {
11204
11792
  return mergeClassNames(IconButton, isSendDisabled ? SendButtonDisabled : '');
11205
11793
  };
@@ -11375,10 +11963,15 @@ const getComposerAttachmentsDom = composerAttachments => {
11375
11963
  childCount: 1 + (removeButtonDom.length > 0 ? 1 : 0) + previewDom.length,
11376
11964
  className: mergeClassNames(ChatComposerAttachment, getComposerAttachmentClassName(attachment.displayType)),
11377
11965
  name: getComposerAttachmentInputName(attachment.attachmentId),
11966
+ onMouseOut: HandleMouseOut,
11967
+ onMouseOver: HandleMouseOver,
11968
+ onPointerOut: HandleMouseOut,
11969
+ onPointerOver: HandleMouseOver,
11378
11970
  type: Div
11379
11971
  }, ...removeButtonDom, ...previewDom, {
11380
11972
  childCount: 1,
11381
11973
  className: ChatComposerAttachmentLabel,
11974
+ name: getComposerAttachmentInputName(attachment.attachmentId),
11382
11975
  type: Span
11383
11976
  }, {
11384
11977
  text: `${getComposerAttachmentLabel(attachment.displayType)} · ${attachment.name}`,
@@ -11400,11 +11993,12 @@ const getComposerTextAreaDom = () => {
11400
11993
  type: TextArea
11401
11994
  };
11402
11995
  };
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) => {
11996
+ 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
11997
  const isSendDisabled = composerValue.trim() === '';
11405
11998
  const showAgentModePicker = hasSpaceForAgentModePicker;
11406
11999
  const showResponsiveRunModePicker = showRunMode && hasSpaceForRunModePicker;
11407
- const bottomControlsCount = 2 + (usageOverviewEnabled ? 1 : 0) + (addContextButtonEnabled ? 1 : 0) + (showCreatePullRequestButton ? 1 : 0) + (voiceDictationEnabled ? 1 : 0);
12000
+ const showScrollDownButton = scrollDownButtonEnabled && !messagesAutoScrollEnabled;
12001
+ const bottomControlsCount = 2 + (usageOverviewEnabled ? 1 : 0) + (addContextButtonEnabled ? 1 : 0) + (showCreatePullRequestButton ? 1 : 0) + (voiceDictationEnabled ? 1 : 0) + (showScrollDownButton ? 1 : 0);
11408
12002
  const primaryControlsCount = 1 + (showAgentModePicker ? 1 : 0) + (reasoningPickerEnabled ? 1 : 0) + (showResponsiveRunModePicker ? 1 : 0);
11409
12003
  const hasTodoList = todoListToolEnabled && todoListItems.length > 0;
11410
12004
  const hasComposerAttachments = composerAttachments.length > 0;
@@ -11428,7 +12022,7 @@ const getChatSendAreaDom = (composerValue, composerAttachments, agentMode, agent
11428
12022
  className: ChatSendAreaPrimaryControls,
11429
12023
  role: 'toolbar',
11430
12024
  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)];
12025
+ }, ...(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
12026
  };
11433
12027
 
11434
12028
  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;';
@@ -11482,6 +12076,10 @@ const getChatHeaderDomFocusMode = (selectedSessionTitle, selectedProjectName) =>
11482
12076
  }, ...items.flatMap(([label, name]) => getFocusHeaderActionButtonDom(label, name))];
11483
12077
  };
11484
12078
 
12079
+ const getAgentModeOptionsVirtualDom = selectedAgentMode => {
12080
+ return agentModes.flatMap(agentMode => getCustomSelectOptionVirtualDom(getAgentModePickerItemInputName(agentMode), getAgentModeLabel(agentMode), agentMode === selectedAgentMode));
12081
+ };
12082
+
11485
12083
  const getCustomSelectPopOverVirtualDom = (optionCount, height, optionNodes, containerClassName = '', popOverClassName = '') => {
11486
12084
  return [{
11487
12085
  childCount: 1,
@@ -11495,14 +12093,13 @@ const getCustomSelectPopOverVirtualDom = (optionCount, height, optionNodes, cont
11495
12093
  }, {
11496
12094
  childCount: optionCount,
11497
12095
  className: ChatModelPickerList,
12096
+ name: PickerList,
12097
+ tabIndex: -1,
11498
12098
  type: Ul
11499
12099
  }, ...optionNodes];
11500
12100
  };
11501
12101
 
11502
12102
  const agentModePickerHeight = agentModes.length * 28;
11503
- const getAgentModeOptionsVirtualDom = selectedAgentMode => {
11504
- return agentModes.flatMap(agentMode => getCustomSelectOptionVirtualDom(getAgentModePickerItemInputName(agentMode), getAgentModeLabel(agentMode), agentMode === selectedAgentMode));
11505
- };
11506
12103
  const getAgentModePickerPopOverVirtualDom = selectedAgentMode => {
11507
12104
  return getCustomSelectPopOverVirtualDom(agentModes.length, agentModePickerHeight, getAgentModeOptionsVirtualDom(selectedAgentMode));
11508
12105
  };
@@ -11573,6 +12170,42 @@ const getChatModelPickerPopOverVirtualDom = (models, selectedModelId, modelPicke
11573
12170
  }, ...getModelPickerHeaderDom(modelPickerSearchValue), ...getChatModelListVirtualDom(visibleModels, selectedModelId)];
11574
12171
  };
11575
12172
 
12173
+ const getComposerAttachmentPreviewOverlayVirtualDom = (composerAttachments, attachmentId, hasError) => {
12174
+ if (!attachmentId) {
12175
+ return [];
12176
+ }
12177
+ const attachment = composerAttachments.find(item => item.attachmentId === attachmentId);
12178
+ if (!attachment || attachment.displayType !== 'image' || !attachment.previewSrc) {
12179
+ return [];
12180
+ }
12181
+ return [{
12182
+ childCount: 1,
12183
+ className: ChatComposerAttachmentPreviewOverlay,
12184
+ name: ComposerAttachmentPreviewOverlay,
12185
+ onMouseOut: HandleMouseOut,
12186
+ onMouseOver: HandleMouseOver,
12187
+ onPointerOut: HandleMouseOut,
12188
+ onPointerOver: HandleMouseOver,
12189
+ type: Div
12190
+ }, ...(hasError ? [{
12191
+ childCount: 1,
12192
+ className: mergeClassNames(ChatComposerAttachmentPreviewOverlayError, ImageErrorMessage),
12193
+ name: ComposerAttachmentPreviewOverlay,
12194
+ type: Div
12195
+ }, {
12196
+ text: imageCouldNotBeLoaded(),
12197
+ type: Text
12198
+ }] : [{
12199
+ alt: `Large image preview for ${attachment.name}`,
12200
+ childCount: 0,
12201
+ className: ChatComposerAttachmentPreviewOverlayImage,
12202
+ name: ComposerAttachmentPreviewOverlay,
12203
+ onError: HandleErrorComposerAttachmentPreviewOverlay,
12204
+ src: attachment.previewSrc,
12205
+ type: Img
12206
+ }])];
12207
+ };
12208
+
11576
12209
  const runModes = ['local', 'background', 'cloud'];
11577
12210
  const runModePickerHeight = runModes.length * 28;
11578
12211
  const getRunModeOptionsVirtualDom = selectedRunMode => {
@@ -11599,6 +12232,10 @@ const getDropOverlayVirtualDom = () => {
11599
12232
  const getChatOverlaysVirtualDom = ({
11600
12233
  agentMode,
11601
12234
  agentModePickerVisible,
12235
+ composerAttachmentPreviewOverlayAttachmentId,
12236
+ composerAttachmentPreviewOverlayError,
12237
+ composerAttachmentPreviewOverlayVisible,
12238
+ composerAttachments,
11602
12239
  dropOverlayVisible,
11603
12240
  modelPickerSearchValue,
11604
12241
  modelPickerVisible,
@@ -11607,7 +12244,7 @@ const getChatOverlaysVirtualDom = ({
11607
12244
  selectedModelId,
11608
12245
  visibleModels
11609
12246
  }) => {
11610
- const overlayChildCount = (dropOverlayVisible ? 1 : 0) + (agentModePickerVisible ? 1 : 0) + (modelPickerVisible ? 1 : 0) + (runModePickerVisible ? 1 : 0);
12247
+ const overlayChildCount = (dropOverlayVisible ? 1 : 0) + (composerAttachmentPreviewOverlayVisible ? 1 : 0) + (agentModePickerVisible ? 1 : 0) + (modelPickerVisible ? 1 : 0) + (runModePickerVisible ? 1 : 0);
11611
12248
  if (!overlayChildCount) {
11612
12249
  return [];
11613
12250
  }
@@ -11615,7 +12252,7 @@ const getChatOverlaysVirtualDom = ({
11615
12252
  childCount: overlayChildCount,
11616
12253
  className: ChatOverlays,
11617
12254
  type: Div
11618
- }, ...(dropOverlayVisible ? getDropOverlayVirtualDom() : []), ...(agentModePickerVisible ? getAgentModePickerPopOverVirtualDom(agentMode) : []), ...(modelPickerVisible ? getChatModelPickerPopOverVirtualDom(visibleModels, selectedModelId, modelPickerSearchValue) : []), ...(runModePickerVisible ? getRunModePickerPopOverVirtualDom(runMode) : [])];
12255
+ }, ...(dropOverlayVisible ? getDropOverlayVirtualDom() : []), ...getComposerAttachmentPreviewOverlayVirtualDom(composerAttachments, composerAttachmentPreviewOverlayAttachmentId, composerAttachmentPreviewOverlayError), ...(agentModePickerVisible ? getAgentModePickerPopOverVirtualDom(agentMode) : []), ...(modelPickerVisible ? getChatModelPickerPopOverVirtualDom(visibleModels, selectedModelId, modelPickerSearchValue) : []), ...(runModePickerVisible ? getRunModePickerPopOverVirtualDom(runMode) : [])];
11619
12256
  };
11620
12257
 
11621
12258
  const getBoldInlineNodeDom = (inlineNode, useChatMathWorker, renderInlineNodeDom) => {
@@ -11748,12 +12385,17 @@ const getTokenDom = token => {
11748
12385
 
11749
12386
  const getCodeBlockDom = node => {
11750
12387
  const tokenDom = node.codeTokens.flatMap(getTokenDom);
12388
+ const languageAttribute = node.language ? {
12389
+ 'data-lang': node.language
12390
+ } : {};
11751
12391
  return [{
11752
12392
  childCount: 1,
11753
- type: Pre
12393
+ type: Pre,
12394
+ ...languageAttribute
11754
12395
  }, {
11755
12396
  childCount: node.codeTokens.length,
11756
- type: Code
12397
+ type: Code,
12398
+ ...languageAttribute
11757
12399
  }, ...tokenDom];
11758
12400
  };
11759
12401
 
@@ -12287,69 +12929,6 @@ const getToolCallCreateDirectoryVirtualDom = toolCall => {
12287
12929
  }, ...getToolCallFileNameDom(directoryName, fileNameClickableProps), ...(statusLabel ? [text(statusLabel)] : [])];
12288
12930
  };
12289
12931
 
12290
- const getToolCallEditFileVirtualDom = toolCall => {
12291
- const target = getReadFileTarget(toolCall.arguments);
12292
- if (!target) {
12293
- return [];
12294
- }
12295
- const fileName = getFileNameFromUri(target.title);
12296
- const fileNameClickableProps = target.clickableUri ? {
12297
- 'data-uri': target.clickableUri,
12298
- onClick: HandleClickFileName
12299
- } : {};
12300
- return [{
12301
- childCount: 3,
12302
- className: ChatOrderedListItem,
12303
- title: target.title,
12304
- type: Li
12305
- }, {
12306
- childCount: 0,
12307
- className: FileIcon,
12308
- type: Div
12309
- }, {
12310
- childCount: 1,
12311
- className: ToolCallName,
12312
- type: Span
12313
- }, text('edit_file '), {
12314
- childCount: 1,
12315
- className: ChatToolCallReadFileLink,
12316
- title: target.clickableUri,
12317
- ...fileNameClickableProps,
12318
- type: Span
12319
- }, ...getToolCallFileNameDom(fileName, fileNameClickableProps)];
12320
- };
12321
-
12322
- const getToolCallGetWorkspaceUriVirtualDom = toolCall => {
12323
- if (!toolCall.result) {
12324
- return [];
12325
- }
12326
- const statusLabel = getToolCallStatusLabel(toolCall);
12327
- const fileName = getFileNameFromUri(toolCall.result);
12328
- const fileNameClickableProps = {
12329
- 'data-uri': toolCall.result,
12330
- onClick: HandleClickFileName
12331
- };
12332
- return [{
12333
- childCount: statusLabel ? 4 : 3,
12334
- className: ChatOrderedListItem,
12335
- title: toolCall.result,
12336
- type: Li
12337
- }, {
12338
- childCount: 0,
12339
- className: FileIcon,
12340
- type: Div
12341
- }, {
12342
- childCount: 1,
12343
- className: ToolCallName,
12344
- type: Span
12345
- }, text('get_workspace_uri '), {
12346
- childCount: 1,
12347
- className: ChatToolCallReadFileLink,
12348
- ...fileNameClickableProps,
12349
- type: Span
12350
- }, ...getToolCallFileNameDom(fileName, fileNameClickableProps), ...(statusLabel ? [text(statusLabel)] : [])];
12351
- };
12352
-
12353
12932
  const getToolCallArgumentPreview = rawArguments => {
12354
12933
  if (!rawArguments.trim()) {
12355
12934
  return '""';
@@ -12424,6 +13003,91 @@ const getToolCallLabel = toolCall => {
12424
13003
  return `${displayName} ${argumentPreview}${statusLabel}`;
12425
13004
  };
12426
13005
 
13006
+ const RE_TOOL_NAME_PREFIX = /^([^ :]+)/;
13007
+ const getToolCallDefaultDom = toolCall => {
13008
+ const label = getToolCallLabel(toolCall);
13009
+ const match = RE_TOOL_NAME_PREFIX.exec(label);
13010
+ const toolNamePrefix = match ? match[1] : label;
13011
+ const suffix = label.slice(toolNamePrefix.length);
13012
+ const hasSuffix = suffix.length > 0;
13013
+ const hoverTitle = hasSuffix && toolCall.arguments.trim() ? toolCall.arguments : undefined;
13014
+ return [{
13015
+ childCount: hasSuffix ? 2 : 1,
13016
+ className: ChatOrderedListItem,
13017
+ ...(hoverTitle ? {
13018
+ title: hoverTitle
13019
+ } : {}),
13020
+ type: Li
13021
+ }, {
13022
+ childCount: 1,
13023
+ className: ToolCallName,
13024
+ type: Span
13025
+ }, text(toolNamePrefix), ...(hasSuffix ? [text(suffix)] : [])];
13026
+ };
13027
+
13028
+ const getToolCallEditFileVirtualDom = toolCall => {
13029
+ const target = getReadFileTarget(toolCall.arguments);
13030
+ if (!target) {
13031
+ return [];
13032
+ }
13033
+ const fileName = getFileNameFromUri(target.title);
13034
+ const fileNameClickableProps = target.clickableUri ? {
13035
+ 'data-uri': target.clickableUri,
13036
+ onClick: HandleClickFileName
13037
+ } : {};
13038
+ return [{
13039
+ childCount: 3,
13040
+ className: ChatOrderedListItem,
13041
+ title: target.title,
13042
+ type: Li
13043
+ }, {
13044
+ childCount: 0,
13045
+ className: FileIcon,
13046
+ type: Div
13047
+ }, {
13048
+ childCount: 1,
13049
+ className: ToolCallName,
13050
+ type: Span
13051
+ }, text('edit_file '), {
13052
+ childCount: 1,
13053
+ className: ChatToolCallReadFileLink,
13054
+ title: target.clickableUri,
13055
+ ...fileNameClickableProps,
13056
+ type: Span
13057
+ }, ...getToolCallFileNameDom(fileName, fileNameClickableProps)];
13058
+ };
13059
+
13060
+ const getToolCallGetWorkspaceUriVirtualDom = toolCall => {
13061
+ if (!toolCall.result) {
13062
+ return [];
13063
+ }
13064
+ const statusLabel = getToolCallStatusLabel(toolCall);
13065
+ const fileName = getFileNameFromUri(toolCall.result);
13066
+ const fileNameClickableProps = {
13067
+ 'data-uri': toolCall.result,
13068
+ onClick: HandleClickFileName
13069
+ };
13070
+ return [{
13071
+ childCount: statusLabel ? 4 : 3,
13072
+ className: ChatOrderedListItem,
13073
+ title: toolCall.result,
13074
+ type: Li
13075
+ }, {
13076
+ childCount: 0,
13077
+ className: FileIcon,
13078
+ type: Div
13079
+ }, {
13080
+ childCount: 1,
13081
+ className: ToolCallName,
13082
+ type: Span
13083
+ }, text('get_workspace_uri '), {
13084
+ childCount: 1,
13085
+ className: ChatToolCallReadFileLink,
13086
+ ...fileNameClickableProps,
13087
+ type: Span
13088
+ }, ...getToolCallFileNameDom(fileName, fileNameClickableProps), ...(statusLabel ? [text(statusLabel)] : [])];
13089
+ };
13090
+
12427
13091
  const getToolCallReadFileVirtualDom = toolCall => {
12428
13092
  const target = getReadFileTarget(toolCall.arguments);
12429
13093
  if (!target) {
@@ -12847,7 +13511,6 @@ const getToolCallWriteFileVirtualDom = toolCall => {
12847
13511
  }, text(` -${linesDeleted}`)] : []), ...(statusLabel ? [text(statusLabel)] : [])];
12848
13512
  };
12849
13513
 
12850
- const RE_TOOL_NAME_PREFIX = /^([^ :]+)/;
12851
13514
  const getToolCallDom = toolCall => {
12852
13515
  if (toolCall.name === 'getWorkspaceUri') {
12853
13516
  const virtualDom = getToolCallGetWorkspaceUriVirtualDom(toolCall);
@@ -12891,24 +13554,7 @@ const getToolCallDom = toolCall => {
12891
13554
  return virtualDom;
12892
13555
  }
12893
13556
  }
12894
- const label = getToolCallLabel(toolCall);
12895
- const match = RE_TOOL_NAME_PREFIX.exec(label);
12896
- const toolNamePrefix = match ? match[1] : label;
12897
- const suffix = label.slice(toolNamePrefix.length);
12898
- const hasSuffix = suffix.length > 0;
12899
- const hoverTitle = hasSuffix && toolCall.arguments.trim() ? toolCall.arguments : undefined;
12900
- return [{
12901
- childCount: hasSuffix ? 2 : 1,
12902
- className: ChatOrderedListItem,
12903
- ...(hoverTitle ? {
12904
- title: hoverTitle
12905
- } : {}),
12906
- type: Li
12907
- }, {
12908
- childCount: 1,
12909
- className: ToolCallName,
12910
- type: Span
12911
- }, text(toolNamePrefix), ...(hasSuffix ? [text(suffix)] : [])];
13557
+ return getToolCallDefaultDom(toolCall);
12912
13558
  };
12913
13559
 
12914
13560
  const withOrderedListMarker = (virtualDom, index) => {
@@ -12966,6 +13612,70 @@ const getTopLevelNodeCount = nodes => {
12966
13612
  return topLevelCount;
12967
13613
  };
12968
13614
 
13615
+ const getChatAttachmentLabel = displayType => {
13616
+ switch (displayType) {
13617
+ case 'file':
13618
+ return 'File';
13619
+ case 'image':
13620
+ return 'Image';
13621
+ case 'invalid-image':
13622
+ return 'Invalid image';
13623
+ case 'text-file':
13624
+ return 'Text file';
13625
+ default:
13626
+ return displayType;
13627
+ }
13628
+ };
13629
+ const getChatAttachmentClassName = displayType => {
13630
+ switch (displayType) {
13631
+ case 'file':
13632
+ return ChatAttachment;
13633
+ case 'image':
13634
+ return ChatAttachmentImage;
13635
+ case 'invalid-image':
13636
+ return ChatAttachmentInvalidImage;
13637
+ case 'text-file':
13638
+ return ChatAttachmentTextFile;
13639
+ default:
13640
+ return ChatAttachment;
13641
+ }
13642
+ };
13643
+ const getChatAttachmentPreviewDom = attachment => {
13644
+ if (!attachment.previewSrc) {
13645
+ return [];
13646
+ }
13647
+ return [{
13648
+ alt: `Attachment preview for ${attachment.name}`,
13649
+ childCount: 0,
13650
+ className: ChatAttachmentPreview,
13651
+ src: attachment.previewSrc,
13652
+ type: Img
13653
+ }];
13654
+ };
13655
+ const getChatAttachmentsDom = attachments => {
13656
+ if (attachments.length === 0) {
13657
+ return [];
13658
+ }
13659
+ return [{
13660
+ childCount: attachments.length,
13661
+ className: ChatAttachments,
13662
+ type: Div
13663
+ }, ...attachments.flatMap(attachment => {
13664
+ const previewDom = getChatAttachmentPreviewDom(attachment);
13665
+ return [{
13666
+ childCount: 1 + previewDom.length,
13667
+ className: mergeClassNames(ChatAttachment, getChatAttachmentClassName(attachment.displayType)),
13668
+ type: Div
13669
+ }, ...previewDom, {
13670
+ childCount: 1,
13671
+ className: ChatAttachmentLabel,
13672
+ type: Span
13673
+ }, {
13674
+ text: `${getChatAttachmentLabel(attachment.displayType)} · ${attachment.name}`,
13675
+ type: Text
13676
+ }];
13677
+ })];
13678
+ };
12969
13679
  const getChatMessageDom = (message, parsedMessageContent, _openRouterApiKeyInput, _openApiApiKeyInput = '', openApiApiKeyState = 'idle', openApiApiKeysSettingsUrl = 'https://platform.openai.com/api-keys', openApiApiKeyInputPattern = '^sk-.+', openRouterApiKeyState = 'idle', useChatMathWorker = false) => {
12970
13680
  const roleClassName = message.role === 'user' ? MessageUser : MessageAssistant;
12971
13681
  const isOpenApiApiKeyMissingMessage = message.role === 'assistant' && message.text === openApiApiKeyRequiredMessage;
@@ -12973,10 +13683,12 @@ const getChatMessageDom = (message, parsedMessageContent, _openRouterApiKeyInput
12973
13683
  const isOpenRouterRequestFailedMessage = message.role === 'assistant' && message.text === openRouterRequestFailedMessage;
12974
13684
  const isOpenRouterTooManyRequestsMessage = message.role === 'assistant' && message.text.startsWith(openRouterTooManyRequestsMessage);
12975
13685
  const messageDom = getMessageContentDom(parsedMessageContent, useChatMathWorker);
13686
+ const attachmentsDom = message.role === 'user' ? getChatAttachmentsDom(message.attachments ?? []) : [];
12976
13687
  const toolCallsDom = getToolCallsDom(message);
12977
13688
  const toolCallsChildCount = toolCallsDom.length > 0 ? 1 : 0;
12978
13689
  const messageDomChildCount = getTopLevelNodeCount(messageDom);
12979
- const extraChildCount = isOpenApiApiKeyMissingMessage || isOpenRouterApiKeyMissingMessage || isOpenRouterRequestFailedMessage || isOpenRouterTooManyRequestsMessage ? messageDomChildCount + 1 + toolCallsChildCount : messageDomChildCount + toolCallsChildCount;
13690
+ const attachmentsChildCount = attachmentsDom.length > 0 ? 1 : 0;
13691
+ const extraChildCount = isOpenApiApiKeyMissingMessage || isOpenRouterApiKeyMissingMessage || isOpenRouterRequestFailedMessage || isOpenRouterTooManyRequestsMessage ? messageDomChildCount + 1 + toolCallsChildCount + attachmentsChildCount : messageDomChildCount + toolCallsChildCount + attachmentsChildCount;
12980
13692
  return [{
12981
13693
  childCount: 1,
12982
13694
  className: mergeClassNames(Message, roleClassName),
@@ -12985,7 +13697,7 @@ const getChatMessageDom = (message, parsedMessageContent, _openRouterApiKeyInput
12985
13697
  childCount: extraChildCount,
12986
13698
  className: ChatMessageContent,
12987
13699
  type: Div
12988
- }, ...toolCallsDom, ...messageDom, ...(isOpenApiApiKeyMissingMessage ? getMissingOpenApiApiKeyDom(openApiApiKeyState, openApiApiKeysSettingsUrl, openApiApiKeyInputPattern) : []), ...(isOpenRouterApiKeyMissingMessage ? getMissingOpenRouterApiKeyDom(openRouterApiKeyState) : []), ...(isOpenRouterRequestFailedMessage ? getOpenRouterRequestFailedDom() : []), ...(isOpenRouterTooManyRequestsMessage ? getOpenRouterTooManyRequestsDom() : [])];
13700
+ }, ...toolCallsDom, ...messageDom, ...attachmentsDom, ...(isOpenApiApiKeyMissingMessage ? getMissingOpenApiApiKeyDom(openApiApiKeyState, openApiApiKeysSettingsUrl, openApiApiKeyInputPattern) : []), ...(isOpenRouterApiKeyMissingMessage ? getMissingOpenRouterApiKeyDom(openRouterApiKeyState) : []), ...(isOpenRouterRequestFailedMessage ? getOpenRouterRequestFailedDom() : []), ...(isOpenRouterTooManyRequestsMessage ? getOpenRouterTooManyRequestsDom() : [])];
12989
13701
  };
12990
13702
 
12991
13703
  const parentNode$1 = {
@@ -13041,7 +13753,18 @@ const getDisplayMessages = (messages, parsedMessages) => {
13041
13753
  };
13042
13754
  const getMessagesDom = (messages, parsedMessages, openRouterApiKeyInput, openApiApiKeyInput = '', openApiApiKeyState = 'idle', openApiApiKeysSettingsUrl = 'https://platform.openai.com/api-keys', openApiApiKeyInputPattern = '^sk-.+', openRouterApiKeyState = 'idle', messagesScrollTop = 0, useChatMathWorker = false, hideWelcomeMessage = false) => {
13043
13755
  if (messages.length === 0) {
13044
- return hideWelcomeMessage ? [] : getEmptyMessagesDom();
13756
+ if (!hideWelcomeMessage) {
13757
+ return getEmptyMessagesDom();
13758
+ }
13759
+ return [{
13760
+ childCount: 0,
13761
+ className: 'ChatMessages',
13762
+ onContextMenu: HandleMessagesContextMenu,
13763
+ onScroll: HandleMessagesScroll,
13764
+ role: 'log',
13765
+ scrollTop: messagesScrollTop,
13766
+ type: Div
13767
+ }];
13045
13768
  }
13046
13769
  const displayMessages = getDisplayMessages(messages, parsedMessages);
13047
13770
  return [{
@@ -13108,10 +13831,10 @@ const getProjectGroupDom = (project, sessions, projectExpandedIds, selectedProje
13108
13831
  tabIndex: 0,
13109
13832
  type: Div
13110
13833
  }, {
13111
- childCount: 1,
13112
- className: ProjectListChevron,
13113
- type: Span
13114
- }, text(expanded ? '▾' : '▸'), {
13834
+ childCount: 0,
13835
+ className: mergeClassNames(ProjectListChevron, MaskIcon, expanded ? MaskIconChevronDown : MaskIconChevronRight),
13836
+ type: Div
13837
+ }, {
13115
13838
  childCount: 0,
13116
13839
  className: 'MaskIcon MaskIconFolder',
13117
13840
  type: Div
@@ -13167,6 +13890,8 @@ const getChatModeChatFocusVirtualDom = ({
13167
13890
  authEnabled = false,
13168
13891
  authErrorMessage = '',
13169
13892
  authStatus = 'signed-out',
13893
+ composerAttachmentPreviewOverlayAttachmentId,
13894
+ composerAttachmentPreviewOverlayError = false,
13170
13895
  composerAttachments,
13171
13896
  composerDropActive = false,
13172
13897
  composerDropEnabled = true,
@@ -13177,6 +13902,7 @@ const getChatModeChatFocusVirtualDom = ({
13177
13902
  composerValue,
13178
13903
  hasSpaceForAgentModePicker,
13179
13904
  hasSpaceForRunModePicker,
13905
+ messagesAutoScrollEnabled,
13180
13906
  messagesScrollTop = 0,
13181
13907
  modelPickerOpen = false,
13182
13908
  modelPickerSearchValue = '',
@@ -13196,6 +13922,7 @@ const getChatModeChatFocusVirtualDom = ({
13196
13922
  reasoningPickerEnabled,
13197
13923
  runMode,
13198
13924
  runModePickerOpen = false,
13925
+ scrollDownButtonEnabled,
13199
13926
  selectedModelId,
13200
13927
  selectedProjectId = '',
13201
13928
  selectedSessionId,
@@ -13217,20 +13944,29 @@ const getChatModeChatFocusVirtualDom = ({
13217
13944
  const selectedProjectName = selectedProject?.name || '';
13218
13945
  const showCreatePullRequestButton = canCreatePullRequest(selectedSession);
13219
13946
  const isDropOverlayVisible = composerDropEnabled && composerDropActive;
13947
+ const isComposerAttachmentPreviewOverlayVisible = !!composerAttachmentPreviewOverlayAttachmentId;
13220
13948
  const isAgentModePickerVisible = hasSpaceForAgentModePicker && agentModePickerOpen;
13221
13949
  const isNewModelPickerVisible = modelPickerOpen;
13222
13950
  const isRunModePickerVisible = showRunMode && hasSpaceForRunModePicker && runModePickerOpen;
13223
- const hasVisibleOverlays = isDropOverlayVisible || isAgentModePickerVisible || isNewModelPickerVisible || isRunModePickerVisible;
13224
- const chatRootChildCount = 4 + (hasVisibleOverlays ? 1 : 0);
13951
+ const hasVisibleOverlays = isDropOverlayVisible || isComposerAttachmentPreviewOverlayVisible || isAgentModePickerVisible || isNewModelPickerVisible || isRunModePickerVisible;
13952
+ const chatRootChildCount = 2 + (hasVisibleOverlays ? 1 : 0);
13225
13953
  return [{
13226
13954
  childCount: chatRootChildCount,
13227
13955
  className: mergeClassNames(Viewlet, Chat, ChatFocus),
13228
13956
  onDragEnter: HandleDragEnterChatView,
13229
13957
  onDragOver: HandleDragOverChatView,
13230
13958
  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({
13959
+ }, ...getProjectListDom(projects, sessions, projectExpandedIds, selectedProjectId, selectedSessionId, projectListScrollTop, true), {
13960
+ childCount: 3,
13961
+ className: ChatFocusMainArea,
13962
+ type: Div
13963
+ }, ...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, scrollDownButtonEnabled, messagesAutoScrollEnabled), ...getChatOverlaysVirtualDom({
13232
13964
  agentMode,
13233
13965
  agentModePickerVisible: isAgentModePickerVisible,
13966
+ composerAttachmentPreviewOverlayAttachmentId,
13967
+ composerAttachmentPreviewOverlayError,
13968
+ composerAttachmentPreviewOverlayVisible: isComposerAttachmentPreviewOverlayVisible,
13969
+ composerAttachments,
13234
13970
  dropOverlayVisible: isDropOverlayVisible,
13235
13971
  modelPickerSearchValue,
13236
13972
  modelPickerVisible: isNewModelPickerVisible,
@@ -13277,22 +14013,31 @@ const getHeaderActionVirtualDom = item => {
13277
14013
  }];
13278
14014
  };
13279
14015
 
13280
- const getChatHeaderActionsDom = (viewMode, authEnabled = false, authStatus = 'signed-out', searchEnabled = false) => {
13281
- const toggleTitle = viewMode === 'chat-focus' ? normalChatMode() : chatFocusMode();
14016
+ const getAuthAction = (authEnabled, authStatus) => {
13282
14017
  const isSigningIn = authStatus === 'signing-in';
13283
- const authAction = authEnabled && authStatus !== 'signed-in' ? {
13284
- disabled: isSigningIn,
13285
- icon: mergeClassNames(MaskIcon, MaskIconAccount),
13286
- name: Login,
13287
- onClick: HandleClick,
13288
- title: isSigningIn ? loggingInToBackend() : loginToBackend()
13289
- } : authEnabled ? {
14018
+ if (!authEnabled) {
14019
+ return undefined;
14020
+ }
14021
+ if (authStatus !== 'signed-in') {
14022
+ return {
14023
+ disabled: isSigningIn,
14024
+ icon: mergeClassNames(MaskIcon, MaskIconAccount),
14025
+ name: Login,
14026
+ onClick: HandleClick,
14027
+ title: isSigningIn ? loggingInToBackend() : loginToBackend()
14028
+ };
14029
+ }
14030
+ return {
13290
14031
  disabled: false,
13291
14032
  icon: mergeClassNames(MaskIcon, MaskIconSignOut),
13292
14033
  name: Logout,
13293
14034
  onClick: HandleClick,
13294
14035
  title: logoutFromBackend()
13295
- } : undefined;
14036
+ };
14037
+ };
14038
+ const getChatHeaderActionsDom = (viewMode, authEnabled = false, authStatus = 'signed-out', searchEnabled = false) => {
14039
+ const toggleTitle = viewMode === 'chat-focus' ? normalChatMode() : chatFocusMode();
14040
+ const authAction = getAuthAction(authEnabled, authStatus);
13296
14041
  const items = [{
13297
14042
  icon: mergeClassNames(MaskIcon, MaskIconLayoutPanelLeft),
13298
14043
  name: ToggleChatFocus,
@@ -13368,6 +14113,8 @@ const getChatModeDetailVirtualDom = ({
13368
14113
  authEnabled = false,
13369
14114
  authErrorMessage = '',
13370
14115
  authStatus = 'signed-out',
14116
+ composerAttachmentPreviewOverlayAttachmentId,
14117
+ composerAttachmentPreviewOverlayError = false,
13371
14118
  composerAttachments,
13372
14119
  composerDropActive = false,
13373
14120
  composerDropEnabled = true,
@@ -13378,6 +14125,7 @@ const getChatModeDetailVirtualDom = ({
13378
14125
  composerValue,
13379
14126
  hasSpaceForAgentModePicker,
13380
14127
  hasSpaceForRunModePicker,
14128
+ messagesAutoScrollEnabled,
13381
14129
  messagesScrollTop = 0,
13382
14130
  modelPickerOpen = false,
13383
14131
  modelPickerSearchValue = '',
@@ -13394,6 +14142,7 @@ const getChatModeDetailVirtualDom = ({
13394
14142
  reasoningPickerEnabled,
13395
14143
  runMode,
13396
14144
  runModePickerOpen = false,
14145
+ scrollDownButtonEnabled,
13397
14146
  selectedModelId,
13398
14147
  selectedSessionId,
13399
14148
  sessions,
@@ -13412,10 +14161,11 @@ const getChatModeDetailVirtualDom = ({
13412
14161
  const messages = selectedSession ? selectedSession.messages : [];
13413
14162
  const showCreatePullRequestButton = canCreatePullRequest(selectedSession);
13414
14163
  const isDropOverlayVisible = composerDropEnabled && composerDropActive;
14164
+ const isComposerAttachmentPreviewOverlayVisible = !!composerAttachmentPreviewOverlayAttachmentId;
13415
14165
  const isAgentModePickerVisible = hasSpaceForAgentModePicker && agentModePickerOpen;
13416
14166
  const isNewModelPickerVisible = modelPickerOpen;
13417
14167
  const isRunModePickerVisible = showRunMode && hasSpaceForRunModePicker && runModePickerOpen;
13418
- const hasVisibleOverlays = isDropOverlayVisible || isAgentModePickerVisible || isNewModelPickerVisible || isRunModePickerVisible;
14168
+ const hasVisibleOverlays = isDropOverlayVisible || isComposerAttachmentPreviewOverlayVisible || isAgentModePickerVisible || isNewModelPickerVisible || isRunModePickerVisible;
13419
14169
  const chatRootChildCount = 3 + (hasVisibleOverlays ? 1 : 0);
13420
14170
  return [{
13421
14171
  childCount: chatRootChildCount,
@@ -13423,9 +14173,13 @@ const getChatModeDetailVirtualDom = ({
13423
14173
  onDragEnter: HandleDragEnterChatView,
13424
14174
  onDragOver: HandleDragOverChatView,
13425
14175
  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({
14176
+ }, ...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
14177
  agentMode,
13428
14178
  agentModePickerVisible: isAgentModePickerVisible,
14179
+ composerAttachmentPreviewOverlayAttachmentId,
14180
+ composerAttachmentPreviewOverlayError,
14181
+ composerAttachmentPreviewOverlayVisible: isComposerAttachmentPreviewOverlayVisible,
14182
+ composerAttachments,
13429
14183
  dropOverlayVisible: isDropOverlayVisible,
13430
14184
  modelPickerSearchValue,
13431
14185
  modelPickerVisible: isNewModelPickerVisible,
@@ -13570,6 +14324,8 @@ const getChatModeListVirtualDom = ({
13570
14324
  authErrorMessage = '',
13571
14325
  authStatus = 'signed-out',
13572
14326
  chatListScrollTop = 0,
14327
+ composerAttachmentPreviewOverlayAttachmentId,
14328
+ composerAttachmentPreviewOverlayError = false,
13573
14329
  composerAttachments,
13574
14330
  composerDropActive = false,
13575
14331
  composerDropEnabled = true,
@@ -13605,10 +14361,11 @@ const getChatModeListVirtualDom = ({
13605
14361
  voiceDictationEnabled = false
13606
14362
  }) => {
13607
14363
  const isDropOverlayVisible = composerDropEnabled && composerDropActive;
14364
+ const isComposerAttachmentPreviewOverlayVisible = !!composerAttachmentPreviewOverlayAttachmentId;
13608
14365
  const isAgentModePickerVisible = hasSpaceForAgentModePicker && agentModePickerOpen;
13609
14366
  const isNewModelPickerVisible = modelPickerOpen;
13610
14367
  const isRunModePickerVisible = showRunMode && hasSpaceForRunModePicker && runModePickerOpen;
13611
- const hasVisibleOverlays = isDropOverlayVisible || isAgentModePickerVisible || isNewModelPickerVisible || isRunModePickerVisible;
14368
+ const hasVisibleOverlays = isDropOverlayVisible || isComposerAttachmentPreviewOverlayVisible || isAgentModePickerVisible || isNewModelPickerVisible || isRunModePickerVisible;
13612
14369
  const chatRootChildCount = 3 + (hasVisibleOverlays ? 1 : 0);
13613
14370
  const searchValueTrimmed = searchValue.trim().toLowerCase();
13614
14371
  const visibleSessions = searchEnabled && searchValueTrimmed ? sessions.filter(session => session.title.toLowerCase().includes(searchValueTrimmed)) : sessions;
@@ -13621,6 +14378,10 @@ const getChatModeListVirtualDom = ({
13621
14378
  }, ...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
14379
  agentMode,
13623
14380
  agentModePickerVisible: isAgentModePickerVisible,
14381
+ composerAttachmentPreviewOverlayAttachmentId,
14382
+ composerAttachmentPreviewOverlayError,
14383
+ composerAttachmentPreviewOverlayVisible: isComposerAttachmentPreviewOverlayVisible,
14384
+ composerAttachments,
13624
14385
  dropOverlayVisible: isDropOverlayVisible,
13625
14386
  modelPickerSearchValue,
13626
14387
  modelPickerVisible: isNewModelPickerVisible,
@@ -13719,6 +14480,8 @@ const getChatVirtualDom = options => {
13719
14480
  authErrorMessage = '',
13720
14481
  authStatus = 'signed-out',
13721
14482
  chatListScrollTop,
14483
+ composerAttachmentPreviewOverlayAttachmentId,
14484
+ composerAttachmentPreviewOverlayError = false,
13722
14485
  composerAttachments,
13723
14486
  composerDropActive = false,
13724
14487
  composerDropEnabled = true,
@@ -13730,6 +14493,7 @@ const getChatVirtualDom = options => {
13730
14493
  hasSpaceForAgentModePicker,
13731
14494
  hasSpaceForRunModePicker,
13732
14495
  listFocusedIndex = -1,
14496
+ messagesAutoScrollEnabled,
13733
14497
  messagesScrollTop,
13734
14498
  modelPickerOpen = false,
13735
14499
  modelPickerSearchValue = '',
@@ -13749,6 +14513,7 @@ const getChatVirtualDom = options => {
13749
14513
  reasoningPickerEnabled,
13750
14514
  runMode,
13751
14515
  runModePickerOpen = false,
14516
+ scrollDownButtonEnabled,
13752
14517
  searchEnabled = false,
13753
14518
  searchFieldVisible = false,
13754
14519
  searchValue = '',
@@ -13777,6 +14542,8 @@ const getChatVirtualDom = options => {
13777
14542
  authEnabled,
13778
14543
  authErrorMessage,
13779
14544
  authStatus,
14545
+ composerAttachmentPreviewOverlayAttachmentId,
14546
+ composerAttachmentPreviewOverlayError,
13780
14547
  composerAttachments,
13781
14548
  composerDropActive,
13782
14549
  composerDropEnabled,
@@ -13787,6 +14554,7 @@ const getChatVirtualDom = options => {
13787
14554
  composerValue,
13788
14555
  hasSpaceForAgentModePicker,
13789
14556
  hasSpaceForRunModePicker,
14557
+ messagesAutoScrollEnabled,
13790
14558
  messagesScrollTop,
13791
14559
  modelPickerOpen,
13792
14560
  modelPickerSearchValue,
@@ -13806,6 +14574,7 @@ const getChatVirtualDom = options => {
13806
14574
  reasoningPickerEnabled,
13807
14575
  runMode,
13808
14576
  runModePickerOpen,
14577
+ scrollDownButtonEnabled,
13809
14578
  selectedModelId,
13810
14579
  selectedProjectId,
13811
14580
  selectedSessionId,
@@ -13828,6 +14597,8 @@ const getChatVirtualDom = options => {
13828
14597
  authEnabled,
13829
14598
  authErrorMessage,
13830
14599
  authStatus,
14600
+ composerAttachmentPreviewOverlayAttachmentId,
14601
+ composerAttachmentPreviewOverlayError,
13831
14602
  composerAttachments,
13832
14603
  composerDropActive,
13833
14604
  composerDropEnabled,
@@ -13838,6 +14609,7 @@ const getChatVirtualDom = options => {
13838
14609
  composerValue,
13839
14610
  hasSpaceForAgentModePicker,
13840
14611
  hasSpaceForRunModePicker,
14612
+ messagesAutoScrollEnabled,
13841
14613
  messagesScrollTop,
13842
14614
  modelPickerOpen,
13843
14615
  modelPickerSearchValue,
@@ -13854,6 +14626,7 @@ const getChatVirtualDom = options => {
13854
14626
  reasoningPickerEnabled,
13855
14627
  runMode,
13856
14628
  runModePickerOpen,
14629
+ scrollDownButtonEnabled,
13857
14630
  selectedModelId,
13858
14631
  selectedSessionId,
13859
14632
  sessions,
@@ -13876,6 +14649,8 @@ const getChatVirtualDom = options => {
13876
14649
  authErrorMessage,
13877
14650
  authStatus,
13878
14651
  chatListScrollTop,
14652
+ composerAttachmentPreviewOverlayAttachmentId,
14653
+ composerAttachmentPreviewOverlayError,
13879
14654
  composerAttachments,
13880
14655
  composerDropActive,
13881
14656
  composerDropEnabled,
@@ -13924,6 +14699,8 @@ const renderItems = (oldState, newState) => {
13924
14699
  authErrorMessage,
13925
14700
  authStatus,
13926
14701
  chatListScrollTop,
14702
+ composerAttachmentPreviewOverlayAttachmentId,
14703
+ composerAttachmentPreviewOverlayError,
13927
14704
  composerAttachments,
13928
14705
  composerDropActive,
13929
14706
  composerDropEnabled,
@@ -13936,6 +14713,7 @@ const renderItems = (oldState, newState) => {
13936
14713
  hasSpaceForRunModePicker,
13937
14714
  initial,
13938
14715
  listFocusedIndex,
14716
+ messagesAutoScrollEnabled,
13939
14717
  messagesScrollTop,
13940
14718
  modelPickerOpen,
13941
14719
  modelPickerSearchValue,
@@ -13955,6 +14733,7 @@ const renderItems = (oldState, newState) => {
13955
14733
  reasoningPickerEnabled,
13956
14734
  runMode,
13957
14735
  runModePickerOpen,
14736
+ scrollDownButtonEnabled,
13958
14737
  searchEnabled,
13959
14738
  searchFieldVisible,
13960
14739
  searchValue,
@@ -13984,6 +14763,8 @@ const renderItems = (oldState, newState) => {
13984
14763
  authErrorMessage,
13985
14764
  authStatus,
13986
14765
  chatListScrollTop,
14766
+ composerAttachmentPreviewOverlayAttachmentId,
14767
+ composerAttachmentPreviewOverlayError,
13987
14768
  composerAttachments,
13988
14769
  composerDropActive,
13989
14770
  composerDropEnabled,
@@ -13995,6 +14776,7 @@ const renderItems = (oldState, newState) => {
13995
14776
  hasSpaceForAgentModePicker,
13996
14777
  hasSpaceForRunModePicker,
13997
14778
  listFocusedIndex,
14779
+ messagesAutoScrollEnabled,
13998
14780
  messagesScrollTop,
13999
14781
  modelPickerOpen,
14000
14782
  modelPickerSearchValue,
@@ -14014,6 +14796,7 @@ const renderItems = (oldState, newState) => {
14014
14796
  reasoningPickerEnabled,
14015
14797
  runMode,
14016
14798
  runModePickerOpen,
14799
+ scrollDownButtonEnabled,
14017
14800
  searchEnabled,
14018
14801
  searchFieldVisible,
14019
14802
  searchValue,
@@ -14132,6 +14915,12 @@ const renderEventListeners = () => {
14132
14915
  }, {
14133
14916
  name: HandleClick,
14134
14917
  params: ['handleClick', TargetName, 'event.target.dataset.id', ClientX, ClientY]
14918
+ }, {
14919
+ name: HandleMouseOver,
14920
+ params: ['handleMouseOver', TargetName]
14921
+ }, {
14922
+ name: HandleMouseOut,
14923
+ params: ['handleMouseOut', TargetName, 'event.relatedTarget && event.relatedTarget.getAttribute ? event.relatedTarget.getAttribute("name") : ""']
14135
14924
  }, {
14136
14925
  name: HandleClickDictationButton,
14137
14926
  params: ['handleClickDictationButton']
@@ -14298,6 +15087,9 @@ const renderEventListeners = () => {
14298
15087
  name: HandleContextMenuChatImageAttachment,
14299
15088
  params: ['handleContextMenuChatImageAttachment', TargetName, ClientX, ClientY],
14300
15089
  preventDefault: true
15090
+ }, {
15091
+ name: HandleErrorComposerAttachmentPreviewOverlay,
15092
+ params: ['handleErrorComposerAttachmentPreviewOverlay']
14301
15093
  }];
14302
15094
  };
14303
15095
 
@@ -14379,6 +15171,8 @@ const saveState = state => {
14379
15171
  const {
14380
15172
  agentMode,
14381
15173
  chatListScrollTop,
15174
+ composerSelectionEnd,
15175
+ composerSelectionStart,
14382
15176
  composerValue,
14383
15177
  lastNormalViewMode,
14384
15178
  maxToolCalls,
@@ -14400,6 +15194,8 @@ const saveState = state => {
14400
15194
  return {
14401
15195
  agentMode,
14402
15196
  chatListScrollTop,
15197
+ composerSelectionEnd,
15198
+ composerSelectionStart,
14403
15199
  composerValue,
14404
15200
  lastNormalViewMode,
14405
15201
  maxToolCalls,
@@ -14496,6 +15292,13 @@ const setResponsivePickerVisibilityEnabled = (state, responsivePickerVisibilityE
14496
15292
  };
14497
15293
  };
14498
15294
 
15295
+ const setScrollDownButtonEnabled = (state, scrollDownButtonEnabled) => {
15296
+ return {
15297
+ ...state,
15298
+ scrollDownButtonEnabled
15299
+ };
15300
+ };
15301
+
14499
15302
  const setSearchEnabled = (state, searchEnabled) => {
14500
15303
  return {
14501
15304
  ...state,
@@ -14628,6 +15431,7 @@ const commandMap = {
14628
15431
  'Chat.getKeyBindings': getKeyBindings,
14629
15432
  'Chat.getMenuEntries': getMenuEntries,
14630
15433
  'Chat.getMenuEntryIds': getMenuEntryIds,
15434
+ 'Chat.getMockOpenApiRequests': wrapGetter(getMockOpenApiRequests),
14631
15435
  'Chat.getQuickPickMenuEntries': getQuickPickMenuEntries,
14632
15436
  'Chat.getSelectedSessionId': wrapGetter(getSelectedSessionId),
14633
15437
  'Chat.getSystemPrompt': wrapGetter(getSystemPrompt),
@@ -14660,6 +15464,7 @@ const commandMap = {
14660
15464
  'Chat.handleDragLeave': wrapCommand(handleDragLeave),
14661
15465
  'Chat.handleDragOver': wrapCommand(handleDragOver),
14662
15466
  'Chat.handleDropFiles': wrapCommand(handleDropFiles),
15467
+ 'Chat.handleErrorComposerAttachmentPreviewOverlay': wrapCommand(handleErrorComposerAttachmentPreviewOverlay),
14663
15468
  'Chat.handleInput': wrapCommand(handleInput),
14664
15469
  'Chat.handleInputFocus': wrapCommand(handleInputFocus),
14665
15470
  'Chat.handleKeyDown': wrapCommand(handleKeyDown),
@@ -14670,6 +15475,8 @@ const commandMap = {
14670
15475
  'Chat.handleModelChange': wrapCommand(handleModelChange),
14671
15476
  'Chat.handleModelInputBlur': wrapCommand(handleModelInputBlur),
14672
15477
  'Chat.handleModelPickerListScroll': wrapCommand(handleModelPickerListScroll),
15478
+ 'Chat.handleMouseOut': wrapCommand(handleMouseOut),
15479
+ 'Chat.handleMouseOver': wrapCommand(handleMouseOver),
14673
15480
  'Chat.handlePointerDownModelPickerList': wrapCommand(handlePointerDownModelPickerList),
14674
15481
  'Chat.handlePointerUpModelPickerList': wrapCommand(handlePointerUpModelPickerList),
14675
15482
  'Chat.handleProjectAddButtonContextMenu': wrapCommand(handleProjectAddButtonContextMenu),
@@ -14679,6 +15486,7 @@ const commandMap = {
14679
15486
  'Chat.handleRunModeChange': wrapCommand(handleRunModeChange),
14680
15487
  'Chat.handleSearchValueChange': wrapCommand(handleSearchValueChange),
14681
15488
  'Chat.handleSubmit': wrapCommand(handleSubmit),
15489
+ 'Chat.hideComposerAttachmentPreviewOverlay': wrapCommand(hideComposerAttachmentPreviewOverlay),
14682
15490
  'Chat.initialize': initialize,
14683
15491
  'Chat.loadContent': wrapCommand(loadContent),
14684
15492
  'Chat.loadContent2': wrapCommand(loadContent),
@@ -14691,6 +15499,7 @@ const commandMap = {
14691
15499
  'Chat.mockOpenApiStreamPushChunk': wrapCommand(mockOpenApiStreamPushChunk),
14692
15500
  'Chat.mockOpenApiStreamReset': wrapCommand(mockOpenApiStreamReset),
14693
15501
  'Chat.openAgentModePicker': wrapCommand(openAgentModePicker),
15502
+ 'Chat.openMockProject': wrapCommand(openMockProject),
14694
15503
  'Chat.openMockSession': wrapCommand(openMockSession),
14695
15504
  'Chat.openModelPicker': wrapCommand(openModelPicker),
14696
15505
  'Chat.openReasoningEffortPicker': wrapCommand(openReasoningEffortPicker),
@@ -14715,6 +15524,7 @@ const commandMap = {
14715
15524
  'Chat.setReasoningEffort': wrapCommand(setReasoningEffort),
14716
15525
  'Chat.setReasoningPickerEnabled': wrapCommand(setReasoningPickerEnabled),
14717
15526
  'Chat.setResponsivePickerVisibilityEnabled': wrapCommand(setResponsivePickerVisibilityEnabled),
15527
+ 'Chat.setScrollDownButtonEnabled': wrapCommand(setScrollDownButtonEnabled),
14718
15528
  'Chat.setSearchEnabled': wrapCommand(setSearchEnabled),
14719
15529
  'Chat.setShowRunMode': wrapCommand(setShowRunMode),
14720
15530
  'Chat.setStreamingEnabled': wrapCommand(setStreamingEnabled),
@@ -14725,6 +15535,7 @@ const commandMap = {
14725
15535
  'Chat.setUseChatMathWorker': wrapCommand(setUseChatMathWorker),
14726
15536
  'Chat.setUseChatMessageParsingWorker': wrapCommand(setUseChatMessageParsingWorker),
14727
15537
  'Chat.setUseChatNetworkWorkerForRequests': wrapCommand(setUseChatNetworkWorkerForRequests),
15538
+ 'Chat.showComposerAttachmentPreviewOverlay': wrapCommand(showComposerAttachmentPreviewOverlay),
14728
15539
  'Chat.terminate': terminate,
14729
15540
  'Chat.useMockApi': wrapCommand(useMockApi)
14730
15541
  };