Forge
csharpdeeb25a2
1#nullable enable
2namespace CascadeIDE.Features.Chat;
3
4public partial class ChatPanelViewModel
5{
6 private readonly ChatSurfaceCompositor _chatSurfaceCompositor = new();
7 private int _lastOverviewThreadCount = -1;
8
9 private void RefreshChatSurfaceSnapshot()
10 {
11 NormalizeOrphanMessageThreadIds();
12 RebuildSedmScopeStrip();
13 var sedmTimeline = BuildSedmTimelineEntries();
14 ChatSurfaceSnapshot = _chatSurfaceCompositor.Compose(new ChatSurfaceIntent(
15 BuildConversationMessages(),
16 _activeClarificationBatch,
17 SelectedMessageIndex,
18 _mainThreadId,
19 _activeThreadId,
20 ThreadBranchHint,
21 BuildProductSpine(),
22 sedmTimeline,
23 BuildThreadDisplayTitles(),
24 _threadForks,
25 _topicPickerPresentation,
26 HighlightedMessageIndices.Count > 0 ? HighlightedMessageIndices : null))
27 with { SedmScopeStrip = _sedmScopeStrip };
28
29 var overview = ChatSurfaceSnapshot.Layout.Overview;
30 if (overview.Count == 0)
31 {
32 SelectedChatThreadId = Guid.Empty;
33 _lastOverviewThreadCount = 0;
34 return;
35 }
36
37 ApplyAdaptiveOverviewDefault(overview.Count);
38
39 if (SelectedChatThreadId == Guid.Empty || !overview.Any(x => x.ThreadId == SelectedChatThreadId))
40 {
41 var preferred = Guid.Empty;
42 foreach (var thread in overview)
43 {
44 if (thread.ThreadId != _activeThreadId)
45 continue;
46 preferred = thread.ThreadId;
47 break;
48 }
49
50 SelectedChatThreadId = preferred != Guid.Empty ? preferred : overview[0].ThreadId;
51 }
52 }
53
54 private void ApplyAdaptiveOverviewDefault(int threadCount) =>
55 ChatTopicOverviewPolicy.ApplyAdaptiveDefault(
56 threadCount,
57 ref _lastOverviewThreadCount,
58 value => IsChatOverviewMode = value,
59 () => IsChatOverviewMode);
60
61 /// <summary>Сообщения без thread_id (legacy AEE trace) — в активную/основную ветку, чтобы не появлялась «призрачная» вкладка 00000000….</summary>
62 private void NormalizeOrphanMessageThreadIds()
63 {
64 var target = ResolveMessageThreadId();
65 if (target == Guid.Empty)
66 return;
67
68 foreach (var message in ChatMessages)
69 {
70 if (message.ThreadId == Guid.Empty)
71 message.AssignThread(target);
72 }
73 }
74
75 private IReadOnlyList<ChatConversationMessage> BuildConversationMessages() =>
76 ChatMessages
77 .Select((message, index) => new ChatConversationMessage(
78 message.MessageId,
79 message.Role,
80 message.Content,
81 message.ThreadId,
82 message.ParentMessageId,
83 index,
84 message.SlashCommandPath,
85 message.SlashCommandArgs,
86 message.SlashCommandStatus,
87 message.Attachments,
88 message.Audience))
89 .ToList();
90}
91
View only · write via MCP/CIDE