@lvce-editor/chat-view 3.2.0 → 3.4.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.
- package/dist/chatViewWorkerMain.js +347 -17
- package/package.json +1 -1
|
@@ -1118,6 +1118,9 @@ const sendMessagePortToExtensionHostWorker$1 = async (port, rpcId = 0) => {
|
|
|
1118
1118
|
const command = 'HandleMessagePort.handleMessagePort2';
|
|
1119
1119
|
await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToExtensionHostWorker', port, command, rpcId);
|
|
1120
1120
|
};
|
|
1121
|
+
const sendMessagePortToChatNetworkWorker = async port => {
|
|
1122
|
+
await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToChatNetworkWorker', port, 'HandleMessagePort.handleMessagePort');
|
|
1123
|
+
};
|
|
1121
1124
|
const activateByEvent$1 = (event, assetDir, platform) => {
|
|
1122
1125
|
return invoke('ExtensionHostManagement.activateByEvent', event, assetDir, platform);
|
|
1123
1126
|
};
|
|
@@ -1275,6 +1278,9 @@ const getComposerHeight = async (state, value, width = state.width) => {
|
|
|
1275
1278
|
composerLineHeight,
|
|
1276
1279
|
maxComposerRows
|
|
1277
1280
|
} = state;
|
|
1281
|
+
if (value === '') {
|
|
1282
|
+
return composerLineHeight;
|
|
1283
|
+
}
|
|
1278
1284
|
const minimumHeight = getMinComposerHeight(composerLineHeight);
|
|
1279
1285
|
const maximumHeight = getMaxComposerHeight(composerLineHeight, maxComposerRows);
|
|
1280
1286
|
const content = value || ' ';
|
|
@@ -1463,6 +1469,7 @@ const createDefaultState = () => {
|
|
|
1463
1469
|
const composerFontSize = 13;
|
|
1464
1470
|
const composerLineHeight = 20;
|
|
1465
1471
|
return {
|
|
1472
|
+
aiSessionTitleGenerationEnabled: false,
|
|
1466
1473
|
assetDir: '',
|
|
1467
1474
|
chatListScrollTop: 0,
|
|
1468
1475
|
chatMessageFontFamily: 'system-ui',
|
|
@@ -5015,6 +5022,10 @@ const handleToolCallsChunkFunction = async (uid, assistantMessageId, toolCalls,
|
|
|
5015
5022
|
};
|
|
5016
5023
|
};
|
|
5017
5024
|
|
|
5025
|
+
const slashCommandRegex = /^\/(clear|export|help|new)(?:\s+.*)?$/;
|
|
5026
|
+
const mentionRegex = /(^|\s)@([^\s]+)/g;
|
|
5027
|
+
const maxMentionCount = 5;
|
|
5028
|
+
const maxMentionTextLength = 8000;
|
|
5018
5029
|
const appendMessageToSelectedSession = (sessions, selectedSessionId, message) => {
|
|
5019
5030
|
return sessions.map(session => {
|
|
5020
5031
|
if (session.id !== selectedSessionId) {
|
|
@@ -5068,8 +5079,208 @@ const isStreamingFunctionCallEvent = parsed => {
|
|
|
5068
5079
|
const getSseEventType = value => {
|
|
5069
5080
|
return value && typeof value === 'object' && Reflect.get(value, 'type') === 'response.completed' ? 'sse-response-completed' : 'sse-response-part';
|
|
5070
5081
|
};
|
|
5082
|
+
const getCommandHelpText = () => {
|
|
5083
|
+
return ['Available commands:', '/new - Create and switch to a new chat session.', '/clear - Clear messages in the selected chat session.', '/export - Export current chat session as Markdown.', '/help - Show this help.'].join('\n');
|
|
5084
|
+
};
|
|
5085
|
+
const withClearedComposer = state => {
|
|
5086
|
+
return focusInput({
|
|
5087
|
+
...state,
|
|
5088
|
+
composerHeight: getMinComposerHeightForState(state),
|
|
5089
|
+
composerValue: '',
|
|
5090
|
+
inputSource: 'script'
|
|
5091
|
+
});
|
|
5092
|
+
};
|
|
5093
|
+
const toMarkdownTranscript = session => {
|
|
5094
|
+
const lines = [`# ${session.title}`, ''];
|
|
5095
|
+
for (const message of session.messages) {
|
|
5096
|
+
const role = message.role === 'assistant' ? 'Assistant' : 'User';
|
|
5097
|
+
lines.push(`## ${role}`);
|
|
5098
|
+
lines.push(message.text || '(empty)');
|
|
5099
|
+
lines.push('');
|
|
5100
|
+
}
|
|
5101
|
+
return lines.join('\n').trim();
|
|
5102
|
+
};
|
|
5103
|
+
const executeSlashCommand = async (state, command) => {
|
|
5104
|
+
if (command === 'new') {
|
|
5105
|
+
const nextState = await createSession(state);
|
|
5106
|
+
return withClearedComposer({
|
|
5107
|
+
...nextState,
|
|
5108
|
+
viewMode: 'detail'
|
|
5109
|
+
});
|
|
5110
|
+
}
|
|
5111
|
+
const selectedSession = state.sessions.find(session => session.id === state.selectedSessionId);
|
|
5112
|
+
if (!selectedSession) {
|
|
5113
|
+
return withClearedComposer(state);
|
|
5114
|
+
}
|
|
5115
|
+
if (command === 'clear') {
|
|
5116
|
+
const updatedSessions = state.sessions.map(session => {
|
|
5117
|
+
if (session.id !== state.selectedSessionId) {
|
|
5118
|
+
return session;
|
|
5119
|
+
}
|
|
5120
|
+
return {
|
|
5121
|
+
...session,
|
|
5122
|
+
messages: []
|
|
5123
|
+
};
|
|
5124
|
+
});
|
|
5125
|
+
const updatedSelectedSession = updatedSessions.find(session => session.id === state.selectedSessionId);
|
|
5126
|
+
if (updatedSelectedSession) {
|
|
5127
|
+
await saveChatSession(updatedSelectedSession);
|
|
5128
|
+
}
|
|
5129
|
+
return withClearedComposer({
|
|
5130
|
+
...state,
|
|
5131
|
+
sessions: updatedSessions
|
|
5132
|
+
});
|
|
5133
|
+
}
|
|
5134
|
+
const assistantText = command === 'help' ? getCommandHelpText() : ['```md', toMarkdownTranscript(selectedSession), '```'].join('\n');
|
|
5135
|
+
const assistantMessage = {
|
|
5136
|
+
id: crypto.randomUUID(),
|
|
5137
|
+
role: 'assistant',
|
|
5138
|
+
text: assistantText,
|
|
5139
|
+
time: new Date().toLocaleTimeString([], {
|
|
5140
|
+
hour: '2-digit',
|
|
5141
|
+
minute: '2-digit'
|
|
5142
|
+
})
|
|
5143
|
+
};
|
|
5144
|
+
const updatedSessions = appendMessageToSelectedSession(state.sessions, state.selectedSessionId, assistantMessage);
|
|
5145
|
+
const updatedSelectedSession = updatedSessions.find(session => session.id === state.selectedSessionId);
|
|
5146
|
+
if (updatedSelectedSession) {
|
|
5147
|
+
await saveChatSession(updatedSelectedSession);
|
|
5148
|
+
}
|
|
5149
|
+
return withClearedComposer({
|
|
5150
|
+
...state,
|
|
5151
|
+
sessions: updatedSessions
|
|
5152
|
+
});
|
|
5153
|
+
};
|
|
5154
|
+
const getSlashCommand = value => {
|
|
5155
|
+
const trimmed = value.trim();
|
|
5156
|
+
const match = trimmed.match(slashCommandRegex);
|
|
5157
|
+
if (!match) {
|
|
5158
|
+
return undefined;
|
|
5159
|
+
}
|
|
5160
|
+
return match[1];
|
|
5161
|
+
};
|
|
5162
|
+
const parseMentionedPaths = value => {
|
|
5163
|
+
const matches = value.matchAll(mentionRegex);
|
|
5164
|
+
const paths = [];
|
|
5165
|
+
for (const match of matches) {
|
|
5166
|
+
const rawPath = match[2] || '';
|
|
5167
|
+
const cleanedPath = rawPath.replaceAll(/[),.;:!?]+$/g, '');
|
|
5168
|
+
if (!cleanedPath || isPathTraversalAttempt(cleanedPath)) {
|
|
5169
|
+
continue;
|
|
5170
|
+
}
|
|
5171
|
+
const normalizedPath = normalizeRelativePath(cleanedPath);
|
|
5172
|
+
if (paths.includes(normalizedPath)) {
|
|
5173
|
+
continue;
|
|
5174
|
+
}
|
|
5175
|
+
paths.push(normalizedPath);
|
|
5176
|
+
if (paths.length >= maxMentionCount) {
|
|
5177
|
+
break;
|
|
5178
|
+
}
|
|
5179
|
+
}
|
|
5180
|
+
return paths;
|
|
5181
|
+
};
|
|
5182
|
+
const getMentionContextMessage = async value => {
|
|
5183
|
+
const paths = parseMentionedPaths(value);
|
|
5184
|
+
if (paths.length === 0) {
|
|
5185
|
+
return undefined;
|
|
5186
|
+
}
|
|
5187
|
+
const sections = [];
|
|
5188
|
+
for (const path of paths) {
|
|
5189
|
+
try {
|
|
5190
|
+
const fileContent = await readFile(path);
|
|
5191
|
+
const truncatedContent = fileContent.length > maxMentionTextLength ? `${fileContent.slice(0, maxMentionTextLength)}\n... [truncated]` : fileContent;
|
|
5192
|
+
sections.push([`File: ${path}`, '```text', truncatedContent, '```'].join('\n'));
|
|
5193
|
+
} catch (error) {
|
|
5194
|
+
sections.push([`File: ${path}`, `Error: ${String(error)}`].join('\n'));
|
|
5195
|
+
}
|
|
5196
|
+
}
|
|
5197
|
+
return {
|
|
5198
|
+
id: crypto.randomUUID(),
|
|
5199
|
+
role: 'user',
|
|
5200
|
+
text: ['Referenced file context:', ...sections].join('\n\n'),
|
|
5201
|
+
time: new Date().toLocaleTimeString([], {
|
|
5202
|
+
hour: '2-digit',
|
|
5203
|
+
minute: '2-digit'
|
|
5204
|
+
})
|
|
5205
|
+
};
|
|
5206
|
+
};
|
|
5207
|
+
const isDefaultSessionTitle = title => {
|
|
5208
|
+
return /^Chat \d+$/.test(title);
|
|
5209
|
+
};
|
|
5210
|
+
const sanitizeGeneratedTitle = value => {
|
|
5211
|
+
return value.replace(/^title:\s*/i, '').replaceAll(/^['"`\s]+|['"`\s]+$/g, '').replaceAll(/\s+/g, ' ').trim().slice(0, 80);
|
|
5212
|
+
};
|
|
5213
|
+
const updateSessionTitle = (sessions, selectedSessionId, title) => {
|
|
5214
|
+
return sessions.map(session => {
|
|
5215
|
+
if (session.id !== selectedSessionId) {
|
|
5216
|
+
return session;
|
|
5217
|
+
}
|
|
5218
|
+
return {
|
|
5219
|
+
...session,
|
|
5220
|
+
title
|
|
5221
|
+
};
|
|
5222
|
+
});
|
|
5223
|
+
};
|
|
5224
|
+
const getAiSessionTitle = async (state, userText, assistantText) => {
|
|
5225
|
+
const {
|
|
5226
|
+
models,
|
|
5227
|
+
openApiApiBaseUrl,
|
|
5228
|
+
openApiApiKey,
|
|
5229
|
+
openRouterApiBaseUrl,
|
|
5230
|
+
openRouterApiKey,
|
|
5231
|
+
selectedModelId,
|
|
5232
|
+
useMockApi
|
|
5233
|
+
} = state;
|
|
5234
|
+
if (useMockApi) {
|
|
5235
|
+
return '';
|
|
5236
|
+
}
|
|
5237
|
+
const usesOpenApiModel = isOpenApiModel(selectedModelId, models);
|
|
5238
|
+
const usesOpenRouterModel = isOpenRouterModel(selectedModelId, models);
|
|
5239
|
+
if (usesOpenApiModel && !openApiApiKey) {
|
|
5240
|
+
return '';
|
|
5241
|
+
}
|
|
5242
|
+
if (usesOpenRouterModel && !openRouterApiKey) {
|
|
5243
|
+
return '';
|
|
5244
|
+
}
|
|
5245
|
+
if (!usesOpenApiModel && !usesOpenRouterModel) {
|
|
5246
|
+
return '';
|
|
5247
|
+
}
|
|
5248
|
+
const titlePrompt = `Create a concise title (max 6 words) for this conversation. Respond only with the title, no punctuation at the end.
|
|
5249
|
+
User: ${userText}
|
|
5250
|
+
Assistant: ${assistantText}`;
|
|
5251
|
+
const promptMessage = {
|
|
5252
|
+
id: crypto.randomUUID(),
|
|
5253
|
+
role: 'user',
|
|
5254
|
+
text: titlePrompt,
|
|
5255
|
+
time: new Date().toLocaleTimeString([], {
|
|
5256
|
+
hour: '2-digit',
|
|
5257
|
+
minute: '2-digit'
|
|
5258
|
+
})
|
|
5259
|
+
};
|
|
5260
|
+
const titleResponse = await getAiResponse({
|
|
5261
|
+
assetDir: state.assetDir,
|
|
5262
|
+
messages: [promptMessage],
|
|
5263
|
+
mockAiResponseDelay: state.mockAiResponseDelay,
|
|
5264
|
+
mockApiCommandId: state.mockApiCommandId,
|
|
5265
|
+
models,
|
|
5266
|
+
openApiApiBaseUrl,
|
|
5267
|
+
openApiApiKey,
|
|
5268
|
+
openRouterApiBaseUrl,
|
|
5269
|
+
openRouterApiKey,
|
|
5270
|
+
passIncludeObfuscation: state.passIncludeObfuscation,
|
|
5271
|
+
platform: state.platform,
|
|
5272
|
+
selectedModelId,
|
|
5273
|
+
streamingEnabled: false,
|
|
5274
|
+
useMockApi,
|
|
5275
|
+
userText: titlePrompt,
|
|
5276
|
+
webSearchEnabled: false
|
|
5277
|
+
});
|
|
5278
|
+
const title = sanitizeGeneratedTitle(titleResponse.text);
|
|
5279
|
+
return title && !isDefaultSessionTitle(title) ? title : '';
|
|
5280
|
+
};
|
|
5071
5281
|
const handleSubmit = async state => {
|
|
5072
5282
|
const {
|
|
5283
|
+
aiSessionTitleGenerationEnabled,
|
|
5073
5284
|
assetDir,
|
|
5074
5285
|
composerValue,
|
|
5075
5286
|
emitStreamingFunctionCallEvents,
|
|
@@ -5095,6 +5306,10 @@ const handleSubmit = async state => {
|
|
|
5095
5306
|
if (!userText) {
|
|
5096
5307
|
return state;
|
|
5097
5308
|
}
|
|
5309
|
+
const slashCommand = getSlashCommand(userText);
|
|
5310
|
+
if (slashCommand) {
|
|
5311
|
+
return executeSlashCommand(state, slashCommand);
|
|
5312
|
+
}
|
|
5098
5313
|
const userTime = new Date().toLocaleTimeString([], {
|
|
5099
5314
|
hour: '2-digit',
|
|
5100
5315
|
minute: '2-digit'
|
|
@@ -5131,6 +5346,7 @@ const handleSubmit = async state => {
|
|
|
5131
5346
|
}
|
|
5132
5347
|
}
|
|
5133
5348
|
let optimisticState;
|
|
5349
|
+
const createsNewSession = viewMode === 'list';
|
|
5134
5350
|
if (viewMode === 'list') {
|
|
5135
5351
|
const newSessionId = generateSessionId();
|
|
5136
5352
|
await appendChatViewEvent({
|
|
@@ -5188,13 +5404,15 @@ const handleSubmit = async state => {
|
|
|
5188
5404
|
};
|
|
5189
5405
|
const selectedOptimisticSession = optimisticState.sessions.find(session => session.id === optimisticState.selectedSessionId);
|
|
5190
5406
|
const messages = (selectedOptimisticSession?.messages ?? []).filter(message => !message.inProgress);
|
|
5407
|
+
const mentionContextMessage = await getMentionContextMessage(userText);
|
|
5408
|
+
const messagesWithMentionContext = mentionContextMessage ? [...messages, mentionContextMessage] : messages;
|
|
5191
5409
|
const handleTextChunkFunctionRef = streamingEnabled ? async chunk => {
|
|
5192
5410
|
handleTextChunkState = await handleTextChunkFunction(state.uid, assistantMessageId, chunk, handleTextChunkState);
|
|
5193
5411
|
} : undefined;
|
|
5194
5412
|
const assistantMessage = await getAiResponse({
|
|
5195
5413
|
assetDir,
|
|
5196
5414
|
messageId: assistantMessageId,
|
|
5197
|
-
messages,
|
|
5415
|
+
messages: messagesWithMentionContext,
|
|
5198
5416
|
mockAiResponseDelay,
|
|
5199
5417
|
mockApiCommandId,
|
|
5200
5418
|
models,
|
|
@@ -5239,7 +5457,16 @@ const handleSubmit = async state => {
|
|
|
5239
5457
|
const {
|
|
5240
5458
|
latestState
|
|
5241
5459
|
} = handleTextChunkState;
|
|
5242
|
-
|
|
5460
|
+
let updatedSessions = streamingEnabled ? updateMessageTextInSelectedSession(latestState.sessions, latestState.selectedSessionId, assistantMessageId, assistantMessage.text, false) : appendMessageToSelectedSession(latestState.sessions, latestState.selectedSessionId, assistantMessage);
|
|
5461
|
+
if (aiSessionTitleGenerationEnabled && createsNewSession) {
|
|
5462
|
+
const selectedSession = updatedSessions.find(session => session.id === latestState.selectedSessionId);
|
|
5463
|
+
if (selectedSession && isDefaultSessionTitle(selectedSession.title)) {
|
|
5464
|
+
const generatedTitle = await getAiSessionTitle(latestState, userText, assistantMessage.text);
|
|
5465
|
+
if (generatedTitle) {
|
|
5466
|
+
updatedSessions = updateSessionTitle(updatedSessions, latestState.selectedSessionId, generatedTitle);
|
|
5467
|
+
}
|
|
5468
|
+
}
|
|
5469
|
+
}
|
|
5243
5470
|
const selectedSession = updatedSessions.find(session => session.id === latestState.selectedSessionId);
|
|
5244
5471
|
if (selectedSession) {
|
|
5245
5472
|
await saveChatSession(selectedSession);
|
|
@@ -5441,6 +5668,9 @@ const handleClickSettings = async () => {
|
|
|
5441
5668
|
};
|
|
5442
5669
|
|
|
5443
5670
|
const handleInput = async (state, name, value, inputSource = 'user') => {
|
|
5671
|
+
const {
|
|
5672
|
+
selectedSessionId
|
|
5673
|
+
} = state;
|
|
5444
5674
|
if (name === OpenApiApiKeyInput) {
|
|
5445
5675
|
return {
|
|
5446
5676
|
...state,
|
|
@@ -5456,9 +5686,9 @@ const handleInput = async (state, name, value, inputSource = 'user') => {
|
|
|
5456
5686
|
if (name !== Composer) {
|
|
5457
5687
|
return state;
|
|
5458
5688
|
}
|
|
5459
|
-
if (
|
|
5689
|
+
if (selectedSessionId) {
|
|
5460
5690
|
await appendChatViewEvent({
|
|
5461
|
-
sessionId:
|
|
5691
|
+
sessionId: selectedSessionId,
|
|
5462
5692
|
timestamp: new Date().toISOString(),
|
|
5463
5693
|
type: 'handle-input',
|
|
5464
5694
|
value
|
|
@@ -5705,6 +5935,15 @@ const getSavedViewMode = savedState => {
|
|
|
5705
5935
|
return viewMode;
|
|
5706
5936
|
};
|
|
5707
5937
|
|
|
5938
|
+
const loadAiSessionTitleGenerationEnabled = async () => {
|
|
5939
|
+
try {
|
|
5940
|
+
const savedAiSessionTitleGenerationEnabled = await get('chatView.aiSessionTitleGenerationEnabled');
|
|
5941
|
+
return typeof savedAiSessionTitleGenerationEnabled === 'boolean' ? savedAiSessionTitleGenerationEnabled : false;
|
|
5942
|
+
} catch {
|
|
5943
|
+
return false;
|
|
5944
|
+
}
|
|
5945
|
+
};
|
|
5946
|
+
|
|
5708
5947
|
const loadEmitStreamingFunctionCallEvents = async () => {
|
|
5709
5948
|
try {
|
|
5710
5949
|
const savedEmitStreamingFunctionCallEvents = await get('chatView.emitStreamingFunctionCallEvents');
|
|
@@ -5759,12 +5998,9 @@ const loadStreamingEnabled = async () => {
|
|
|
5759
5998
|
};
|
|
5760
5999
|
|
|
5761
6000
|
const loadPreferences = async () => {
|
|
5762
|
-
const openApiApiKey = await loadOpenApiApiKey();
|
|
5763
|
-
const openRouterApiKey = await loadOpenRouterApiKey();
|
|
5764
|
-
const emitStreamingFunctionCallEvents = await loadEmitStreamingFunctionCallEvents();
|
|
5765
|
-
const streamingEnabled = await loadStreamingEnabled();
|
|
5766
|
-
const passIncludeObfuscation = await loadPassIncludeObfuscation();
|
|
6001
|
+
const [aiSessionTitleGenerationEnabled, openApiApiKey, openRouterApiKey, emitStreamingFunctionCallEvents, streamingEnabled, passIncludeObfuscation] = await Promise.all([loadAiSessionTitleGenerationEnabled(), loadOpenApiApiKey(), loadOpenRouterApiKey(), loadEmitStreamingFunctionCallEvents(), loadStreamingEnabled(), loadPassIncludeObfuscation()]);
|
|
5767
6002
|
return {
|
|
6003
|
+
aiSessionTitleGenerationEnabled,
|
|
5768
6004
|
emitStreamingFunctionCallEvents,
|
|
5769
6005
|
openApiApiKey,
|
|
5770
6006
|
openRouterApiKey,
|
|
@@ -5800,6 +6036,7 @@ const loadContent = async (state, savedState) => {
|
|
|
5800
6036
|
const savedSelectedModelId = getSavedSelectedModelId(savedState);
|
|
5801
6037
|
const savedViewMode = getSavedViewMode(savedState);
|
|
5802
6038
|
const {
|
|
6039
|
+
aiSessionTitleGenerationEnabled,
|
|
5803
6040
|
emitStreamingFunctionCallEvents,
|
|
5804
6041
|
openApiApiKey,
|
|
5805
6042
|
openRouterApiKey,
|
|
@@ -5832,6 +6069,7 @@ const loadContent = async (state, savedState) => {
|
|
|
5832
6069
|
const viewMode = sessions.length === 0 || !selectedSessionId ? 'list' : preferredViewMode === 'detail' ? 'detail' : 'list';
|
|
5833
6070
|
return {
|
|
5834
6071
|
...state,
|
|
6072
|
+
aiSessionTitleGenerationEnabled,
|
|
5835
6073
|
chatListScrollTop,
|
|
5836
6074
|
emitStreamingFunctionCallEvents,
|
|
5837
6075
|
initial: false,
|
|
@@ -5903,6 +6141,13 @@ const openMockSession = async (state, mockSessionId, mockChatMessages) => {
|
|
|
5903
6141
|
};
|
|
5904
6142
|
};
|
|
5905
6143
|
|
|
6144
|
+
const registerMockResponse = (state, mockResponse) => {
|
|
6145
|
+
reset$1();
|
|
6146
|
+
pushChunk(mockResponse.text);
|
|
6147
|
+
finish();
|
|
6148
|
+
return state;
|
|
6149
|
+
};
|
|
6150
|
+
|
|
5906
6151
|
const getCss = (composerHeight, listItemHeight, chatMessageFontSize, chatMessageLineHeight, chatMessageFontFamily, renderHtmlCss) => {
|
|
5907
6152
|
const baseCss = `:root {
|
|
5908
6153
|
--ChatInputBoxHeight: ${composerHeight}px;
|
|
@@ -5968,6 +6213,17 @@ const getCss = (composerHeight, listItemHeight, chatMessageFontSize, chatMessage
|
|
|
5968
6213
|
cursor: pointer;
|
|
5969
6214
|
}
|
|
5970
6215
|
|
|
6216
|
+
.ChatOrderedList,
|
|
6217
|
+
.ChatUnorderedList {
|
|
6218
|
+
margin: 6px 0;
|
|
6219
|
+
padding-inline-start: 20px;
|
|
6220
|
+
}
|
|
6221
|
+
|
|
6222
|
+
.ChatOrderedListItem,
|
|
6223
|
+
.ChatUnorderedListItem {
|
|
6224
|
+
margin: 2px 0;
|
|
6225
|
+
}
|
|
6226
|
+
|
|
5971
6227
|
.MarkdownTable {
|
|
5972
6228
|
width: 100%;
|
|
5973
6229
|
margin: 6px 0;
|
|
@@ -6072,6 +6328,8 @@ const ChatToolCallRenderHtmlBody = 'ChatToolCallRenderHtmlBody';
|
|
|
6072
6328
|
const ChatMessageLink = 'ChatMessageLink';
|
|
6073
6329
|
const ChatOrderedList = 'ChatOrderedList';
|
|
6074
6330
|
const ChatOrderedListItem = 'ChatOrderedListItem';
|
|
6331
|
+
const ChatUnorderedList = 'ChatUnorderedList';
|
|
6332
|
+
const ChatUnorderedListItem = 'ChatUnorderedListItem';
|
|
6075
6333
|
const MessageUser = 'MessageUser';
|
|
6076
6334
|
const MessageAssistant = 'MessageAssistant';
|
|
6077
6335
|
const MultilineInputBox = 'MultilineInputBox';
|
|
@@ -6331,6 +6589,13 @@ const getOrderedListItemDom = item => {
|
|
|
6331
6589
|
type: Li
|
|
6332
6590
|
}, ...item.children.flatMap(getInlineNodeDom)];
|
|
6333
6591
|
};
|
|
6592
|
+
const getUnorderedListItemDom = item => {
|
|
6593
|
+
return [{
|
|
6594
|
+
childCount: item.children.length,
|
|
6595
|
+
className: ChatUnorderedListItem,
|
|
6596
|
+
type: Li
|
|
6597
|
+
}, ...item.children.flatMap(getInlineNodeDom)];
|
|
6598
|
+
};
|
|
6334
6599
|
const getTableHeadCellDom = cell => {
|
|
6335
6600
|
return [{
|
|
6336
6601
|
childCount: cell.children.length,
|
|
@@ -6365,6 +6630,28 @@ const getTableDom = node => {
|
|
|
6365
6630
|
type: TBody
|
|
6366
6631
|
}, ...node.rows.flatMap(getTableRowDom)];
|
|
6367
6632
|
};
|
|
6633
|
+
const getHeadingElementType = level => {
|
|
6634
|
+
switch (level) {
|
|
6635
|
+
case 1:
|
|
6636
|
+
return H1;
|
|
6637
|
+
case 2:
|
|
6638
|
+
return H2;
|
|
6639
|
+
case 3:
|
|
6640
|
+
return H3;
|
|
6641
|
+
case 4:
|
|
6642
|
+
return H4;
|
|
6643
|
+
case 5:
|
|
6644
|
+
return H5;
|
|
6645
|
+
case 6:
|
|
6646
|
+
return H6;
|
|
6647
|
+
}
|
|
6648
|
+
};
|
|
6649
|
+
const getHeadingDom = node => {
|
|
6650
|
+
return [{
|
|
6651
|
+
childCount: node.children.length,
|
|
6652
|
+
type: getHeadingElementType(node.level)
|
|
6653
|
+
}, ...node.children.flatMap(getInlineNodeDom)];
|
|
6654
|
+
};
|
|
6368
6655
|
const getMessageNodeDom = node => {
|
|
6369
6656
|
if (node.type === 'text') {
|
|
6370
6657
|
return [{
|
|
@@ -6379,11 +6666,21 @@ const getMessageNodeDom = node => {
|
|
|
6379
6666
|
if (node.type === 'code-block') {
|
|
6380
6667
|
return getCodeBlockDom(node);
|
|
6381
6668
|
}
|
|
6669
|
+
if (node.type === 'heading') {
|
|
6670
|
+
return getHeadingDom(node);
|
|
6671
|
+
}
|
|
6672
|
+
if (node.type === 'ordered-list') {
|
|
6673
|
+
return [{
|
|
6674
|
+
childCount: node.items.length,
|
|
6675
|
+
className: ChatOrderedList,
|
|
6676
|
+
type: Ol
|
|
6677
|
+
}, ...node.items.flatMap(getOrderedListItemDom)];
|
|
6678
|
+
}
|
|
6382
6679
|
return [{
|
|
6383
6680
|
childCount: node.items.length,
|
|
6384
|
-
className:
|
|
6385
|
-
type:
|
|
6386
|
-
}, ...node.items.flatMap(
|
|
6681
|
+
className: ChatUnorderedList,
|
|
6682
|
+
type: Ul
|
|
6683
|
+
}, ...node.items.flatMap(getUnorderedListItemDom)];
|
|
6387
6684
|
};
|
|
6388
6685
|
|
|
6389
6686
|
const getMessageContentDom = nodes => {
|
|
@@ -6929,9 +7226,11 @@ const getToolCallsDom = message => {
|
|
|
6929
7226
|
};
|
|
6930
7227
|
|
|
6931
7228
|
const orderedListItemRegex = /^\s*\d+\.\s+(.*)$/;
|
|
7229
|
+
const unorderedListItemRegex = /^\s*[-*]\s+(.*)$/;
|
|
6932
7230
|
const markdownInlineRegex = /\[([^\]]+)\]\(([^)]+)\)|\*\*([^*]+)\*\*/g;
|
|
6933
7231
|
const markdownTableSeparatorCellRegex = /^:?-{3,}:?$/;
|
|
6934
7232
|
const fencedCodeBlockRegex = /^```/;
|
|
7233
|
+
const markdownHeadingRegex = /^\s*(#{1,6})\s+(.*)$/;
|
|
6935
7234
|
const normalizeInlineTables = value => {
|
|
6936
7235
|
return value.split(/\r?\n/).map(line => {
|
|
6937
7236
|
if (!line.includes('|')) {
|
|
@@ -7034,6 +7333,7 @@ const parseMessageContent = rawMessage => {
|
|
|
7034
7333
|
const nodes = [];
|
|
7035
7334
|
let paragraphLines = [];
|
|
7036
7335
|
let listItems = [];
|
|
7336
|
+
let listType = '';
|
|
7037
7337
|
const flushParagraph = () => {
|
|
7038
7338
|
if (paragraphLines.length === 0) {
|
|
7039
7339
|
return;
|
|
@@ -7050,9 +7350,10 @@ const parseMessageContent = rawMessage => {
|
|
|
7050
7350
|
}
|
|
7051
7351
|
nodes.push({
|
|
7052
7352
|
items: listItems,
|
|
7053
|
-
type: 'list'
|
|
7353
|
+
type: listType || 'ordered-list'
|
|
7054
7354
|
});
|
|
7055
7355
|
listItems = [];
|
|
7356
|
+
listType = '';
|
|
7056
7357
|
};
|
|
7057
7358
|
for (let i = 0; i < lines.length; i++) {
|
|
7058
7359
|
const line = lines[i];
|
|
@@ -7099,15 +7400,43 @@ const parseMessageContent = rawMessage => {
|
|
|
7099
7400
|
continue;
|
|
7100
7401
|
}
|
|
7101
7402
|
}
|
|
7102
|
-
const
|
|
7103
|
-
if (
|
|
7403
|
+
const orderedMatch = line.match(orderedListItemRegex);
|
|
7404
|
+
if (orderedMatch) {
|
|
7405
|
+
if (listType && listType !== 'ordered-list') {
|
|
7406
|
+
flushList();
|
|
7407
|
+
}
|
|
7104
7408
|
flushParagraph();
|
|
7409
|
+
listType = 'ordered-list';
|
|
7105
7410
|
listItems.push({
|
|
7106
|
-
children: parseInlineNodes(
|
|
7411
|
+
children: parseInlineNodes(orderedMatch[1]),
|
|
7107
7412
|
type: 'list-item'
|
|
7108
7413
|
});
|
|
7109
7414
|
continue;
|
|
7110
7415
|
}
|
|
7416
|
+
const unorderedMatch = line.match(unorderedListItemRegex);
|
|
7417
|
+
if (unorderedMatch) {
|
|
7418
|
+
if (listType && listType !== 'unordered-list') {
|
|
7419
|
+
flushList();
|
|
7420
|
+
}
|
|
7421
|
+
flushParagraph();
|
|
7422
|
+
listType = 'unordered-list';
|
|
7423
|
+
listItems.push({
|
|
7424
|
+
children: parseInlineNodes(unorderedMatch[1]),
|
|
7425
|
+
type: 'list-item'
|
|
7426
|
+
});
|
|
7427
|
+
continue;
|
|
7428
|
+
}
|
|
7429
|
+
const headingMatch = line.match(markdownHeadingRegex);
|
|
7430
|
+
if (headingMatch) {
|
|
7431
|
+
flushList();
|
|
7432
|
+
flushParagraph();
|
|
7433
|
+
nodes.push({
|
|
7434
|
+
children: parseInlineNodes(headingMatch[2]),
|
|
7435
|
+
level: headingMatch[1].length,
|
|
7436
|
+
type: 'heading'
|
|
7437
|
+
});
|
|
7438
|
+
continue;
|
|
7439
|
+
}
|
|
7111
7440
|
flushList();
|
|
7112
7441
|
paragraphLines.push(line);
|
|
7113
7442
|
}
|
|
@@ -7561,6 +7890,7 @@ const commandMap = {
|
|
|
7561
7890
|
'Chat.mockOpenApiStreamPushChunk': wrapCommand(mockOpenApiStreamPushChunk),
|
|
7562
7891
|
'Chat.mockOpenApiStreamReset': wrapCommand(mockOpenApiStreamReset),
|
|
7563
7892
|
'Chat.openMockSession': wrapCommand(openMockSession),
|
|
7893
|
+
'Chat.registerMockResponse': wrapCommand(registerMockResponse),
|
|
7564
7894
|
'Chat.render2': render2,
|
|
7565
7895
|
'Chat.renderEventListeners': renderEventListeners,
|
|
7566
7896
|
'Chat.rerender': wrapCommand(rerender),
|
|
@@ -7576,7 +7906,7 @@ const commandMap = {
|
|
|
7576
7906
|
};
|
|
7577
7907
|
|
|
7578
7908
|
const send = port => {
|
|
7579
|
-
return
|
|
7909
|
+
return sendMessagePortToChatNetworkWorker(port);
|
|
7580
7910
|
};
|
|
7581
7911
|
const initializeChatNetworkWorker = async () => {
|
|
7582
7912
|
const rpc = await create$4({
|