@lvce-editor/chat-view 3.1.0 → 3.3.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.
@@ -1463,6 +1463,7 @@ const createDefaultState = () => {
1463
1463
  const composerFontSize = 13;
1464
1464
  const composerLineHeight = 20;
1465
1465
  return {
1466
+ aiSessionTitleGenerationEnabled: false,
1466
1467
  assetDir: '',
1467
1468
  chatListScrollTop: 0,
1468
1469
  chatMessageFontFamily: 'system-ui',
@@ -1508,7 +1509,7 @@ const createDefaultState = () => {
1508
1509
  messages: [],
1509
1510
  title: defaultSessionTitle()
1510
1511
  }],
1511
- streamingEnabled: false,
1512
+ streamingEnabled: true,
1512
1513
  tokensMax: 0,
1513
1514
  tokensUsed: 0,
1514
1515
  uid: 0,
@@ -4663,7 +4664,7 @@ const getAiResponse = async ({
4663
4664
  passIncludeObfuscation = false,
4664
4665
  platform,
4665
4666
  selectedModelId,
4666
- streamingEnabled = false,
4667
+ streamingEnabled = true,
4667
4668
  useMockApi,
4668
4669
  userText,
4669
4670
  webSearchEnabled = false
@@ -4807,6 +4808,7 @@ const handleClickSaveOpenApiApiKey = async state => {
4807
4808
  openRouterApiKey: updatedState.openRouterApiKey,
4808
4809
  platform: updatedState.platform,
4809
4810
  selectedModelId: updatedState.selectedModelId,
4811
+ streamingEnabled: updatedState.streamingEnabled,
4810
4812
  useMockApi: updatedState.useMockApi,
4811
4813
  userText: previousUserMessage.text
4812
4814
  });
@@ -5014,6 +5016,10 @@ const handleToolCallsChunkFunction = async (uid, assistantMessageId, toolCalls,
5014
5016
  };
5015
5017
  };
5016
5018
 
5019
+ const slashCommandRegex = /^\/(clear|export|help|new)(?:\s+.*)?$/;
5020
+ const mentionRegex = /(^|\s)@([^\s]+)/g;
5021
+ const maxMentionCount = 5;
5022
+ const maxMentionTextLength = 8000;
5017
5023
  const appendMessageToSelectedSession = (sessions, selectedSessionId, message) => {
5018
5024
  return sessions.map(session => {
5019
5025
  if (session.id !== selectedSessionId) {
@@ -5067,8 +5073,208 @@ const isStreamingFunctionCallEvent = parsed => {
5067
5073
  const getSseEventType = value => {
5068
5074
  return value && typeof value === 'object' && Reflect.get(value, 'type') === 'response.completed' ? 'sse-response-completed' : 'sse-response-part';
5069
5075
  };
5076
+ const getCommandHelpText = () => {
5077
+ 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');
5078
+ };
5079
+ const withClearedComposer = state => {
5080
+ return focusInput({
5081
+ ...state,
5082
+ composerHeight: getMinComposerHeightForState(state),
5083
+ composerValue: '',
5084
+ inputSource: 'script'
5085
+ });
5086
+ };
5087
+ const toMarkdownTranscript = session => {
5088
+ const lines = [`# ${session.title}`, ''];
5089
+ for (const message of session.messages) {
5090
+ const role = message.role === 'assistant' ? 'Assistant' : 'User';
5091
+ lines.push(`## ${role}`);
5092
+ lines.push(message.text || '(empty)');
5093
+ lines.push('');
5094
+ }
5095
+ return lines.join('\n').trim();
5096
+ };
5097
+ const executeSlashCommand = async (state, command) => {
5098
+ if (command === 'new') {
5099
+ const nextState = await createSession(state);
5100
+ return withClearedComposer({
5101
+ ...nextState,
5102
+ viewMode: 'detail'
5103
+ });
5104
+ }
5105
+ const selectedSession = state.sessions.find(session => session.id === state.selectedSessionId);
5106
+ if (!selectedSession) {
5107
+ return withClearedComposer(state);
5108
+ }
5109
+ if (command === 'clear') {
5110
+ const updatedSessions = state.sessions.map(session => {
5111
+ if (session.id !== state.selectedSessionId) {
5112
+ return session;
5113
+ }
5114
+ return {
5115
+ ...session,
5116
+ messages: []
5117
+ };
5118
+ });
5119
+ const updatedSelectedSession = updatedSessions.find(session => session.id === state.selectedSessionId);
5120
+ if (updatedSelectedSession) {
5121
+ await saveChatSession(updatedSelectedSession);
5122
+ }
5123
+ return withClearedComposer({
5124
+ ...state,
5125
+ sessions: updatedSessions
5126
+ });
5127
+ }
5128
+ const assistantText = command === 'help' ? getCommandHelpText() : ['```md', toMarkdownTranscript(selectedSession), '```'].join('\n');
5129
+ const assistantMessage = {
5130
+ id: crypto.randomUUID(),
5131
+ role: 'assistant',
5132
+ text: assistantText,
5133
+ time: new Date().toLocaleTimeString([], {
5134
+ hour: '2-digit',
5135
+ minute: '2-digit'
5136
+ })
5137
+ };
5138
+ const updatedSessions = appendMessageToSelectedSession(state.sessions, state.selectedSessionId, assistantMessage);
5139
+ const updatedSelectedSession = updatedSessions.find(session => session.id === state.selectedSessionId);
5140
+ if (updatedSelectedSession) {
5141
+ await saveChatSession(updatedSelectedSession);
5142
+ }
5143
+ return withClearedComposer({
5144
+ ...state,
5145
+ sessions: updatedSessions
5146
+ });
5147
+ };
5148
+ const getSlashCommand = value => {
5149
+ const trimmed = value.trim();
5150
+ const match = trimmed.match(slashCommandRegex);
5151
+ if (!match) {
5152
+ return undefined;
5153
+ }
5154
+ return match[1];
5155
+ };
5156
+ const parseMentionedPaths = value => {
5157
+ const matches = value.matchAll(mentionRegex);
5158
+ const paths = [];
5159
+ for (const match of matches) {
5160
+ const rawPath = match[2] || '';
5161
+ const cleanedPath = rawPath.replaceAll(/[),.;:!?]+$/g, '');
5162
+ if (!cleanedPath || isPathTraversalAttempt(cleanedPath)) {
5163
+ continue;
5164
+ }
5165
+ const normalizedPath = normalizeRelativePath(cleanedPath);
5166
+ if (paths.includes(normalizedPath)) {
5167
+ continue;
5168
+ }
5169
+ paths.push(normalizedPath);
5170
+ if (paths.length >= maxMentionCount) {
5171
+ break;
5172
+ }
5173
+ }
5174
+ return paths;
5175
+ };
5176
+ const getMentionContextMessage = async value => {
5177
+ const paths = parseMentionedPaths(value);
5178
+ if (paths.length === 0) {
5179
+ return undefined;
5180
+ }
5181
+ const sections = [];
5182
+ for (const path of paths) {
5183
+ try {
5184
+ const fileContent = await readFile(path);
5185
+ const truncatedContent = fileContent.length > maxMentionTextLength ? `${fileContent.slice(0, maxMentionTextLength)}\n... [truncated]` : fileContent;
5186
+ sections.push([`File: ${path}`, '```text', truncatedContent, '```'].join('\n'));
5187
+ } catch (error) {
5188
+ sections.push([`File: ${path}`, `Error: ${String(error)}`].join('\n'));
5189
+ }
5190
+ }
5191
+ return {
5192
+ id: crypto.randomUUID(),
5193
+ role: 'user',
5194
+ text: ['Referenced file context:', ...sections].join('\n\n'),
5195
+ time: new Date().toLocaleTimeString([], {
5196
+ hour: '2-digit',
5197
+ minute: '2-digit'
5198
+ })
5199
+ };
5200
+ };
5201
+ const isDefaultSessionTitle = title => {
5202
+ return /^Chat \d+$/.test(title);
5203
+ };
5204
+ const sanitizeGeneratedTitle = value => {
5205
+ return value.replace(/^title:\s*/i, '').replaceAll(/^['"`\s]+|['"`\s]+$/g, '').replaceAll(/\s+/g, ' ').trim().slice(0, 80);
5206
+ };
5207
+ const updateSessionTitle = (sessions, selectedSessionId, title) => {
5208
+ return sessions.map(session => {
5209
+ if (session.id !== selectedSessionId) {
5210
+ return session;
5211
+ }
5212
+ return {
5213
+ ...session,
5214
+ title
5215
+ };
5216
+ });
5217
+ };
5218
+ const getAiSessionTitle = async (state, userText, assistantText) => {
5219
+ const {
5220
+ models,
5221
+ openApiApiBaseUrl,
5222
+ openApiApiKey,
5223
+ openRouterApiBaseUrl,
5224
+ openRouterApiKey,
5225
+ selectedModelId,
5226
+ useMockApi
5227
+ } = state;
5228
+ if (useMockApi) {
5229
+ return '';
5230
+ }
5231
+ const usesOpenApiModel = isOpenApiModel(selectedModelId, models);
5232
+ const usesOpenRouterModel = isOpenRouterModel(selectedModelId, models);
5233
+ if (usesOpenApiModel && !openApiApiKey) {
5234
+ return '';
5235
+ }
5236
+ if (usesOpenRouterModel && !openRouterApiKey) {
5237
+ return '';
5238
+ }
5239
+ if (!usesOpenApiModel && !usesOpenRouterModel) {
5240
+ return '';
5241
+ }
5242
+ const titlePrompt = `Create a concise title (max 6 words) for this conversation. Respond only with the title, no punctuation at the end.
5243
+ User: ${userText}
5244
+ Assistant: ${assistantText}`;
5245
+ const promptMessage = {
5246
+ id: crypto.randomUUID(),
5247
+ role: 'user',
5248
+ text: titlePrompt,
5249
+ time: new Date().toLocaleTimeString([], {
5250
+ hour: '2-digit',
5251
+ minute: '2-digit'
5252
+ })
5253
+ };
5254
+ const titleResponse = await getAiResponse({
5255
+ assetDir: state.assetDir,
5256
+ messages: [promptMessage],
5257
+ mockAiResponseDelay: state.mockAiResponseDelay,
5258
+ mockApiCommandId: state.mockApiCommandId,
5259
+ models,
5260
+ openApiApiBaseUrl,
5261
+ openApiApiKey,
5262
+ openRouterApiBaseUrl,
5263
+ openRouterApiKey,
5264
+ passIncludeObfuscation: state.passIncludeObfuscation,
5265
+ platform: state.platform,
5266
+ selectedModelId,
5267
+ streamingEnabled: false,
5268
+ useMockApi,
5269
+ userText: titlePrompt,
5270
+ webSearchEnabled: false
5271
+ });
5272
+ const title = sanitizeGeneratedTitle(titleResponse.text);
5273
+ return title && !isDefaultSessionTitle(title) ? title : '';
5274
+ };
5070
5275
  const handleSubmit = async state => {
5071
5276
  const {
5277
+ aiSessionTitleGenerationEnabled,
5072
5278
  assetDir,
5073
5279
  composerValue,
5074
5280
  emitStreamingFunctionCallEvents,
@@ -5094,6 +5300,10 @@ const handleSubmit = async state => {
5094
5300
  if (!userText) {
5095
5301
  return state;
5096
5302
  }
5303
+ const slashCommand = getSlashCommand(userText);
5304
+ if (slashCommand) {
5305
+ return executeSlashCommand(state, slashCommand);
5306
+ }
5097
5307
  const userTime = new Date().toLocaleTimeString([], {
5098
5308
  hour: '2-digit',
5099
5309
  minute: '2-digit'
@@ -5130,6 +5340,7 @@ const handleSubmit = async state => {
5130
5340
  }
5131
5341
  }
5132
5342
  let optimisticState;
5343
+ const createsNewSession = viewMode === 'list';
5133
5344
  if (viewMode === 'list') {
5134
5345
  const newSessionId = generateSessionId();
5135
5346
  await appendChatViewEvent({
@@ -5187,13 +5398,15 @@ const handleSubmit = async state => {
5187
5398
  };
5188
5399
  const selectedOptimisticSession = optimisticState.sessions.find(session => session.id === optimisticState.selectedSessionId);
5189
5400
  const messages = (selectedOptimisticSession?.messages ?? []).filter(message => !message.inProgress);
5401
+ const mentionContextMessage = await getMentionContextMessage(userText);
5402
+ const messagesWithMentionContext = mentionContextMessage ? [...messages, mentionContextMessage] : messages;
5190
5403
  const handleTextChunkFunctionRef = streamingEnabled ? async chunk => {
5191
5404
  handleTextChunkState = await handleTextChunkFunction(state.uid, assistantMessageId, chunk, handleTextChunkState);
5192
5405
  } : undefined;
5193
5406
  const assistantMessage = await getAiResponse({
5194
5407
  assetDir,
5195
5408
  messageId: assistantMessageId,
5196
- messages,
5409
+ messages: messagesWithMentionContext,
5197
5410
  mockAiResponseDelay,
5198
5411
  mockApiCommandId,
5199
5412
  models,
@@ -5238,7 +5451,16 @@ const handleSubmit = async state => {
5238
5451
  const {
5239
5452
  latestState
5240
5453
  } = handleTextChunkState;
5241
- const updatedSessions = streamingEnabled ? updateMessageTextInSelectedSession(latestState.sessions, latestState.selectedSessionId, assistantMessageId, assistantMessage.text, false) : appendMessageToSelectedSession(latestState.sessions, latestState.selectedSessionId, assistantMessage);
5454
+ let updatedSessions = streamingEnabled ? updateMessageTextInSelectedSession(latestState.sessions, latestState.selectedSessionId, assistantMessageId, assistantMessage.text, false) : appendMessageToSelectedSession(latestState.sessions, latestState.selectedSessionId, assistantMessage);
5455
+ if (aiSessionTitleGenerationEnabled && createsNewSession) {
5456
+ const selectedSession = updatedSessions.find(session => session.id === latestState.selectedSessionId);
5457
+ if (selectedSession && isDefaultSessionTitle(selectedSession.title)) {
5458
+ const generatedTitle = await getAiSessionTitle(latestState, userText, assistantMessage.text);
5459
+ if (generatedTitle) {
5460
+ updatedSessions = updateSessionTitle(updatedSessions, latestState.selectedSessionId, generatedTitle);
5461
+ }
5462
+ }
5463
+ }
5242
5464
  const selectedSession = updatedSessions.find(session => session.id === latestState.selectedSessionId);
5243
5465
  if (selectedSession) {
5244
5466
  await saveChatSession(selectedSession);
@@ -5704,6 +5926,15 @@ const getSavedViewMode = savedState => {
5704
5926
  return viewMode;
5705
5927
  };
5706
5928
 
5929
+ const loadAiSessionTitleGenerationEnabled = async () => {
5930
+ try {
5931
+ const savedAiSessionTitleGenerationEnabled = await get('chatView.aiSessionTitleGenerationEnabled');
5932
+ return typeof savedAiSessionTitleGenerationEnabled === 'boolean' ? savedAiSessionTitleGenerationEnabled : false;
5933
+ } catch {
5934
+ return false;
5935
+ }
5936
+ };
5937
+
5707
5938
  const loadEmitStreamingFunctionCallEvents = async () => {
5708
5939
  try {
5709
5940
  const savedEmitStreamingFunctionCallEvents = await get('chatView.emitStreamingFunctionCallEvents');
@@ -5751,19 +5982,16 @@ const loadPassIncludeObfuscation = async () => {
5751
5982
  const loadStreamingEnabled = async () => {
5752
5983
  try {
5753
5984
  const savedStreamingEnabled = await get('chatView.streamingEnabled');
5754
- return typeof savedStreamingEnabled === 'boolean' ? savedStreamingEnabled : false;
5985
+ return typeof savedStreamingEnabled === 'boolean' ? savedStreamingEnabled : true;
5755
5986
  } catch {
5756
- return false;
5987
+ return true;
5757
5988
  }
5758
5989
  };
5759
5990
 
5760
5991
  const loadPreferences = async () => {
5761
- const openApiApiKey = await loadOpenApiApiKey();
5762
- const openRouterApiKey = await loadOpenRouterApiKey();
5763
- const emitStreamingFunctionCallEvents = await loadEmitStreamingFunctionCallEvents();
5764
- const streamingEnabled = await loadStreamingEnabled();
5765
- const passIncludeObfuscation = await loadPassIncludeObfuscation();
5992
+ const [aiSessionTitleGenerationEnabled, openApiApiKey, openRouterApiKey, emitStreamingFunctionCallEvents, streamingEnabled, passIncludeObfuscation] = await Promise.all([loadAiSessionTitleGenerationEnabled(), loadOpenApiApiKey(), loadOpenRouterApiKey(), loadEmitStreamingFunctionCallEvents(), loadStreamingEnabled(), loadPassIncludeObfuscation()]);
5766
5993
  return {
5994
+ aiSessionTitleGenerationEnabled,
5767
5995
  emitStreamingFunctionCallEvents,
5768
5996
  openApiApiKey,
5769
5997
  openRouterApiKey,
@@ -5799,6 +6027,7 @@ const loadContent = async (state, savedState) => {
5799
6027
  const savedSelectedModelId = getSavedSelectedModelId(savedState);
5800
6028
  const savedViewMode = getSavedViewMode(savedState);
5801
6029
  const {
6030
+ aiSessionTitleGenerationEnabled,
5802
6031
  emitStreamingFunctionCallEvents,
5803
6032
  openApiApiKey,
5804
6033
  openRouterApiKey,
@@ -5831,6 +6060,7 @@ const loadContent = async (state, savedState) => {
5831
6060
  const viewMode = sessions.length === 0 || !selectedSessionId ? 'list' : preferredViewMode === 'detail' ? 'detail' : 'list';
5832
6061
  return {
5833
6062
  ...state,
6063
+ aiSessionTitleGenerationEnabled,
5834
6064
  chatListScrollTop,
5835
6065
  emitStreamingFunctionCallEvents,
5836
6066
  initial: false,
@@ -5902,6 +6132,13 @@ const openMockSession = async (state, mockSessionId, mockChatMessages) => {
5902
6132
  };
5903
6133
  };
5904
6134
 
6135
+ const registerMockResponse = (state, mockResponse) => {
6136
+ reset$1();
6137
+ pushChunk(mockResponse.text);
6138
+ finish();
6139
+ return state;
6140
+ };
6141
+
5905
6142
  const getCss = (composerHeight, listItemHeight, chatMessageFontSize, chatMessageLineHeight, chatMessageFontFamily, renderHtmlCss) => {
5906
6143
  const baseCss = `:root {
5907
6144
  --ChatInputBoxHeight: ${composerHeight}px;
@@ -5967,6 +6204,17 @@ const getCss = (composerHeight, listItemHeight, chatMessageFontSize, chatMessage
5967
6204
  cursor: pointer;
5968
6205
  }
5969
6206
 
6207
+ .ChatOrderedList,
6208
+ .ChatUnorderedList {
6209
+ margin: 6px 0;
6210
+ padding-inline-start: 20px;
6211
+ }
6212
+
6213
+ .ChatOrderedListItem,
6214
+ .ChatUnorderedListItem {
6215
+ margin: 2px 0;
6216
+ }
6217
+
5970
6218
  .MarkdownTable {
5971
6219
  width: 100%;
5972
6220
  margin: 6px 0;
@@ -6071,6 +6319,8 @@ const ChatToolCallRenderHtmlBody = 'ChatToolCallRenderHtmlBody';
6071
6319
  const ChatMessageLink = 'ChatMessageLink';
6072
6320
  const ChatOrderedList = 'ChatOrderedList';
6073
6321
  const ChatOrderedListItem = 'ChatOrderedListItem';
6322
+ const ChatUnorderedList = 'ChatUnorderedList';
6323
+ const ChatUnorderedListItem = 'ChatUnorderedListItem';
6074
6324
  const MessageUser = 'MessageUser';
6075
6325
  const MessageAssistant = 'MessageAssistant';
6076
6326
  const MultilineInputBox = 'MultilineInputBox';
@@ -6314,6 +6564,15 @@ const getInlineNodeDom = inlineNode => {
6314
6564
  }, text(inlineNode.text)];
6315
6565
  };
6316
6566
 
6567
+ const getCodeBlockDom = node => {
6568
+ return [{
6569
+ childCount: 1,
6570
+ type: Pre
6571
+ }, {
6572
+ childCount: 1,
6573
+ type: Code
6574
+ }, text(node.text)];
6575
+ };
6317
6576
  const getOrderedListItemDom = item => {
6318
6577
  return [{
6319
6578
  childCount: item.children.length,
@@ -6321,6 +6580,13 @@ const getOrderedListItemDom = item => {
6321
6580
  type: Li
6322
6581
  }, ...item.children.flatMap(getInlineNodeDom)];
6323
6582
  };
6583
+ const getUnorderedListItemDom = item => {
6584
+ return [{
6585
+ childCount: item.children.length,
6586
+ className: ChatUnorderedListItem,
6587
+ type: Li
6588
+ }, ...item.children.flatMap(getInlineNodeDom)];
6589
+ };
6324
6590
  const getTableHeadCellDom = cell => {
6325
6591
  return [{
6326
6592
  childCount: cell.children.length,
@@ -6355,6 +6621,28 @@ const getTableDom = node => {
6355
6621
  type: TBody
6356
6622
  }, ...node.rows.flatMap(getTableRowDom)];
6357
6623
  };
6624
+ const getHeadingElementType = level => {
6625
+ switch (level) {
6626
+ case 1:
6627
+ return H1;
6628
+ case 2:
6629
+ return H2;
6630
+ case 3:
6631
+ return H3;
6632
+ case 4:
6633
+ return H4;
6634
+ case 5:
6635
+ return H5;
6636
+ case 6:
6637
+ return H6;
6638
+ }
6639
+ };
6640
+ const getHeadingDom = node => {
6641
+ return [{
6642
+ childCount: node.children.length,
6643
+ type: getHeadingElementType(node.level)
6644
+ }, ...node.children.flatMap(getInlineNodeDom)];
6645
+ };
6358
6646
  const getMessageNodeDom = node => {
6359
6647
  if (node.type === 'text') {
6360
6648
  return [{
@@ -6366,11 +6654,24 @@ const getMessageNodeDom = node => {
6366
6654
  if (node.type === 'table') {
6367
6655
  return getTableDom(node);
6368
6656
  }
6657
+ if (node.type === 'code-block') {
6658
+ return getCodeBlockDom(node);
6659
+ }
6660
+ if (node.type === 'heading') {
6661
+ return getHeadingDom(node);
6662
+ }
6663
+ if (node.type === 'ordered-list') {
6664
+ return [{
6665
+ childCount: node.items.length,
6666
+ className: ChatOrderedList,
6667
+ type: Ol
6668
+ }, ...node.items.flatMap(getOrderedListItemDom)];
6669
+ }
6369
6670
  return [{
6370
6671
  childCount: node.items.length,
6371
- className: ChatOrderedList,
6372
- type: Ol
6373
- }, ...node.items.flatMap(getOrderedListItemDom)];
6672
+ className: ChatUnorderedList,
6673
+ type: Ul
6674
+ }, ...node.items.flatMap(getUnorderedListItemDom)];
6374
6675
  };
6375
6676
 
6376
6677
  const getMessageContentDom = nodes => {
@@ -6916,8 +7217,11 @@ const getToolCallsDom = message => {
6916
7217
  };
6917
7218
 
6918
7219
  const orderedListItemRegex = /^\s*\d+\.\s+(.*)$/;
7220
+ const unorderedListItemRegex = /^\s*[-*]\s+(.*)$/;
6919
7221
  const markdownInlineRegex = /\[([^\]]+)\]\(([^)]+)\)|\*\*([^*]+)\*\*/g;
6920
7222
  const markdownTableSeparatorCellRegex = /^:?-{3,}:?$/;
7223
+ const fencedCodeBlockRegex = /^```/;
7224
+ const markdownHeadingRegex = /^\s*(#{1,6})\s+(.*)$/;
6921
7225
  const normalizeInlineTables = value => {
6922
7226
  return value.split(/\r?\n/).map(line => {
6923
7227
  if (!line.includes('|')) {
@@ -7020,6 +7324,7 @@ const parseMessageContent = rawMessage => {
7020
7324
  const nodes = [];
7021
7325
  let paragraphLines = [];
7022
7326
  let listItems = [];
7327
+ let listType = '';
7023
7328
  const flushParagraph = () => {
7024
7329
  if (paragraphLines.length === 0) {
7025
7330
  return;
@@ -7036,9 +7341,10 @@ const parseMessageContent = rawMessage => {
7036
7341
  }
7037
7342
  nodes.push({
7038
7343
  items: listItems,
7039
- type: 'list'
7344
+ type: listType || 'ordered-list'
7040
7345
  });
7041
7346
  listItems = [];
7347
+ listType = '';
7042
7348
  };
7043
7349
  for (let i = 0; i < lines.length; i++) {
7044
7350
  const line = lines[i];
@@ -7047,6 +7353,21 @@ const parseMessageContent = rawMessage => {
7047
7353
  flushParagraph();
7048
7354
  continue;
7049
7355
  }
7356
+ if (fencedCodeBlockRegex.test(line.trim())) {
7357
+ flushList();
7358
+ flushParagraph();
7359
+ const codeLines = [];
7360
+ i++;
7361
+ while (i < lines.length && !fencedCodeBlockRegex.test(lines[i].trim())) {
7362
+ codeLines.push(lines[i]);
7363
+ i++;
7364
+ }
7365
+ nodes.push({
7366
+ text: codeLines.join('\n'),
7367
+ type: 'code-block'
7368
+ });
7369
+ continue;
7370
+ }
7050
7371
  if (isTableRow(line) && i + 1 < lines.length) {
7051
7372
  const headerCells = getTableCells(line);
7052
7373
  if (isTableSeparatorRow(lines[i + 1], headerCells.length)) {
@@ -7070,15 +7391,43 @@ const parseMessageContent = rawMessage => {
7070
7391
  continue;
7071
7392
  }
7072
7393
  }
7073
- const match = line.match(orderedListItemRegex);
7074
- if (match) {
7394
+ const orderedMatch = line.match(orderedListItemRegex);
7395
+ if (orderedMatch) {
7396
+ if (listType && listType !== 'ordered-list') {
7397
+ flushList();
7398
+ }
7399
+ flushParagraph();
7400
+ listType = 'ordered-list';
7401
+ listItems.push({
7402
+ children: parseInlineNodes(orderedMatch[1]),
7403
+ type: 'list-item'
7404
+ });
7405
+ continue;
7406
+ }
7407
+ const unorderedMatch = line.match(unorderedListItemRegex);
7408
+ if (unorderedMatch) {
7409
+ if (listType && listType !== 'unordered-list') {
7410
+ flushList();
7411
+ }
7075
7412
  flushParagraph();
7413
+ listType = 'unordered-list';
7076
7414
  listItems.push({
7077
- children: parseInlineNodes(match[1]),
7415
+ children: parseInlineNodes(unorderedMatch[1]),
7078
7416
  type: 'list-item'
7079
7417
  });
7080
7418
  continue;
7081
7419
  }
7420
+ const headingMatch = line.match(markdownHeadingRegex);
7421
+ if (headingMatch) {
7422
+ flushList();
7423
+ flushParagraph();
7424
+ nodes.push({
7425
+ children: parseInlineNodes(headingMatch[2]),
7426
+ level: headingMatch[1].length,
7427
+ type: 'heading'
7428
+ });
7429
+ continue;
7430
+ }
7082
7431
  flushList();
7083
7432
  paragraphLines.push(line);
7084
7433
  }
@@ -7532,6 +7881,7 @@ const commandMap = {
7532
7881
  'Chat.mockOpenApiStreamPushChunk': wrapCommand(mockOpenApiStreamPushChunk),
7533
7882
  'Chat.mockOpenApiStreamReset': wrapCommand(mockOpenApiStreamReset),
7534
7883
  'Chat.openMockSession': wrapCommand(openMockSession),
7884
+ 'Chat.registerMockResponse': wrapCommand(registerMockResponse),
7535
7885
  'Chat.render2': render2,
7536
7886
  'Chat.renderEventListeners': renderEventListeners,
7537
7887
  'Chat.rerender': wrapCommand(rerender),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/chat-view",
3
- "version": "3.1.0",
3
+ "version": "3.3.0",
4
4
  "description": "Chat View Worker",
5
5
  "repository": {
6
6
  "type": "git",