@lvce-editor/chat-view 6.10.0 → 6.13.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.
@@ -1664,6 +1664,10 @@ const createDefaultState = () => {
1664
1664
  chatMessageFontFamily: 'system-ui',
1665
1665
  chatMessageFontSize,
1666
1666
  chatMessageLineHeight,
1667
+ chatSendAreaPaddingBottom: 10,
1668
+ chatSendAreaPaddingLeft: 8,
1669
+ chatSendAreaPaddingRight: 8,
1670
+ chatSendAreaPaddingTop: 10,
1667
1671
  composerDropActive: false,
1668
1672
  composerDropEnabled: true,
1669
1673
  composerFontFamily: 'system-ui',
@@ -1710,6 +1714,7 @@ const createDefaultState = () => {
1710
1714
  }],
1711
1715
  questionToolEnabled: false,
1712
1716
  renamingSessionId: '',
1717
+ runMode: 'local',
1713
1718
  selectedModelId: defaultModelId,
1714
1719
  selectedProjectId: defaultProjectId,
1715
1720
  selectedSessionId: defaultSessionId,
@@ -1719,7 +1724,13 @@ const createDefaultState = () => {
1719
1724
  projectId: defaultProjectId,
1720
1725
  title: defaultSessionTitle()
1721
1726
  }],
1727
+ showRunMode: false,
1722
1728
  streamingEnabled: true,
1729
+ textAreaPaddingBottom: 0,
1730
+ textAreaPaddingLeft: 12,
1731
+ textAreaPaddingRight: 12,
1732
+ textAreaPaddingTop: 0,
1733
+ todoListToolEnabled: false,
1723
1734
  tokensMax: 0,
1724
1735
  tokensUsed: 0,
1725
1736
  uid: 0,
@@ -2513,7 +2524,7 @@ const getRenderHtmlCss = (sessions, selectedSessionId) => {
2513
2524
  const isEqual$1 = (oldState, newState) => {
2514
2525
  const oldRenderHtmlCss = getRenderHtmlCss(oldState.sessions, oldState.selectedSessionId);
2515
2526
  const newRenderHtmlCss = getRenderHtmlCss(newState.sessions, newState.selectedSessionId);
2516
- return oldState.initial === newState.initial && oldState.chatMessageFontFamily === newState.chatMessageFontFamily && oldState.chatMessageFontSize === newState.chatMessageFontSize && oldState.chatMessageLineHeight === newState.chatMessageLineHeight && oldState.composerHeight === newState.composerHeight && oldState.composerLineHeight === newState.composerLineHeight && oldState.composerFontFamily === newState.composerFontFamily && oldState.composerFontSize === newState.composerFontSize && oldState.listItemHeight === newState.listItemHeight && oldRenderHtmlCss === newRenderHtmlCss;
2527
+ return oldState.initial === newState.initial && oldState.chatMessageFontFamily === newState.chatMessageFontFamily && oldState.chatMessageFontSize === newState.chatMessageFontSize && oldState.chatMessageLineHeight === newState.chatMessageLineHeight && oldState.chatSendAreaPaddingTop === newState.chatSendAreaPaddingTop && oldState.chatSendAreaPaddingLeft === newState.chatSendAreaPaddingLeft && oldState.chatSendAreaPaddingRight === newState.chatSendAreaPaddingRight && oldState.chatSendAreaPaddingBottom === newState.chatSendAreaPaddingBottom && oldState.composerHeight === newState.composerHeight && oldState.composerLineHeight === newState.composerLineHeight && oldState.composerFontFamily === newState.composerFontFamily && oldState.composerFontSize === newState.composerFontSize && oldState.listItemHeight === newState.listItemHeight && oldState.textAreaPaddingTop === newState.textAreaPaddingTop && oldState.textAreaPaddingLeft === newState.textAreaPaddingLeft && oldState.textAreaPaddingRight === newState.textAreaPaddingRight && oldState.textAreaPaddingBottom === newState.textAreaPaddingBottom && oldRenderHtmlCss === newRenderHtmlCss;
2517
2528
  };
2518
2529
 
2519
2530
  const diffFocus = (oldState, newState) => {
@@ -2535,7 +2546,7 @@ const isEqualProjectExpandedIds = (a, b) => {
2535
2546
  return true;
2536
2547
  };
2537
2548
  const isEqual = (oldState, newState) => {
2538
- return oldState.authEnabled === newState.authEnabled && oldState.authErrorMessage === newState.authErrorMessage && oldState.authStatus === newState.authStatus && oldState.composerDropActive === newState.composerDropActive && oldState.composerDropEnabled === newState.composerDropEnabled && oldState.composerValue === newState.composerValue && oldState.initial === newState.initial && isEqualProjectExpandedIds(oldState.projectExpandedIds, newState.projectExpandedIds) && oldState.projectListScrollTop === newState.projectListScrollTop && oldState.renamingSessionId === newState.renamingSessionId && oldState.selectedModelId === newState.selectedModelId && oldState.selectedProjectId === newState.selectedProjectId && oldState.selectedSessionId === newState.selectedSessionId && oldState.sessions === newState.sessions && oldState.tokensMax === newState.tokensMax && oldState.tokensUsed === newState.tokensUsed && oldState.usageOverviewEnabled === newState.usageOverviewEnabled && oldState.useChatMathWorker === newState.useChatMathWorker && oldState.viewMode === newState.viewMode && oldState.voiceDictationEnabled === newState.voiceDictationEnabled;
2549
+ return oldState.authEnabled === newState.authEnabled && oldState.authErrorMessage === newState.authErrorMessage && oldState.authStatus === newState.authStatus && oldState.composerDropActive === newState.composerDropActive && oldState.composerDropEnabled === newState.composerDropEnabled && oldState.composerValue === newState.composerValue && oldState.initial === newState.initial && isEqualProjectExpandedIds(oldState.projectExpandedIds, newState.projectExpandedIds) && oldState.projectListScrollTop === newState.projectListScrollTop && oldState.renamingSessionId === newState.renamingSessionId && oldState.selectedModelId === newState.selectedModelId && oldState.selectedProjectId === newState.selectedProjectId && oldState.selectedSessionId === newState.selectedSessionId && oldState.showRunMode === newState.showRunMode && oldState.runMode === newState.runMode && oldState.sessions === newState.sessions && oldState.tokensMax === newState.tokensMax && oldState.tokensUsed === newState.tokensUsed && oldState.usageOverviewEnabled === newState.usageOverviewEnabled && oldState.useChatMathWorker === newState.useChatMathWorker && oldState.viewMode === newState.viewMode && oldState.voiceDictationEnabled === newState.voiceDictationEnabled;
2539
2550
  };
2540
2551
 
2541
2552
  const diffScrollTop = (oldState, newState) => {
@@ -3126,12 +3137,14 @@ const selectProject = async (state, projectId) => {
3126
3137
  };
3127
3138
  };
3128
3139
 
3140
+ const fileSchemeRegex = /^file:\/\//;
3141
+ const trailingSlashesRegex$4 = /\/+$/;
3129
3142
  const getProjectName = (uri, fallbackIndex) => {
3130
3143
  if (!uri) {
3131
3144
  return `Project ${fallbackIndex}`;
3132
3145
  }
3133
- const withoutScheme = uri.replace(/^file:\/\//, '');
3134
- const normalized = withoutScheme.replace(/\/+$/, '');
3146
+ const withoutScheme = uri.replace(fileSchemeRegex, '');
3147
+ const normalized = withoutScheme.replace(trailingSlashesRegex$4, '');
3135
3148
  const lastSegment = normalized.split('/').pop();
3136
3149
  if (!lastSegment) {
3137
3150
  return `Project ${fallbackIndex}`;
@@ -3194,6 +3207,7 @@ const update = async settings => {
3194
3207
  await invoke$1('Preferences.update', settings);
3195
3208
  };
3196
3209
 
3210
+ const trailingSlashesRegex$3 = /\/+$/;
3197
3211
  const isLoginResponse = value => {
3198
3212
  if (!value || typeof value !== 'object') {
3199
3213
  return false;
@@ -3201,7 +3215,7 @@ const isLoginResponse = value => {
3201
3215
  return true;
3202
3216
  };
3203
3217
  const trimTrailingSlashes = value => {
3204
- return value.replace(/\/+$/, '');
3218
+ return value.replace(trailingSlashesRegex$3, '');
3205
3219
  };
3206
3220
  const handleClickLogin = async state => {
3207
3221
  if (!state.backendUrl) {
@@ -4001,6 +4015,8 @@ const getTextContent = content => {
4001
4015
 
4002
4016
  /* eslint-disable @typescript-eslint/prefer-readonly-parameter-types */
4003
4017
 
4018
+ const errorPrefixRegex = /^Error:\s*/;
4019
+ const notFoundErrorRegex = /not[\s_-]?found|enoent/i;
4004
4020
  const getOpenAiTools = tools => {
4005
4021
  return tools.map(tool => {
4006
4022
  if (!tool || typeof tool !== 'object') {
@@ -4066,7 +4082,7 @@ const getStreamChunkText = content => {
4066
4082
  }).join('');
4067
4083
  };
4068
4084
  const getShortToolErrorMessage = error => {
4069
- const trimmed = error.trim().replace(/^Error:\s*/, '');
4085
+ const trimmed = error.trim().replace(errorPrefixRegex, '');
4070
4086
  const firstLine = trimmed.split('\n')[0];
4071
4087
  if (firstLine.length <= 80) {
4072
4088
  return firstLine;
@@ -4098,7 +4114,7 @@ const getToolCallExecutionStatus = content => {
4098
4114
  const rawStack = Reflect.get(parsed, 'errorStack') ?? Reflect.get(parsed, 'stack');
4099
4115
  const errorStack = typeof rawStack === 'string' && rawStack.trim() ? rawStack : undefined;
4100
4116
  const errorMessage = getShortToolErrorMessage(rawError);
4101
- if (/not[\s_-]?found|enoent/i.test(errorMessage)) {
4117
+ if (notFoundErrorRegex.test(errorMessage)) {
4102
4118
  return {
4103
4119
  ...(errorStack ? {
4104
4120
  errorStack
@@ -5086,13 +5102,15 @@ const getOpenApiModelId = selectedModelId => {
5086
5102
  return selectedModelId;
5087
5103
  };
5088
5104
 
5105
+ const trailingSlashesRegex$2 = /\/+$/;
5089
5106
  const getOpenRouterApiEndpoint = openRouterApiBaseUrl => {
5090
- const trimmedBaseUrl = openRouterApiBaseUrl.replace(/\/+$/, '');
5107
+ const trimmedBaseUrl = openRouterApiBaseUrl.replace(trailingSlashesRegex$2, '');
5091
5108
  return `${trimmedBaseUrl}/chat/completions`;
5092
5109
  };
5093
5110
 
5111
+ const trailingSlashesRegex$1 = /\/+$/;
5094
5112
  const getOpenRouterKeyEndpoint = openRouterApiBaseUrl => {
5095
- const trimmedBaseUrl = openRouterApiBaseUrl.replace(/\/+$/, '');
5113
+ const trimmedBaseUrl = openRouterApiBaseUrl.replace(trailingSlashesRegex$1, '');
5096
5114
  return `${trimmedBaseUrl}/auth/key`;
5097
5115
  };
5098
5116
 
@@ -5478,8 +5496,9 @@ const getAll = () => {
5478
5496
 
5479
5497
  /* eslint-disable prefer-destructuring */
5480
5498
 
5499
+ const trailingSlashesRegex = /\/+$/;
5481
5500
  const getBackendCompletionsEndpoint = backendUrl => {
5482
- const trimmedBackendUrl = backendUrl.replace(/\/+$/, '');
5501
+ const trimmedBackendUrl = backendUrl.replace(trailingSlashesRegex, '');
5483
5502
  return `${trimmedBackendUrl}/v1/chat/completions`;
5484
5503
  };
5485
5504
  const getEffectiveBackendModelId = selectedModelId => {
@@ -6254,21 +6273,25 @@ const parseInlineNodes = value => {
6254
6273
  };
6255
6274
 
6256
6275
  const markdownMathBlockDelimiter = '$$';
6276
+ const escapedNewlineRegex = /\\r\\n|\\n/g;
6277
+ const lineBreakRegex = /\r?\n/;
6278
+ const tableDelimiterRegex = /\|\s*[-:]{3,}/;
6279
+ const inlineTableCellRegex = /\|\s+\|/g;
6257
6280
  const normalizeEscapedNewlines = value => {
6258
6281
  if (value.includes('\\n')) {
6259
- return value.replaceAll(/\\r\\n|\\n/g, '\n');
6282
+ return value.replaceAll(escapedNewlineRegex, '\n');
6260
6283
  }
6261
6284
  return value;
6262
6285
  };
6263
6286
  const normalizeInlineTables = value => {
6264
- return value.split(/\r?\n/).map(line => {
6287
+ return value.split(lineBreakRegex).map(line => {
6265
6288
  if (!line.includes('|')) {
6266
6289
  return line;
6267
6290
  }
6268
- if (!/\|\s*[-:]{3,}/.test(line)) {
6291
+ if (!tableDelimiterRegex.test(line)) {
6269
6292
  return line;
6270
6293
  }
6271
- return line.replaceAll(/\|\s+\|/g, '|\n|');
6294
+ return line.replaceAll(inlineTableCellRegex, '|\n|');
6272
6295
  }).join('\n');
6273
6296
  };
6274
6297
  const parseHeadingLine = line => {
@@ -6347,6 +6370,33 @@ const parseBlockQuoteLine = line => {
6347
6370
  }
6348
6371
  return content;
6349
6372
  };
6373
+ const isThematicBreakLine = line => {
6374
+ const trimmedStart = line.trimStart();
6375
+ const leadingSpaces = line.length - trimmedStart.length;
6376
+ if (leadingSpaces > 3) {
6377
+ return false;
6378
+ }
6379
+ const trimmed = trimmedStart.trimEnd();
6380
+ if (!trimmed) {
6381
+ return false;
6382
+ }
6383
+ const marker = trimmed[0];
6384
+ if (marker !== '-' && marker !== '_' && marker !== '*') {
6385
+ return false;
6386
+ }
6387
+ let markerCount = 0;
6388
+ for (let i = 0; i < trimmed.length; i++) {
6389
+ const char = trimmed[i];
6390
+ if (char === marker) {
6391
+ markerCount++;
6392
+ continue;
6393
+ }
6394
+ if (char !== ' ') {
6395
+ return false;
6396
+ }
6397
+ }
6398
+ return markerCount >= 3;
6399
+ };
6350
6400
  const isTableRow = line => {
6351
6401
  const trimmed = line.trim();
6352
6402
  if (!trimmed.startsWith('|') || !trimmed.endsWith('|')) {
@@ -6360,7 +6410,7 @@ const getTableCells = line => {
6360
6410
  };
6361
6411
  const scanBlockTokens = rawMessage => {
6362
6412
  const normalizedMessage = normalizeInlineTables(normalizeEscapedNewlines(rawMessage));
6363
- const lines = normalizedMessage.split(/\r?\n/);
6413
+ const lines = normalizedMessage.split(lineBreakRegex);
6364
6414
  const tokens = [];
6365
6415
  for (let i = 0; i < lines.length; i++) {
6366
6416
  const line = lines[i];
@@ -6426,6 +6476,12 @@ const scanBlockTokens = rawMessage => {
6426
6476
  });
6427
6477
  continue;
6428
6478
  }
6479
+ if (isThematicBreakLine(line)) {
6480
+ tokens.push({
6481
+ type: 'thematic-break'
6482
+ });
6483
+ continue;
6484
+ }
6429
6485
  const ordered = parseOrderedListItemLine(line);
6430
6486
  if (ordered) {
6431
6487
  tokens.push({
@@ -6571,6 +6627,14 @@ const parseBlockTokens = tokens => {
6571
6627
  });
6572
6628
  continue;
6573
6629
  }
6630
+ if (token.type === 'thematic-break') {
6631
+ flushList();
6632
+ flushParagraph();
6633
+ nodes.push({
6634
+ type: 'thematic-break'
6635
+ });
6636
+ continue;
6637
+ }
6574
6638
  if (token.type === 'blockquote-line') {
6575
6639
  flushList();
6576
6640
  flushParagraph();
@@ -6944,12 +7008,16 @@ const executeSlashCommand = async (state, command) => {
6944
7008
  });
6945
7009
  };
6946
7010
 
7011
+ const defaultSessionTitleRegex = /^Chat \d+$/;
6947
7012
  const isDefaultSessionTitle = title => {
6948
- return /^Chat \d+$/.test(title);
7013
+ return defaultSessionTitleRegex.test(title);
6949
7014
  };
6950
7015
 
7016
+ const titlePrefixRegex = /^title:\s*/i;
7017
+ const wrappedQuotesAndWhitespaceRegex = /^['"`\s]+|['"`\s]+$/g;
7018
+ const consecutiveWhitespaceRegex = /\s+/g;
6951
7019
  const sanitizeGeneratedTitle = value => {
6952
- return value.replace(/^title:\s*/i, '').replaceAll(/^['"`\s]+|['"`\s]+$/g, '').replaceAll(/\s+/g, ' ').trim().slice(0, 80);
7020
+ return value.replace(titlePrefixRegex, '').replaceAll(wrappedQuotesAndWhitespaceRegex, '').replaceAll(consecutiveWhitespaceRegex, ' ').trim().slice(0, 80);
6953
7021
  };
6954
7022
 
6955
7023
  const getAiSessionTitle = async (state, userText, assistantText) => {
@@ -7020,6 +7088,8 @@ Assistant: ${assistantText}`;
7020
7088
  return title && !isDefaultSessionTitle(title) ? title : '';
7021
7089
  };
7022
7090
 
7091
+ const windowsAbsolutePathRegex = /^[a-zA-Z]:[\\/]/;
7092
+ const pathSeparatorRegex$1 = /[\\/]/;
7023
7093
  const isPathTraversalAttempt = path => {
7024
7094
  if (!path) {
7025
7095
  return false;
@@ -7030,15 +7100,16 @@ const isPathTraversalAttempt = path => {
7030
7100
  if (path.startsWith('file://')) {
7031
7101
  return true;
7032
7102
  }
7033
- if (/^[a-zA-Z]:[\\/]/.test(path)) {
7103
+ if (windowsAbsolutePathRegex.test(path)) {
7034
7104
  return true;
7035
7105
  }
7036
- const segments = path.split(/[\\/]/);
7106
+ const segments = path.split(pathSeparatorRegex$1);
7037
7107
  return segments.includes('..');
7038
7108
  };
7039
7109
 
7110
+ const pathSeparatorRegex = /[\\/]/;
7040
7111
  const normalizeRelativePath = path => {
7041
- const segments = path.split(/[\\/]/).filter(segment => segment && segment !== '.');
7112
+ const segments = path.split(pathSeparatorRegex).filter(segment => segment && segment !== '.');
7042
7113
  if (segments.length === 0) {
7043
7114
  return '.';
7044
7115
  }
@@ -7046,13 +7117,14 @@ const normalizeRelativePath = path => {
7046
7117
  };
7047
7118
 
7048
7119
  const mentionRegex = /(^|\s)@([^\s]+)/g;
7120
+ const trailingPunctuationRegex = /[),.;:!?]+$/g;
7049
7121
  const maxMentionCount = 5;
7050
7122
  const parseMentionedPaths = value => {
7051
7123
  const matches = value.matchAll(mentionRegex);
7052
7124
  const paths = [];
7053
7125
  for (const match of matches) {
7054
7126
  const rawPath = match[2] || '';
7055
- const cleanedPath = rawPath.replaceAll(/[),.;:!?]+$/g, '');
7127
+ const cleanedPath = rawPath.replaceAll(trailingPunctuationRegex, '');
7056
7128
  if (!cleanedPath || isPathTraversalAttempt(cleanedPath)) {
7057
7129
  continue;
7058
7130
  }
@@ -7115,12 +7187,38 @@ const getSseEventType = value => {
7115
7187
  return value && typeof value === 'object' && Reflect.get(value, 'type') === 'response.completed' ? 'sse-response-completed' : 'sse-response-part';
7116
7188
  };
7117
7189
 
7190
+ const getMessageById = (messages, messageId) => {
7191
+ return messages.find(message => message.id === messageId);
7192
+ };
7193
+
7194
+ const getNextHandleTextChunkState = (latestState, parsedMessages, sessions) => {
7195
+ return {
7196
+ ...latestState,
7197
+ ...(latestState.messagesAutoScrollEnabled ? {
7198
+ messagesScrollTop: getNextAutoScrollTop(latestState.messagesScrollTop)
7199
+ } : {}),
7200
+ parsedMessages,
7201
+ sessions
7202
+ };
7203
+ };
7204
+
7205
+ const getSelectedSession = (sessions, selectedSessionId) => {
7206
+ return sessions.find(session => session.id === selectedSessionId);
7207
+ };
7208
+
7209
+ const setAndRerenderHandleTextChunkState = async (uid, previousState, nextState) => {
7210
+ set$1(uid, previousState, nextState);
7211
+ // @ts-ignore
7212
+ await invoke$1('Chat.rerender');
7213
+ };
7214
+
7118
7215
  const getToolCallMergeKey = toolCall => {
7119
7216
  if (toolCall.id) {
7120
7217
  return `id:${toolCall.id}`;
7121
7218
  }
7122
7219
  return `value:${toolCall.name}:${toolCall.arguments}`;
7123
7220
  };
7221
+
7124
7222
  const mergeToolCalls = (existing = [], incoming) => {
7125
7223
  if (incoming.length === 0) {
7126
7224
  return existing;
@@ -7142,8 +7240,9 @@ const mergeToolCalls = (existing = [], incoming) => {
7142
7240
  }
7143
7241
  return merged;
7144
7242
  };
7145
- const updateMessageTextInSelectedSession = async (sessions, parsedMessages, selectedSessionId, messageId, text, inProgress) => {
7146
- let updatedMessage;
7243
+
7244
+ const updateMessageToolCallsInSelectedSession = (sessions, parsedMessages, selectedSessionId, messageId, toolCalls) => {
7245
+ let nextParsedMessages = parsedMessages;
7147
7246
  const updatedSessions = sessions.map(session => {
7148
7247
  if (session.id !== selectedSessionId) {
7149
7248
  return session;
@@ -7154,26 +7253,47 @@ const updateMessageTextInSelectedSession = async (sessions, parsedMessages, sele
7154
7253
  if (message.id !== messageId) {
7155
7254
  return message;
7156
7255
  }
7157
- updatedMessage = {
7256
+ const updatedMessage = {
7158
7257
  ...message,
7159
- inProgress,
7160
- text
7258
+ toolCalls: mergeToolCalls(message.toolCalls, toolCalls)
7161
7259
  };
7260
+ nextParsedMessages = copyParsedMessageContent(nextParsedMessages, message.id, updatedMessage.id);
7162
7261
  return updatedMessage;
7163
7262
  })
7164
7263
  };
7165
7264
  });
7166
- let nextParsedMessages = parsedMessages;
7167
- if (updatedMessage) {
7168
- nextParsedMessages = await parseAndStoreMessageContent(parsedMessages, updatedMessage);
7169
- }
7170
7265
  return {
7171
7266
  parsedMessages: nextParsedMessages,
7172
7267
  sessions: updatedSessions
7173
7268
  };
7174
7269
  };
7175
- const updateMessageToolCallsInSelectedSession = (sessions, parsedMessages, selectedSessionId, messageId, toolCalls) => {
7176
- let nextParsedMessages = parsedMessages;
7270
+
7271
+ const handleToolCallsChunkFunction = async (uid, assistantMessageId, toolCalls, handleTextChunkState) => {
7272
+ const selectedSession = getSelectedSession(handleTextChunkState.latestState.sessions, handleTextChunkState.latestState.selectedSessionId);
7273
+ if (!selectedSession) {
7274
+ return {
7275
+ latestState: handleTextChunkState.latestState,
7276
+ previousState: handleTextChunkState.previousState
7277
+ };
7278
+ }
7279
+ const assistantMessage = getMessageById(selectedSession.messages, assistantMessageId);
7280
+ if (!assistantMessage) {
7281
+ return {
7282
+ latestState: handleTextChunkState.latestState,
7283
+ previousState: handleTextChunkState.previousState
7284
+ };
7285
+ }
7286
+ const updated = updateMessageToolCallsInSelectedSession(handleTextChunkState.latestState.sessions, handleTextChunkState.latestState.parsedMessages, handleTextChunkState.latestState.selectedSessionId, assistantMessageId, toolCalls);
7287
+ const nextState = getNextHandleTextChunkState(handleTextChunkState.latestState, updated.parsedMessages, updated.sessions);
7288
+ await setAndRerenderHandleTextChunkState(uid, handleTextChunkState.previousState, nextState);
7289
+ return {
7290
+ latestState: nextState,
7291
+ previousState: nextState
7292
+ };
7293
+ };
7294
+
7295
+ const updateMessageTextInSelectedSession = async (sessions, parsedMessages, selectedSessionId, messageId, text, inProgress) => {
7296
+ let updatedMessage;
7177
7297
  const updatedSessions = sessions.map(session => {
7178
7298
  if (session.id !== selectedSessionId) {
7179
7299
  return session;
@@ -7184,15 +7304,19 @@ const updateMessageToolCallsInSelectedSession = (sessions, parsedMessages, selec
7184
7304
  if (message.id !== messageId) {
7185
7305
  return message;
7186
7306
  }
7187
- const updatedMessage = {
7307
+ updatedMessage = {
7188
7308
  ...message,
7189
- toolCalls: mergeToolCalls(message.toolCalls, toolCalls)
7309
+ inProgress,
7310
+ text
7190
7311
  };
7191
- nextParsedMessages = copyParsedMessageContent(nextParsedMessages, message.id, updatedMessage.id);
7192
7312
  return updatedMessage;
7193
7313
  })
7194
7314
  };
7195
7315
  });
7316
+ let nextParsedMessages = parsedMessages;
7317
+ if (updatedMessage) {
7318
+ nextParsedMessages = await parseAndStoreMessageContent(parsedMessages, updatedMessage);
7319
+ }
7196
7320
  return {
7197
7321
  parsedMessages: nextParsedMessages,
7198
7322
  sessions: updatedSessions
@@ -7231,38 +7355,6 @@ const handleTextChunkFunction = async (uid, assistantMessageId, chunk, handleTex
7231
7355
  previousState: nextState
7232
7356
  };
7233
7357
  };
7234
- const handleToolCallsChunkFunction = async (uid, assistantMessageId, toolCalls, handleTextChunkState) => {
7235
- const selectedSession = handleTextChunkState.latestState.sessions.find(session => session.id === handleTextChunkState.latestState.selectedSessionId);
7236
- if (!selectedSession) {
7237
- return {
7238
- latestState: handleTextChunkState.latestState,
7239
- previousState: handleTextChunkState.previousState
7240
- };
7241
- }
7242
- const assistantMessage = selectedSession.messages.find(message => message.id === assistantMessageId);
7243
- if (!assistantMessage) {
7244
- return {
7245
- latestState: handleTextChunkState.latestState,
7246
- previousState: handleTextChunkState.previousState
7247
- };
7248
- }
7249
- const updated = updateMessageToolCallsInSelectedSession(handleTextChunkState.latestState.sessions, handleTextChunkState.latestState.parsedMessages, handleTextChunkState.latestState.selectedSessionId, assistantMessageId, toolCalls);
7250
- const nextState = {
7251
- ...handleTextChunkState.latestState,
7252
- ...(handleTextChunkState.latestState.messagesAutoScrollEnabled ? {
7253
- messagesScrollTop: getNextAutoScrollTop(handleTextChunkState.latestState.messagesScrollTop)
7254
- } : {}),
7255
- parsedMessages: updated.parsedMessages,
7256
- sessions: updated.sessions
7257
- };
7258
- set$1(uid, handleTextChunkState.previousState, nextState);
7259
- // @ts-ignore
7260
- await invoke$1('Chat.rerender');
7261
- return {
7262
- latestState: nextState,
7263
- previousState: nextState
7264
- };
7265
- };
7266
7358
 
7267
7359
  const hasLegacyStreamingToolCalls = parsed => {
7268
7360
  if (!parsed || typeof parsed !== 'object') {
@@ -7580,6 +7672,7 @@ const Dictate = 'dictate';
7580
7672
  const Send = 'send';
7581
7673
  const Back = 'back';
7582
7674
  const Model = 'model';
7675
+ const RunMode = 'runMode';
7583
7676
  const ToggleChatFocus = 'toggle-chat-focus';
7584
7677
  const CreateProject = 'create-project';
7585
7678
  const CreateSession = 'create-session';
@@ -8097,6 +8190,19 @@ const handleProjectListContextMenu = async state => {
8097
8190
  return state;
8098
8191
  };
8099
8192
 
8193
+ const isRunMode = value => {
8194
+ return value === 'local' || value === 'background' || value === 'cloud';
8195
+ };
8196
+ const handleRunModeChange = async (state, value) => {
8197
+ if (!isRunMode(value)) {
8198
+ return state;
8199
+ }
8200
+ return {
8201
+ ...state,
8202
+ runMode: value
8203
+ };
8204
+ };
8205
+
8100
8206
  const handleChatListScroll = async (state, chatListScrollTop) => {
8101
8207
  if (state.chatListScrollTop === chatListScrollTop) {
8102
8208
  return state;
@@ -8338,6 +8444,15 @@ const loadStreamingEnabled = async () => {
8338
8444
  }
8339
8445
  };
8340
8446
 
8447
+ const loadTodoListToolEnabled = async () => {
8448
+ try {
8449
+ const savedTodoListToolEnabled = await get('chatView.todoListToolEnabled');
8450
+ return typeof savedTodoListToolEnabled === 'boolean' ? savedTodoListToolEnabled : false;
8451
+ } catch {
8452
+ return false;
8453
+ }
8454
+ };
8455
+
8341
8456
  const loadUseChatCoordinatorWorker = async () => {
8342
8457
  try {
8343
8458
  const savedUseChatCoordinatorWorker = await get('chatView.useChatCoordinatorWorker');
@@ -8384,7 +8499,7 @@ const loadVoiceDictationEnabled = async () => {
8384
8499
  };
8385
8500
 
8386
8501
  const loadPreferences = async () => {
8387
- const [aiSessionTitleGenerationEnabled, authAccessToken, authEnabled, authRefreshToken, backendUrl, composerDropEnabled, openApiApiKey, openRouterApiKey, emitStreamingFunctionCallEvents, streamingEnabled, passIncludeObfuscation, useChatCoordinatorWorker, useChatMathWorker, useChatNetworkWorkerForRequests, useChatToolWorker, voiceDictationEnabled] = await Promise.all([loadAiSessionTitleGenerationEnabled(), loadBackendAccessToken(), loadAuthEnabled(), loadBackendRefreshToken(), loadBackendUrl(), loadComposerDropEnabled(), loadOpenApiApiKey(), loadOpenRouterApiKey(), loadEmitStreamingFunctionCallEvents(), loadStreamingEnabled(), loadPassIncludeObfuscation(), loadUseChatCoordinatorWorker(), loadUseChatMathWorker(), loadUseChatNetworkWorkerForRequests(), loadUseChatToolWorker(), loadVoiceDictationEnabled()]);
8502
+ const [aiSessionTitleGenerationEnabled, authAccessToken, authEnabled, authRefreshToken, backendUrl, composerDropEnabled, openApiApiKey, openRouterApiKey, emitStreamingFunctionCallEvents, streamingEnabled, todoListToolEnabled, passIncludeObfuscation, useChatCoordinatorWorker, useChatMathWorker, useChatNetworkWorkerForRequests, useChatToolWorker, voiceDictationEnabled] = await Promise.all([loadAiSessionTitleGenerationEnabled(), loadBackendAccessToken(), loadAuthEnabled(), loadBackendRefreshToken(), loadBackendUrl(), loadComposerDropEnabled(), loadOpenApiApiKey(), loadOpenRouterApiKey(), loadEmitStreamingFunctionCallEvents(), loadStreamingEnabled(), loadTodoListToolEnabled(), loadPassIncludeObfuscation(), loadUseChatCoordinatorWorker(), loadUseChatMathWorker(), loadUseChatNetworkWorkerForRequests(), loadUseChatToolWorker(), loadVoiceDictationEnabled()]);
8388
8503
  return {
8389
8504
  aiSessionTitleGenerationEnabled,
8390
8505
  authAccessToken,
@@ -8397,6 +8512,7 @@ const loadPreferences = async () => {
8397
8512
  openRouterApiKey,
8398
8513
  passIncludeObfuscation,
8399
8514
  streamingEnabled,
8515
+ todoListToolEnabled,
8400
8516
  useChatCoordinatorWorker,
8401
8517
  useChatMathWorker,
8402
8518
  useChatNetworkWorkerForRequests,
@@ -8542,6 +8658,7 @@ const loadContent = async (state, savedState) => {
8542
8658
  openRouterApiKey,
8543
8659
  passIncludeObfuscation,
8544
8660
  streamingEnabled,
8661
+ todoListToolEnabled,
8545
8662
  useChatCoordinatorWorker,
8546
8663
  useChatMathWorker,
8547
8664
  useChatNetworkWorkerForRequests,
@@ -8624,6 +8741,7 @@ const loadContent = async (state, savedState) => {
8624
8741
  selectedSessionId,
8625
8742
  sessions,
8626
8743
  streamingEnabled,
8744
+ todoListToolEnabled,
8627
8745
  useChatCoordinatorWorker,
8628
8746
  useChatMathWorker,
8629
8747
  useChatNetworkWorkerForRequests,
@@ -8740,301 +8858,184 @@ const registerMockResponse = (state, mockResponse) => {
8740
8858
  return state;
8741
8859
  };
8742
8860
 
8743
- const getCss = (composerHeight, listItemHeight, chatMessageFontSize, chatMessageLineHeight, chatMessageFontFamily, renderHtmlCss) => {
8861
+ const getCss = (composerHeight, listItemHeight, chatMessageFontSize, chatMessageLineHeight, chatMessageFontFamily, textAreaPaddingTop, textAreaPaddingLeft, textAreaPaddingRight, textAreaPaddingBottom, chatSendAreaPaddingTop, chatSendAreaPaddingLeft, chatSendAreaPaddingRight, chatSendAreaPaddingBottom, renderHtmlCss) => {
8744
8862
  const baseCss = `:root {
8745
8863
  --ChatInputBoxHeight: ${composerHeight}px;
8864
+ --ChatTextAreaHeight: ${composerHeight}px;
8865
+ --ChatSendAreaHeight: ${composerHeight + 62}px;
8866
+ --ChatTextAreaPaddingTop: ${textAreaPaddingTop}px;
8867
+ --ChatTextAreaPaddingLeft: ${textAreaPaddingLeft}px;
8868
+ --ChatTextAreaPaddingRight: ${textAreaPaddingRight}px;
8869
+ --ChatTextAreaPaddingBottom: ${textAreaPaddingBottom}px;
8870
+ --ChatSendAreaPaddingTop: ${chatSendAreaPaddingTop}px;
8871
+ --ChatSendAreaPaddingLeft: ${chatSendAreaPaddingLeft}px;
8872
+ --ChatSendAreaPaddingRight: ${chatSendAreaPaddingRight}px;
8873
+ --ChatSendAreaPaddingBottom: ${chatSendAreaPaddingBottom}px;
8746
8874
  --ChatListItemHeight: ${listItemHeight}px;
8747
8875
  --ChatMessageFontSize: ${chatMessageFontSize}px;
8748
8876
  --ChatMessageLineHeight: ${chatMessageLineHeight}px;
8749
8877
  --ChatMessageFontFamily: ${chatMessageFontFamily};
8750
- }
8751
-
8752
- .Viewlet.Chat.ChatFocus {
8753
- background: linear-gradient(180deg, var(--ColorViewBackground, #1d2229) 0%, #1f252d 100%);
8754
- display: grid;
8755
- grid-template-columns: 320px 1fr;
8756
- grid-template-rows: auto 1fr auto;
8757
- }
8878
+ }`;
8879
+ if (!renderHtmlCss.trim()) {
8880
+ return `${baseCss}
8758
8881
 
8759
- .Chat.ChatFocus .ChatHeader {
8760
- grid-column: 1 / 3;
8882
+ .ChatTodoList {
8883
+ background: var(--vscode-editorWidget-background);
8884
+ border: 1px solid var(--vscode-editorWidget-border);
8885
+ border-radius: 6px;
8886
+ margin-bottom: 8px;
8887
+ overflow: hidden;
8761
8888
  }
8762
8889
 
8763
- .Chat.ChatFocus .ProjectSidebar {
8764
- background: color-mix(in srgb, var(--ColorSideBarBackground, #232b35) 88%, #1f2b38 12%);
8765
- border-right: 1px solid var(--ColorBorder, #3a3d41);
8766
- box-shadow: inset -1px 0 0 rgba(0, 0, 0, 0.2);
8767
- display: flex;
8768
- flex-direction: column;
8769
- grid-column: 1;
8770
- grid-row: 2 / 4;
8771
- min-height: 0;
8890
+ .ChatTodoListHeader {
8891
+ border-bottom: 1px solid var(--vscode-editorWidget-border);
8892
+ color: var(--vscode-descriptionForeground);
8893
+ font-size: 12px;
8894
+ line-height: 18px;
8895
+ padding: 6px 10px;
8772
8896
  }
8773
8897
 
8774
- .Chat.ChatFocus .ProjectList {
8775
- min-height: 0;
8898
+ .ChatTodoListItems {
8899
+ list-style: none;
8900
+ margin: 0;
8901
+ max-height: 180px;
8776
8902
  overflow: auto;
8777
- padding: 10px 8px 12px;
8778
- scrollbar-color: color-mix(in srgb, var(--ColorScrollBarSliderBackground, #4b5563) 78%, transparent)
8779
- color-mix(in srgb, var(--ColorSideBarBackground, #232b35) 92%, transparent);
8780
- scrollbar-width: thin;
8903
+ padding: 4px 0;
8781
8904
  }
8782
8905
 
8783
- .Chat.ChatFocus .ProjectList::-webkit-scrollbar {
8784
- height: 10px;
8785
- width: 10px;
8786
- }
8787
-
8788
- .Chat.ChatFocus .ProjectList::-webkit-scrollbar-track {
8789
- background: color-mix(in srgb, var(--ColorSideBarBackground, #232b35) 94%, transparent);
8790
- border-radius: 999px;
8791
- }
8792
-
8793
- .Chat.ChatFocus .ProjectList::-webkit-scrollbar-thumb {
8794
- background: color-mix(in srgb, var(--ColorScrollBarSliderBackground, #4b5563) 70%, transparent);
8795
- border: 2px solid color-mix(in srgb, var(--ColorSideBarBackground, #232b35) 94%, transparent);
8796
- border-radius: 999px;
8797
- }
8798
-
8799
- .Chat.ChatFocus .ProjectList::-webkit-scrollbar-thumb:hover {
8800
- background: color-mix(in srgb, var(--ColorScrollBarSliderHoverBackground, #667284) 80%, transparent);
8801
- }
8802
-
8803
- .ProjectListGroup {
8804
- border: 1px solid transparent;
8805
- border-radius: 8px;
8806
- margin-bottom: 6px;
8807
- overflow: hidden;
8808
- }
8809
-
8810
- .ProjectListItem {
8811
- align-items: center;
8812
- display: flex;
8813
- gap: 6px;
8814
- min-height: calc(var(--ChatListItemHeight) - 2px);
8815
- }
8816
-
8817
- .ProjectListItemLabel {
8906
+ .ChatTodoListItem {
8818
8907
  align-items: center;
8819
- border-radius: 6px;
8820
- color: var(--ColorForeground, #d5dbe3);
8821
- cursor: pointer;
8908
+ color: var(--vscode-foreground);
8822
8909
  display: flex;
8823
- flex: 1;
8824
- font-weight: 500;
8825
- gap: 2px;
8826
- overflow: hidden;
8910
+ font-size: 12px;
8911
+ line-height: 18px;
8912
+ min-height: 24px;
8827
8913
  padding: 0 10px;
8828
- text-overflow: ellipsis;
8829
- transition: background-color 80ms ease, color 80ms ease;
8830
- white-space: nowrap;
8831
8914
  }
8832
8915
 
8833
- .ProjectListChevron {
8834
- color: color-mix(in srgb, var(--ColorForeground, #c8d0da) 70%, transparent);
8916
+ .ChatTodoListItem::before {
8917
+ color: var(--vscode-descriptionForeground);
8918
+ content: "○";
8835
8919
  display: inline-block;
8836
- flex: 0 0 12px;
8837
- font-size: 11px;
8838
- margin-right: 4px;
8839
- text-align: center;
8840
- width: 12px;
8920
+ margin-right: 8px;
8921
+ width: 1em;
8841
8922
  }
8842
8923
 
8843
- .ProjectListItemSelected {
8844
- background: color-mix(in srgb, var(--ColorListInactiveSelectionBackground, #39424d) 84%, #2f3741 16%);
8924
+ .ChatTodoListItemTodo::before {
8925
+ content: "○";
8845
8926
  }
8846
8927
 
8847
- .ProjectListItem:not(.ProjectListItemSelected):hover,
8848
- .ProjectListItem:not(.ProjectListItemSelected):focus-within {
8849
- background: color-mix(in srgb, var(--ColorListHoverBackground, #38414b) 50%, transparent);
8928
+ .ChatTodoListItem.todo::before {
8929
+ content: "○";
8850
8930
  }
8851
8931
 
8852
- .ProjectListItemSelected .ProjectListItemLabel {
8853
- color: var(--ColorListInactiveSelectionForeground, #e5ebf2);
8932
+ .ChatTodoListItemInProgress::before {
8933
+ color: var(--vscode-textLink-foreground);
8934
+ content: "◐";
8854
8935
  }
8855
8936
 
8856
- .ProjectListItemActions {
8857
- display: flex;
8858
- padding-right: 6px;
8937
+ .ChatTodoListItem.inProgress::before {
8938
+ color: var(--vscode-textLink-foreground);
8939
+ content: "◐";
8859
8940
  }
8860
8941
 
8861
- .ProjectListItemAddChatButton {
8862
- align-items: center;
8863
- background: color-mix(in srgb, var(--ColorButtonSecondaryBackground, #3a434f) 76%, transparent);
8864
- border: 0;
8865
- border-radius: 5px;
8866
- color: var(--ColorForeground, #d0d8e2);
8867
- cursor: pointer;
8868
- display: inline-flex;
8869
- font-size: 13px;
8870
- font-weight: 500;
8871
- height: 18px;
8872
- justify-content: center;
8873
- opacity: 0;
8874
- padding: 0;
8875
- transition: opacity 90ms ease, background-color 90ms ease;
8876
- visibility: hidden;
8877
- width: 18px;
8942
+ .ChatTodoListItemCompleted {
8943
+ color: var(--vscode-disabledForeground);
8878
8944
  }
8879
8945
 
8880
- .ProjectListItem:hover .ProjectListItemAddChatButton,
8881
- .ProjectListItem:focus-within .ProjectListItemAddChatButton {
8882
- opacity: 1;
8883
- visibility: visible;
8946
+ .ChatTodoListItem.completed {
8947
+ color: var(--vscode-disabledForeground);
8884
8948
  }
8885
8949
 
8886
- .ProjectListItemAddChatButton:hover,
8887
- .ProjectListItemAddChatButton:focus-visible {
8888
- background: color-mix(in srgb, var(--ColorButtonSecondaryHoverBackground, #4a5460) 82%, transparent);
8950
+ .ChatTodoListItemCompleted::before {
8951
+ color: var(--vscode-testing-iconPassed);
8952
+ content: "✓";
8889
8953
  }
8890
8954
 
8891
- .ProjectSessionItem {
8892
- align-items: center;
8893
- display: flex;
8894
- min-height: calc(var(--ChatListItemHeight) - 5px);
8895
- }
8955
+ .ChatTodoListItem.completed::before {
8956
+ color: var(--vscode-testing-iconPassed);
8957
+ content: "✓";
8958
+ }`;
8959
+ }
8960
+ return `${baseCss}
8896
8961
 
8897
- .ProjectSessionItemLabel {
8962
+ .ChatTodoList {
8963
+ background: var(--vscode-editorWidget-background);
8964
+ border: 1px solid var(--vscode-editorWidget-border);
8898
8965
  border-radius: 6px;
8899
- color: color-mix(in srgb, var(--ColorForeground, #cfd7df) 92%, transparent);
8900
- cursor: pointer;
8901
- display: block;
8902
- flex: 1;
8903
- font-size: 12.5px;
8966
+ margin-bottom: 8px;
8904
8967
  overflow: hidden;
8905
- padding: 0 10px 0 28px;
8906
- text-overflow: ellipsis;
8907
- transition: background-color 80ms ease, color 80ms ease;
8908
- white-space: nowrap;
8909
- }
8910
-
8911
- .ProjectSessionItemSelected {
8912
- background: color-mix(in srgb, var(--ColorListInactiveSelectionBackground, #353f4a) 86%, #2c3540 14%);
8913
- }
8914
-
8915
- .ProjectSessionItem:not(.ProjectSessionItemSelected):hover,
8916
- .ProjectSessionItem:not(.ProjectSessionItemSelected):focus-within {
8917
- background: color-mix(in srgb, var(--ColorListHoverBackground, #38414c) 46%, transparent);
8918
- }
8919
-
8920
- .ProjectSessionItemSelected .ProjectSessionItemLabel {
8921
- color: var(--ColorListInactiveSelectionForeground, #f2f6fc);
8922
- }
8923
-
8924
- .Chat.ChatFocus .ProjectAddButton {
8925
- background: color-mix(in srgb, var(--ColorButtonSecondaryBackground, #21252c) 72%, transparent);
8926
- border: 0;
8927
- border-top: 1px solid color-mix(in srgb, var(--ColorBorder, #3a3d41) 70%, transparent);
8928
- color: var(--ColorForeground, #d2d9e2);
8929
- cursor: pointer;
8930
- font-size: 12.5px;
8931
- letter-spacing: 0.01em;
8932
- margin-top: auto;
8933
- min-height: var(--ChatListItemHeight);
8934
- padding: 0 12px;
8935
- text-align: left;
8936
- transition: background-color 80ms ease;
8937
8968
  }
8938
8969
 
8939
- .Chat.ChatFocus .ProjectAddButton:hover,
8940
- .Chat.ChatFocus .ProjectAddButton:focus-visible {
8941
- background: color-mix(in srgb, var(--ColorButtonSecondaryHoverBackground, #2a3039) 78%, transparent);
8970
+ .ChatTodoListHeader {
8971
+ border-bottom: 1px solid var(--vscode-editorWidget-border);
8972
+ color: var(--vscode-descriptionForeground);
8973
+ font-size: 12px;
8974
+ line-height: 18px;
8975
+ padding: 6px 10px;
8942
8976
  }
8943
8977
 
8944
- .ChatList,
8945
- .ChatListEmpty,
8946
- .ChatMessages {
8947
- min-height: 0;
8948
- }
8949
-
8950
- .Chat.ChatFocus .ChatList,
8951
- .Chat.ChatFocus .ChatListEmpty {
8952
- display: none;
8953
- }
8954
-
8955
- .Chat.ChatFocus .ChatMessages {
8956
- grid-column: 2;
8957
- grid-row: 2;
8978
+ .ChatTodoListItems {
8979
+ list-style: none;
8980
+ margin: 0;
8981
+ max-height: 180px;
8982
+ overflow: auto;
8983
+ padding: 4px 0;
8958
8984
  }
8959
8985
 
8960
- .Chat.ChatFocus .ChatSendArea {
8961
- grid-column: 2;
8962
- grid-row: 3;
8986
+ .ChatTodoListItem {
8987
+ align-items: center;
8988
+ color: var(--vscode-foreground);
8989
+ display: flex;
8990
+ font-size: 12px;
8991
+ line-height: 18px;
8992
+ min-height: 24px;
8993
+ padding: 0 10px;
8963
8994
  }
8964
8995
 
8965
- .MarkdownMathInline {
8996
+ .ChatTodoListItem::before {
8997
+ color: var(--vscode-descriptionForeground);
8998
+ content: "○";
8966
8999
  display: inline-block;
8967
- max-width: 100%;
8968
- vertical-align: middle;
9000
+ margin-right: 8px;
9001
+ width: 1em;
8969
9002
  }
8970
9003
 
8971
- .MarkdownQuote {
8972
- border-left: 3px solid var(--ColorBorder, #3a3d41);
8973
- margin: 8px 0;
8974
- padding-left: 12px;
9004
+ .ChatTodoListItemTodo::before {
9005
+ content: "○";
8975
9006
  }
8976
9007
 
8977
- .MarkdownMathBlock {
8978
- margin: 8px 0;
8979
- overflow-x: auto;
8980
- overflow-y: hidden;
9008
+ .ChatTodoListItem.todo::before {
9009
+ content: "○";
8981
9010
  }
8982
9011
 
8983
- .StrikeThrough {
8984
- text-decoration: line-through;
9012
+ .ChatTodoListItemInProgress::before {
9013
+ color: var(--vscode-textLink-foreground);
9014
+ content: "◐";
8985
9015
  }
8986
9016
 
8987
- /* syntax highlight token colors */
8988
- .TokenComment {
8989
- color: var(--ColorSymbolIconColorForeground, #7f8794);
9017
+ .ChatTodoListItem.inProgress::before {
9018
+ color: var(--vscode-textLink-foreground);
9019
+ content: "◐";
8990
9020
  }
8991
9021
 
8992
- .TokenString {
8993
- color: var(--ColorChartsGreen, #a6d189);
9022
+ .ChatTodoListItemCompleted {
9023
+ color: var(--vscode-disabledForeground);
8994
9024
  }
8995
9025
 
8996
- .TokenNumber,
8997
- .TokenValue {
8998
- color: var(--ColorChartsBlue, #8caaee);
9026
+ .ChatTodoListItem.completed {
9027
+ color: var(--vscode-disabledForeground);
8999
9028
  }
9000
9029
 
9001
- .TokenKeyword,
9002
- .TokenTag {
9003
- color: var(--ColorChartsPurple, #ca9ee6);
9030
+ .ChatTodoListItemCompleted::before {
9031
+ color: var(--vscode-testing-iconPassed);
9032
+ content: "✓";
9004
9033
  }
9005
9034
 
9006
- .TokenAttribute,
9007
- .TokenProperty {
9008
- color: var(--ColorChartsOrange, #ef9f76);
9009
- }
9010
-
9011
- .ChatToolCallQuestionText {
9012
- margin-bottom: 6px;
9013
- }
9014
-
9015
- .ChatToolCallQuestionOptions {
9016
- display: flex;
9017
- flex-wrap: wrap;
9018
- gap: 6px;
9019
- }
9020
-
9021
- .ChatToolCallQuestionOption {
9022
- background: color-mix(in srgb, var(--ColorBadgeBackground, #2f3640) 70%, transparent);
9023
- border: 1px solid color-mix(in srgb, var(--ColorBorder, #3a3d41) 70%, transparent);
9024
- border-radius: 999px;
9025
- color: var(--ColorForeground, #d5dbe3);
9026
- display: inline-block;
9027
- max-width: 100%;
9028
- overflow: hidden;
9029
- padding: 2px 8px;
9030
- text-overflow: ellipsis;
9031
- white-space: nowrap;
9035
+ .ChatTodoListItem.completed::before {
9036
+ color: var(--vscode-testing-iconPassed);
9037
+ content: "✓";
9032
9038
  }
9033
- `;
9034
- if (!renderHtmlCss.trim()) {
9035
- return baseCss;
9036
- }
9037
- return `${baseCss}
9038
9039
 
9039
9040
  /* render_html tool css */
9040
9041
  ${renderHtmlCss}`;
@@ -9048,14 +9049,22 @@ const renderCss = (oldState, newState) => {
9048
9049
  chatMessageFontFamily,
9049
9050
  chatMessageFontSize,
9050
9051
  chatMessageLineHeight,
9052
+ chatSendAreaPaddingBottom,
9053
+ chatSendAreaPaddingLeft,
9054
+ chatSendAreaPaddingRight,
9055
+ chatSendAreaPaddingTop,
9051
9056
  composerHeight,
9052
9057
  listItemHeight,
9053
9058
  selectedSessionId,
9054
9059
  sessions,
9060
+ textAreaPaddingBottom,
9061
+ textAreaPaddingLeft,
9062
+ textAreaPaddingRight,
9063
+ textAreaPaddingTop,
9055
9064
  uid
9056
9065
  } = newState;
9057
9066
  const renderHtmlCss = getRenderHtmlCss(sessions, selectedSessionId);
9058
- const css = getCss(composerHeight, listItemHeight, chatMessageFontSize, chatMessageLineHeight, chatMessageFontFamily, renderHtmlCss);
9067
+ const css = getCss(composerHeight, listItemHeight, chatMessageFontSize, chatMessageLineHeight, chatMessageFontFamily, textAreaPaddingTop, textAreaPaddingLeft, textAreaPaddingRight, textAreaPaddingBottom, chatSendAreaPaddingTop, chatSendAreaPaddingLeft, chatSendAreaPaddingRight, chatSendAreaPaddingBottom, renderHtmlCss);
9059
9068
  return [SetCss, uid, css];
9060
9069
  };
9061
9070
 
@@ -9093,6 +9102,13 @@ const ChatViewDropOverlayActive = 'ChatViewDropOverlayActive';
9093
9102
  const SendButtonDisabled = 'SendButtonDisabled';
9094
9103
  const ChatSendAreaBottom = 'ChatSendAreaBottom';
9095
9104
  const ChatSendAreaContent = 'ChatSendAreaContent';
9105
+ const ChatTodoList = 'ChatTodoList';
9106
+ const ChatTodoListHeader = 'ChatTodoListHeader';
9107
+ const ChatTodoListItems = 'ChatTodoListItems';
9108
+ const ChatTodoListItem = 'ChatTodoListItem';
9109
+ const ChatTodoListItemTodo = 'ChatTodoListItemTodo';
9110
+ const ChatTodoListItemInProgress = 'ChatTodoListItemInProgress';
9111
+ const ChatTodoListItemCompleted = 'ChatTodoListItemCompleted';
9096
9112
  const Chat = 'Chat';
9097
9113
  const ChatHeader = 'ChatHeader';
9098
9114
  const Button = 'Button';
@@ -9134,6 +9150,7 @@ const ChatMessageContent = 'ChatMessageContent';
9134
9150
  const ChatToolCalls = 'ChatToolCalls';
9135
9151
  const ChatToolCallsLabel = 'ChatToolCallsLabel';
9136
9152
  const ChatToolCallReadFileLink = 'ChatToolCallReadFileLink';
9153
+ const ChatToolCallFileName = 'ChatToolCallFileName';
9137
9154
  const ChatToolCallQuestionOption = 'ChatToolCallQuestionOption';
9138
9155
  const ChatToolCallQuestionOptions = 'ChatToolCallQuestionOptions';
9139
9156
  const ChatToolCallQuestionText = 'ChatToolCallQuestionText';
@@ -9193,6 +9210,7 @@ const HandleProjectListScroll = 32;
9193
9210
  const HandleProjectListContextMenu = 33;
9194
9211
  const HandleClickDictationButton = 34;
9195
9212
  const HandleMissingApiKeySubmit = 35;
9213
+ const HandleRunModeChange = 36;
9196
9214
 
9197
9215
  const getModelLabel = model => {
9198
9216
  if (model.provider === 'openRouter') {
@@ -9226,6 +9244,28 @@ const getChatSelectVirtualDom = (models, selectedModelId) => {
9226
9244
  }, ...modelOptions];
9227
9245
  };
9228
9246
 
9247
+ const getRunModeOptionDom = (runMode, selectedRunMode) => {
9248
+ return [{
9249
+ childCount: 1,
9250
+ selected: runMode === selectedRunMode,
9251
+ type: Option$1,
9252
+ value: runMode
9253
+ }, text(runMode)];
9254
+ };
9255
+
9256
+ const runModes = ['local', 'background', 'cloud'];
9257
+ const getRunModeSelectVirtualDom = selectedRunMode => {
9258
+ const runModeOptions = runModes.flatMap(runMode => getRunModeOptionDom(runMode, selectedRunMode));
9259
+ return [{
9260
+ childCount: runModes.length,
9261
+ className: Select,
9262
+ name: RunMode,
9263
+ onInput: HandleRunModeChange,
9264
+ type: Select$1,
9265
+ value: selectedRunMode
9266
+ }, ...runModeOptions];
9267
+ };
9268
+
9229
9269
  const getSendButtonClassName = isSendDisabled => {
9230
9270
  return isSendDisabled ? `${IconButton} ${SendButtonDisabled}` : `${IconButton}`;
9231
9271
  };
@@ -9293,19 +9333,48 @@ const getUsageOverviewDom = (tokensUsed, tokensMax) => {
9293
9333
  }, text(usageLabel)];
9294
9334
  };
9295
9335
 
9296
- const getChatSendAreaDom = (composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, voiceDictationEnabled = false) => {
9336
+ const getChatSendAreaDom = (composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, showRunMode, runMode, todoListToolEnabled, todoListItems, voiceDictationEnabled = false) => {
9297
9337
  const isSendDisabled = composerValue.trim() === '';
9298
- const controlsCount = usageOverviewEnabled ? 3 : 2;
9338
+ const controlsCount = 2 + (usageOverviewEnabled ? 1 : 0) + (showRunMode ? 1 : 0);
9339
+ const hasTodoList = todoListToolEnabled && todoListItems.length > 0;
9340
+ const todoHeaderText = `Todos (${todoListItems.filter(item => item.status === 'completed').length}/${todoListItems.length})`;
9341
+ const getTodoItemClassName = status => {
9342
+ if (status === 'completed') {
9343
+ return `${ChatTodoListItem} ${ChatTodoListItemCompleted} completed`;
9344
+ }
9345
+ if (status === 'inProgress') {
9346
+ return `${ChatTodoListItem} ${ChatTodoListItemInProgress} inProgress`;
9347
+ }
9348
+ return `${ChatTodoListItem} ${ChatTodoListItemTodo} todo`;
9349
+ };
9299
9350
  return [{
9300
9351
  childCount: 1,
9301
9352
  className: ChatSendArea,
9302
9353
  onSubmit: HandleSubmit,
9303
9354
  type: Form
9304
9355
  }, {
9305
- childCount: 2,
9356
+ childCount: hasTodoList ? 3 : 2,
9306
9357
  className: ChatSendAreaContent,
9307
9358
  type: Div
9359
+ }, ...(hasTodoList ? [{
9360
+ childCount: 2,
9361
+ className: ChatTodoList,
9362
+ type: Div
9363
+ }, {
9364
+ childCount: 1,
9365
+ className: ChatTodoListHeader,
9366
+ type: Div
9367
+ }, {
9368
+ ...text(todoHeaderText)
9308
9369
  }, {
9370
+ childCount: todoListItems.length,
9371
+ className: ChatTodoListItems,
9372
+ type: Ul
9373
+ }, ...todoListItems.flatMap(item => [{
9374
+ childCount: 1,
9375
+ className: getTodoItemClassName(item.status),
9376
+ type: Li
9377
+ }, text(item.text)])] : []), {
9309
9378
  childCount: 0,
9310
9379
  className: MultilineInputBox,
9311
9380
  name: Composer,
@@ -9318,7 +9387,7 @@ const getChatSendAreaDom = (composerValue, models, selectedModelId, usageOvervie
9318
9387
  childCount: voiceDictationEnabled ? controlsCount + 1 : controlsCount,
9319
9388
  className: ChatSendAreaBottom,
9320
9389
  type: Div
9321
- }, ...getChatSelectVirtualDom(models, selectedModelId), ...(usageOverviewEnabled ? getUsageOverviewDom(tokensUsed, tokensMax) : []), ...getSendButtonDom(isSendDisabled, voiceDictationEnabled)];
9390
+ }, ...getChatSelectVirtualDom(models, selectedModelId), ...(showRunMode ? getRunModeSelectVirtualDom(runMode) : []), ...(usageOverviewEnabled ? getUsageOverviewDom(tokensUsed, tokensMax) : []), ...getSendButtonDom(isSendDisabled, voiceDictationEnabled)];
9322
9391
  };
9323
9392
 
9324
9393
  const getImageAltText = alt => {
@@ -9683,6 +9752,12 @@ const getMessageNodeDom = (node, useChatMathWorker = false) => {
9683
9752
  if (node.type === 'math-block-dom') {
9684
9753
  return node.dom;
9685
9754
  }
9755
+ if (node.type === 'thematic-break') {
9756
+ return [{
9757
+ childCount: 0,
9758
+ type: Hr
9759
+ }];
9760
+ }
9686
9761
  if (node.type === 'heading') {
9687
9762
  return getHeadingDom(node, useChatMathWorker);
9688
9763
  }
@@ -9817,22 +9892,88 @@ const getOpenRouterTooManyRequestsDom = () => {
9817
9892
  })];
9818
9893
  };
9819
9894
 
9820
- const RE_QUERY_OR_HASH = /[?#].*$/;
9821
- const RE_TRAILING_SLASH = /\/$/;
9822
- const getFileNameFromUri = uri => {
9823
- const stripped = uri.replace(RE_QUERY_OR_HASH, '').replace(RE_TRAILING_SLASH, '');
9824
- const slashIndex = Math.max(stripped.lastIndexOf('/'), stripped.lastIndexOf('\\\\'));
9825
- const fileName = slashIndex === -1 ? stripped : stripped.slice(slashIndex + 1);
9826
- return fileName || uri;
9895
+ const getToolCallStatusLabel = toolCall => {
9896
+ if (toolCall.status === 'not-found') {
9897
+ return ' (not-found)';
9898
+ }
9899
+ if (toolCall.status === 'error') {
9900
+ if (toolCall.errorMessage) {
9901
+ return ` (error: ${toolCall.errorMessage})`;
9902
+ }
9903
+ return ' (error)';
9904
+ }
9905
+ return '';
9827
9906
  };
9828
9907
 
9829
- const isCompleteJson = value => {
9830
- const trimmed = value.trim();
9831
- if (!trimmed) {
9832
- return false;
9908
+ const parseAskQuestionArguments = rawArguments => {
9909
+ let parsed;
9910
+ try {
9911
+ parsed = JSON.parse(rawArguments);
9912
+ } catch {
9913
+ return {
9914
+ answers: [],
9915
+ question: ''
9916
+ };
9833
9917
  }
9834
- let depth = 0;
9835
- let inString = false;
9918
+ if (!parsed || typeof parsed !== 'object') {
9919
+ return {
9920
+ answers: [],
9921
+ question: ''
9922
+ };
9923
+ }
9924
+ const question = Reflect.get(parsed, 'question');
9925
+ const rawAnswers = Reflect.get(parsed, 'answers');
9926
+ const rawChoices = Reflect.get(parsed, 'choices');
9927
+ const rawOptions = Reflect.get(parsed, 'options');
9928
+ const arrayValue = Array.isArray(rawAnswers) ? rawAnswers : Array.isArray(rawChoices) ? rawChoices : Array.isArray(rawOptions) ? rawOptions : [];
9929
+ const answers = arrayValue.filter(value => typeof value === 'string');
9930
+ return {
9931
+ answers,
9932
+ question: typeof question === 'string' ? question : ''
9933
+ };
9934
+ };
9935
+
9936
+ const getToolCallAskQuestionVirtualDom = toolCall => {
9937
+ const parsed = parseAskQuestionArguments(toolCall.arguments);
9938
+ const statusLabel = getToolCallStatusLabel(toolCall);
9939
+ const questionLabel = parsed.question.trim() ? parsed.question : '(empty question)';
9940
+ const answers = parsed.answers.length > 0 ? parsed.answers : ['(no answers)'];
9941
+ const childCount = 2;
9942
+ return [{
9943
+ childCount,
9944
+ className: ChatOrderedListItem,
9945
+ type: Li
9946
+ }, {
9947
+ childCount: 1,
9948
+ className: ChatToolCallQuestionText,
9949
+ type: Div
9950
+ }, text(`ask_question: ${questionLabel}${statusLabel}`), {
9951
+ childCount: answers.length,
9952
+ className: ChatToolCallQuestionOptions,
9953
+ type: Div
9954
+ }, ...answers.flatMap(answer => [{
9955
+ childCount: 1,
9956
+ className: ChatToolCallQuestionOption,
9957
+ type: Span
9958
+ }, text(answer.trim() ? answer : '(empty answer)')])];
9959
+ };
9960
+
9961
+ const RE_QUERY_OR_HASH = /[?#].*$/;
9962
+ const RE_TRAILING_SLASH = /\/$/;
9963
+ const getFileNameFromUri = uri => {
9964
+ const stripped = uri.replace(RE_QUERY_OR_HASH, '').replace(RE_TRAILING_SLASH, '');
9965
+ const slashIndex = Math.max(stripped.lastIndexOf('/'), stripped.lastIndexOf('\\\\'));
9966
+ const fileName = slashIndex === -1 ? stripped : stripped.slice(slashIndex + 1);
9967
+ return fileName || uri;
9968
+ };
9969
+
9970
+ const isCompleteJson = value => {
9971
+ const trimmed = value.trim();
9972
+ if (!trimmed) {
9973
+ return false;
9974
+ }
9975
+ let depth = 0;
9976
+ let inString = false;
9836
9977
  let escaped = false;
9837
9978
  for (const char of trimmed) {
9838
9979
  if (inString) {
@@ -9896,6 +10037,66 @@ const getReadFileTarget = rawArguments => {
9896
10037
  };
9897
10038
  };
9898
10039
 
10040
+ const getToolCallEditFileVirtualDom = toolCall => {
10041
+ const target = getReadFileTarget(toolCall.arguments);
10042
+ if (!target) {
10043
+ return [];
10044
+ }
10045
+ const fileName = getFileNameFromUri(target.title);
10046
+ const fileNameClickableProps = target.clickableUri ? {
10047
+ 'data-uri': target.clickableUri,
10048
+ onClick: HandleClickReadFile
10049
+ } : {};
10050
+ return [{
10051
+ childCount: 3,
10052
+ className: ChatOrderedListItem,
10053
+ title: target.title,
10054
+ type: Li
10055
+ }, {
10056
+ childCount: 0,
10057
+ className: FileIcon,
10058
+ type: Div
10059
+ }, text('edit_file '), {
10060
+ childCount: 1,
10061
+ className: ChatToolCallReadFileLink,
10062
+ title: target.clickableUri,
10063
+ ...fileNameClickableProps,
10064
+ type: Span
10065
+ }, {
10066
+ childCount: 1,
10067
+ className: ChatToolCallFileName,
10068
+ type: Span
10069
+ }, text(fileName)];
10070
+ };
10071
+
10072
+ const getToolCallGetWorkspaceUriVirtualDom = toolCall => {
10073
+ if (!toolCall.result) {
10074
+ return [];
10075
+ }
10076
+ const statusLabel = getToolCallStatusLabel(toolCall);
10077
+ const fileName = getFileNameFromUri(toolCall.result);
10078
+ return [{
10079
+ childCount: statusLabel ? 4 : 3,
10080
+ className: ChatOrderedListItem,
10081
+ title: toolCall.result,
10082
+ type: Li
10083
+ }, {
10084
+ childCount: 0,
10085
+ className: FileIcon,
10086
+ type: Div
10087
+ }, text('get_workspace_uri '), {
10088
+ childCount: 1,
10089
+ className: ChatToolCallReadFileLink,
10090
+ 'data-uri': toolCall.result,
10091
+ onClick: HandleClickReadFile,
10092
+ type: Span
10093
+ }, {
10094
+ childCount: 1,
10095
+ className: ChatToolCallFileName,
10096
+ type: Span
10097
+ }, text(fileName), ...(statusLabel ? [text(statusLabel)] : [])];
10098
+ };
10099
+
9899
10100
  const getToolCallArgumentPreview = rawArguments => {
9900
10101
  if (!rawArguments.trim()) {
9901
10102
  return '""';
@@ -9923,69 +10124,33 @@ const getToolCallArgumentPreview = rawArguments => {
9923
10124
  return rawArguments;
9924
10125
  };
9925
10126
 
9926
- const getToolCallStatusLabel = toolCall => {
9927
- if (toolCall.status === 'not-found') {
9928
- return ' (not-found)';
9929
- }
9930
- if (toolCall.status === 'error') {
9931
- if (toolCall.errorMessage) {
9932
- return ` (error: ${toolCall.errorMessage})`;
9933
- }
9934
- return ' (error)';
10127
+ const getToolCallDisplayName = name => {
10128
+ if (name === 'getWorkspaceUri') {
10129
+ return 'get_workspace_uri';
9935
10130
  }
9936
- return '';
10131
+ return name;
9937
10132
  };
9938
10133
 
9939
- const parseAskQuestionArguments = rawArguments => {
9940
- let parsed;
10134
+ const hasIncompleteJsonArguments = rawArguments => {
9941
10135
  try {
9942
- parsed = JSON.parse(rawArguments);
10136
+ JSON.parse(rawArguments);
10137
+ return false;
9943
10138
  } catch {
9944
- return {
9945
- answers: [],
9946
- question: ''
9947
- };
9948
- }
9949
- if (!parsed || typeof parsed !== 'object') {
9950
- return {
9951
- answers: [],
9952
- question: ''
9953
- };
10139
+ return true;
9954
10140
  }
9955
- const question = Reflect.get(parsed, 'question');
9956
- const rawAnswers = Reflect.get(parsed, 'answers');
9957
- const rawChoices = Reflect.get(parsed, 'choices');
9958
- const rawOptions = Reflect.get(parsed, 'options');
9959
- const arrayValue = Array.isArray(rawAnswers) ? rawAnswers : Array.isArray(rawChoices) ? rawChoices : Array.isArray(rawOptions) ? rawOptions : [];
9960
- const answers = arrayValue.filter(value => typeof value === 'string');
9961
- return {
9962
- answers,
9963
- question: typeof question === 'string' ? question : ''
9964
- };
9965
10141
  };
9966
- const getToolCallAskQuestionVirtualDom = toolCall => {
9967
- const parsed = parseAskQuestionArguments(toolCall.arguments);
10142
+
10143
+ const getToolCallLabel = toolCall => {
10144
+ const displayName = getToolCallDisplayName(toolCall.name);
10145
+ if (toolCall.name === 'write_file' && !toolCall.status && hasIncompleteJsonArguments(toolCall.arguments)) {
10146
+ return `${displayName} (in progress)`;
10147
+ }
10148
+ const argumentPreview = getToolCallArgumentPreview(toolCall.arguments);
9968
10149
  const statusLabel = getToolCallStatusLabel(toolCall);
9969
- const questionLabel = parsed.question.trim() ? parsed.question : '(empty question)';
9970
- const answers = parsed.answers.length > 0 ? parsed.answers : ['(no answers)'];
9971
- const childCount = 2;
9972
- return [{
9973
- childCount,
9974
- className: ChatOrderedListItem,
9975
- type: Li
9976
- }, {
9977
- childCount: 1,
9978
- className: ChatToolCallQuestionText,
9979
- type: Div
9980
- }, text(`ask_question: ${questionLabel}${statusLabel}`), {
9981
- childCount: answers.length,
9982
- className: ChatToolCallQuestionOptions,
9983
- type: Div
9984
- }, ...answers.flatMap(answer => [{
9985
- childCount: 1,
9986
- className: ChatToolCallQuestionOption,
9987
- type: Span
9988
- }, text(answer.trim() ? answer : '(empty answer)')])];
10150
+ if (argumentPreview === '{}') {
10151
+ return `${displayName}${statusLabel}`;
10152
+ }
10153
+ return `${displayName} ${argumentPreview}${statusLabel}`;
9989
10154
  };
9990
10155
 
9991
10156
  const getToolCallReadFileVirtualDom = toolCall => {
@@ -10014,22 +10179,34 @@ const getToolCallReadFileVirtualDom = toolCall => {
10014
10179
  className: ChatToolCallReadFileLink,
10015
10180
  ...fileNameClickableProps,
10016
10181
  type: Span
10182
+ }, {
10183
+ childCount: 1,
10184
+ className: ChatToolCallFileName,
10185
+ type: Span
10017
10186
  }, text(fileName), ...(statusLabel ? [text(statusLabel)] : [])];
10018
10187
  };
10019
10188
 
10020
10189
  const maxHtmlLength = 40_000;
10021
10190
  const tokenRegex = /<!--[\s\S]*?-->|<\/?[a-zA-Z][\w:-]*(?:\s[^<>]*?)?>|[^<]+/g;
10022
10191
  const attributeRegex = /([^\s=/>]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
10192
+ const scriptTagRegex = /<script\b[\s\S]*?<\/script>/gi;
10193
+ const styleTagRegex = /<style\b[\s\S]*?<\/style>/gi;
10194
+ const headTagRegex = /<head\b[\s\S]*?<\/head>/gi;
10195
+ const metaTagRegex = /<meta\b[^>]*>/gi;
10196
+ const linkTagRegex = /<link\b[^>]*>/gi;
10197
+ const tagPrefixRegex = /^<\/?\s*[a-zA-Z][\w:-]*/;
10198
+ const tagSuffixRegex = /\/?\s*>$/;
10199
+ const openTagNameRegex = /^<\s*([a-zA-Z][\w:-]*)/;
10023
10200
  const inlineTags = new Set(['a', 'abbr', 'b', 'code', 'em', 'i', 'label', 'small', 'span', 'strong', 'sub', 'sup', 'u']);
10024
10201
  const voidElements = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr']);
10025
10202
  const sanitizeHtml = value => {
10026
- return value.slice(0, maxHtmlLength).replaceAll(/<script\b[\s\S]*?<\/script>/gi, '').replaceAll(/<style\b[\s\S]*?<\/style>/gi, '').replaceAll(/<head\b[\s\S]*?<\/head>/gi, '').replaceAll(/<meta\b[^>]*>/gi, '').replaceAll(/<link\b[^>]*>/gi, '');
10203
+ return value.slice(0, maxHtmlLength).replaceAll(scriptTagRegex, '').replaceAll(styleTagRegex, '').replaceAll(headTagRegex, '').replaceAll(metaTagRegex, '').replaceAll(linkTagRegex, '');
10027
10204
  };
10028
10205
  const decodeEntities = value => {
10029
10206
  return value.replaceAll('&nbsp;', ' ').replaceAll('&quot;', '"').replaceAll('&#39;', "'").replaceAll('&lt;', '<').replaceAll('&gt;', '>').replaceAll('&amp;', '&');
10030
10207
  };
10031
10208
  const parseAttributes = token => {
10032
- const withoutTag = token.replace(/^<\/?\s*[a-zA-Z][\w:-]*/, '').replace(/\/?\s*>$/, '').trim();
10209
+ const withoutTag = token.replace(tagPrefixRegex, '').replace(tagSuffixRegex, '').trim();
10033
10210
  if (!withoutTag) {
10034
10211
  return Object.create(null);
10035
10212
  }
@@ -10076,7 +10253,7 @@ const parseHtml = value => {
10076
10253
  continue;
10077
10254
  }
10078
10255
  if (token.startsWith('<')) {
10079
- const openTagNameMatch = /^<\s*([a-zA-Z][\w:-]*)/.exec(token);
10256
+ const openTagNameMatch = openTagNameRegex.exec(token);
10080
10257
  if (!openTagNameMatch) {
10081
10258
  continue;
10082
10259
  }
@@ -10320,55 +10497,6 @@ const getToolCallRenderHtmlVirtualDom = toolCall => {
10320
10497
  }, ...parsedHtml.virtualDom];
10321
10498
  };
10322
10499
 
10323
- const getToolCallDisplayName = name => {
10324
- if (name === 'getWorkspaceUri') {
10325
- return 'get_workspace_uri';
10326
- }
10327
- return name;
10328
- };
10329
- const hasIncompleteJsonArguments = rawArguments => {
10330
- try {
10331
- JSON.parse(rawArguments);
10332
- return false;
10333
- } catch {
10334
- return true;
10335
- }
10336
- };
10337
- const getToolCallLabel = toolCall => {
10338
- const displayName = getToolCallDisplayName(toolCall.name);
10339
- if (toolCall.name === 'write_file' && !toolCall.status && hasIncompleteJsonArguments(toolCall.arguments)) {
10340
- return `${displayName} (in progress)`;
10341
- }
10342
- const argumentPreview = getToolCallArgumentPreview(toolCall.arguments);
10343
- const statusLabel = getToolCallStatusLabel(toolCall);
10344
- if (argumentPreview === '{}') {
10345
- return `${displayName}${statusLabel}`;
10346
- }
10347
- return `${displayName} ${argumentPreview}${statusLabel}`;
10348
- };
10349
- const getToolCallGetWorkspaceUriVirtualDom = toolCall => {
10350
- if (!toolCall.result) {
10351
- return [];
10352
- }
10353
- const statusLabel = getToolCallStatusLabel(toolCall);
10354
- const fileName = getFileNameFromUri(toolCall.result);
10355
- return [{
10356
- childCount: statusLabel ? 4 : 3,
10357
- className: ChatOrderedListItem,
10358
- title: toolCall.result,
10359
- type: Li
10360
- }, {
10361
- childCount: 0,
10362
- className: FileIcon,
10363
- type: Div
10364
- }, text('get_workspace_uri '), {
10365
- childCount: 1,
10366
- className: ChatToolCallReadFileLink,
10367
- 'data-uri': toolCall.result,
10368
- onClick: HandleClickReadFile,
10369
- type: Span
10370
- }, text(fileName), ...(statusLabel ? [text(statusLabel)] : [])];
10371
- };
10372
10500
  const parseWriteFileLineCounts = rawResult => {
10373
10501
  if (!rawResult) {
10374
10502
  return {
@@ -10398,6 +10526,7 @@ const parseWriteFileLineCounts = rawResult => {
10398
10526
  linesDeleted: typeof linesDeleted === 'number' ? Math.max(0, linesDeleted) : 0
10399
10527
  };
10400
10528
  };
10529
+
10401
10530
  const getToolCallWriteFileVirtualDom = toolCall => {
10402
10531
  const target = getReadFileTarget(toolCall.arguments);
10403
10532
  if (!target) {
@@ -10427,6 +10556,10 @@ const getToolCallWriteFileVirtualDom = toolCall => {
10427
10556
  className: ChatToolCallReadFileLink,
10428
10557
  ...fileNameClickableProps,
10429
10558
  type: Span
10559
+ }, {
10560
+ childCount: 1,
10561
+ className: ChatToolCallFileName,
10562
+ type: Span
10430
10563
  }, text(fileName), {
10431
10564
  childCount: 1,
10432
10565
  className: Insertion,
@@ -10437,33 +10570,7 @@ const getToolCallWriteFileVirtualDom = toolCall => {
10437
10570
  type: Span
10438
10571
  }, text(` -${linesDeleted}`), ...(statusLabel ? [text(statusLabel)] : [])];
10439
10572
  };
10440
- const getToolCallEditFileVirtualDom = toolCall => {
10441
- const target = getReadFileTarget(toolCall.arguments);
10442
- if (!target) {
10443
- return [];
10444
- }
10445
- const fileName = getFileNameFromUri(target.title);
10446
- const fileNameClickableProps = target.clickableUri ? {
10447
- 'data-uri': target.clickableUri,
10448
- onClick: HandleClickReadFile
10449
- } : {};
10450
- return [{
10451
- childCount: 3,
10452
- className: ChatOrderedListItem,
10453
- title: target.title,
10454
- type: Li
10455
- }, {
10456
- childCount: 0,
10457
- className: FileIcon,
10458
- type: Div
10459
- }, text('edit_file '), {
10460
- childCount: 1,
10461
- className: ChatToolCallReadFileLink,
10462
- title: target.clickableUri,
10463
- ...fileNameClickableProps,
10464
- type: Span
10465
- }, text(fileName)];
10466
- };
10573
+
10467
10574
  const getToolCallDom = toolCall => {
10468
10575
  if (toolCall.name === 'getWorkspaceUri') {
10469
10576
  const virtualDom = getToolCallGetWorkspaceUriVirtualDom(toolCall);
@@ -10598,9 +10705,9 @@ const getDisplayMessages = (messages, parsedMessages) => {
10598
10705
  }
10599
10706
  return displayMessages;
10600
10707
  };
10601
- const getMessagesDom = (messages, parsedMessages, openRouterApiKeyInput, openApiApiKeyInput = '', openRouterApiKeyState = 'idle', messagesScrollTop = 0, useChatMathWorker = false) => {
10708
+ const getMessagesDom = (messages, parsedMessages, openRouterApiKeyInput, openApiApiKeyInput = '', openRouterApiKeyState = 'idle', messagesScrollTop = 0, useChatMathWorker = false, hideWelcomeMessage = false) => {
10602
10709
  if (messages.length === 0) {
10603
- return getEmptyMessagesDom();
10710
+ return hideWelcomeMessage ? [] : getEmptyMessagesDom();
10604
10711
  }
10605
10712
  const displayMessages = getDisplayMessages(messages, parsedMessages);
10606
10713
  return [{
@@ -10698,7 +10805,40 @@ const getProjectListDom = (projects, sessions, projectExpandedIds, selectedProje
10698
10805
  }, text('+ Add Project')];
10699
10806
  };
10700
10807
 
10701
- const getChatModeChatFocusVirtualDom = (sessions, selectedSessionId, composerValue, openRouterApiKeyInput, openApiApiKeyInput, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, openRouterApiKeyState = 'idle', composerHeight = 28, composerFontSize = 13, composerFontFamily = 'system-ui', composerLineHeight = 20, messagesScrollTop = 0, composerDropActive = false, composerDropEnabled = true, projects = [], projectExpandedIds = [], selectedProjectId = '', projectListScrollTop = 0, voiceDictationEnabled = false, useChatMathWorker = false, parsedMessages = [], authEnabled = false, authStatus = 'signed-out', authErrorMessage = '') => {
10808
+ const getChatModeChatFocusVirtualDom = ({
10809
+ authEnabled = false,
10810
+ authErrorMessage = '',
10811
+ authStatus = 'signed-out',
10812
+ composerDropActive = false,
10813
+ composerDropEnabled = true,
10814
+ composerFontFamily = 'system-ui',
10815
+ composerFontSize = 13,
10816
+ composerHeight = 28,
10817
+ composerLineHeight = 20,
10818
+ composerValue,
10819
+ messagesScrollTop = 0,
10820
+ models,
10821
+ openApiApiKeyInput,
10822
+ openRouterApiKeyInput,
10823
+ openRouterApiKeyState = 'idle',
10824
+ parsedMessages = [],
10825
+ projectExpandedIds = [],
10826
+ projectListScrollTop = 0,
10827
+ projects = [],
10828
+ runMode,
10829
+ selectedModelId,
10830
+ selectedProjectId = '',
10831
+ selectedSessionId,
10832
+ sessions,
10833
+ showRunMode,
10834
+ todoListItems,
10835
+ todoListToolEnabled,
10836
+ tokensMax,
10837
+ tokensUsed,
10838
+ usageOverviewEnabled,
10839
+ useChatMathWorker = false,
10840
+ voiceDictationEnabled = false
10841
+ }) => {
10702
10842
  const selectedSession = sessions.find(session => session.id === selectedSessionId);
10703
10843
  const messages = selectedSession ? selectedSession.messages : [];
10704
10844
  const isDropOverlayVisible = composerDropEnabled && composerDropActive;
@@ -10708,7 +10848,7 @@ const getChatModeChatFocusVirtualDom = (sessions, selectedSessionId, composerVal
10708
10848
  onDragEnter: HandleDragEnterChatView,
10709
10849
  onDragOver: HandleDragOverChatView,
10710
10850
  type: Div
10711
- }, ...getProjectListDom(projects, sessions, projectExpandedIds, selectedProjectId, selectedSessionId, projectListScrollTop), ...getMessagesDom(messages, parsedMessages, openRouterApiKeyInput, openApiApiKeyInput, openRouterApiKeyState, messagesScrollTop, useChatMathWorker), ...getChatSendAreaDom(composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, voiceDictationEnabled), ...(isDropOverlayVisible ? [{
10851
+ }, ...getProjectListDom(projects, sessions, projectExpandedIds, selectedProjectId, selectedSessionId, projectListScrollTop), ...getMessagesDom(messages, parsedMessages, openRouterApiKeyInput, openApiApiKeyInput, openRouterApiKeyState, messagesScrollTop, useChatMathWorker, true), ...getChatSendAreaDom(composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, showRunMode, runMode, todoListToolEnabled, todoListItems, voiceDictationEnabled), ...(isDropOverlayVisible ? [{
10712
10852
  childCount: 1,
10713
10853
  className: mergeClassNames(ChatViewDropOverlay, ChatViewDropOverlayActive),
10714
10854
  name: ComposerDropTarget,
@@ -10824,7 +10964,36 @@ const getChatHeaderDomDetailMode = (selectedSessionTitle, authEnabled = false, a
10824
10964
  }, text(authErrorMessage)] : [])];
10825
10965
  };
10826
10966
 
10827
- const getChatModeDetailVirtualDom = (sessions, selectedSessionId, composerValue, openRouterApiKeyInput, openApiApiKeyInput, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, openRouterApiKeyState = 'idle', composerHeight = 28, composerFontSize = 13, composerFontFamily = 'system-ui', composerLineHeight = 20, messagesScrollTop = 0, composerDropActive = false, composerDropEnabled = true, voiceDictationEnabled = false, useChatMathWorker = false, parsedMessages = [], authEnabled = false, authStatus = 'signed-out', authErrorMessage = '') => {
10967
+ const getChatModeDetailVirtualDom = ({
10968
+ authEnabled = false,
10969
+ authErrorMessage = '',
10970
+ authStatus = 'signed-out',
10971
+ composerDropActive = false,
10972
+ composerDropEnabled = true,
10973
+ composerFontFamily = 'system-ui',
10974
+ composerFontSize = 13,
10975
+ composerHeight = 28,
10976
+ composerLineHeight = 20,
10977
+ composerValue,
10978
+ messagesScrollTop = 0,
10979
+ models,
10980
+ openApiApiKeyInput,
10981
+ openRouterApiKeyInput,
10982
+ openRouterApiKeyState = 'idle',
10983
+ parsedMessages = [],
10984
+ runMode,
10985
+ selectedModelId,
10986
+ selectedSessionId,
10987
+ sessions,
10988
+ showRunMode,
10989
+ todoListItems,
10990
+ todoListToolEnabled,
10991
+ tokensMax,
10992
+ tokensUsed,
10993
+ usageOverviewEnabled,
10994
+ useChatMathWorker = false,
10995
+ voiceDictationEnabled = false
10996
+ }) => {
10828
10997
  const selectedSession = sessions.find(session => session.id === selectedSessionId);
10829
10998
  const selectedSessionTitle = selectedSession?.title || chatTitle();
10830
10999
  const messages = selectedSession ? selectedSession.messages : [];
@@ -10835,7 +11004,7 @@ const getChatModeDetailVirtualDom = (sessions, selectedSessionId, composerValue,
10835
11004
  onDragEnter: HandleDragEnterChatView,
10836
11005
  onDragOver: HandleDragOverChatView,
10837
11006
  type: Div
10838
- }, ...getChatHeaderDomDetailMode(selectedSessionTitle, authEnabled, authStatus, authErrorMessage), ...getMessagesDom(messages, parsedMessages, openRouterApiKeyInput, openApiApiKeyInput, openRouterApiKeyState, messagesScrollTop, useChatMathWorker), ...getChatSendAreaDom(composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, voiceDictationEnabled), ...(isDropOverlayVisible ? [{
11007
+ }, ...getChatHeaderDomDetailMode(selectedSessionTitle, authEnabled, authStatus, authErrorMessage), ...getMessagesDom(messages, parsedMessages, openRouterApiKeyInput, openApiApiKeyInput, openRouterApiKeyState, messagesScrollTop, useChatMathWorker), ...getChatSendAreaDom(composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, showRunMode, runMode, todoListToolEnabled, todoListItems, voiceDictationEnabled), ...(isDropOverlayVisible ? [{
10839
11008
  childCount: 1,
10840
11009
  className: mergeClassNames(ChatViewDropOverlay, ChatViewDropOverlayActive),
10841
11010
  name: ComposerDropTarget,
@@ -10883,7 +11052,7 @@ const getSessionDom = session => {
10883
11052
  return [{
10884
11053
  childCount: 2,
10885
11054
  className: sessionClassName,
10886
- type: Div
11055
+ type: Li
10887
11056
  }, {
10888
11057
  childCount: 1,
10889
11058
  className: ChatListItemLabel,
@@ -10917,11 +11086,35 @@ const getChatListDom = (sessions, selectedSessionId, chatListScrollTop = 0) => {
10917
11086
  onClick: HandleClickList,
10918
11087
  onScroll: HandleChatListScroll,
10919
11088
  scrollTop: chatListScrollTop,
10920
- type: Div
11089
+ type: Ul
10921
11090
  }, ...sessions.flatMap(getSessionDom)];
10922
11091
  };
10923
11092
 
10924
- const getChatModeListVirtualDom = (sessions, selectedSessionId, composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, composerHeight = 28, composerFontSize = 13, composerFontFamily = 'system-ui', composerLineHeight = 20, chatListScrollTop = 0, composerDropActive = false, composerDropEnabled = true, voiceDictationEnabled = false, authEnabled = false, authStatus = 'signed-out', authErrorMessage = '') => {
11093
+ const getChatModeListVirtualDom = ({
11094
+ authEnabled = false,
11095
+ authErrorMessage = '',
11096
+ authStatus = 'signed-out',
11097
+ chatListScrollTop = 0,
11098
+ composerDropActive = false,
11099
+ composerDropEnabled = true,
11100
+ composerFontFamily = 'system-ui',
11101
+ composerFontSize = 13,
11102
+ composerHeight = 28,
11103
+ composerLineHeight = 20,
11104
+ composerValue,
11105
+ models,
11106
+ runMode,
11107
+ selectedModelId,
11108
+ selectedSessionId,
11109
+ sessions,
11110
+ showRunMode,
11111
+ todoListItems,
11112
+ todoListToolEnabled,
11113
+ tokensMax,
11114
+ tokensUsed,
11115
+ usageOverviewEnabled,
11116
+ voiceDictationEnabled = false
11117
+ }) => {
10925
11118
  const isDropOverlayVisible = composerDropEnabled && composerDropActive;
10926
11119
  return [{
10927
11120
  childCount: isDropOverlayVisible ? 4 : 3,
@@ -10929,7 +11122,7 @@ const getChatModeListVirtualDom = (sessions, selectedSessionId, composerValue, m
10929
11122
  onDragEnter: HandleDragEnterChatView,
10930
11123
  onDragOver: HandleDragOverChatView,
10931
11124
  type: Div
10932
- }, ...getChatHeaderListModeDom(authEnabled, authStatus, authErrorMessage), ...getChatListDom(sessions, selectedSessionId, chatListScrollTop), ...getChatSendAreaDom(composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, voiceDictationEnabled), ...(isDropOverlayVisible ? [{
11125
+ }, ...getChatHeaderListModeDom(authEnabled, authStatus, authErrorMessage), ...getChatListDom(sessions, selectedSessionId, chatListScrollTop), ...getChatSendAreaDom(composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, showRunMode, runMode, todoListToolEnabled, todoListItems, voiceDictationEnabled), ...(isDropOverlayVisible ? [{
10933
11126
  childCount: 1,
10934
11127
  className: mergeClassNames(ChatViewDropOverlay, ChatViewDropOverlayActive),
10935
11128
  name: ComposerDropTarget,
@@ -10950,6 +11143,62 @@ const getChatModeUnsupportedVirtualDom = () => {
10950
11143
  }, text(unknownViewMode())];
10951
11144
  };
10952
11145
 
11146
+ const isTodoStatus = status => {
11147
+ return status === 'todo' || status === 'inProgress' || status === 'completed';
11148
+ };
11149
+ const parseTodoListArguments = rawArguments => {
11150
+ let parsed;
11151
+ try {
11152
+ parsed = JSON.parse(rawArguments);
11153
+ } catch {
11154
+ return [];
11155
+ }
11156
+ if (!parsed || typeof parsed !== 'object') {
11157
+ return [];
11158
+ }
11159
+ const rawTodos = Reflect.get(parsed, 'todos');
11160
+ if (!Array.isArray(rawTodos)) {
11161
+ return [];
11162
+ }
11163
+ const todos = [];
11164
+ for (const rawTodo of rawTodos) {
11165
+ if (!rawTodo || typeof rawTodo !== 'object') {
11166
+ continue;
11167
+ }
11168
+ const text = Reflect.get(rawTodo, 'text');
11169
+ const status = Reflect.get(rawTodo, 'status');
11170
+ if (typeof text !== 'string' || !isTodoStatus(status)) {
11171
+ continue;
11172
+ }
11173
+ todos.push({
11174
+ status,
11175
+ text
11176
+ });
11177
+ }
11178
+ return todos;
11179
+ };
11180
+
11181
+ const getTodoListItems = (sessions, selectedSessionId) => {
11182
+ const selectedSession = sessions.find(session => session.id === selectedSessionId);
11183
+ if (!selectedSession) {
11184
+ return [];
11185
+ }
11186
+ let todoItems = [];
11187
+ for (const message of selectedSession.messages) {
11188
+ if (message.role !== 'assistant' || !message.toolCalls) {
11189
+ continue;
11190
+ }
11191
+ for (const toolCall of message.toolCalls) {
11192
+ if (toolCall.name !== 'todo_list') {
11193
+ continue;
11194
+ }
11195
+ const parsedTodos = parseTodoListArguments(toolCall.arguments);
11196
+ todoItems = parsedTodos;
11197
+ }
11198
+ }
11199
+ return todoItems;
11200
+ };
11201
+
10953
11202
  const getFallbackParsedMessages = sessions => {
10954
11203
  const parsedMessages = [];
10955
11204
  for (const session of sessions) {
@@ -10966,15 +11215,137 @@ const getFallbackParsedMessages = sessions => {
10966
11215
  }
10967
11216
  return parsedMessages;
10968
11217
  };
10969
- const getChatVirtualDom = (sessions, selectedSessionId, composerValue, openRouterApiKeyInput, viewMode, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, openApiApiKeyInput, openRouterApiKeyState, composerHeight, composerFontSize, composerFontFamily, composerLineHeight, chatListScrollTop, messagesScrollTop, composerDropActive = false, composerDropEnabled = true, projects = [], projectExpandedIds = [], selectedProjectId = '', projectListScrollTop = 0, voiceDictationEnabled = false, useChatMathWorker = false, parsedMessages, authEnabled = false, authStatus = 'signed-out', authErrorMessage = '') => {
10970
- const effectiveParsedMessages = parsedMessages || getFallbackParsedMessages(sessions);
11218
+ const getChatVirtualDom = options => {
11219
+ const {
11220
+ authEnabled = false,
11221
+ authErrorMessage = '',
11222
+ authStatus = 'signed-out',
11223
+ chatListScrollTop,
11224
+ composerDropActive = false,
11225
+ composerDropEnabled = true,
11226
+ composerFontFamily,
11227
+ composerFontSize,
11228
+ composerHeight,
11229
+ composerLineHeight,
11230
+ composerValue,
11231
+ messagesScrollTop,
11232
+ models,
11233
+ openApiApiKeyInput,
11234
+ openRouterApiKeyInput,
11235
+ openRouterApiKeyState,
11236
+ parsedMessages: parsedMessagesInput,
11237
+ projectExpandedIds = [],
11238
+ projectListScrollTop = 0,
11239
+ projects = [],
11240
+ runMode,
11241
+ selectedModelId,
11242
+ selectedProjectId = '',
11243
+ selectedSessionId,
11244
+ sessions,
11245
+ showRunMode,
11246
+ todoListToolEnabled,
11247
+ tokensMax,
11248
+ tokensUsed,
11249
+ usageOverviewEnabled,
11250
+ useChatMathWorker = false,
11251
+ viewMode,
11252
+ voiceDictationEnabled = false
11253
+ } = options;
11254
+ const parsedMessages = parsedMessagesInput ?? getFallbackParsedMessages(sessions);
11255
+ const todoListItems = getTodoListItems(sessions, selectedSessionId);
10971
11256
  switch (viewMode) {
10972
11257
  case 'chat-focus':
10973
- return getChatModeChatFocusVirtualDom(sessions, selectedSessionId, composerValue, openRouterApiKeyInput, openApiApiKeyInput, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, openRouterApiKeyState, composerHeight, composerFontSize, composerFontFamily, composerLineHeight, messagesScrollTop, composerDropActive, composerDropEnabled, projects, projectExpandedIds, selectedProjectId, projectListScrollTop, voiceDictationEnabled, useChatMathWorker, effectiveParsedMessages, authEnabled, authStatus, authErrorMessage);
11258
+ return getChatModeChatFocusVirtualDom({
11259
+ authEnabled,
11260
+ authErrorMessage,
11261
+ authStatus,
11262
+ composerDropActive,
11263
+ composerDropEnabled,
11264
+ composerFontFamily,
11265
+ composerFontSize,
11266
+ composerHeight,
11267
+ composerLineHeight,
11268
+ composerValue,
11269
+ messagesScrollTop,
11270
+ models,
11271
+ openApiApiKeyInput,
11272
+ openRouterApiKeyInput,
11273
+ openRouterApiKeyState,
11274
+ parsedMessages,
11275
+ projectExpandedIds,
11276
+ projectListScrollTop,
11277
+ projects,
11278
+ runMode,
11279
+ selectedModelId,
11280
+ selectedProjectId,
11281
+ selectedSessionId,
11282
+ sessions,
11283
+ showRunMode,
11284
+ todoListItems,
11285
+ todoListToolEnabled,
11286
+ tokensMax,
11287
+ tokensUsed,
11288
+ usageOverviewEnabled,
11289
+ useChatMathWorker,
11290
+ voiceDictationEnabled
11291
+ });
10974
11292
  case 'detail':
10975
- return getChatModeDetailVirtualDom(sessions, selectedSessionId, composerValue, openRouterApiKeyInput, openApiApiKeyInput, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, openRouterApiKeyState, composerHeight, composerFontSize, composerFontFamily, composerLineHeight, messagesScrollTop, composerDropActive, composerDropEnabled, voiceDictationEnabled, useChatMathWorker, effectiveParsedMessages, authEnabled, authStatus, authErrorMessage);
11293
+ return getChatModeDetailVirtualDom({
11294
+ authEnabled,
11295
+ authErrorMessage,
11296
+ authStatus,
11297
+ composerDropActive,
11298
+ composerDropEnabled,
11299
+ composerFontFamily,
11300
+ composerFontSize,
11301
+ composerHeight,
11302
+ composerLineHeight,
11303
+ composerValue,
11304
+ messagesScrollTop,
11305
+ models,
11306
+ openApiApiKeyInput,
11307
+ openRouterApiKeyInput,
11308
+ openRouterApiKeyState,
11309
+ parsedMessages,
11310
+ runMode,
11311
+ selectedModelId,
11312
+ selectedSessionId,
11313
+ sessions,
11314
+ showRunMode,
11315
+ todoListItems,
11316
+ todoListToolEnabled,
11317
+ tokensMax,
11318
+ tokensUsed,
11319
+ usageOverviewEnabled,
11320
+ useChatMathWorker,
11321
+ voiceDictationEnabled
11322
+ });
10976
11323
  case 'list':
10977
- return getChatModeListVirtualDom(sessions, selectedSessionId, composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, composerHeight, composerFontSize, composerFontFamily, composerLineHeight, chatListScrollTop, composerDropActive, composerDropEnabled, voiceDictationEnabled, authEnabled, authStatus, authErrorMessage);
11324
+ return getChatModeListVirtualDom({
11325
+ authEnabled,
11326
+ authErrorMessage,
11327
+ authStatus,
11328
+ chatListScrollTop,
11329
+ composerDropActive,
11330
+ composerDropEnabled,
11331
+ composerFontFamily,
11332
+ composerFontSize,
11333
+ composerHeight,
11334
+ composerLineHeight,
11335
+ composerValue,
11336
+ models,
11337
+ runMode,
11338
+ selectedModelId,
11339
+ selectedSessionId,
11340
+ sessions,
11341
+ showRunMode,
11342
+ todoListItems,
11343
+ todoListToolEnabled,
11344
+ tokensMax,
11345
+ tokensUsed,
11346
+ usageOverviewEnabled,
11347
+ voiceDictationEnabled
11348
+ });
10978
11349
  default:
10979
11350
  return getChatModeUnsupportedVirtualDom();
10980
11351
  }
@@ -11003,10 +11374,13 @@ const renderItems = (oldState, newState) => {
11003
11374
  projectExpandedIds,
11004
11375
  projectListScrollTop,
11005
11376
  projects,
11377
+ runMode,
11006
11378
  selectedModelId,
11007
11379
  selectedProjectId,
11008
11380
  selectedSessionId,
11009
11381
  sessions,
11382
+ showRunMode,
11383
+ todoListToolEnabled,
11010
11384
  tokensMax,
11011
11385
  tokensUsed,
11012
11386
  uid,
@@ -11018,7 +11392,41 @@ const renderItems = (oldState, newState) => {
11018
11392
  if (initial) {
11019
11393
  return [SetDom2, uid, []];
11020
11394
  }
11021
- const dom = getChatVirtualDom(sessions, selectedSessionId, composerValue, openRouterApiKeyInput, viewMode, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, openApiApiKeyInput, openRouterApiKeyState, composerHeight, composerFontSize, composerFontFamily, composerLineHeight, chatListScrollTop, messagesScrollTop, composerDropActive, composerDropEnabled, projects, projectExpandedIds, selectedProjectId, projectListScrollTop, voiceDictationEnabled, useChatMathWorker, parsedMessages, authEnabled, authStatus, authErrorMessage);
11395
+ const dom = getChatVirtualDom({
11396
+ authEnabled,
11397
+ authErrorMessage,
11398
+ authStatus,
11399
+ chatListScrollTop,
11400
+ composerDropActive,
11401
+ composerDropEnabled,
11402
+ composerFontFamily,
11403
+ composerFontSize,
11404
+ composerHeight,
11405
+ composerLineHeight,
11406
+ composerValue,
11407
+ messagesScrollTop,
11408
+ models,
11409
+ openApiApiKeyInput,
11410
+ openRouterApiKeyInput,
11411
+ openRouterApiKeyState,
11412
+ parsedMessages,
11413
+ projectExpandedIds,
11414
+ projectListScrollTop,
11415
+ projects,
11416
+ runMode,
11417
+ selectedModelId,
11418
+ selectedProjectId,
11419
+ selectedSessionId,
11420
+ sessions,
11421
+ showRunMode,
11422
+ todoListToolEnabled,
11423
+ tokensMax,
11424
+ tokensUsed,
11425
+ usageOverviewEnabled,
11426
+ useChatMathWorker,
11427
+ viewMode,
11428
+ voiceDictationEnabled
11429
+ });
11022
11430
  return [SetDom2, uid, dom];
11023
11431
  };
11024
11432
 
@@ -11222,6 +11630,9 @@ const renderEventListeners = () => {
11222
11630
  }, {
11223
11631
  name: HandleModelChange,
11224
11632
  params: ['handleModelChange', TargetValue]
11633
+ }, {
11634
+ name: HandleRunModeChange,
11635
+ params: ['handleRunModeChange', TargetValue]
11225
11636
  }, {
11226
11637
  name: HandleChatListScroll,
11227
11638
  params: ['handleChatListScroll', 'event.target.scrollTop']
@@ -11247,7 +11658,8 @@ const renderEventListeners = () => {
11247
11658
  params: ['handleKeyDown', Key, ShiftKey]
11248
11659
  }, {
11249
11660
  name: HandleSubmit,
11250
- params: ['handleSubmit']
11661
+ params: ['handleSubmit'],
11662
+ preventDefault: true
11251
11663
  }, {
11252
11664
  name: HandleMissingApiKeySubmit,
11253
11665
  params: ['handleMissingApiKeySubmit', 'event.submitter?.name || ""'],
@@ -11367,6 +11779,13 @@ const setQuestionToolEnabled = (state, questionToolEnabled) => {
11367
11779
  };
11368
11780
  };
11369
11781
 
11782
+ const setShowRunMode = (state, showRunMode) => {
11783
+ return {
11784
+ ...state,
11785
+ showRunMode
11786
+ };
11787
+ };
11788
+
11370
11789
  const setStreamingEnabled = (state, streamingEnabled) => {
11371
11790
  return {
11372
11791
  ...state,
@@ -11374,6 +11793,13 @@ const setStreamingEnabled = (state, streamingEnabled) => {
11374
11793
  };
11375
11794
  };
11376
11795
 
11796
+ const setTodoListToolEnabled = (state, todoListToolEnabled) => {
11797
+ return {
11798
+ ...state,
11799
+ todoListToolEnabled
11800
+ };
11801
+ };
11802
+
11377
11803
  const setUseChatCoordinatorWorker = async (state, useChatCoordinatorWorker, persist = true) => {
11378
11804
  if (persist) {
11379
11805
  await update({
@@ -11463,6 +11889,7 @@ const commandMap = {
11463
11889
  'Chat.handleModelChange': wrapCommand(handleModelChange),
11464
11890
  'Chat.handleProjectListContextMenu': wrapCommand(handleProjectListContextMenu),
11465
11891
  'Chat.handleProjectListScroll': wrapCommand(handleProjectListScroll),
11892
+ 'Chat.handleRunModeChange': wrapCommand(handleRunModeChange),
11466
11893
  'Chat.handleSubmit': wrapCommand(handleSubmit),
11467
11894
  'Chat.initialize': initialize,
11468
11895
  'Chat.loadContent': wrapCommand(loadContent),
@@ -11489,7 +11916,9 @@ const commandMap = {
11489
11916
  'Chat.setEmitStreamingFunctionCallEvents': wrapCommand(setEmitStreamingFunctionCallEvents),
11490
11917
  'Chat.setOpenRouterApiKey': wrapCommand(setOpenRouterApiKey),
11491
11918
  'Chat.setQuestionToolEnabled': wrapCommand(setQuestionToolEnabled),
11919
+ 'Chat.setShowRunMode': wrapCommand(setShowRunMode),
11492
11920
  'Chat.setStreamingEnabled': wrapCommand(setStreamingEnabled),
11921
+ 'Chat.setTodoListToolEnabled': wrapCommand(setTodoListToolEnabled),
11493
11922
  'Chat.setUseChatCoordinatorWorker': wrapCommand(setUseChatCoordinatorWorker),
11494
11923
  'Chat.setUseChatMathWorker': wrapCommand(setUseChatMathWorker),
11495
11924
  'Chat.setUseChatNetworkWorkerForRequests': wrapCommand(setUseChatNetworkWorkerForRequests),