@lvce-editor/chat-view 6.10.0 → 6.12.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,12 @@ 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,
1723
1733
  tokensMax: 0,
1724
1734
  tokensUsed: 0,
1725
1735
  uid: 0,
@@ -2513,7 +2523,7 @@ const getRenderHtmlCss = (sessions, selectedSessionId) => {
2513
2523
  const isEqual$1 = (oldState, newState) => {
2514
2524
  const oldRenderHtmlCss = getRenderHtmlCss(oldState.sessions, oldState.selectedSessionId);
2515
2525
  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;
2526
+ 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
2527
  };
2518
2528
 
2519
2529
  const diffFocus = (oldState, newState) => {
@@ -2535,7 +2545,7 @@ const isEqualProjectExpandedIds = (a, b) => {
2535
2545
  return true;
2536
2546
  };
2537
2547
  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;
2548
+ 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
2549
  };
2540
2550
 
2541
2551
  const diffScrollTop = (oldState, newState) => {
@@ -3126,12 +3136,14 @@ const selectProject = async (state, projectId) => {
3126
3136
  };
3127
3137
  };
3128
3138
 
3139
+ const fileSchemeRegex = /^file:\/\//;
3140
+ const trailingSlashesRegex$4 = /\/+$/;
3129
3141
  const getProjectName = (uri, fallbackIndex) => {
3130
3142
  if (!uri) {
3131
3143
  return `Project ${fallbackIndex}`;
3132
3144
  }
3133
- const withoutScheme = uri.replace(/^file:\/\//, '');
3134
- const normalized = withoutScheme.replace(/\/+$/, '');
3145
+ const withoutScheme = uri.replace(fileSchemeRegex, '');
3146
+ const normalized = withoutScheme.replace(trailingSlashesRegex$4, '');
3135
3147
  const lastSegment = normalized.split('/').pop();
3136
3148
  if (!lastSegment) {
3137
3149
  return `Project ${fallbackIndex}`;
@@ -3194,6 +3206,7 @@ const update = async settings => {
3194
3206
  await invoke$1('Preferences.update', settings);
3195
3207
  };
3196
3208
 
3209
+ const trailingSlashesRegex$3 = /\/+$/;
3197
3210
  const isLoginResponse = value => {
3198
3211
  if (!value || typeof value !== 'object') {
3199
3212
  return false;
@@ -3201,7 +3214,7 @@ const isLoginResponse = value => {
3201
3214
  return true;
3202
3215
  };
3203
3216
  const trimTrailingSlashes = value => {
3204
- return value.replace(/\/+$/, '');
3217
+ return value.replace(trailingSlashesRegex$3, '');
3205
3218
  };
3206
3219
  const handleClickLogin = async state => {
3207
3220
  if (!state.backendUrl) {
@@ -4001,6 +4014,8 @@ const getTextContent = content => {
4001
4014
 
4002
4015
  /* eslint-disable @typescript-eslint/prefer-readonly-parameter-types */
4003
4016
 
4017
+ const errorPrefixRegex = /^Error:\s*/;
4018
+ const notFoundErrorRegex = /not[\s_-]?found|enoent/i;
4004
4019
  const getOpenAiTools = tools => {
4005
4020
  return tools.map(tool => {
4006
4021
  if (!tool || typeof tool !== 'object') {
@@ -4066,7 +4081,7 @@ const getStreamChunkText = content => {
4066
4081
  }).join('');
4067
4082
  };
4068
4083
  const getShortToolErrorMessage = error => {
4069
- const trimmed = error.trim().replace(/^Error:\s*/, '');
4084
+ const trimmed = error.trim().replace(errorPrefixRegex, '');
4070
4085
  const firstLine = trimmed.split('\n')[0];
4071
4086
  if (firstLine.length <= 80) {
4072
4087
  return firstLine;
@@ -4098,7 +4113,7 @@ const getToolCallExecutionStatus = content => {
4098
4113
  const rawStack = Reflect.get(parsed, 'errorStack') ?? Reflect.get(parsed, 'stack');
4099
4114
  const errorStack = typeof rawStack === 'string' && rawStack.trim() ? rawStack : undefined;
4100
4115
  const errorMessage = getShortToolErrorMessage(rawError);
4101
- if (/not[\s_-]?found|enoent/i.test(errorMessage)) {
4116
+ if (notFoundErrorRegex.test(errorMessage)) {
4102
4117
  return {
4103
4118
  ...(errorStack ? {
4104
4119
  errorStack
@@ -5086,13 +5101,15 @@ const getOpenApiModelId = selectedModelId => {
5086
5101
  return selectedModelId;
5087
5102
  };
5088
5103
 
5104
+ const trailingSlashesRegex$2 = /\/+$/;
5089
5105
  const getOpenRouterApiEndpoint = openRouterApiBaseUrl => {
5090
- const trimmedBaseUrl = openRouterApiBaseUrl.replace(/\/+$/, '');
5106
+ const trimmedBaseUrl = openRouterApiBaseUrl.replace(trailingSlashesRegex$2, '');
5091
5107
  return `${trimmedBaseUrl}/chat/completions`;
5092
5108
  };
5093
5109
 
5110
+ const trailingSlashesRegex$1 = /\/+$/;
5094
5111
  const getOpenRouterKeyEndpoint = openRouterApiBaseUrl => {
5095
- const trimmedBaseUrl = openRouterApiBaseUrl.replace(/\/+$/, '');
5112
+ const trimmedBaseUrl = openRouterApiBaseUrl.replace(trailingSlashesRegex$1, '');
5096
5113
  return `${trimmedBaseUrl}/auth/key`;
5097
5114
  };
5098
5115
 
@@ -5478,8 +5495,9 @@ const getAll = () => {
5478
5495
 
5479
5496
  /* eslint-disable prefer-destructuring */
5480
5497
 
5498
+ const trailingSlashesRegex = /\/+$/;
5481
5499
  const getBackendCompletionsEndpoint = backendUrl => {
5482
- const trimmedBackendUrl = backendUrl.replace(/\/+$/, '');
5500
+ const trimmedBackendUrl = backendUrl.replace(trailingSlashesRegex, '');
5483
5501
  return `${trimmedBackendUrl}/v1/chat/completions`;
5484
5502
  };
5485
5503
  const getEffectiveBackendModelId = selectedModelId => {
@@ -6254,21 +6272,25 @@ const parseInlineNodes = value => {
6254
6272
  };
6255
6273
 
6256
6274
  const markdownMathBlockDelimiter = '$$';
6275
+ const escapedNewlineRegex = /\\r\\n|\\n/g;
6276
+ const lineBreakRegex = /\r?\n/;
6277
+ const tableDelimiterRegex = /\|\s*[-:]{3,}/;
6278
+ const inlineTableCellRegex = /\|\s+\|/g;
6257
6279
  const normalizeEscapedNewlines = value => {
6258
6280
  if (value.includes('\\n')) {
6259
- return value.replaceAll(/\\r\\n|\\n/g, '\n');
6281
+ return value.replaceAll(escapedNewlineRegex, '\n');
6260
6282
  }
6261
6283
  return value;
6262
6284
  };
6263
6285
  const normalizeInlineTables = value => {
6264
- return value.split(/\r?\n/).map(line => {
6286
+ return value.split(lineBreakRegex).map(line => {
6265
6287
  if (!line.includes('|')) {
6266
6288
  return line;
6267
6289
  }
6268
- if (!/\|\s*[-:]{3,}/.test(line)) {
6290
+ if (!tableDelimiterRegex.test(line)) {
6269
6291
  return line;
6270
6292
  }
6271
- return line.replaceAll(/\|\s+\|/g, '|\n|');
6293
+ return line.replaceAll(inlineTableCellRegex, '|\n|');
6272
6294
  }).join('\n');
6273
6295
  };
6274
6296
  const parseHeadingLine = line => {
@@ -6347,6 +6369,33 @@ const parseBlockQuoteLine = line => {
6347
6369
  }
6348
6370
  return content;
6349
6371
  };
6372
+ const isThematicBreakLine = line => {
6373
+ const trimmedStart = line.trimStart();
6374
+ const leadingSpaces = line.length - trimmedStart.length;
6375
+ if (leadingSpaces > 3) {
6376
+ return false;
6377
+ }
6378
+ const trimmed = trimmedStart.trimEnd();
6379
+ if (!trimmed) {
6380
+ return false;
6381
+ }
6382
+ const marker = trimmed[0];
6383
+ if (marker !== '-' && marker !== '_' && marker !== '*') {
6384
+ return false;
6385
+ }
6386
+ let markerCount = 0;
6387
+ for (let i = 0; i < trimmed.length; i++) {
6388
+ const char = trimmed[i];
6389
+ if (char === marker) {
6390
+ markerCount++;
6391
+ continue;
6392
+ }
6393
+ if (char !== ' ') {
6394
+ return false;
6395
+ }
6396
+ }
6397
+ return markerCount >= 3;
6398
+ };
6350
6399
  const isTableRow = line => {
6351
6400
  const trimmed = line.trim();
6352
6401
  if (!trimmed.startsWith('|') || !trimmed.endsWith('|')) {
@@ -6360,7 +6409,7 @@ const getTableCells = line => {
6360
6409
  };
6361
6410
  const scanBlockTokens = rawMessage => {
6362
6411
  const normalizedMessage = normalizeInlineTables(normalizeEscapedNewlines(rawMessage));
6363
- const lines = normalizedMessage.split(/\r?\n/);
6412
+ const lines = normalizedMessage.split(lineBreakRegex);
6364
6413
  const tokens = [];
6365
6414
  for (let i = 0; i < lines.length; i++) {
6366
6415
  const line = lines[i];
@@ -6426,6 +6475,12 @@ const scanBlockTokens = rawMessage => {
6426
6475
  });
6427
6476
  continue;
6428
6477
  }
6478
+ if (isThematicBreakLine(line)) {
6479
+ tokens.push({
6480
+ type: 'thematic-break'
6481
+ });
6482
+ continue;
6483
+ }
6429
6484
  const ordered = parseOrderedListItemLine(line);
6430
6485
  if (ordered) {
6431
6486
  tokens.push({
@@ -6571,6 +6626,14 @@ const parseBlockTokens = tokens => {
6571
6626
  });
6572
6627
  continue;
6573
6628
  }
6629
+ if (token.type === 'thematic-break') {
6630
+ flushList();
6631
+ flushParagraph();
6632
+ nodes.push({
6633
+ type: 'thematic-break'
6634
+ });
6635
+ continue;
6636
+ }
6574
6637
  if (token.type === 'blockquote-line') {
6575
6638
  flushList();
6576
6639
  flushParagraph();
@@ -6944,12 +7007,16 @@ const executeSlashCommand = async (state, command) => {
6944
7007
  });
6945
7008
  };
6946
7009
 
7010
+ const defaultSessionTitleRegex = /^Chat \d+$/;
6947
7011
  const isDefaultSessionTitle = title => {
6948
- return /^Chat \d+$/.test(title);
7012
+ return defaultSessionTitleRegex.test(title);
6949
7013
  };
6950
7014
 
7015
+ const titlePrefixRegex = /^title:\s*/i;
7016
+ const wrappedQuotesAndWhitespaceRegex = /^['"`\s]+|['"`\s]+$/g;
7017
+ const consecutiveWhitespaceRegex = /\s+/g;
6951
7018
  const sanitizeGeneratedTitle = value => {
6952
- return value.replace(/^title:\s*/i, '').replaceAll(/^['"`\s]+|['"`\s]+$/g, '').replaceAll(/\s+/g, ' ').trim().slice(0, 80);
7019
+ return value.replace(titlePrefixRegex, '').replaceAll(wrappedQuotesAndWhitespaceRegex, '').replaceAll(consecutiveWhitespaceRegex, ' ').trim().slice(0, 80);
6953
7020
  };
6954
7021
 
6955
7022
  const getAiSessionTitle = async (state, userText, assistantText) => {
@@ -7020,6 +7087,8 @@ Assistant: ${assistantText}`;
7020
7087
  return title && !isDefaultSessionTitle(title) ? title : '';
7021
7088
  };
7022
7089
 
7090
+ const windowsAbsolutePathRegex = /^[a-zA-Z]:[\\/]/;
7091
+ const pathSeparatorRegex$1 = /[\\/]/;
7023
7092
  const isPathTraversalAttempt = path => {
7024
7093
  if (!path) {
7025
7094
  return false;
@@ -7030,15 +7099,16 @@ const isPathTraversalAttempt = path => {
7030
7099
  if (path.startsWith('file://')) {
7031
7100
  return true;
7032
7101
  }
7033
- if (/^[a-zA-Z]:[\\/]/.test(path)) {
7102
+ if (windowsAbsolutePathRegex.test(path)) {
7034
7103
  return true;
7035
7104
  }
7036
- const segments = path.split(/[\\/]/);
7105
+ const segments = path.split(pathSeparatorRegex$1);
7037
7106
  return segments.includes('..');
7038
7107
  };
7039
7108
 
7109
+ const pathSeparatorRegex = /[\\/]/;
7040
7110
  const normalizeRelativePath = path => {
7041
- const segments = path.split(/[\\/]/).filter(segment => segment && segment !== '.');
7111
+ const segments = path.split(pathSeparatorRegex).filter(segment => segment && segment !== '.');
7042
7112
  if (segments.length === 0) {
7043
7113
  return '.';
7044
7114
  }
@@ -7046,13 +7116,14 @@ const normalizeRelativePath = path => {
7046
7116
  };
7047
7117
 
7048
7118
  const mentionRegex = /(^|\s)@([^\s]+)/g;
7119
+ const trailingPunctuationRegex = /[),.;:!?]+$/g;
7049
7120
  const maxMentionCount = 5;
7050
7121
  const parseMentionedPaths = value => {
7051
7122
  const matches = value.matchAll(mentionRegex);
7052
7123
  const paths = [];
7053
7124
  for (const match of matches) {
7054
7125
  const rawPath = match[2] || '';
7055
- const cleanedPath = rawPath.replaceAll(/[),.;:!?]+$/g, '');
7126
+ const cleanedPath = rawPath.replaceAll(trailingPunctuationRegex, '');
7056
7127
  if (!cleanedPath || isPathTraversalAttempt(cleanedPath)) {
7057
7128
  continue;
7058
7129
  }
@@ -7580,6 +7651,7 @@ const Dictate = 'dictate';
7580
7651
  const Send = 'send';
7581
7652
  const Back = 'back';
7582
7653
  const Model = 'model';
7654
+ const RunMode = 'runMode';
7583
7655
  const ToggleChatFocus = 'toggle-chat-focus';
7584
7656
  const CreateProject = 'create-project';
7585
7657
  const CreateSession = 'create-session';
@@ -8097,6 +8169,19 @@ const handleProjectListContextMenu = async state => {
8097
8169
  return state;
8098
8170
  };
8099
8171
 
8172
+ const isRunMode = value => {
8173
+ return value === 'local' || value === 'background' || value === 'cloud';
8174
+ };
8175
+ const handleRunModeChange = async (state, value) => {
8176
+ if (!isRunMode(value)) {
8177
+ return state;
8178
+ }
8179
+ return {
8180
+ ...state,
8181
+ runMode: value
8182
+ };
8183
+ };
8184
+
8100
8185
  const handleChatListScroll = async (state, chatListScrollTop) => {
8101
8186
  if (state.chatListScrollTop === chatListScrollTop) {
8102
8187
  return state;
@@ -8740,15 +8825,31 @@ const registerMockResponse = (state, mockResponse) => {
8740
8825
  return state;
8741
8826
  };
8742
8827
 
8743
- const getCss = (composerHeight, listItemHeight, chatMessageFontSize, chatMessageLineHeight, chatMessageFontFamily, renderHtmlCss) => {
8828
+ const getCss = (composerHeight, listItemHeight, chatMessageFontSize, chatMessageLineHeight, chatMessageFontFamily, textAreaPaddingTop, textAreaPaddingLeft, textAreaPaddingRight, textAreaPaddingBottom, chatSendAreaPaddingTop, chatSendAreaPaddingLeft, chatSendAreaPaddingRight, chatSendAreaPaddingBottom, renderHtmlCss) => {
8744
8829
  const baseCss = `:root {
8745
8830
  --ChatInputBoxHeight: ${composerHeight}px;
8831
+ --ChatTextAreaHeight: ${composerHeight}px;
8832
+ --ChatSendAreaHeight: ${composerHeight + 62}px;
8833
+ --ChatTextAreaPaddingTop: ${textAreaPaddingTop}px;
8834
+ --ChatTextAreaPaddingLeft: ${textAreaPaddingLeft}px;
8835
+ --ChatTextAreaPaddingRight: ${textAreaPaddingRight}px;
8836
+ --ChatTextAreaPaddingBottom: ${textAreaPaddingBottom}px;
8837
+ --ChatSendAreaPaddingTop: ${chatSendAreaPaddingTop}px;
8838
+ --ChatSendAreaPaddingLeft: ${chatSendAreaPaddingLeft}px;
8839
+ --ChatSendAreaPaddingRight: ${chatSendAreaPaddingRight}px;
8840
+ --ChatSendAreaPaddingBottom: ${chatSendAreaPaddingBottom}px;
8746
8841
  --ChatListItemHeight: ${listItemHeight}px;
8747
8842
  --ChatMessageFontSize: ${chatMessageFontSize}px;
8748
8843
  --ChatMessageLineHeight: ${chatMessageLineHeight}px;
8749
8844
  --ChatMessageFontFamily: ${chatMessageFontFamily};
8750
8845
  }
8751
8846
 
8847
+
8848
+ .ChatSendArea{
8849
+ height: var(--ChatSendAreaHeight);
8850
+ padding: var(--ChatSendAreaPaddingTop) var(--ChatSendAreaPaddingRight) var(--ChatSendAreaPaddingBottom) var(--ChatSendAreaPaddingLeft);
8851
+ }
8852
+
8752
8853
  .Viewlet.Chat.ChatFocus {
8753
8854
  background: linear-gradient(180deg, var(--ColorViewBackground, #1d2229) 0%, #1f252d 100%);
8754
8855
  display: grid;
@@ -8760,6 +8861,11 @@ const getCss = (composerHeight, listItemHeight, chatMessageFontSize, chatMessage
8760
8861
  grid-column: 1 / 3;
8761
8862
  }
8762
8863
 
8864
+ .ChatHeader .Label {
8865
+ text-decoration: underline;
8866
+ text-underline-offset: 5px;
8867
+ }
8868
+
8763
8869
  .Chat.ChatFocus .ProjectSidebar {
8764
8870
  background: color-mix(in srgb, var(--ColorSideBarBackground, #232b35) 88%, #1f2b38 12%);
8765
8871
  border-right: 1px solid var(--ColorBorder, #3a3d41);
@@ -8944,7 +9050,9 @@ const getCss = (composerHeight, listItemHeight, chatMessageFontSize, chatMessage
8944
9050
  .ChatList,
8945
9051
  .ChatListEmpty,
8946
9052
  .ChatMessages {
9053
+ margin: 0;
8947
9054
  min-height: 0;
9055
+ padding: 0;
8948
9056
  }
8949
9057
 
8950
9058
  .Chat.ChatFocus .ChatList,
@@ -8960,6 +9068,32 @@ const getCss = (composerHeight, listItemHeight, chatMessageFontSize, chatMessage
8960
9068
  .Chat.ChatFocus .ChatSendArea {
8961
9069
  grid-column: 2;
8962
9070
  grid-row: 3;
9071
+ height: var(--ChatSendAreaHeight);
9072
+ min-height: var(--ChatSendAreaHeight);
9073
+ }
9074
+
9075
+ .Chat .MultilineInputBox {
9076
+ height: var(--ChatTextAreaHeight);
9077
+ min-height: var(--ChatTextAreaHeight);
9078
+ padding: var(--ChatTextAreaPaddingTop) var(--ChatTextAreaPaddingRight) var(--ChatTextAreaPaddingBottom) var(--ChatTextAreaPaddingLeft);
9079
+ }
9080
+
9081
+ .Select {
9082
+ -moz-appearance: none;
9083
+ -webkit-appearance: none;
9084
+ appearance: none;
9085
+ background-image:
9086
+ linear-gradient(45deg, transparent 50%, color-mix(in srgb, var(--ColorForeground, #d5dbe3) 84%, transparent) 50%),
9087
+ linear-gradient(135deg, color-mix(in srgb, var(--ColorForeground, #d5dbe3) 84%, transparent) 50%, transparent 50%);
9088
+ background-position:
9089
+ calc(100% - 10px) 50%,
9090
+ calc(100% - 6px) 50%;
9091
+ background-repeat: no-repeat;
9092
+ background-size:
9093
+ 4px 4px,
9094
+ 4px 4px;
9095
+ max-width: 60px;
9096
+ padding-right: 16px;
8963
9097
  }
8964
9098
 
8965
9099
  .MarkdownMathInline {
@@ -8980,6 +9114,16 @@ const getCss = (composerHeight, listItemHeight, chatMessageFontSize, chatMessage
8980
9114
  overflow-y: hidden;
8981
9115
  }
8982
9116
 
9117
+ .ChatMessageContent hr,
9118
+ .ChatToolCallRenderHtmlBody hr {
9119
+ border: 0;
9120
+ border-top: 1px solid color-mix(in srgb, var(--ColorBorder, #3a3d41) 78%, transparent);
9121
+ display: block;
9122
+ height: 0;
9123
+ margin: 12px 0;
9124
+ width: 100%;
9125
+ }
9126
+
8983
9127
  .StrikeThrough {
8984
9128
  text-decoration: line-through;
8985
9129
  }
@@ -9048,14 +9192,22 @@ const renderCss = (oldState, newState) => {
9048
9192
  chatMessageFontFamily,
9049
9193
  chatMessageFontSize,
9050
9194
  chatMessageLineHeight,
9195
+ chatSendAreaPaddingBottom,
9196
+ chatSendAreaPaddingLeft,
9197
+ chatSendAreaPaddingRight,
9198
+ chatSendAreaPaddingTop,
9051
9199
  composerHeight,
9052
9200
  listItemHeight,
9053
9201
  selectedSessionId,
9054
9202
  sessions,
9203
+ textAreaPaddingBottom,
9204
+ textAreaPaddingLeft,
9205
+ textAreaPaddingRight,
9206
+ textAreaPaddingTop,
9055
9207
  uid
9056
9208
  } = newState;
9057
9209
  const renderHtmlCss = getRenderHtmlCss(sessions, selectedSessionId);
9058
- const css = getCss(composerHeight, listItemHeight, chatMessageFontSize, chatMessageLineHeight, chatMessageFontFamily, renderHtmlCss);
9210
+ const css = getCss(composerHeight, listItemHeight, chatMessageFontSize, chatMessageLineHeight, chatMessageFontFamily, textAreaPaddingTop, textAreaPaddingLeft, textAreaPaddingRight, textAreaPaddingBottom, chatSendAreaPaddingTop, chatSendAreaPaddingLeft, chatSendAreaPaddingRight, chatSendAreaPaddingBottom, renderHtmlCss);
9059
9211
  return [SetCss, uid, css];
9060
9212
  };
9061
9213
 
@@ -9193,6 +9345,7 @@ const HandleProjectListScroll = 32;
9193
9345
  const HandleProjectListContextMenu = 33;
9194
9346
  const HandleClickDictationButton = 34;
9195
9347
  const HandleMissingApiKeySubmit = 35;
9348
+ const HandleRunModeChange = 36;
9196
9349
 
9197
9350
  const getModelLabel = model => {
9198
9351
  if (model.provider === 'openRouter') {
@@ -9226,6 +9379,28 @@ const getChatSelectVirtualDom = (models, selectedModelId) => {
9226
9379
  }, ...modelOptions];
9227
9380
  };
9228
9381
 
9382
+ const getRunModeOptionDom = (runMode, selectedRunMode) => {
9383
+ return [{
9384
+ childCount: 1,
9385
+ selected: runMode === selectedRunMode,
9386
+ type: Option$1,
9387
+ value: runMode
9388
+ }, text(runMode)];
9389
+ };
9390
+
9391
+ const runModes = ['local', 'background', 'cloud'];
9392
+ const getRunModeSelectVirtualDom = selectedRunMode => {
9393
+ const runModeOptions = runModes.flatMap(runMode => getRunModeOptionDom(runMode, selectedRunMode));
9394
+ return [{
9395
+ childCount: runModes.length,
9396
+ className: Select,
9397
+ name: RunMode,
9398
+ onInput: HandleRunModeChange,
9399
+ type: Select$1,
9400
+ value: selectedRunMode
9401
+ }, ...runModeOptions];
9402
+ };
9403
+
9229
9404
  const getSendButtonClassName = isSendDisabled => {
9230
9405
  return isSendDisabled ? `${IconButton} ${SendButtonDisabled}` : `${IconButton}`;
9231
9406
  };
@@ -9293,9 +9468,9 @@ const getUsageOverviewDom = (tokensUsed, tokensMax) => {
9293
9468
  }, text(usageLabel)];
9294
9469
  };
9295
9470
 
9296
- const getChatSendAreaDom = (composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, voiceDictationEnabled = false) => {
9471
+ const getChatSendAreaDom = (composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, showRunMode, runMode, voiceDictationEnabled = false) => {
9297
9472
  const isSendDisabled = composerValue.trim() === '';
9298
- const controlsCount = usageOverviewEnabled ? 3 : 2;
9473
+ const controlsCount = 2 + (usageOverviewEnabled ? 1 : 0) + (showRunMode ? 1 : 0);
9299
9474
  return [{
9300
9475
  childCount: 1,
9301
9476
  className: ChatSendArea,
@@ -9318,7 +9493,7 @@ const getChatSendAreaDom = (composerValue, models, selectedModelId, usageOvervie
9318
9493
  childCount: voiceDictationEnabled ? controlsCount + 1 : controlsCount,
9319
9494
  className: ChatSendAreaBottom,
9320
9495
  type: Div
9321
- }, ...getChatSelectVirtualDom(models, selectedModelId), ...(usageOverviewEnabled ? getUsageOverviewDom(tokensUsed, tokensMax) : []), ...getSendButtonDom(isSendDisabled, voiceDictationEnabled)];
9496
+ }, ...getChatSelectVirtualDom(models, selectedModelId), ...(showRunMode ? getRunModeSelectVirtualDom(runMode) : []), ...(usageOverviewEnabled ? getUsageOverviewDom(tokensUsed, tokensMax) : []), ...getSendButtonDom(isSendDisabled, voiceDictationEnabled)];
9322
9497
  };
9323
9498
 
9324
9499
  const getImageAltText = alt => {
@@ -9683,6 +9858,12 @@ const getMessageNodeDom = (node, useChatMathWorker = false) => {
9683
9858
  if (node.type === 'math-block-dom') {
9684
9859
  return node.dom;
9685
9860
  }
9861
+ if (node.type === 'thematic-break') {
9862
+ return [{
9863
+ childCount: 0,
9864
+ type: Hr
9865
+ }];
9866
+ }
9686
9867
  if (node.type === 'heading') {
9687
9868
  return getHeadingDom(node, useChatMathWorker);
9688
9869
  }
@@ -9817,6 +9998,72 @@ const getOpenRouterTooManyRequestsDom = () => {
9817
9998
  })];
9818
9999
  };
9819
10000
 
10001
+ const getToolCallStatusLabel = toolCall => {
10002
+ if (toolCall.status === 'not-found') {
10003
+ return ' (not-found)';
10004
+ }
10005
+ if (toolCall.status === 'error') {
10006
+ if (toolCall.errorMessage) {
10007
+ return ` (error: ${toolCall.errorMessage})`;
10008
+ }
10009
+ return ' (error)';
10010
+ }
10011
+ return '';
10012
+ };
10013
+
10014
+ const parseAskQuestionArguments = rawArguments => {
10015
+ let parsed;
10016
+ try {
10017
+ parsed = JSON.parse(rawArguments);
10018
+ } catch {
10019
+ return {
10020
+ answers: [],
10021
+ question: ''
10022
+ };
10023
+ }
10024
+ if (!parsed || typeof parsed !== 'object') {
10025
+ return {
10026
+ answers: [],
10027
+ question: ''
10028
+ };
10029
+ }
10030
+ const question = Reflect.get(parsed, 'question');
10031
+ const rawAnswers = Reflect.get(parsed, 'answers');
10032
+ const rawChoices = Reflect.get(parsed, 'choices');
10033
+ const rawOptions = Reflect.get(parsed, 'options');
10034
+ const arrayValue = Array.isArray(rawAnswers) ? rawAnswers : Array.isArray(rawChoices) ? rawChoices : Array.isArray(rawOptions) ? rawOptions : [];
10035
+ const answers = arrayValue.filter(value => typeof value === 'string');
10036
+ return {
10037
+ answers,
10038
+ question: typeof question === 'string' ? question : ''
10039
+ };
10040
+ };
10041
+
10042
+ const getToolCallAskQuestionVirtualDom = toolCall => {
10043
+ const parsed = parseAskQuestionArguments(toolCall.arguments);
10044
+ const statusLabel = getToolCallStatusLabel(toolCall);
10045
+ const questionLabel = parsed.question.trim() ? parsed.question : '(empty question)';
10046
+ const answers = parsed.answers.length > 0 ? parsed.answers : ['(no answers)'];
10047
+ const childCount = 2;
10048
+ return [{
10049
+ childCount,
10050
+ className: ChatOrderedListItem,
10051
+ type: Li
10052
+ }, {
10053
+ childCount: 1,
10054
+ className: ChatToolCallQuestionText,
10055
+ type: Div
10056
+ }, text(`ask_question: ${questionLabel}${statusLabel}`), {
10057
+ childCount: answers.length,
10058
+ className: ChatToolCallQuestionOptions,
10059
+ type: Div
10060
+ }, ...answers.flatMap(answer => [{
10061
+ childCount: 1,
10062
+ className: ChatToolCallQuestionOption,
10063
+ type: Span
10064
+ }, text(answer.trim() ? answer : '(empty answer)')])];
10065
+ };
10066
+
9820
10067
  const RE_QUERY_OR_HASH = /[?#].*$/;
9821
10068
  const RE_TRAILING_SLASH = /\/$/;
9822
10069
  const getFileNameFromUri = uri => {
@@ -9896,6 +10143,58 @@ const getReadFileTarget = rawArguments => {
9896
10143
  };
9897
10144
  };
9898
10145
 
10146
+ const getToolCallEditFileVirtualDom = toolCall => {
10147
+ const target = getReadFileTarget(toolCall.arguments);
10148
+ if (!target) {
10149
+ return [];
10150
+ }
10151
+ const fileName = getFileNameFromUri(target.title);
10152
+ const fileNameClickableProps = target.clickableUri ? {
10153
+ 'data-uri': target.clickableUri,
10154
+ onClick: HandleClickReadFile
10155
+ } : {};
10156
+ return [{
10157
+ childCount: 3,
10158
+ className: ChatOrderedListItem,
10159
+ title: target.title,
10160
+ type: Li
10161
+ }, {
10162
+ childCount: 0,
10163
+ className: FileIcon,
10164
+ type: Div
10165
+ }, text('edit_file '), {
10166
+ childCount: 1,
10167
+ className: ChatToolCallReadFileLink,
10168
+ title: target.clickableUri,
10169
+ ...fileNameClickableProps,
10170
+ type: Span
10171
+ }, text(fileName)];
10172
+ };
10173
+
10174
+ const getToolCallGetWorkspaceUriVirtualDom = toolCall => {
10175
+ if (!toolCall.result) {
10176
+ return [];
10177
+ }
10178
+ const statusLabel = getToolCallStatusLabel(toolCall);
10179
+ const fileName = getFileNameFromUri(toolCall.result);
10180
+ return [{
10181
+ childCount: statusLabel ? 4 : 3,
10182
+ className: ChatOrderedListItem,
10183
+ title: toolCall.result,
10184
+ type: Li
10185
+ }, {
10186
+ childCount: 0,
10187
+ className: FileIcon,
10188
+ type: Div
10189
+ }, text('get_workspace_uri '), {
10190
+ childCount: 1,
10191
+ className: ChatToolCallReadFileLink,
10192
+ 'data-uri': toolCall.result,
10193
+ onClick: HandleClickReadFile,
10194
+ type: Span
10195
+ }, text(fileName), ...(statusLabel ? [text(statusLabel)] : [])];
10196
+ };
10197
+
9899
10198
  const getToolCallArgumentPreview = rawArguments => {
9900
10199
  if (!rawArguments.trim()) {
9901
10200
  return '""';
@@ -9923,69 +10222,33 @@ const getToolCallArgumentPreview = rawArguments => {
9923
10222
  return rawArguments;
9924
10223
  };
9925
10224
 
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)';
10225
+ const getToolCallDisplayName = name => {
10226
+ if (name === 'getWorkspaceUri') {
10227
+ return 'get_workspace_uri';
9935
10228
  }
9936
- return '';
10229
+ return name;
9937
10230
  };
9938
10231
 
9939
- const parseAskQuestionArguments = rawArguments => {
9940
- let parsed;
10232
+ const hasIncompleteJsonArguments = rawArguments => {
9941
10233
  try {
9942
- parsed = JSON.parse(rawArguments);
10234
+ JSON.parse(rawArguments);
10235
+ return false;
9943
10236
  } catch {
9944
- return {
9945
- answers: [],
9946
- question: ''
9947
- };
9948
- }
9949
- if (!parsed || typeof parsed !== 'object') {
9950
- return {
9951
- answers: [],
9952
- question: ''
9953
- };
10237
+ return true;
9954
10238
  }
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
10239
  };
9966
- const getToolCallAskQuestionVirtualDom = toolCall => {
9967
- const parsed = parseAskQuestionArguments(toolCall.arguments);
10240
+
10241
+ const getToolCallLabel = toolCall => {
10242
+ const displayName = getToolCallDisplayName(toolCall.name);
10243
+ if (toolCall.name === 'write_file' && !toolCall.status && hasIncompleteJsonArguments(toolCall.arguments)) {
10244
+ return `${displayName} (in progress)`;
10245
+ }
10246
+ const argumentPreview = getToolCallArgumentPreview(toolCall.arguments);
9968
10247
  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)')])];
10248
+ if (argumentPreview === '{}') {
10249
+ return `${displayName}${statusLabel}`;
10250
+ }
10251
+ return `${displayName} ${argumentPreview}${statusLabel}`;
9989
10252
  };
9990
10253
 
9991
10254
  const getToolCallReadFileVirtualDom = toolCall => {
@@ -10020,16 +10283,24 @@ const getToolCallReadFileVirtualDom = toolCall => {
10020
10283
  const maxHtmlLength = 40_000;
10021
10284
  const tokenRegex = /<!--[\s\S]*?-->|<\/?[a-zA-Z][\w:-]*(?:\s[^<>]*?)?>|[^<]+/g;
10022
10285
  const attributeRegex = /([^\s=/>]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/g;
10286
+ const scriptTagRegex = /<script\b[\s\S]*?<\/script>/gi;
10287
+ const styleTagRegex = /<style\b[\s\S]*?<\/style>/gi;
10288
+ const headTagRegex = /<head\b[\s\S]*?<\/head>/gi;
10289
+ const metaTagRegex = /<meta\b[^>]*>/gi;
10290
+ const linkTagRegex = /<link\b[^>]*>/gi;
10291
+ const tagPrefixRegex = /^<\/?\s*[a-zA-Z][\w:-]*/;
10292
+ const tagSuffixRegex = /\/?\s*>$/;
10293
+ const openTagNameRegex = /^<\s*([a-zA-Z][\w:-]*)/;
10023
10294
  const inlineTags = new Set(['a', 'abbr', 'b', 'code', 'em', 'i', 'label', 'small', 'span', 'strong', 'sub', 'sup', 'u']);
10024
10295
  const voidElements = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr']);
10025
10296
  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, '');
10297
+ return value.slice(0, maxHtmlLength).replaceAll(scriptTagRegex, '').replaceAll(styleTagRegex, '').replaceAll(headTagRegex, '').replaceAll(metaTagRegex, '').replaceAll(linkTagRegex, '');
10027
10298
  };
10028
10299
  const decodeEntities = value => {
10029
10300
  return value.replaceAll('&nbsp;', ' ').replaceAll('&quot;', '"').replaceAll('&#39;', "'").replaceAll('&lt;', '<').replaceAll('&gt;', '>').replaceAll('&amp;', '&');
10030
10301
  };
10031
10302
  const parseAttributes = token => {
10032
- const withoutTag = token.replace(/^<\/?\s*[a-zA-Z][\w:-]*/, '').replace(/\/?\s*>$/, '').trim();
10303
+ const withoutTag = token.replace(tagPrefixRegex, '').replace(tagSuffixRegex, '').trim();
10033
10304
  if (!withoutTag) {
10034
10305
  return Object.create(null);
10035
10306
  }
@@ -10076,7 +10347,7 @@ const parseHtml = value => {
10076
10347
  continue;
10077
10348
  }
10078
10349
  if (token.startsWith('<')) {
10079
- const openTagNameMatch = /^<\s*([a-zA-Z][\w:-]*)/.exec(token);
10350
+ const openTagNameMatch = openTagNameRegex.exec(token);
10080
10351
  if (!openTagNameMatch) {
10081
10352
  continue;
10082
10353
  }
@@ -10320,55 +10591,6 @@ const getToolCallRenderHtmlVirtualDom = toolCall => {
10320
10591
  }, ...parsedHtml.virtualDom];
10321
10592
  };
10322
10593
 
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
10594
  const parseWriteFileLineCounts = rawResult => {
10373
10595
  if (!rawResult) {
10374
10596
  return {
@@ -10398,6 +10620,7 @@ const parseWriteFileLineCounts = rawResult => {
10398
10620
  linesDeleted: typeof linesDeleted === 'number' ? Math.max(0, linesDeleted) : 0
10399
10621
  };
10400
10622
  };
10623
+
10401
10624
  const getToolCallWriteFileVirtualDom = toolCall => {
10402
10625
  const target = getReadFileTarget(toolCall.arguments);
10403
10626
  if (!target) {
@@ -10437,33 +10660,7 @@ const getToolCallWriteFileVirtualDom = toolCall => {
10437
10660
  type: Span
10438
10661
  }, text(` -${linesDeleted}`), ...(statusLabel ? [text(statusLabel)] : [])];
10439
10662
  };
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
- };
10663
+
10467
10664
  const getToolCallDom = toolCall => {
10468
10665
  if (toolCall.name === 'getWorkspaceUri') {
10469
10666
  const virtualDom = getToolCallGetWorkspaceUriVirtualDom(toolCall);
@@ -10598,9 +10795,9 @@ const getDisplayMessages = (messages, parsedMessages) => {
10598
10795
  }
10599
10796
  return displayMessages;
10600
10797
  };
10601
- const getMessagesDom = (messages, parsedMessages, openRouterApiKeyInput, openApiApiKeyInput = '', openRouterApiKeyState = 'idle', messagesScrollTop = 0, useChatMathWorker = false) => {
10798
+ const getMessagesDom = (messages, parsedMessages, openRouterApiKeyInput, openApiApiKeyInput = '', openRouterApiKeyState = 'idle', messagesScrollTop = 0, useChatMathWorker = false, hideWelcomeMessage = false) => {
10602
10799
  if (messages.length === 0) {
10603
- return getEmptyMessagesDom();
10800
+ return hideWelcomeMessage ? [] : getEmptyMessagesDom();
10604
10801
  }
10605
10802
  const displayMessages = getDisplayMessages(messages, parsedMessages);
10606
10803
  return [{
@@ -10698,7 +10895,38 @@ const getProjectListDom = (projects, sessions, projectExpandedIds, selectedProje
10698
10895
  }, text('+ Add Project')];
10699
10896
  };
10700
10897
 
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 = '') => {
10898
+ const getChatModeChatFocusVirtualDom = ({
10899
+ authEnabled = false,
10900
+ authErrorMessage = '',
10901
+ authStatus = 'signed-out',
10902
+ composerDropActive = false,
10903
+ composerDropEnabled = true,
10904
+ composerFontFamily = 'system-ui',
10905
+ composerFontSize = 13,
10906
+ composerHeight = 28,
10907
+ composerLineHeight = 20,
10908
+ composerValue,
10909
+ messagesScrollTop = 0,
10910
+ models,
10911
+ openApiApiKeyInput,
10912
+ openRouterApiKeyInput,
10913
+ openRouterApiKeyState = 'idle',
10914
+ parsedMessages = [],
10915
+ projectExpandedIds = [],
10916
+ projectListScrollTop = 0,
10917
+ projects = [],
10918
+ runMode,
10919
+ selectedModelId,
10920
+ selectedProjectId = '',
10921
+ selectedSessionId,
10922
+ sessions,
10923
+ showRunMode,
10924
+ tokensMax,
10925
+ tokensUsed,
10926
+ usageOverviewEnabled,
10927
+ useChatMathWorker = false,
10928
+ voiceDictationEnabled = false
10929
+ }) => {
10702
10930
  const selectedSession = sessions.find(session => session.id === selectedSessionId);
10703
10931
  const messages = selectedSession ? selectedSession.messages : [];
10704
10932
  const isDropOverlayVisible = composerDropEnabled && composerDropActive;
@@ -10708,7 +10936,7 @@ const getChatModeChatFocusVirtualDom = (sessions, selectedSessionId, composerVal
10708
10936
  onDragEnter: HandleDragEnterChatView,
10709
10937
  onDragOver: HandleDragOverChatView,
10710
10938
  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 ? [{
10939
+ }, ...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, voiceDictationEnabled), ...(isDropOverlayVisible ? [{
10712
10940
  childCount: 1,
10713
10941
  className: mergeClassNames(ChatViewDropOverlay, ChatViewDropOverlayActive),
10714
10942
  name: ComposerDropTarget,
@@ -10824,7 +11052,34 @@ const getChatHeaderDomDetailMode = (selectedSessionTitle, authEnabled = false, a
10824
11052
  }, text(authErrorMessage)] : [])];
10825
11053
  };
10826
11054
 
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 = '') => {
11055
+ const getChatModeDetailVirtualDom = ({
11056
+ authEnabled = false,
11057
+ authErrorMessage = '',
11058
+ authStatus = 'signed-out',
11059
+ composerDropActive = false,
11060
+ composerDropEnabled = true,
11061
+ composerFontFamily = 'system-ui',
11062
+ composerFontSize = 13,
11063
+ composerHeight = 28,
11064
+ composerLineHeight = 20,
11065
+ composerValue,
11066
+ messagesScrollTop = 0,
11067
+ models,
11068
+ openApiApiKeyInput,
11069
+ openRouterApiKeyInput,
11070
+ openRouterApiKeyState = 'idle',
11071
+ parsedMessages = [],
11072
+ runMode,
11073
+ selectedModelId,
11074
+ selectedSessionId,
11075
+ sessions,
11076
+ showRunMode,
11077
+ tokensMax,
11078
+ tokensUsed,
11079
+ usageOverviewEnabled,
11080
+ useChatMathWorker = false,
11081
+ voiceDictationEnabled = false
11082
+ }) => {
10828
11083
  const selectedSession = sessions.find(session => session.id === selectedSessionId);
10829
11084
  const selectedSessionTitle = selectedSession?.title || chatTitle();
10830
11085
  const messages = selectedSession ? selectedSession.messages : [];
@@ -10835,7 +11090,7 @@ const getChatModeDetailVirtualDom = (sessions, selectedSessionId, composerValue,
10835
11090
  onDragEnter: HandleDragEnterChatView,
10836
11091
  onDragOver: HandleDragOverChatView,
10837
11092
  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 ? [{
11093
+ }, ...getChatHeaderDomDetailMode(selectedSessionTitle, authEnabled, authStatus, authErrorMessage), ...getMessagesDom(messages, parsedMessages, openRouterApiKeyInput, openApiApiKeyInput, openRouterApiKeyState, messagesScrollTop, useChatMathWorker), ...getChatSendAreaDom(composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, showRunMode, runMode, voiceDictationEnabled), ...(isDropOverlayVisible ? [{
10839
11094
  childCount: 1,
10840
11095
  className: mergeClassNames(ChatViewDropOverlay, ChatViewDropOverlayActive),
10841
11096
  name: ComposerDropTarget,
@@ -10883,7 +11138,7 @@ const getSessionDom = session => {
10883
11138
  return [{
10884
11139
  childCount: 2,
10885
11140
  className: sessionClassName,
10886
- type: Div
11141
+ type: Li
10887
11142
  }, {
10888
11143
  childCount: 1,
10889
11144
  className: ChatListItemLabel,
@@ -10917,11 +11172,33 @@ const getChatListDom = (sessions, selectedSessionId, chatListScrollTop = 0) => {
10917
11172
  onClick: HandleClickList,
10918
11173
  onScroll: HandleChatListScroll,
10919
11174
  scrollTop: chatListScrollTop,
10920
- type: Div
11175
+ type: Ul
10921
11176
  }, ...sessions.flatMap(getSessionDom)];
10922
11177
  };
10923
11178
 
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 = '') => {
11179
+ const getChatModeListVirtualDom = ({
11180
+ authEnabled = false,
11181
+ authErrorMessage = '',
11182
+ authStatus = 'signed-out',
11183
+ chatListScrollTop = 0,
11184
+ composerDropActive = false,
11185
+ composerDropEnabled = true,
11186
+ composerFontFamily = 'system-ui',
11187
+ composerFontSize = 13,
11188
+ composerHeight = 28,
11189
+ composerLineHeight = 20,
11190
+ composerValue,
11191
+ models,
11192
+ runMode,
11193
+ selectedModelId,
11194
+ selectedSessionId,
11195
+ sessions,
11196
+ showRunMode,
11197
+ tokensMax,
11198
+ tokensUsed,
11199
+ usageOverviewEnabled,
11200
+ voiceDictationEnabled = false
11201
+ }) => {
10925
11202
  const isDropOverlayVisible = composerDropEnabled && composerDropActive;
10926
11203
  return [{
10927
11204
  childCount: isDropOverlayVisible ? 4 : 3,
@@ -10929,7 +11206,7 @@ const getChatModeListVirtualDom = (sessions, selectedSessionId, composerValue, m
10929
11206
  onDragEnter: HandleDragEnterChatView,
10930
11207
  onDragOver: HandleDragOverChatView,
10931
11208
  type: Div
10932
- }, ...getChatHeaderListModeDom(authEnabled, authStatus, authErrorMessage), ...getChatListDom(sessions, selectedSessionId, chatListScrollTop), ...getChatSendAreaDom(composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, voiceDictationEnabled), ...(isDropOverlayVisible ? [{
11209
+ }, ...getChatHeaderListModeDom(authEnabled, authStatus, authErrorMessage), ...getChatListDom(sessions, selectedSessionId, chatListScrollTop), ...getChatSendAreaDom(composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, showRunMode, runMode, voiceDictationEnabled), ...(isDropOverlayVisible ? [{
10933
11210
  childCount: 1,
10934
11211
  className: mergeClassNames(ChatViewDropOverlay, ChatViewDropOverlayActive),
10935
11212
  name: ComposerDropTarget,
@@ -10966,15 +11243,129 @@ const getFallbackParsedMessages = sessions => {
10966
11243
  }
10967
11244
  return parsedMessages;
10968
11245
  };
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);
11246
+ const getChatVirtualDom = options => {
11247
+ const {
11248
+ authEnabled = false,
11249
+ authErrorMessage = '',
11250
+ authStatus = 'signed-out',
11251
+ chatListScrollTop,
11252
+ composerDropActive = false,
11253
+ composerDropEnabled = true,
11254
+ composerFontFamily,
11255
+ composerFontSize,
11256
+ composerHeight,
11257
+ composerLineHeight,
11258
+ composerValue,
11259
+ messagesScrollTop,
11260
+ models,
11261
+ openApiApiKeyInput,
11262
+ openRouterApiKeyInput,
11263
+ openRouterApiKeyState,
11264
+ parsedMessages: parsedMessagesInput,
11265
+ projectExpandedIds = [],
11266
+ projectListScrollTop = 0,
11267
+ projects = [],
11268
+ runMode,
11269
+ selectedModelId,
11270
+ selectedProjectId = '',
11271
+ selectedSessionId,
11272
+ sessions,
11273
+ showRunMode,
11274
+ tokensMax,
11275
+ tokensUsed,
11276
+ usageOverviewEnabled,
11277
+ useChatMathWorker = false,
11278
+ viewMode,
11279
+ voiceDictationEnabled = false
11280
+ } = options;
11281
+ const parsedMessages = parsedMessagesInput ?? getFallbackParsedMessages(sessions);
10971
11282
  switch (viewMode) {
10972
11283
  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);
11284
+ return getChatModeChatFocusVirtualDom({
11285
+ authEnabled,
11286
+ authErrorMessage,
11287
+ authStatus,
11288
+ composerDropActive,
11289
+ composerDropEnabled,
11290
+ composerFontFamily,
11291
+ composerFontSize,
11292
+ composerHeight,
11293
+ composerLineHeight,
11294
+ composerValue,
11295
+ messagesScrollTop,
11296
+ models,
11297
+ openApiApiKeyInput,
11298
+ openRouterApiKeyInput,
11299
+ openRouterApiKeyState,
11300
+ parsedMessages,
11301
+ projectExpandedIds,
11302
+ projectListScrollTop,
11303
+ projects,
11304
+ runMode,
11305
+ selectedModelId,
11306
+ selectedProjectId,
11307
+ selectedSessionId,
11308
+ sessions,
11309
+ showRunMode,
11310
+ tokensMax,
11311
+ tokensUsed,
11312
+ usageOverviewEnabled,
11313
+ useChatMathWorker,
11314
+ voiceDictationEnabled
11315
+ });
10974
11316
  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);
11317
+ return getChatModeDetailVirtualDom({
11318
+ authEnabled,
11319
+ authErrorMessage,
11320
+ authStatus,
11321
+ composerDropActive,
11322
+ composerDropEnabled,
11323
+ composerFontFamily,
11324
+ composerFontSize,
11325
+ composerHeight,
11326
+ composerLineHeight,
11327
+ composerValue,
11328
+ messagesScrollTop,
11329
+ models,
11330
+ openApiApiKeyInput,
11331
+ openRouterApiKeyInput,
11332
+ openRouterApiKeyState,
11333
+ parsedMessages,
11334
+ runMode,
11335
+ selectedModelId,
11336
+ selectedSessionId,
11337
+ sessions,
11338
+ showRunMode,
11339
+ tokensMax,
11340
+ tokensUsed,
11341
+ usageOverviewEnabled,
11342
+ useChatMathWorker,
11343
+ voiceDictationEnabled
11344
+ });
10976
11345
  case 'list':
10977
- return getChatModeListVirtualDom(sessions, selectedSessionId, composerValue, models, selectedModelId, usageOverviewEnabled, tokensUsed, tokensMax, composerHeight, composerFontSize, composerFontFamily, composerLineHeight, chatListScrollTop, composerDropActive, composerDropEnabled, voiceDictationEnabled, authEnabled, authStatus, authErrorMessage);
11346
+ return getChatModeListVirtualDom({
11347
+ authEnabled,
11348
+ authErrorMessage,
11349
+ authStatus,
11350
+ chatListScrollTop,
11351
+ composerDropActive,
11352
+ composerDropEnabled,
11353
+ composerFontFamily,
11354
+ composerFontSize,
11355
+ composerHeight,
11356
+ composerLineHeight,
11357
+ composerValue,
11358
+ models,
11359
+ runMode,
11360
+ selectedModelId,
11361
+ selectedSessionId,
11362
+ sessions,
11363
+ showRunMode,
11364
+ tokensMax,
11365
+ tokensUsed,
11366
+ usageOverviewEnabled,
11367
+ voiceDictationEnabled
11368
+ });
10978
11369
  default:
10979
11370
  return getChatModeUnsupportedVirtualDom();
10980
11371
  }
@@ -11003,10 +11394,12 @@ const renderItems = (oldState, newState) => {
11003
11394
  projectExpandedIds,
11004
11395
  projectListScrollTop,
11005
11396
  projects,
11397
+ runMode,
11006
11398
  selectedModelId,
11007
11399
  selectedProjectId,
11008
11400
  selectedSessionId,
11009
11401
  sessions,
11402
+ showRunMode,
11010
11403
  tokensMax,
11011
11404
  tokensUsed,
11012
11405
  uid,
@@ -11018,7 +11411,40 @@ const renderItems = (oldState, newState) => {
11018
11411
  if (initial) {
11019
11412
  return [SetDom2, uid, []];
11020
11413
  }
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);
11414
+ const dom = getChatVirtualDom({
11415
+ authEnabled,
11416
+ authErrorMessage,
11417
+ authStatus,
11418
+ chatListScrollTop,
11419
+ composerDropActive,
11420
+ composerDropEnabled,
11421
+ composerFontFamily,
11422
+ composerFontSize,
11423
+ composerHeight,
11424
+ composerLineHeight,
11425
+ composerValue,
11426
+ messagesScrollTop,
11427
+ models,
11428
+ openApiApiKeyInput,
11429
+ openRouterApiKeyInput,
11430
+ openRouterApiKeyState,
11431
+ parsedMessages,
11432
+ projectExpandedIds,
11433
+ projectListScrollTop,
11434
+ projects,
11435
+ runMode,
11436
+ selectedModelId,
11437
+ selectedProjectId,
11438
+ selectedSessionId,
11439
+ sessions,
11440
+ showRunMode,
11441
+ tokensMax,
11442
+ tokensUsed,
11443
+ usageOverviewEnabled,
11444
+ useChatMathWorker,
11445
+ viewMode,
11446
+ voiceDictationEnabled
11447
+ });
11022
11448
  return [SetDom2, uid, dom];
11023
11449
  };
11024
11450
 
@@ -11222,6 +11648,9 @@ const renderEventListeners = () => {
11222
11648
  }, {
11223
11649
  name: HandleModelChange,
11224
11650
  params: ['handleModelChange', TargetValue]
11651
+ }, {
11652
+ name: HandleRunModeChange,
11653
+ params: ['handleRunModeChange', TargetValue]
11225
11654
  }, {
11226
11655
  name: HandleChatListScroll,
11227
11656
  params: ['handleChatListScroll', 'event.target.scrollTop']
@@ -11367,6 +11796,13 @@ const setQuestionToolEnabled = (state, questionToolEnabled) => {
11367
11796
  };
11368
11797
  };
11369
11798
 
11799
+ const setShowRunMode = (state, showRunMode) => {
11800
+ return {
11801
+ ...state,
11802
+ showRunMode
11803
+ };
11804
+ };
11805
+
11370
11806
  const setStreamingEnabled = (state, streamingEnabled) => {
11371
11807
  return {
11372
11808
  ...state,
@@ -11463,6 +11899,7 @@ const commandMap = {
11463
11899
  'Chat.handleModelChange': wrapCommand(handleModelChange),
11464
11900
  'Chat.handleProjectListContextMenu': wrapCommand(handleProjectListContextMenu),
11465
11901
  'Chat.handleProjectListScroll': wrapCommand(handleProjectListScroll),
11902
+ 'Chat.handleRunModeChange': wrapCommand(handleRunModeChange),
11466
11903
  'Chat.handleSubmit': wrapCommand(handleSubmit),
11467
11904
  'Chat.initialize': initialize,
11468
11905
  'Chat.loadContent': wrapCommand(loadContent),
@@ -11489,6 +11926,7 @@ const commandMap = {
11489
11926
  'Chat.setEmitStreamingFunctionCallEvents': wrapCommand(setEmitStreamingFunctionCallEvents),
11490
11927
  'Chat.setOpenRouterApiKey': wrapCommand(setOpenRouterApiKey),
11491
11928
  'Chat.setQuestionToolEnabled': wrapCommand(setQuestionToolEnabled),
11929
+ 'Chat.setShowRunMode': wrapCommand(setShowRunMode),
11492
11930
  'Chat.setStreamingEnabled': wrapCommand(setStreamingEnabled),
11493
11931
  'Chat.setUseChatCoordinatorWorker': wrapCommand(setUseChatCoordinatorWorker),
11494
11932
  'Chat.setUseChatMathWorker': wrapCommand(setUseChatMathWorker),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/chat-view",
3
- "version": "6.10.0",
3
+ "version": "6.12.0",
4
4
  "description": "Chat View Worker",
5
5
  "repository": {
6
6
  "type": "git",