From e98cf460970395931bd4d2ecb0fa076ba57e8f1a Mon Sep 17 00:00:00 2001 From: mika kuns Date: Mon, 10 Aug 2026 13:53:36 +0200 Subject: [PATCH] perf(ui): build task row context menu lazily on right-click --- .../Views/Islands/TaskRowView.axaml | 54 +------------------ .../Views/Islands/TaskRowView.axaml.cs | 53 ++++++++++++++++++ 2 files changed, 55 insertions(+), 52 deletions(-) diff --git a/src/ClaudeDo.Ui/Views/Islands/TaskRowView.axaml b/src/ClaudeDo.Ui/Views/Islands/TaskRowView.axaml index 24cb5ae8..dd3f4ee9 100644 --- a/src/ClaudeDo.Ui/Views/Islands/TaskRowView.axaml +++ b/src/ClaudeDo.Ui/Views/Islands/TaskRowView.axaml @@ -31,58 +31,8 @@ Margin="0" Classes.selected="{Binding IsSelected}" Classes.dragging="{Binding IsDragging}" - Classes.done="{Binding Done}"> - - - - - - - - - - - - - - - - - - - - - - - - - - + Classes.done="{Binding Done}" + ContextRequested="OnRowContextRequested"> diff --git a/src/ClaudeDo.Ui/Views/Islands/TaskRowView.axaml.cs b/src/ClaudeDo.Ui/Views/Islands/TaskRowView.axaml.cs index 8fc3e09a..e7f2524c 100644 --- a/src/ClaudeDo.Ui/Views/Islands/TaskRowView.axaml.cs +++ b/src/ClaudeDo.Ui/Views/Islands/TaskRowView.axaml.cs @@ -1,9 +1,11 @@ using System.Linq; using Avalonia.Controls; using Avalonia.Controls.Primitives; +using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.VisualTree; using ClaudeDo.Data.Models; +using ClaudeDo.Ui.Localization; using ClaudeDo.Ui.ViewModels.Islands; using TaskStatus = ClaudeDo.Data.Models.TaskStatus; @@ -19,6 +21,57 @@ public partial class TaskRowView : UserControl this.GetVisualAncestors().OfType() .Select(ic => ic.DataContext).OfType().FirstOrDefault(); + // The context menu is built here instead of declared in XAML so the ~18 MenuItems only + // exist while a row's menu is actually open, not for every row rendered in the list. + private void OnRowContextRequested(object? sender, ContextRequestedEventArgs e) + { + if (sender is not Border border) return; + if (DataContext is not TaskRowViewModel row) return; + if (FindTasksVm() is not { } vm) return; + + // OnTunnelPointerPressed (TasksIslandView) only selects on the left button, so + // right-click needs its own explicit selection before the menu opens. + vm.SelectedTask = row; + + var menu = new ContextMenu { DataContext = row }; + + MenuItem MakeItem(string key, EventHandler click, bool isVisible = true, string? tag = null) + { + var item = new MenuItem { Header = Loc.T(key), DataContext = row, IsVisible = isVisible }; + if (tag is not null) item.Tag = tag; + item.Click += click; + return item; + } + + menu.Items.Add(MakeItem("tasks.ctxSendToQueue", OnSendToQueueClick, row.CanSendToQueue)); + menu.Items.Add(MakeItem("tasks.ctxRemoveFromQueue", OnRemoveFromQueueClick, row.CanRemoveFromQueue)); + menu.Items.Add(MakeItem("tasks.ctxCancelExecution", OnCancelExecutionClick, row.IsRunning)); + menu.Items.Add(new Separator()); + + var markAs = new MenuItem { Header = Loc.T("tasks.ctxMarkAs"), DataContext = row }; + markAs.Items.Add(MakeItem("tasks.ctxMarkDone", OnSetStatusClick, tag: "Done")); + markAs.Items.Add(MakeItem("tasks.ctxMarkCancelled", OnSetStatusClick, tag: "Cancelled")); + markAs.Items.Add(new Separator()); + markAs.Items.Add(MakeItem("tasks.ctxMarkManual", OnToggleManualClick, !row.IsManual)); + markAs.Items.Add(MakeItem("tasks.ctxMarkClaudeTask", OnToggleManualClick, row.IsManual)); + menu.Items.Add(markAs); + + menu.Items.Add(new Separator()); + menu.Items.Add(MakeItem("tasks.ctxOpenConPtySession", OnOpenConPtySessionClick)); + menu.Items.Add(MakeItem("tasks.ctxOpenPlanningSession", OnOpenPlanningSessionClick, row.CanOpenPlanningSession)); + menu.Items.Add(MakeItem("tasks.ctxResumePlanningSession", OnResumePlanningSessionClick, row.CanResumeOrDiscardPlanning)); + menu.Items.Add(MakeItem("tasks.ctxFinalizePlanningSession", OnFinalizePlanningSessionClick, row.CanFinalizePlanning)); + menu.Items.Add(MakeItem("tasks.ctxDiscardPlanningSession", OnDiscardPlanningSessionClick, row.CanResumeOrDiscardPlanning)); + menu.Items.Add(new Separator()); + menu.Items.Add(MakeItem("tasks.ctxScheduleFor", OnScheduleForClick)); + menu.Items.Add(MakeItem("tasks.ctxClearSchedule", OnClearScheduleClick, row.HasSchedule)); + menu.Items.Add(MakeItem("tasks.ctxAddToMyDay", OnAddToMyDayClick, row.CanAddToMyDay)); + menu.Items.Add(MakeItem("tasks.ctxRemoveFromMyDay", OnRemoveFromMyDayClick, row.IsMyDay)); + + menu.Open(border); + e.Handled = true; + } + private async void OnSendToQueueClick(object? sender, RoutedEventArgs e) { if (DataContext is TaskRowViewModel row && FindTasksVm() is { } vm)