feat(ui): interactive chat composer in the session terminal + work console
SessionTerminalView gains an opt-in composer (IsComposerVisible / ComposerText / SubmitCommand / ComposerPlaceholder styled props); Mission Control binds it to the monitor VM. Task detail's WorkConsole output tab gets a matching shell-prompt composer bound through Monitor.*, shown only while an interactive session is live. log-user lines render in the accent color. Adds session.composer.* (en/de).
This commit is contained in:
@@ -231,7 +231,12 @@
|
||||
"chipDone": "FERTIG",
|
||||
"chipFailed": "FEHLGESCHLAGEN",
|
||||
"reviewContinueTip": "Dieses Feedback senden und die Aufgabe erneut ausführen",
|
||||
"reviewResetTip": "Alle Änderungen verwerfen und die Aufgabe auf Leerlauf zurücksetzen"
|
||||
"reviewResetTip": "Alle Änderungen verwerfen und die Aufgabe auf Leerlauf zurücksetzen",
|
||||
"composer": {
|
||||
"placeholder": "Nachricht an die Sitzung…",
|
||||
"send": "Senden",
|
||||
"stop": "Sitzung beenden"
|
||||
}
|
||||
},
|
||||
"missionControl": {
|
||||
"openInApp": "In App öffnen",
|
||||
|
||||
@@ -231,7 +231,12 @@
|
||||
"chipDone": "DONE",
|
||||
"chipFailed": "FAILED",
|
||||
"reviewContinueTip": "Send this feedback and re-run the task",
|
||||
"reviewResetTip": "Discard all changes and reset the task to Idle"
|
||||
"reviewResetTip": "Discard all changes and reset the task to Idle",
|
||||
"composer": {
|
||||
"placeholder": "Message the session…",
|
||||
"send": "Send",
|
||||
"stop": "Stop session"
|
||||
}
|
||||
},
|
||||
"missionControl": {
|
||||
"openInApp": "Open in app",
|
||||
|
||||
@@ -528,6 +528,10 @@
|
||||
<Style Selector="Border.terminal TextBlock[Tag=log-msg]">
|
||||
<Setter Property="Foreground" Value="{StaticResource TextDimBrush}" />
|
||||
</Style>
|
||||
<!-- log-user: user's own messages in interactive sessions — accent color to stand out -->
|
||||
<Style Selector="Border.terminal TextBlock[Tag=log-user]">
|
||||
<Setter Property="Foreground" Value="{DynamicResource AccentBrush}" />
|
||||
</Style>
|
||||
|
||||
<!-- ============================================================ -->
|
||||
<!-- TERMINAL HEADER -->
|
||||
|
||||
@@ -263,6 +263,36 @@
|
||||
Command="{Binding RejectReviewCommand}" />
|
||||
</Grid>
|
||||
|
||||
<!-- Interactive composer — chat with a live in-app session; shell-prompt style,
|
||||
only while an interactive session is running for this task. -->
|
||||
<Grid DockPanel.Dock="Bottom"
|
||||
IsVisible="{Binding Monitor.IsInteractiveLive}"
|
||||
ColumnDefinitions="Auto,*,Auto"
|
||||
Margin="12,2,12,8">
|
||||
<TextBlock Grid.Column="0" Text="❯"
|
||||
FontFamily="{StaticResource MonoFont}"
|
||||
FontSize="{StaticResource FontSizeMono}"
|
||||
Foreground="{DynamicResource AccentBrush}"
|
||||
VerticalAlignment="Center" Margin="0,0,8,0" />
|
||||
<TextBox Grid.Column="1"
|
||||
Classes="review-prompt"
|
||||
Text="{Binding Monitor.ComposerDraft, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
AcceptsReturn="False"
|
||||
TextWrapping="Wrap"
|
||||
MaxHeight="160"
|
||||
PlaceholderText="{loc:Tr session.composer.placeholder}"
|
||||
VerticalContentAlignment="Center"
|
||||
FontFamily="{StaticResource MonoFont}"
|
||||
FontSize="{StaticResource FontSizeMono}">
|
||||
<TextBox.KeyBindings>
|
||||
<KeyBinding Gesture="Enter" Command="{Binding Monitor.SubmitComposerCommand}" />
|
||||
</TextBox.KeyBindings>
|
||||
</TextBox>
|
||||
<Button Grid.Column="2" Classes="prompt-action accent" Content="[Send]"
|
||||
VerticalAlignment="Center" Margin="12,0,0,0"
|
||||
Command="{Binding Monitor.SubmitComposerCommand}" />
|
||||
</Grid>
|
||||
|
||||
<ScrollViewer Name="LogScroll"
|
||||
VerticalScrollBarVisibility="Visible"
|
||||
AllowAutoHide="False"
|
||||
|
||||
@@ -50,6 +50,29 @@
|
||||
</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">
|
||||
<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"
|
||||
Content="{loc:Tr session.composer.send}"
|
||||
Command="{Binding #Root.SubmitCommand}"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- ── Log output ── -->
|
||||
<ScrollViewer Name="LogScroll"
|
||||
VerticalScrollBarVisibility="Visible"
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
using System.Windows.Input;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Data;
|
||||
|
||||
namespace ClaudeDo.Ui.Views.Islands;
|
||||
|
||||
@@ -17,12 +19,24 @@ public partial class SessionTerminalView : UserControl
|
||||
AvaloniaProperty.Register<SessionTerminalView, bool>(nameof(IsDone));
|
||||
public static readonly StyledProperty<bool> IsFailedProperty =
|
||||
AvaloniaProperty.Register<SessionTerminalView, bool>(nameof(IsFailed));
|
||||
public static readonly StyledProperty<bool> IsComposerVisibleProperty =
|
||||
AvaloniaProperty.Register<SessionTerminalView, bool>(nameof(IsComposerVisible), defaultValue: false);
|
||||
public static readonly StyledProperty<string?> ComposerTextProperty =
|
||||
AvaloniaProperty.Register<SessionTerminalView, string?>(nameof(ComposerText), defaultBindingMode: BindingMode.TwoWay);
|
||||
public static readonly StyledProperty<ICommand?> SubmitCommandProperty =
|
||||
AvaloniaProperty.Register<SessionTerminalView, ICommand?>(nameof(SubmitCommand));
|
||||
public static readonly StyledProperty<string?> ComposerPlaceholderProperty =
|
||||
AvaloniaProperty.Register<SessionTerminalView, string?>(nameof(ComposerPlaceholder));
|
||||
|
||||
public IEnumerable? Entries { get => GetValue(EntriesProperty); set => SetValue(EntriesProperty, value); }
|
||||
public string? Label { get => GetValue(LabelProperty); set => SetValue(LabelProperty, value); }
|
||||
public bool IsRunning { get => GetValue(IsRunningProperty); set => SetValue(IsRunningProperty, value); }
|
||||
public bool IsDone { get => GetValue(IsDoneProperty); set => SetValue(IsDoneProperty, value); }
|
||||
public bool IsFailed { get => GetValue(IsFailedProperty); set => SetValue(IsFailedProperty, value); }
|
||||
public bool IsComposerVisible { get => GetValue(IsComposerVisibleProperty); set => SetValue(IsComposerVisibleProperty, value); }
|
||||
public string? ComposerText { get => GetValue(ComposerTextProperty); set => SetValue(ComposerTextProperty, value); }
|
||||
public ICommand? SubmitCommand { get => GetValue(SubmitCommandProperty); set => SetValue(SubmitCommandProperty, value); }
|
||||
public string? ComposerPlaceholder { get => GetValue(ComposerPlaceholderProperty); set => SetValue(ComposerPlaceholderProperty, value); }
|
||||
|
||||
private INotifyCollectionChanged? _subscribedCollection;
|
||||
|
||||
|
||||
@@ -107,7 +107,11 @@
|
||||
Label="{Binding DisplayTitle}"
|
||||
IsRunning="{Binding IsRunning}"
|
||||
IsDone="{Binding IsDone}"
|
||||
IsFailed="{Binding IsFailed}" />
|
||||
IsFailed="{Binding IsFailed}"
|
||||
IsComposerVisible="{Binding IsInteractiveLive}"
|
||||
ComposerText="{Binding ComposerDraft, Mode=TwoWay}"
|
||||
SubmitCommand="{Binding SubmitComposerCommand}"
|
||||
ComposerPlaceholder="{loc:Tr session.composer.placeholder}" />
|
||||
|
||||
</DockPanel>
|
||||
</Border>
|
||||
|
||||
Reference in New Issue
Block a user