fix(ui): fix live output visibility and editor dialog graying out

- Remove wrapping ScrollViewer from live output TextBox — Avalonia
  TextBox with AcceptsReturn handles its own scrolling; nested
  ScrollViewer caused layout collapse
- Auto-scroll via CaretIndex instead of removed ScrollViewer
- Add OnWindowClosed to both editor ViewModels, ensuring the
  TaskCompletionSource always resolves when the dialog closes
  (including via X button), preventing RelayCommand from staying
  permanently disabled

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Mika Kuns
2026-04-14 17:01:08 +02:00
parent 7363e48496
commit 2a1f26d817
6 changed files with 32 additions and 18 deletions

View File

@@ -114,6 +114,11 @@ public partial class ListEditorViewModel : ViewModelBase
RequestClose?.Invoke(); RequestClose?.Invoke();
} }
public void OnWindowClosed()
{
_tcs.TrySetResult(null);
}
public Task<ListEntity?> ShowAndWaitAsync() public Task<ListEntity?> ShowAndWaitAsync()
{ {
_tcs = new TaskCompletionSource<ListEntity?>(); _tcs = new TaskCompletionSource<ListEntity?>();

View File

@@ -83,6 +83,7 @@ public partial class MainWindowViewModel : ViewModelBase
var window = new ListEditorView { DataContext = editor }; var window = new ListEditorView { DataContext = editor };
editor.RequestClose += () => window.Close(); editor.RequestClose += () => window.Close();
window.Closed += (_, _) => editor.OnWindowClosed();
_ = ShowDialogAsync(window); _ = ShowDialogAsync(window);
var entity = await editor.ShowAndWaitAsync(); var entity = await editor.ShowAndWaitAsync();
@@ -116,6 +117,7 @@ public partial class MainWindowViewModel : ViewModelBase
var window = new ListEditorView { DataContext = editor }; var window = new ListEditorView { DataContext = editor };
editor.RequestClose += () => window.Close(); editor.RequestClose += () => window.Close();
window.Closed += (_, _) => editor.OnWindowClosed();
_ = ShowDialogAsync(window); _ = ShowDialogAsync(window);
var entity = await editor.ShowAndWaitAsync(); var entity = await editor.ShowAndWaitAsync();

View File

@@ -113,6 +113,11 @@ public partial class TaskEditorViewModel : ViewModelBase
RequestClose?.Invoke(); RequestClose?.Invoke();
} }
public void OnWindowClosed()
{
_tcs.TrySetResult(null);
}
public Task<TaskEntity?> ShowAndWaitAsync() public Task<TaskEntity?> ShowAndWaitAsync()
{ {
_tcs = new TaskCompletionSource<TaskEntity?>(); _tcs = new TaskCompletionSource<TaskEntity?>();

View File

@@ -151,6 +151,7 @@ public partial class TaskListViewModel : ViewModelBase
var window = new TaskEditorView { DataContext = editor }; var window = new TaskEditorView { DataContext = editor };
editor.RequestClose += () => window.Close(); editor.RequestClose += () => window.Close();
window.Closed += (_, _) => editor.OnWindowClosed();
_ = ShowDialogAsync(window); _ = ShowDialogAsync(window);
var saved = await editor.ShowAndWaitAsync(); var saved = await editor.ShowAndWaitAsync();
@@ -197,6 +198,7 @@ public partial class TaskListViewModel : ViewModelBase
var window = new TaskEditorView { DataContext = editor }; var window = new TaskEditorView { DataContext = editor };
editor.RequestClose += () => window.Close(); editor.RequestClose += () => window.Close();
window.Closed += (_, _) => editor.OnWindowClosed();
_ = ShowDialogAsync(window); _ = ShowDialogAsync(window);
var saved = await editor.ShowAndWaitAsync(); var saved = await editor.ShowAndWaitAsync();

View File

@@ -106,22 +106,19 @@
<TextBlock Text="Live Output" FontWeight="SemiBold" FontSize="12" <TextBlock Text="Live Output" FontWeight="SemiBold" FontSize="12"
Foreground="{StaticResource TextSecondaryBrush}" Margin="0,8,0,2"/> Foreground="{StaticResource TextSecondaryBrush}" Margin="0,8,0,2"/>
<Border BorderBrush="{StaticResource BorderSubtleBrush}" BorderThickness="1" <TextBox x:Name="LiveOutputBox"
CornerRadius="6" Padding="6" MaxHeight="300"> Text="{Binding LiveText, Mode=OneWay}"
<ScrollViewer x:Name="LiveOutputScroll"> IsReadOnly="True"
<TextBox x:Name="LiveOutputBox" AcceptsReturn="True"
Text="{Binding LiveText, Mode=OneWay}" TextWrapping="NoWrap"
IsReadOnly="True" FontFamily="Consolas,Courier New,monospace"
AcceptsReturn="True" FontSize="11"
TextWrapping="NoWrap" MaxHeight="300"
FontFamily="Consolas,Courier New,monospace" Foreground="{StaticResource TextPrimaryBrush}"
FontSize="11" BorderBrush="{StaticResource BorderSubtleBrush}"
Foreground="{StaticResource TextPrimaryBrush}" BorderThickness="1"
Background="Transparent" CornerRadius="6"
BorderThickness="0" Padding="6"/>
Padding="0"/>
</ScrollViewer>
</Border>
<Border IsVisible="{Binding HasWorktree}" BorderBrush="{StaticResource AccentBrush}" <Border IsVisible="{Binding HasWorktree}" BorderBrush="{StaticResource AccentBrush}"
BorderThickness="1" CornerRadius="8" Padding="10" Margin="0,8,0,0"> BorderThickness="1" CornerRadius="8" Padding="10" Margin="0,8,0,0">

View File

@@ -49,8 +49,11 @@ public partial class TaskDetailView : UserControl
{ {
if (e.PropertyName == nameof(TaskDetailViewModel.LiveText)) if (e.PropertyName == nameof(TaskDetailViewModel.LiveText))
{ {
var scroll = this.FindControl<ScrollViewer>("LiveOutputScroll"); var box = this.FindControl<TextBox>("LiveOutputBox");
scroll?.ScrollToEnd(); if (box is not null)
{
box.CaretIndex = box.Text?.Length ?? 0;
}
} }
} }
} }