feat(ui): show queued interactive messages above the composer

A queued message now appears in a pending strip above the input box (driven by
InteractiveQueueChanged), not optimistically in the transcript. The transcript
user line is added on delivery via InteractiveMessageSent. SessionTerminalView
gains QueuedMessages/HasQueuedMessages styled props (Mission Control); WorkConsole
binds Monitor.* (task detail). Adds session.composer.queued (en/de).
This commit is contained in:
Mika Kuns
2026-06-26 16:11:53 +02:00
parent 84034e8395
commit 7c9ff18ced
8 changed files with 250 additions and 70 deletions
@@ -50,35 +50,67 @@
</Border>
</Grid>
<!-- ── Composer bar ── -->
<Border DockPanel.Dock="Bottom"
IsVisible="{Binding #Root.IsComposerVisible}"
Background="{DynamicResource Surface2Brush}"
BorderBrush="{DynamicResource LineBrush}"
BorderThickness="0,1,0,0"
Padding="6,5">
<Grid ColumnDefinitions="*,Auto,Auto">
<TextBox Grid.Column="0"
Text="{Binding #Root.ComposerText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
PlaceholderText="{Binding #Root.ComposerPlaceholder}"
AcceptsReturn="False">
<TextBox.KeyBindings>
<KeyBinding Gesture="Enter" Command="{Binding #Root.SubmitCommand}"/>
</TextBox.KeyBindings>
</TextBox>
<Button Grid.Column="1"
Margin="6,0,0,0"
Classes="title-ctrl"
Command="{Binding #Root.InterruptCommand}"
ToolTip.Tip="{loc:Tr session.composer.interrupt}">
<PathIcon Data="{StaticResource Icon.Stop}" Width="10" Height="10"/>
</Button>
<Button Grid.Column="2"
Margin="6,0,0,0"
Content="{loc:Tr session.composer.send}"
Command="{Binding #Root.SubmitCommand}"/>
</Grid>
</Border>
<!-- ── Queued strip + Composer bar ── -->
<StackPanel DockPanel.Dock="Bottom" Orientation="Vertical">
<!-- Queued messages strip -->
<Border IsVisible="{Binding #Root.HasQueuedMessages}"
Background="{DynamicResource Surface2Brush}"
BorderBrush="{DynamicResource LineBrush}"
BorderThickness="0,0,0,1"
Padding="8,4">
<StackPanel Spacing="2">
<TextBlock Classes="meta"
Text="{loc:Tr session.composer.queued}"
Foreground="{DynamicResource TextMuteBrush}" />
<ItemsControl ItemsSource="{Binding #Root.QueuedMessages}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Spacing="6">
<TextBlock Text=""
Foreground="{DynamicResource TextMuteBrush}"
FontFamily="{StaticResource MonoFont}"
FontSize="{StaticResource FontSizeMono}"
VerticalAlignment="Center" />
<TextBlock Text="{Binding}"
Foreground="{DynamicResource TextDimBrush}"
FontFamily="{StaticResource MonoFont}"
FontSize="{StaticResource FontSizeMono}"
TextTrimming="CharacterEllipsis" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Border>
<!-- Composer input row -->
<Border IsVisible="{Binding #Root.IsComposerVisible}"
Background="{DynamicResource Surface2Brush}"
BorderBrush="{DynamicResource LineBrush}"
BorderThickness="0,1,0,0"
Padding="6,5">
<Grid ColumnDefinitions="*,Auto,Auto">
<TextBox Grid.Column="0"
Text="{Binding #Root.ComposerText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
PlaceholderText="{Binding #Root.ComposerPlaceholder}"
AcceptsReturn="False">
<TextBox.KeyBindings>
<KeyBinding Gesture="Enter" Command="{Binding #Root.SubmitCommand}"/>
</TextBox.KeyBindings>
</TextBox>
<Button Grid.Column="1"
Margin="6,0,0,0"
Classes="title-ctrl"
Command="{Binding #Root.InterruptCommand}"
ToolTip.Tip="{loc:Tr session.composer.interrupt}">
<PathIcon Data="{StaticResource Icon.Stop}" Width="10" Height="10"/>
</Button>
<Button Grid.Column="2"
Margin="6,0,0,0"
Content="{loc:Tr session.composer.send}"
Command="{Binding #Root.SubmitCommand}"/>
</Grid>
</Border>
</StackPanel>
<!-- ── Log output ── -->
<ScrollViewer Name="LogScroll"