@lvce-editor/chat-view 2.5.0 → 2.7.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.
@@ -1078,6 +1078,8 @@ const {
1078
1078
  set: set$3
1079
1079
  } = create$2(6002);
1080
1080
 
1081
+ const Ol$1 = 49;
1082
+
1081
1083
  const ClientX = 'event.clientX';
1082
1084
  const ClientY = 'event.clientY';
1083
1085
  const Key = 'event.key';
@@ -1467,6 +1469,7 @@ const createDefaultState = () => {
1467
1469
  composerHeight: composerLineHeight + 8,
1468
1470
  composerLineHeight,
1469
1471
  composerValue: '',
1472
+ emitStreamingFunctionCallEvents: false,
1470
1473
  errorCount: 0,
1471
1474
  focus: 'composer',
1472
1475
  focused: false,
@@ -2739,22 +2742,166 @@ const readNextChunk = async () => {
2739
2742
  return promise;
2740
2743
  };
2741
2744
 
2742
- const getMockOpenApiAssistantText = async (stream, onTextChunk) => {
2745
+ const parseSseDataLines = eventChunk => {
2746
+ const lines = eventChunk.split('\n');
2747
+ const dataLines = [];
2748
+ for (const line of lines) {
2749
+ if (!line.startsWith('data:')) {
2750
+ continue;
2751
+ }
2752
+ dataLines.push(line.slice(5).trimStart());
2753
+ }
2754
+ return dataLines;
2755
+ };
2756
+ const emitToolCalls = async (toolCallAccumulator, onToolCallsChunk) => {
2757
+ if (!onToolCallsChunk) {
2758
+ return;
2759
+ }
2760
+ const toolCalls = Object.entries(toolCallAccumulator).toSorted((a, b) => Number(a[0]) - Number(b[0])).map(entry => entry[1]).filter(toolCall => !!toolCall.name);
2761
+ if (toolCalls.length === 0) {
2762
+ return;
2763
+ }
2764
+ await onToolCallsChunk(toolCalls);
2765
+ };
2766
+ const getMockOpenApiAssistantText = async (stream, onTextChunk, onToolCallsChunk, onDataEvent, onEventStreamFinished) => {
2743
2767
  const error = takeErrorResponse();
2744
2768
  if (error) {
2745
2769
  return error;
2746
2770
  }
2747
2771
  let text = '';
2772
+ let remainder = '';
2773
+ let toolCallAccumulator = {};
2774
+ let finishedNotified = false;
2775
+ const notifyFinished = async () => {
2776
+ if (finishedNotified) {
2777
+ return;
2778
+ }
2779
+ finishedNotified = true;
2780
+ if (onEventStreamFinished) {
2781
+ await onEventStreamFinished();
2782
+ }
2783
+ };
2784
+ const handleParsedSseEvent = async parsed => {
2785
+ if (onDataEvent) {
2786
+ await onDataEvent(parsed);
2787
+ }
2788
+ if (!parsed || typeof parsed !== 'object') {
2789
+ return;
2790
+ }
2791
+ const eventType = Reflect.get(parsed, 'type');
2792
+ if (eventType === 'response.completed') {
2793
+ await notifyFinished();
2794
+ return;
2795
+ }
2796
+ if (eventType === 'response.output_text.delta') {
2797
+ const delta = Reflect.get(parsed, 'delta');
2798
+ if (typeof delta !== 'string' || !delta) {
2799
+ return;
2800
+ }
2801
+ text += delta;
2802
+ if (stream && onTextChunk) {
2803
+ await onTextChunk(delta);
2804
+ }
2805
+ return;
2806
+ }
2807
+ if (eventType === 'response.output_item.added') {
2808
+ const outputIndex = Reflect.get(parsed, 'output_index');
2809
+ const item = Reflect.get(parsed, 'item');
2810
+ if (typeof outputIndex !== 'number' || !item || typeof item !== 'object') {
2811
+ return;
2812
+ }
2813
+ if (Reflect.get(item, 'type') !== 'function_call') {
2814
+ return;
2815
+ }
2816
+ const name = Reflect.get(item, 'name');
2817
+ const argumentsValue = Reflect.get(item, 'arguments');
2818
+ const callId = Reflect.get(item, 'call_id');
2819
+ toolCallAccumulator = {
2820
+ ...toolCallAccumulator,
2821
+ [outputIndex]: {
2822
+ arguments: typeof argumentsValue === 'string' ? argumentsValue : '',
2823
+ ...(typeof callId === 'string' ? {
2824
+ id: callId
2825
+ } : {}),
2826
+ name: typeof name === 'string' ? name : ''
2827
+ }
2828
+ };
2829
+ await emitToolCalls(toolCallAccumulator, onToolCallsChunk);
2830
+ return;
2831
+ }
2832
+ if (eventType === 'response.function_call_arguments.delta' || eventType === 'response.function_call_arguments.done') {
2833
+ const outputIndex = Reflect.get(parsed, 'output_index');
2834
+ if (typeof outputIndex !== 'number') {
2835
+ return;
2836
+ }
2837
+ const current = toolCallAccumulator[outputIndex] || {
2838
+ arguments: '',
2839
+ name: ''
2840
+ };
2841
+ const delta = Reflect.get(parsed, 'delta');
2842
+ const argumentsValue = Reflect.get(parsed, 'arguments');
2843
+ const name = Reflect.get(parsed, 'name');
2844
+ const callId = Reflect.get(parsed, 'call_id');
2845
+ const next = {
2846
+ arguments: typeof argumentsValue === 'string' ? argumentsValue : typeof delta === 'string' ? `${current.arguments}${delta}` : current.arguments,
2847
+ ...(typeof callId === 'string' ? {
2848
+ id: callId
2849
+ } : current.id ? {
2850
+ id: current.id
2851
+ } : {}),
2852
+ name: typeof name === 'string' && name ? name : current.name
2853
+ };
2854
+ toolCallAccumulator = {
2855
+ ...toolCallAccumulator,
2856
+ [outputIndex]: next
2857
+ };
2858
+ await emitToolCalls(toolCallAccumulator, onToolCallsChunk);
2859
+ }
2860
+ };
2861
+ const consumeSseDataLines = async dataLines => {
2862
+ for (const line of dataLines) {
2863
+ if (line === '[DONE]') {
2864
+ await notifyFinished();
2865
+ continue;
2866
+ }
2867
+ let parsed;
2868
+ try {
2869
+ parsed = JSON.parse(line);
2870
+ } catch {
2871
+ continue;
2872
+ }
2873
+ await handleParsedSseEvent(parsed);
2874
+ }
2875
+ };
2748
2876
  while (true) {
2749
2877
  const chunk = await readNextChunk();
2750
2878
  if (typeof chunk !== 'string') {
2751
2879
  break;
2752
2880
  }
2881
+ if (chunk.startsWith('data:')) {
2882
+ remainder += chunk;
2883
+ while (true) {
2884
+ const separatorIndex = remainder.indexOf('\n\n');
2885
+ if (separatorIndex === -1) {
2886
+ break;
2887
+ }
2888
+ const rawEvent = remainder.slice(0, separatorIndex);
2889
+ remainder = remainder.slice(separatorIndex + 2);
2890
+ const dataLines = parseSseDataLines(rawEvent);
2891
+ await consumeSseDataLines(dataLines);
2892
+ }
2893
+ continue;
2894
+ }
2753
2895
  text += chunk;
2754
2896
  if (stream && onTextChunk) {
2755
2897
  await onTextChunk(chunk);
2756
2898
  }
2757
2899
  }
2900
+ if (remainder) {
2901
+ const dataLines = parseSseDataLines(remainder);
2902
+ await consumeSseDataLines(dataLines);
2903
+ }
2904
+ await notifyFinished();
2758
2905
  return {
2759
2906
  text,
2760
2907
  type: 'success'
@@ -3309,7 +3456,14 @@ const updateToolCallAccumulator = (accumulator, chunk) => {
3309
3456
  toolCalls
3310
3457
  };
3311
3458
  };
3312
- const parseOpenApiStream = async (response, onTextChunk, onToolCallsChunk, onDataEvent, onEventStreamFinished) => {
3459
+ const getResponseFunctionCallsFromStreamingAccumulator = toolCallAccumulator => {
3460
+ return Object.entries(toolCallAccumulator).toSorted((a, b) => Number(a[0]) - Number(b[0])).map(entry => entry[1]).filter(toolCall => typeof toolCall.id === 'string' && !!toolCall.id && !!toolCall.name).map(toolCall => ({
3461
+ arguments: toolCall.arguments,
3462
+ callId: toolCall.id,
3463
+ name: toolCall.name
3464
+ }));
3465
+ };
3466
+ const parseOpenApiStream = async (response, onTextChunk, onToolCallsChunk, onDataEvent) => {
3313
3467
  if (!response.body) {
3314
3468
  return {
3315
3469
  details: 'request-failed',
@@ -3321,17 +3475,9 @@ const parseOpenApiStream = async (response, onTextChunk, onToolCallsChunk, onDat
3321
3475
  let remainder = '';
3322
3476
  let text = '';
3323
3477
  let done = false;
3324
- let finishedNotified = false;
3325
3478
  let toolCallAccumulator = {};
3326
- const notifyFinished = async () => {
3327
- if (finishedNotified) {
3328
- return;
3329
- }
3330
- finishedNotified = true;
3331
- if (onEventStreamFinished) {
3332
- await onEventStreamFinished();
3333
- }
3334
- };
3479
+ let responseId;
3480
+ let completedResponseFunctionCalls = [];
3335
3481
  const emitToolCallAccumulator = async () => {
3336
3482
  if (!onToolCallsChunk) {
3337
3483
  return;
@@ -3351,7 +3497,25 @@ const parseOpenApiStream = async (response, onTextChunk, onToolCallsChunk, onDat
3351
3497
  }
3352
3498
  const eventType = Reflect.get(parsed, 'type');
3353
3499
  if (eventType === 'response.completed') {
3354
- await notifyFinished();
3500
+ const response = Reflect.get(parsed, 'response');
3501
+ if (response && typeof response === 'object') {
3502
+ const parsedResponseId = Reflect.get(response, 'id');
3503
+ if (typeof parsedResponseId === 'string' && parsedResponseId) {
3504
+ responseId = parsedResponseId;
3505
+ }
3506
+ completedResponseFunctionCalls = getResponseFunctionCalls(response);
3507
+ }
3508
+ return;
3509
+ }
3510
+ if (eventType === 'response.created' || eventType === 'response.in_progress') {
3511
+ const response = Reflect.get(parsed, 'response');
3512
+ if (!response || typeof response !== 'object') {
3513
+ return;
3514
+ }
3515
+ const parsedResponseId = Reflect.get(response, 'id');
3516
+ if (typeof parsedResponseId === 'string' && parsedResponseId) {
3517
+ responseId = parsedResponseId;
3518
+ }
3355
3519
  return;
3356
3520
  }
3357
3521
  if (eventType === 'response.output_text.delta') {
@@ -3392,6 +3556,38 @@ const parseOpenApiStream = async (response, onTextChunk, onToolCallsChunk, onDat
3392
3556
  await emitToolCallAccumulator();
3393
3557
  return;
3394
3558
  }
3559
+ if (eventType === 'response.output_item.done') {
3560
+ const outputIndex = Reflect.get(parsed, 'output_index');
3561
+ const item = Reflect.get(parsed, 'item');
3562
+ if (typeof outputIndex !== 'number' || !item || typeof item !== 'object') {
3563
+ return;
3564
+ }
3565
+ const itemType = Reflect.get(item, 'type');
3566
+ if (itemType !== 'function_call') {
3567
+ return;
3568
+ }
3569
+ const callId = Reflect.get(item, 'call_id');
3570
+ const name = Reflect.get(item, 'name');
3571
+ const rawArguments = Reflect.get(item, 'arguments');
3572
+ const current = toolCallAccumulator[outputIndex] || {
3573
+ arguments: '',
3574
+ name: ''
3575
+ };
3576
+ toolCallAccumulator = {
3577
+ ...toolCallAccumulator,
3578
+ [outputIndex]: {
3579
+ arguments: typeof rawArguments === 'string' ? rawArguments : current.arguments,
3580
+ ...(typeof callId === 'string' ? {
3581
+ id: callId
3582
+ } : current.id ? {
3583
+ id: current.id
3584
+ } : {}),
3585
+ name: typeof name === 'string' && name ? name : current.name
3586
+ }
3587
+ };
3588
+ await emitToolCallAccumulator();
3589
+ return;
3590
+ }
3395
3591
  if (eventType === 'response.function_call_arguments.delta' || eventType === 'response.function_call_arguments.done') {
3396
3592
  const outputIndex = Reflect.get(parsed, 'output_index');
3397
3593
  if (typeof outputIndex !== 'number') {
@@ -3476,7 +3672,6 @@ const parseOpenApiStream = async (response, onTextChunk, onToolCallsChunk, onDat
3476
3672
  }
3477
3673
  for (const line of dataLines) {
3478
3674
  if (line === '[DONE]') {
3479
- await notifyFinished();
3480
3675
  done = true;
3481
3676
  break;
3482
3677
  }
@@ -3494,7 +3689,6 @@ const parseOpenApiStream = async (response, onTextChunk, onToolCallsChunk, onDat
3494
3689
  const dataLines = parseSseEvent(remainder);
3495
3690
  for (const line of dataLines) {
3496
3691
  if (line === '[DONE]') {
3497
- await notifyFinished();
3498
3692
  continue;
3499
3693
  }
3500
3694
  let parsed;
@@ -3506,8 +3700,12 @@ const parseOpenApiStream = async (response, onTextChunk, onToolCallsChunk, onDat
3506
3700
  await handleParsedStreamEvent(parsed);
3507
3701
  }
3508
3702
  }
3509
- await notifyFinished();
3703
+ const responseFunctionCalls = completedResponseFunctionCalls.length > 0 ? completedResponseFunctionCalls : getResponseFunctionCallsFromStreamingAccumulator(toolCallAccumulator);
3510
3704
  return {
3705
+ ...(responseId ? {
3706
+ responseId
3707
+ } : {}),
3708
+ responseFunctionCalls,
3511
3709
  text,
3512
3710
  type: 'success'
3513
3711
  };
@@ -3599,7 +3797,20 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
3599
3797
  };
3600
3798
  }
3601
3799
  if (stream) {
3602
- return parseOpenApiStream(response, onTextChunk, onToolCallsChunk, onDataEvent, onEventStreamFinished);
3800
+ const streamResult = await parseOpenApiStream(response, onTextChunk, onToolCallsChunk, onDataEvent);
3801
+ if (streamResult.type !== 'success') {
3802
+ return streamResult;
3803
+ }
3804
+ if (streamResult.responseId) {
3805
+ previousResponseId = streamResult.responseId;
3806
+ }
3807
+ if (onEventStreamFinished) {
3808
+ await onEventStreamFinished();
3809
+ }
3810
+ return {
3811
+ text: streamResult.text,
3812
+ type: 'success'
3813
+ };
3603
3814
  }
3604
3815
  let parsed;
3605
3816
  try {
@@ -4079,7 +4290,7 @@ const getAiResponse = async ({
4079
4290
  const usesOpenRouterModel = isOpenRouterModel(selectedModelId, models);
4080
4291
  if (usesOpenApiModel) {
4081
4292
  if (useMockApi) {
4082
- const result = await getMockOpenApiAssistantText(streamingEnabled, onTextChunk);
4293
+ const result = await getMockOpenApiAssistantText(streamingEnabled, onTextChunk, onToolCallsChunk, onDataEvent, onEventStreamFinished);
4083
4294
  if (result.type === 'success') {
4084
4295
  const {
4085
4296
  text: assistantText
@@ -4430,10 +4641,53 @@ const appendMessageToSelectedSession = (sessions, selectedSessionId, message) =>
4430
4641
  };
4431
4642
  });
4432
4643
  };
4644
+ const hasLegacyStreamingToolCalls = parsed => {
4645
+ if (!parsed || typeof parsed !== 'object') {
4646
+ return false;
4647
+ }
4648
+ const choices = Reflect.get(parsed, 'choices');
4649
+ if (!Array.isArray(choices) || choices.length === 0) {
4650
+ return false;
4651
+ }
4652
+ const firstChoice = choices[0];
4653
+ if (!firstChoice || typeof firstChoice !== 'object') {
4654
+ return false;
4655
+ }
4656
+ const delta = Reflect.get(firstChoice, 'delta');
4657
+ if (!delta || typeof delta !== 'object') {
4658
+ return false;
4659
+ }
4660
+ const toolCalls = Reflect.get(delta, 'tool_calls');
4661
+ return Array.isArray(toolCalls) && toolCalls.length > 0;
4662
+ };
4663
+ const isStreamingFunctionCallEvent = parsed => {
4664
+ if (hasLegacyStreamingToolCalls(parsed)) {
4665
+ return true;
4666
+ }
4667
+ if (!parsed || typeof parsed !== 'object') {
4668
+ return false;
4669
+ }
4670
+ const type = Reflect.get(parsed, 'type');
4671
+ if (type === 'response.function_call_arguments.delta' || type === 'response.function_call_arguments.done') {
4672
+ return true;
4673
+ }
4674
+ if (type !== 'response.output_item.added' && type !== 'response.output_item.done') {
4675
+ return false;
4676
+ }
4677
+ const item = Reflect.get(parsed, 'item');
4678
+ if (!item || typeof item !== 'object') {
4679
+ return false;
4680
+ }
4681
+ return Reflect.get(item, 'type') === 'function_call';
4682
+ };
4683
+ const getSseEventType = value => {
4684
+ return value && typeof value === 'object' && Reflect.get(value, 'type') === 'response.completed' ? 'sse-response-completed' : 'sse-response-part';
4685
+ };
4433
4686
  const handleSubmit = async state => {
4434
4687
  const {
4435
4688
  assetDir,
4436
4689
  composerValue,
4690
+ emitStreamingFunctionCallEvents,
4437
4691
  mockAiResponseDelay,
4438
4692
  mockApiCommandId,
4439
4693
  models,
@@ -4559,10 +4813,14 @@ const handleSubmit = async state => {
4559
4813
  mockApiCommandId,
4560
4814
  models,
4561
4815
  onDataEvent: async value => {
4816
+ if (!emitStreamingFunctionCallEvents && isStreamingFunctionCallEvent(value)) {
4817
+ return;
4818
+ }
4819
+ const sseEventType = getSseEventType(value);
4562
4820
  await appendChatViewEvent({
4563
4821
  sessionId: optimisticState.selectedSessionId,
4564
4822
  timestamp: new Date().toISOString(),
4565
- type: 'sse-response-part',
4823
+ type: sseEventType,
4566
4824
  value
4567
4825
  });
4568
4826
  },
@@ -4770,6 +5028,13 @@ const handleClickNew = async state => {
4770
5028
  return createSession(state);
4771
5029
  };
4772
5030
 
5031
+ const handleClickReadFile = async uri => {
5032
+ if (!uri) {
5033
+ return;
5034
+ }
5035
+ await invoke('Main.openUri', uri);
5036
+ };
5037
+
4773
5038
  const handleClickSessionDebug = async state => {
4774
5039
  await invoke('Main.openUri', `chat-debug://${state.selectedSessionId}`);
4775
5040
  return state;
@@ -5040,6 +5305,15 @@ const getSavedViewMode = savedState => {
5040
5305
  return viewMode;
5041
5306
  };
5042
5307
 
5308
+ const loadEmitStreamingFunctionCallEvents = async () => {
5309
+ try {
5310
+ const savedEmitStreamingFunctionCallEvents = await get('chatView.emitStreamingFunctionCallEvents');
5311
+ return typeof savedEmitStreamingFunctionCallEvents === 'boolean' ? savedEmitStreamingFunctionCallEvents : false;
5312
+ } catch {
5313
+ return false;
5314
+ }
5315
+ };
5316
+
5043
5317
  const loadOpenApiApiKey = async () => {
5044
5318
  try {
5045
5319
  const savedOpenApiKey = await get('secrets.openApiKey');
@@ -5087,9 +5361,11 @@ const loadStreamingEnabled = async () => {
5087
5361
  const loadPreferences = async () => {
5088
5362
  const openApiApiKey = await loadOpenApiApiKey();
5089
5363
  const openRouterApiKey = await loadOpenRouterApiKey();
5364
+ const emitStreamingFunctionCallEvents = await loadEmitStreamingFunctionCallEvents();
5090
5365
  const streamingEnabled = await loadStreamingEnabled();
5091
5366
  const passIncludeObfuscation = await loadPassIncludeObfuscation();
5092
5367
  return {
5368
+ emitStreamingFunctionCallEvents,
5093
5369
  openApiApiKey,
5094
5370
  openRouterApiKey,
5095
5371
  passIncludeObfuscation,
@@ -5124,6 +5400,7 @@ const loadContent = async (state, savedState) => {
5124
5400
  const savedSelectedModelId = getSavedSelectedModelId(savedState);
5125
5401
  const savedViewMode = getSavedViewMode(savedState);
5126
5402
  const {
5403
+ emitStreamingFunctionCallEvents,
5127
5404
  openApiApiKey,
5128
5405
  openRouterApiKey,
5129
5406
  passIncludeObfuscation,
@@ -5156,6 +5433,7 @@ const loadContent = async (state, savedState) => {
5156
5433
  return {
5157
5434
  ...state,
5158
5435
  chatListScrollTop,
5436
+ emitStreamingFunctionCallEvents,
5159
5437
  initial: false,
5160
5438
  messagesScrollTop,
5161
5439
  openApiApiKey,
@@ -5287,6 +5565,7 @@ const ChatHeader = 'ChatHeader';
5287
5565
  const Button = 'Button';
5288
5566
  const ButtonPrimary = 'ButtonPrimary';
5289
5567
  const ButtonSecondary = 'ButtonSecondary';
5568
+ const FileIcon = 'FileIcon';
5290
5569
  const IconButton = 'IconButton';
5291
5570
  const InputBox = 'InputBox';
5292
5571
  const Label = 'Label';
@@ -5327,6 +5606,7 @@ const HandleModelChange = 20;
5327
5606
  const HandleChatListScroll = 21;
5328
5607
  const HandleMessagesScroll = 22;
5329
5608
  const HandleClickSessionDebug = 23;
5609
+ const HandleClickReadFile = 24;
5330
5610
 
5331
5611
  const getModelLabel = model => {
5332
5612
  if (model.provider === 'openRouter') {
@@ -5641,6 +5921,94 @@ const getToolCallArgumentPreview = rawArguments => {
5641
5921
  return rawArguments;
5642
5922
  };
5643
5923
 
5924
+ const getFileNameFromUri = uri => {
5925
+ const stripped = uri.replace(/[?#].*$/, '').replace(/\/$/, '');
5926
+ const slashIndex = Math.max(stripped.lastIndexOf('/'), stripped.lastIndexOf('\\\\'));
5927
+ const fileName = slashIndex === -1 ? stripped : stripped.slice(slashIndex + 1);
5928
+ return fileName || uri;
5929
+ };
5930
+
5931
+ const getReadFileTarget = rawArguments => {
5932
+ let parsed;
5933
+ try {
5934
+ parsed = JSON.parse(rawArguments);
5935
+ } catch {
5936
+ return undefined;
5937
+ }
5938
+ if (!parsed || typeof parsed !== 'object') {
5939
+ return undefined;
5940
+ }
5941
+ const uri = Reflect.get(parsed, 'uri');
5942
+ const path = Reflect.get(parsed, 'path');
5943
+ const uriValue = typeof uri === 'string' ? uri : '';
5944
+ const pathValue = typeof path === 'string' ? path : '';
5945
+ const title = uriValue || pathValue;
5946
+ if (!title) {
5947
+ return undefined;
5948
+ }
5949
+ const clickableUri = uriValue || (pathValue.startsWith('file://') ? pathValue : '');
5950
+ return {
5951
+ clickableUri,
5952
+ title
5953
+ };
5954
+ };
5955
+
5956
+ const getToolCallReadFileVirtualDom = toolCall => {
5957
+ const target = getReadFileTarget(toolCall.arguments);
5958
+ if (!target) {
5959
+ return [];
5960
+ }
5961
+ const fileName = getFileNameFromUri(target.title);
5962
+ const clickableProps = target.clickableUri ? {
5963
+ 'data-uri': target.clickableUri,
5964
+ onClick: HandleClickReadFile
5965
+ } : {};
5966
+ return [{
5967
+ childCount: 2,
5968
+ className: ChatOrderedListItem,
5969
+ ...clickableProps,
5970
+ title: target.title,
5971
+ type: Li
5972
+ }, {
5973
+ childCount: 0,
5974
+ className: FileIcon,
5975
+ ...clickableProps,
5976
+ type: Div
5977
+ }, {
5978
+ childCount: 1,
5979
+ ...clickableProps,
5980
+ style: 'color: var(--vscode-textLink-foreground); text-decoration: underline;',
5981
+ type: Span
5982
+ }, text(fileName)];
5983
+ };
5984
+
5985
+ const getToolCallDom = toolCall => {
5986
+ if (toolCall.name === 'read_file') {
5987
+ const virtualDom = getToolCallReadFileVirtualDom(toolCall);
5988
+ if (virtualDom.length > 0) {
5989
+ return virtualDom;
5990
+ }
5991
+ }
5992
+ const argumentPreview = getToolCallArgumentPreview(toolCall.arguments);
5993
+ const label = `${toolCall.name} ${argumentPreview}`;
5994
+ return [{
5995
+ childCount: 1,
5996
+ className: ChatOrderedListItem,
5997
+ type: Li
5998
+ }, text(label)];
5999
+ };
6000
+
6001
+ const getToolCallsDom = message => {
6002
+ if (message.role !== 'assistant' || !message.toolCalls || message.toolCalls.length === 0) {
6003
+ return [];
6004
+ }
6005
+ return [{
6006
+ childCount: message.toolCalls.length,
6007
+ className: ChatOrderedList,
6008
+ type: Ol$1
6009
+ }, ...message.toolCalls.flatMap(getToolCallDom)];
6010
+ };
6011
+
5644
6012
  const orderedListItemRegex = /^\s*\d+\.\s+(.*)$/;
5645
6013
  const parseMessageContent = rawMessage => {
5646
6014
  if (rawMessage === '') {
@@ -5721,20 +6089,6 @@ const getMessageContentDom = nodes => {
5721
6089
  });
5722
6090
  };
5723
6091
 
5724
- const getToolCallsDom = message => {
5725
- if (message.role !== 'assistant' || !message.toolCalls || message.toolCalls.length === 0) {
5726
- return [];
5727
- }
5728
- return message.toolCalls.flatMap(toolCall => {
5729
- const argumentPreview = getToolCallArgumentPreview(toolCall.arguments);
5730
- const label = `${toolCall.name} ${argumentPreview}`;
5731
- return [{
5732
- childCount: 1,
5733
- className: Markdown,
5734
- type: P
5735
- }, text(label)];
5736
- });
5737
- };
5738
6092
  const getChatMessageDom = (message, openRouterApiKeyInput, openApiApiKeyInput = '', openRouterApiKeyState = 'idle') => {
5739
6093
  const roleClassName = message.role === 'user' ? MessageUser : MessageAssistant;
5740
6094
  const isOpenApiApiKeyMissingMessage = message.role === 'assistant' && message.text === openApiApiKeyRequiredMessage;
@@ -5744,7 +6098,8 @@ const getChatMessageDom = (message, openRouterApiKeyInput, openApiApiKeyInput =
5744
6098
  const messageIntermediate = parseMessageContent(message.text);
5745
6099
  const messageDom = getMessageContentDom(messageIntermediate);
5746
6100
  const toolCallsDom = getToolCallsDom(message);
5747
- const extraChildCount = isOpenApiApiKeyMissingMessage || isOpenRouterApiKeyMissingMessage || isOpenRouterRequestFailedMessage || isOpenRouterTooManyRequestsMessage ? messageIntermediate.length + 1 + toolCallsDom.length : messageIntermediate.length + toolCallsDom.length;
6101
+ const toolCallsChildCount = toolCallsDom.length > 0 ? 1 : 0;
6102
+ const extraChildCount = isOpenApiApiKeyMissingMessage || isOpenRouterApiKeyMissingMessage || isOpenRouterRequestFailedMessage || isOpenRouterTooManyRequestsMessage ? messageIntermediate.length + 1 + toolCallsChildCount : messageIntermediate.length + toolCallsChildCount;
5748
6103
  return [{
5749
6104
  childCount: 1,
5750
6105
  className: mergeClassNames(Message, roleClassName),
@@ -5753,7 +6108,7 @@ const getChatMessageDom = (message, openRouterApiKeyInput, openApiApiKeyInput =
5753
6108
  childCount: extraChildCount,
5754
6109
  className: ChatMessageContent,
5755
6110
  type: Div
5756
- }, ...messageDom, ...toolCallsDom, ...(isOpenApiApiKeyMissingMessage ? getMissingOpenApiApiKeyDom(openApiApiKeyInput) : []), ...(isOpenRouterApiKeyMissingMessage ? getMissingOpenRouterApiKeyDom(openRouterApiKeyInput, openRouterApiKeyState) : []), ...(isOpenRouterRequestFailedMessage ? getOpenRouterRequestFailedDom() : []), ...(isOpenRouterTooManyRequestsMessage ? getOpenRouterTooManyRequestsDom() : [])];
6111
+ }, ...toolCallsDom, ...messageDom, ...(isOpenApiApiKeyMissingMessage ? getMissingOpenApiApiKeyDom(openApiApiKeyInput) : []), ...(isOpenRouterApiKeyMissingMessage ? getMissingOpenRouterApiKeyDom(openRouterApiKeyInput, openRouterApiKeyState) : []), ...(isOpenRouterRequestFailedMessage ? getOpenRouterRequestFailedDom() : []), ...(isOpenRouterTooManyRequestsMessage ? getOpenRouterTooManyRequestsDom() : [])];
5757
6112
  };
5758
6113
 
5759
6114
  const getEmptyMessagesDom = () => {
@@ -5985,6 +6340,9 @@ const renderEventListeners = () => {
5985
6340
  }, {
5986
6341
  name: HandleClick,
5987
6342
  params: ['handleClick', TargetName, 'event.target.dataset.id']
6343
+ }, {
6344
+ name: HandleClickReadFile,
6345
+ params: ['handleClickReadFile', 'event.target.dataset.uri']
5988
6346
  }, {
5989
6347
  name: HandleClickDelete,
5990
6348
  params: ['handleClickDelete', 'event.target.dataset.id']
@@ -6110,6 +6468,13 @@ const setChatList = state => {
6110
6468
  };
6111
6469
  };
6112
6470
 
6471
+ const setEmitStreamingFunctionCallEvents = (state, emitStreamingFunctionCallEvents) => {
6472
+ return {
6473
+ ...state,
6474
+ emitStreamingFunctionCallEvents
6475
+ };
6476
+ };
6477
+
6113
6478
  const setStreamingEnabled = (state, streamingEnabled) => {
6114
6479
  return {
6115
6480
  ...state,
@@ -6148,6 +6513,7 @@ const commandMap = {
6148
6513
  'Chat.handleClickDelete': wrapCommand(handleClickDelete),
6149
6514
  'Chat.handleClickList': wrapCommand(handleClickList),
6150
6515
  'Chat.handleClickNew': wrapCommand(handleClickNew),
6516
+ 'Chat.handleClickReadFile': handleClickReadFile,
6151
6517
  'Chat.handleClickSessionDebug': wrapCommand(handleClickSessionDebug),
6152
6518
  'Chat.handleClickSettings': handleClickSettings,
6153
6519
  'Chat.handleInput': wrapCommand(handleInput),
@@ -6171,6 +6537,7 @@ const commandMap = {
6171
6537
  'Chat.resize': wrapCommand(resize),
6172
6538
  'Chat.saveState': wrapGetter(saveState),
6173
6539
  'Chat.setChatList': wrapCommand(setChatList),
6540
+ 'Chat.setEmitStreamingFunctionCallEvents': wrapCommand(setEmitStreamingFunctionCallEvents),
6174
6541
  'Chat.setOpenRouterApiKey': wrapCommand(setOpenRouterApiKey),
6175
6542
  'Chat.setStreamingEnabled': wrapCommand(setStreamingEnabled),
6176
6543
  'Chat.terminate': terminate,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/chat-view",
3
- "version": "2.5.0",
3
+ "version": "2.7.0",
4
4
  "description": "Chat View Worker",
5
5
  "repository": {
6
6
  "type": "git",