@lvce-editor/chat-view 6.67.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.
@@ -1856,6 +1856,7 @@ const Paste = 'Paste';
1856
1856
  const Rename = 'Rename';
1857
1857
  const Archive = 'Archive';
1858
1858
  const AddProjectChat = 'Add Project';
1859
+ const RemoveProject = 'Remove Project';
1859
1860
  const Settings$1 = 'Settings';
1860
1861
  const LoginToBackend = 'Login to backend';
1861
1862
  const LogoutFromBackend = 'Logout from backend';
@@ -1915,6 +1916,9 @@ const newChat = () => {
1915
1916
  const addProject = () => {
1916
1917
  return i18nString(AddProjectChat);
1917
1918
  };
1919
+ const removeProject = () => {
1920
+ return i18nString(RemoveProject);
1921
+ };
1918
1922
  const debug = () => {
1919
1923
  return i18nString(Debug);
1920
1924
  };
@@ -3412,6 +3416,7 @@ const Login = 'login';
3412
3416
  const Logout = 'logout';
3413
3417
  const CloseChat = 'close-chat';
3414
3418
  const SessionDelete = 'SessionDelete';
3419
+ const ProjectDelete = 'ProjectDelete';
3415
3420
  const ProjectPrefix = 'project:';
3416
3421
  const SessionPrefix = 'session:';
3417
3422
  const RenamePrefix = 'session-rename:';
@@ -3520,17 +3525,27 @@ const menuEntryAddProject = {
3520
3525
  id: 'addProject',
3521
3526
  label: addProject()
3522
3527
  };
3523
- const getMenuEntriesChatProjectList = (projectId = '') => {
3528
+ const getMenuEntriesChatProjectList = (projectId = '', canRemoveProject = true) => {
3524
3529
  if (!projectId) {
3525
3530
  return [menuEntryAddProject];
3526
3531
  }
3527
- return [{
3532
+ const entries = [{
3528
3533
  args: [getCreateSessionInProjectInputName(projectId)],
3529
3534
  command: 'Chat.handleClick',
3530
3535
  flags: None,
3531
3536
  id: 'newChat',
3532
3537
  label: newChat()
3533
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;
3534
3549
  };
3535
3550
 
3536
3551
  const getMenuEntriesProjectAddButton = () => {
@@ -3564,7 +3579,7 @@ const getMenuEntries = (menuId, props) => {
3564
3579
  case MenuChatList:
3565
3580
  return getMenuEntriesChatList();
3566
3581
  case MenuChatProjectList:
3567
- return getMenuEntriesChatProjectList(props.projectId);
3582
+ return getMenuEntriesChatProjectList(props.projectId, props.canRemoveProject);
3568
3583
  case MenuProjectAddButton:
3569
3584
  return getMenuEntriesProjectAddButton();
3570
3585
  default:
@@ -3665,6 +3680,15 @@ const handleChatListContextMenu = async (state, eventX, eventY) => {
3665
3680
  };
3666
3681
  };
3667
3682
 
3683
+ const focusInput = state => {
3684
+ return {
3685
+ ...state,
3686
+ focus: 'composer',
3687
+ focused: true,
3688
+ listFocusedIndex: -1
3689
+ };
3690
+ };
3691
+
3668
3692
  const generateSessionId = () => {
3669
3693
  return crypto.randomUUID();
3670
3694
  };
@@ -3680,7 +3704,7 @@ const createSession = async (state, projectIdOverride = '') => {
3680
3704
  title: `Chat ${state.sessions.length + 1}`
3681
3705
  };
3682
3706
  await saveChatSession(session);
3683
- return {
3707
+ return focusInput({
3684
3708
  ...state,
3685
3709
  composerAttachments: [],
3686
3710
  composerAttachmentsHeight: 0,
@@ -3689,6 +3713,84 @@ const createSession = async (state, projectIdOverride = '') => {
3689
3713
  selectedProjectId: projectId,
3690
3714
  selectedSessionId: id,
3691
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)
3692
3794
  };
3693
3795
  };
3694
3796
 
@@ -6726,6 +6828,7 @@ const ChatAuthError = 'ChatAuthError';
6726
6828
  const ChatFocus = 'ChatFocus';
6727
6829
  const ChatFocusActions = 'ChatFocusActions';
6728
6830
  const ChatFocusHeader = 'ChatFocusHeader';
6831
+ const ChatFocusMainArea = 'ChatFocusMainArea';
6729
6832
  const ChatFocusProject = 'ChatFocusProject';
6730
6833
  const ChatHeader = 'ChatHeader';
6731
6834
  const ChatHeaderLabel = 'ChatHeaderLabel';
@@ -6768,6 +6871,13 @@ const ChatComposerAttachmentPreviewOverlayImage = 'ChatComposerAttachmentPreview
6768
6871
  const ChatComposerAttachmentRemoveButton = 'ChatComposerAttachmentRemoveButton';
6769
6872
  const ChatComposerAttachments = 'ChatComposerAttachments';
6770
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';
6771
6881
  const ChatSendArea = 'ChatSendArea';
6772
6882
  const ChatSendAreaBottom = 'ChatSendAreaBottom';
6773
6883
  const ChatSendAreaContent = 'ChatSendAreaContent';
@@ -6817,6 +6927,7 @@ const MaskIconAccount = 'MaskIconAccount';
6817
6927
  const MaskIconAdd = 'MaskIconAdd';
6818
6928
  const MaskIconArrowLeft = 'MaskIconArrowLeft';
6819
6929
  const MaskIconChevronDown = 'MaskIconChevronDown';
6930
+ const MaskIconChevronRight = 'MaskIconChevronRight';
6820
6931
  const MaskIconChevronUp = 'MaskIconChevronUp';
6821
6932
  const MaskIconClose = 'MaskIconClose';
6822
6933
  const MaskIconDebugPause = 'MaskIconDebugPause';
@@ -8416,15 +8527,6 @@ const getSlashCommandHandler = command => {
8416
8527
  return slashCommandRegistry[command];
8417
8528
  };
8418
8529
 
8419
- const focusInput = state => {
8420
- return {
8421
- ...state,
8422
- focus: 'composer',
8423
- focused: true,
8424
- listFocusedIndex: -1
8425
- };
8426
- };
8427
-
8428
8530
  const withClearedComposer = state => {
8429
8531
  return focusInput({
8430
8532
  ...state,
@@ -9556,6 +9658,8 @@ const handleClick = async (state, name, id = '', eventX = 0, eventY = 0) => {
9556
9658
  }
9557
9659
  case name === SessionDelete:
9558
9660
  return deleteSession(state, id);
9661
+ case name === ProjectDelete:
9662
+ return deleteProject(state, id);
9559
9663
  case name === Send:
9560
9664
  return handleClickSend(state);
9561
9665
  case name === ScrollDown:
@@ -10142,7 +10246,7 @@ const handleProjectAddButtonContextMenu = async (state, button, x, y) => {
10142
10246
  return state;
10143
10247
  };
10144
10248
 
10145
- const getProjectIdAtIndex = (state, index) => {
10249
+ const getProjectAtIndex = (state, index) => {
10146
10250
  const {
10147
10251
  projectExpandedIds,
10148
10252
  projects,
@@ -10152,7 +10256,7 @@ const getProjectIdAtIndex = (state, index) => {
10152
10256
  let currentIndex = 0;
10153
10257
  for (const project of projects) {
10154
10258
  if (currentIndex === index) {
10155
- return project.id;
10259
+ return project;
10156
10260
  }
10157
10261
  currentIndex++;
10158
10262
  if (projectExpandedIds.includes(project.id)) {
@@ -10162,13 +10266,13 @@ const getProjectIdAtIndex = (state, index) => {
10162
10266
  continue;
10163
10267
  }
10164
10268
  if (currentIndex === index) {
10165
- return project.id;
10269
+ return project;
10166
10270
  }
10167
10271
  currentIndex++;
10168
10272
  }
10169
10273
  }
10170
10274
  }
10171
- return '';
10275
+ return undefined;
10172
10276
  };
10173
10277
  const handleProjectListContextMenu = async (state, button, x, y) => {
10174
10278
  const {
@@ -10181,13 +10285,14 @@ const handleProjectListContextMenu = async (state, button, x, y) => {
10181
10285
  if (index < 0) {
10182
10286
  return state;
10183
10287
  }
10184
- const projectId = getProjectIdAtIndex(state, index);
10185
- if (!projectId) {
10288
+ const project = getProjectAtIndex(state, index);
10289
+ if (!project) {
10186
10290
  return state;
10187
10291
  }
10188
10292
  await showContextMenu2(uid, MenuChatProjectList, x, y, {
10293
+ canRemoveProject: project.name !== '_blank',
10189
10294
  menuId: MenuChatProjectList,
10190
- projectId
10295
+ projectId: project.id
10191
10296
  });
10192
10297
  return state;
10193
10298
  };
@@ -11006,6 +11111,29 @@ const mockOpenApiStreamReset = state => {
11006
11111
  return state;
11007
11112
  };
11008
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
+
11009
11137
  const openMockSession = async (state, mockSessionId, mockChatMessages) => {
11010
11138
  const {
11011
11139
  sessions: currentSessions
@@ -11158,6 +11286,56 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
11158
11286
  border-color: var(--vscode-charts-green, var(--vscode-widget-border, var(--vscode-panel-border)));
11159
11287
  }
11160
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
+
11161
11339
  .Chat{
11162
11340
  position: relative;
11163
11341
  }
@@ -11232,6 +11410,13 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
11232
11410
  color: var(--vscode-textLink-foreground);
11233
11411
  }
11234
11412
 
11413
+ .ProjectListChevron{
11414
+ display: inline-block;
11415
+ flex: none;
11416
+ height: 16px;
11417
+ width: 16px;
11418
+ }
11419
+
11235
11420
  .ChatOrderedListItem{
11236
11421
  align-items: flex-start;
11237
11422
  display: flex;
@@ -11286,6 +11471,10 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
11286
11471
  justify-content: flex-start;
11287
11472
  }
11288
11473
 
11474
+ .ChatMessages > .MessageUser .ChatAttachments{
11475
+ justify-content: flex-end;
11476
+ }
11477
+
11289
11478
  .ChatFocus .ChatMessages > .Message{
11290
11479
  inline-size: fit-content;
11291
11480
  max-inline-size: min(100%, var(--ChatFocusContentMaxWidth));
@@ -11295,6 +11484,24 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
11295
11484
  max-inline-size: 100%;
11296
11485
  }
11297
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
+
11298
11505
  `;
11299
11506
  return `${baseCss}
11300
11507
 
@@ -11869,6 +12076,10 @@ const getChatHeaderDomFocusMode = (selectedSessionTitle, selectedProjectName) =>
11869
12076
  }, ...items.flatMap(([label, name]) => getFocusHeaderActionButtonDom(label, name))];
11870
12077
  };
11871
12078
 
12079
+ const getAgentModeOptionsVirtualDom = selectedAgentMode => {
12080
+ return agentModes.flatMap(agentMode => getCustomSelectOptionVirtualDom(getAgentModePickerItemInputName(agentMode), getAgentModeLabel(agentMode), agentMode === selectedAgentMode));
12081
+ };
12082
+
11872
12083
  const getCustomSelectPopOverVirtualDom = (optionCount, height, optionNodes, containerClassName = '', popOverClassName = '') => {
11873
12084
  return [{
11874
12085
  childCount: 1,
@@ -11889,9 +12100,6 @@ const getCustomSelectPopOverVirtualDom = (optionCount, height, optionNodes, cont
11889
12100
  };
11890
12101
 
11891
12102
  const agentModePickerHeight = agentModes.length * 28;
11892
- const getAgentModeOptionsVirtualDom = selectedAgentMode => {
11893
- return agentModes.flatMap(agentMode => getCustomSelectOptionVirtualDom(getAgentModePickerItemInputName(agentMode), getAgentModeLabel(agentMode), agentMode === selectedAgentMode));
11894
- };
11895
12103
  const getAgentModePickerPopOverVirtualDom = selectedAgentMode => {
11896
12104
  return getCustomSelectPopOverVirtualDom(agentModes.length, agentModePickerHeight, getAgentModeOptionsVirtualDom(selectedAgentMode));
11897
12105
  };
@@ -12177,12 +12385,17 @@ const getTokenDom = token => {
12177
12385
 
12178
12386
  const getCodeBlockDom = node => {
12179
12387
  const tokenDom = node.codeTokens.flatMap(getTokenDom);
12388
+ const languageAttribute = node.language ? {
12389
+ 'data-lang': node.language
12390
+ } : {};
12180
12391
  return [{
12181
12392
  childCount: 1,
12182
- type: Pre
12393
+ type: Pre,
12394
+ ...languageAttribute
12183
12395
  }, {
12184
12396
  childCount: node.codeTokens.length,
12185
- type: Code
12397
+ type: Code,
12398
+ ...languageAttribute
12186
12399
  }, ...tokenDom];
12187
12400
  };
12188
12401
 
@@ -12716,69 +12929,6 @@ const getToolCallCreateDirectoryVirtualDom = toolCall => {
12716
12929
  }, ...getToolCallFileNameDom(directoryName, fileNameClickableProps), ...(statusLabel ? [text(statusLabel)] : [])];
12717
12930
  };
12718
12931
 
12719
- const getToolCallEditFileVirtualDom = toolCall => {
12720
- const target = getReadFileTarget(toolCall.arguments);
12721
- if (!target) {
12722
- return [];
12723
- }
12724
- const fileName = getFileNameFromUri(target.title);
12725
- const fileNameClickableProps = target.clickableUri ? {
12726
- 'data-uri': target.clickableUri,
12727
- onClick: HandleClickFileName
12728
- } : {};
12729
- return [{
12730
- childCount: 3,
12731
- className: ChatOrderedListItem,
12732
- title: target.title,
12733
- type: Li
12734
- }, {
12735
- childCount: 0,
12736
- className: FileIcon,
12737
- type: Div
12738
- }, {
12739
- childCount: 1,
12740
- className: ToolCallName,
12741
- type: Span
12742
- }, text('edit_file '), {
12743
- childCount: 1,
12744
- className: ChatToolCallReadFileLink,
12745
- title: target.clickableUri,
12746
- ...fileNameClickableProps,
12747
- type: Span
12748
- }, ...getToolCallFileNameDom(fileName, fileNameClickableProps)];
12749
- };
12750
-
12751
- const getToolCallGetWorkspaceUriVirtualDom = toolCall => {
12752
- if (!toolCall.result) {
12753
- return [];
12754
- }
12755
- const statusLabel = getToolCallStatusLabel(toolCall);
12756
- const fileName = getFileNameFromUri(toolCall.result);
12757
- const fileNameClickableProps = {
12758
- 'data-uri': toolCall.result,
12759
- onClick: HandleClickFileName
12760
- };
12761
- return [{
12762
- childCount: statusLabel ? 4 : 3,
12763
- className: ChatOrderedListItem,
12764
- title: toolCall.result,
12765
- type: Li
12766
- }, {
12767
- childCount: 0,
12768
- className: FileIcon,
12769
- type: Div
12770
- }, {
12771
- childCount: 1,
12772
- className: ToolCallName,
12773
- type: Span
12774
- }, text('get_workspace_uri '), {
12775
- childCount: 1,
12776
- className: ChatToolCallReadFileLink,
12777
- ...fileNameClickableProps,
12778
- type: Span
12779
- }, ...getToolCallFileNameDom(fileName, fileNameClickableProps), ...(statusLabel ? [text(statusLabel)] : [])];
12780
- };
12781
-
12782
12932
  const getToolCallArgumentPreview = rawArguments => {
12783
12933
  if (!rawArguments.trim()) {
12784
12934
  return '""';
@@ -12853,6 +13003,91 @@ const getToolCallLabel = toolCall => {
12853
13003
  return `${displayName} ${argumentPreview}${statusLabel}`;
12854
13004
  };
12855
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
+
12856
13091
  const getToolCallReadFileVirtualDom = toolCall => {
12857
13092
  const target = getReadFileTarget(toolCall.arguments);
12858
13093
  if (!target) {
@@ -13276,7 +13511,6 @@ const getToolCallWriteFileVirtualDom = toolCall => {
13276
13511
  }, text(` -${linesDeleted}`)] : []), ...(statusLabel ? [text(statusLabel)] : [])];
13277
13512
  };
13278
13513
 
13279
- const RE_TOOL_NAME_PREFIX = /^([^ :]+)/;
13280
13514
  const getToolCallDom = toolCall => {
13281
13515
  if (toolCall.name === 'getWorkspaceUri') {
13282
13516
  const virtualDom = getToolCallGetWorkspaceUriVirtualDom(toolCall);
@@ -13320,24 +13554,7 @@ const getToolCallDom = toolCall => {
13320
13554
  return virtualDom;
13321
13555
  }
13322
13556
  }
13323
- const label = getToolCallLabel(toolCall);
13324
- const match = RE_TOOL_NAME_PREFIX.exec(label);
13325
- const toolNamePrefix = match ? match[1] : label;
13326
- const suffix = label.slice(toolNamePrefix.length);
13327
- const hasSuffix = suffix.length > 0;
13328
- const hoverTitle = hasSuffix && toolCall.arguments.trim() ? toolCall.arguments : undefined;
13329
- return [{
13330
- childCount: hasSuffix ? 2 : 1,
13331
- className: ChatOrderedListItem,
13332
- ...(hoverTitle ? {
13333
- title: hoverTitle
13334
- } : {}),
13335
- type: Li
13336
- }, {
13337
- childCount: 1,
13338
- className: ToolCallName,
13339
- type: Span
13340
- }, text(toolNamePrefix), ...(hasSuffix ? [text(suffix)] : [])];
13557
+ return getToolCallDefaultDom(toolCall);
13341
13558
  };
13342
13559
 
13343
13560
  const withOrderedListMarker = (virtualDom, index) => {
@@ -13395,6 +13612,70 @@ const getTopLevelNodeCount = nodes => {
13395
13612
  return topLevelCount;
13396
13613
  };
13397
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
+ };
13398
13679
  const getChatMessageDom = (message, parsedMessageContent, _openRouterApiKeyInput, _openApiApiKeyInput = '', openApiApiKeyState = 'idle', openApiApiKeysSettingsUrl = 'https://platform.openai.com/api-keys', openApiApiKeyInputPattern = '^sk-.+', openRouterApiKeyState = 'idle', useChatMathWorker = false) => {
13399
13680
  const roleClassName = message.role === 'user' ? MessageUser : MessageAssistant;
13400
13681
  const isOpenApiApiKeyMissingMessage = message.role === 'assistant' && message.text === openApiApiKeyRequiredMessage;
@@ -13402,10 +13683,12 @@ const getChatMessageDom = (message, parsedMessageContent, _openRouterApiKeyInput
13402
13683
  const isOpenRouterRequestFailedMessage = message.role === 'assistant' && message.text === openRouterRequestFailedMessage;
13403
13684
  const isOpenRouterTooManyRequestsMessage = message.role === 'assistant' && message.text.startsWith(openRouterTooManyRequestsMessage);
13404
13685
  const messageDom = getMessageContentDom(parsedMessageContent, useChatMathWorker);
13686
+ const attachmentsDom = message.role === 'user' ? getChatAttachmentsDom(message.attachments ?? []) : [];
13405
13687
  const toolCallsDom = getToolCallsDom(message);
13406
13688
  const toolCallsChildCount = toolCallsDom.length > 0 ? 1 : 0;
13407
13689
  const messageDomChildCount = getTopLevelNodeCount(messageDom);
13408
- 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;
13409
13692
  return [{
13410
13693
  childCount: 1,
13411
13694
  className: mergeClassNames(Message, roleClassName),
@@ -13414,7 +13697,7 @@ const getChatMessageDom = (message, parsedMessageContent, _openRouterApiKeyInput
13414
13697
  childCount: extraChildCount,
13415
13698
  className: ChatMessageContent,
13416
13699
  type: Div
13417
- }, ...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() : [])];
13418
13701
  };
13419
13702
 
13420
13703
  const parentNode$1 = {
@@ -13470,7 +13753,18 @@ const getDisplayMessages = (messages, parsedMessages) => {
13470
13753
  };
13471
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) => {
13472
13755
  if (messages.length === 0) {
13473
- 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
+ }];
13474
13768
  }
13475
13769
  const displayMessages = getDisplayMessages(messages, parsedMessages);
13476
13770
  return [{
@@ -13537,10 +13831,10 @@ const getProjectGroupDom = (project, sessions, projectExpandedIds, selectedProje
13537
13831
  tabIndex: 0,
13538
13832
  type: Div
13539
13833
  }, {
13540
- childCount: 1,
13541
- className: ProjectListChevron,
13542
- type: Span
13543
- }, text(expanded ? '▾' : '▸'), {
13834
+ childCount: 0,
13835
+ className: mergeClassNames(ProjectListChevron, MaskIcon, expanded ? MaskIconChevronDown : MaskIconChevronRight),
13836
+ type: Div
13837
+ }, {
13544
13838
  childCount: 0,
13545
13839
  className: 'MaskIcon MaskIconFolder',
13546
13840
  type: Div
@@ -13655,14 +13949,18 @@ const getChatModeChatFocusVirtualDom = ({
13655
13949
  const isNewModelPickerVisible = modelPickerOpen;
13656
13950
  const isRunModePickerVisible = showRunMode && hasSpaceForRunModePicker && runModePickerOpen;
13657
13951
  const hasVisibleOverlays = isDropOverlayVisible || isComposerAttachmentPreviewOverlayVisible || isAgentModePickerVisible || isNewModelPickerVisible || isRunModePickerVisible;
13658
- const chatRootChildCount = 4 + (hasVisibleOverlays ? 1 : 0);
13952
+ const chatRootChildCount = 2 + (hasVisibleOverlays ? 1 : 0);
13659
13953
  return [{
13660
13954
  childCount: chatRootChildCount,
13661
13955
  className: mergeClassNames(Viewlet, Chat, ChatFocus),
13662
13956
  onDragEnter: HandleDragEnterChatView,
13663
13957
  onDragOver: HandleDragOverChatView,
13664
13958
  type: Div
13665
- }, ...getChatHeaderDomFocusMode(selectedSessionTitle, selectedProjectName), ...getMessagesDom(messages, parsedMessages, openRouterApiKeyInput, openApiApiKeyInput, openApiApiKeyState, openApiApiKeysSettingsUrl, openApiApiKeyInputPattern, openRouterApiKeyState, messagesScrollTop, useChatMathWorker, true), ...getProjectListDom(projects, sessions, projectExpandedIds, selectedProjectId, selectedSessionId, projectListScrollTop, true), ...getChatSendAreaDom(composerValue, composerAttachments, agentMode, agentModePickerOpen, hasSpaceForAgentModePicker, modelPickerOpen, models, selectedModelId, reasoningPickerEnabled, reasoningEffort, reasoningEffortPickerOpen, usageOverviewEnabled, tokensUsed, tokensMax, addContextButtonEnabled, showRunMode, hasSpaceForRunModePicker, runMode, runModePickerOpen, todoListToolEnabled, todoListItems, showCreatePullRequestButton, voiceDictationEnabled, scrollDownButtonEnabled, messagesAutoScrollEnabled), ...getChatOverlaysVirtualDom({
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({
13666
13964
  agentMode,
13667
13965
  agentModePickerVisible: isAgentModePickerVisible,
13668
13966
  composerAttachmentPreviewOverlayAttachmentId,
@@ -13715,22 +14013,31 @@ const getHeaderActionVirtualDom = item => {
13715
14013
  }];
13716
14014
  };
13717
14015
 
13718
- const getChatHeaderActionsDom = (viewMode, authEnabled = false, authStatus = 'signed-out', searchEnabled = false) => {
13719
- const toggleTitle = viewMode === 'chat-focus' ? normalChatMode() : chatFocusMode();
14016
+ const getAuthAction = (authEnabled, authStatus) => {
13720
14017
  const isSigningIn = authStatus === 'signing-in';
13721
- const authAction = authEnabled && authStatus !== 'signed-in' ? {
13722
- disabled: isSigningIn,
13723
- icon: mergeClassNames(MaskIcon, MaskIconAccount),
13724
- name: Login,
13725
- onClick: HandleClick,
13726
- title: isSigningIn ? loggingInToBackend() : loginToBackend()
13727
- } : 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 {
13728
14031
  disabled: false,
13729
14032
  icon: mergeClassNames(MaskIcon, MaskIconSignOut),
13730
14033
  name: Logout,
13731
14034
  onClick: HandleClick,
13732
14035
  title: logoutFromBackend()
13733
- } : 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);
13734
14041
  const items = [{
13735
14042
  icon: mergeClassNames(MaskIcon, MaskIconLayoutPanelLeft),
13736
14043
  name: ToggleChatFocus,
@@ -15192,6 +15499,7 @@ const commandMap = {
15192
15499
  'Chat.mockOpenApiStreamPushChunk': wrapCommand(mockOpenApiStreamPushChunk),
15193
15500
  'Chat.mockOpenApiStreamReset': wrapCommand(mockOpenApiStreamReset),
15194
15501
  'Chat.openAgentModePicker': wrapCommand(openAgentModePicker),
15502
+ 'Chat.openMockProject': wrapCommand(openMockProject),
15195
15503
  'Chat.openMockSession': wrapCommand(openMockSession),
15196
15504
  'Chat.openModelPicker': wrapCommand(openModelPicker),
15197
15505
  'Chat.openReasoningEffortPicker': wrapCommand(openReasoningEffortPicker),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/chat-view",
3
- "version": "6.67.0",
3
+ "version": "6.68.0",
4
4
  "description": "Chat View Worker",
5
5
  "repository": {
6
6
  "type": "git",