Skip to content

Commit 25eb95b

Browse files
Add view modal for saved responses
Saved response list items now have a view button (eye icon) that opens the full content rendered as markdown in a scrollable panel without inserting into chat. From the view, users can go Back to the list or Insert into chat. Clicking the item label still inserts directly as before. Light and dark mode styles included. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 4e4af2e commit 25eb95b

2 files changed

Lines changed: 96 additions & 15 deletions

File tree

src/RockBot.UserProxy.Blazor/Pages/Chat.razor

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -231,29 +231,49 @@
231231
{
232232
<div class="modal-overlay" @onclick="ToggleSavedModal">
233233
<div class="saved-modal" @onclick:stopPropagation="true">
234-
<h5>Saved Responses</h5>
235-
@if (_savedItems is null)
234+
@if (_viewingItem is not null)
236235
{
237-
<p>Loading...</p>
238-
}
239-
else if (_savedItems.Count == 0)
240-
{
241-
<p class="text-muted">No saved responses yet.</p>
236+
<div class="saved-view-header">
237+
<h5>@_viewingItem.Label</h5>
238+
<small class="saved-item-meta">@_viewingItem.AgentName@TimeZoneInfo.ConvertTime(_viewingItem.SavedAt, _displayZone).ToString("MMM d, yyyy h:mm tt")</small>
239+
</div>
240+
<div class="saved-view-content">
241+
@((MarkupString)Markdig.Markdown.ToHtml(_viewingItem.Content, MarkdownPipeline))
242+
</div>
243+
<div class="d-flex gap-2 mt-2">
244+
<button class="btn btn-sm btn-secondary" @onclick="BackToSavedList">Back</button>
245+
<button class="btn btn-sm btn-primary" @onclick="() => InsertViewedItem()">Insert into chat</button>
246+
</div>
242247
}
243248
else
244249
{
245-
@foreach (var item in _savedItems)
250+
<h5>Saved Responses</h5>
251+
@if (_savedItems is null)
252+
{
253+
<p>Loading...</p>
254+
}
255+
else if (_savedItems.Count == 0)
256+
{
257+
<p class="text-muted">No saved responses yet.</p>
258+
}
259+
else
246260
{
247-
<div class="saved-item d-flex justify-content-between align-items-center p-2 mb-1 rounded">
248-
<div class="saved-item-info" @onclick="() => LoadSavedItem(item.Id)" style="cursor:pointer;">
249-
<strong>@item.Label</strong>
250-
<small class="saved-item-meta d-block">@item.AgentName@TimeZoneInfo.ConvertTime(item.SavedAt, _displayZone).ToString("MMM d, yyyy h:mm tt")</small>
261+
@foreach (var item in _savedItems)
262+
{
263+
<div class="saved-item d-flex justify-content-between align-items-center p-2 mb-1 rounded">
264+
<div class="saved-item-info" @onclick="() => LoadSavedItem(item.Id)" style="cursor:pointer;" title="Insert into chat">
265+
<strong>@item.Label</strong>
266+
<small class="saved-item-meta d-block">@item.AgentName@TimeZoneInfo.ConvertTime(item.SavedAt, _displayZone).ToString("MMM d, yyyy h:mm tt")</small>
267+
</div>
268+
<div class="d-flex gap-1">
269+
<button class="btn btn-sm btn-outline-secondary" @onclick="() => ViewSavedItem(item.Id)" title="View">👁️</button>
270+
<button class="btn btn-sm btn-outline-danger" @onclick="() => DeleteSavedItem(item.Id)" title="Delete">🗑️</button>
271+
</div>
251272
</div>
252-
<button class="btn btn-sm btn-outline-danger" @onclick="() => DeleteSavedItem(item.Id)">🗑️</button>
253-
</div>
273+
}
254274
}
275+
<button class="btn btn-sm btn-secondary mt-2" @onclick="ToggleSavedModal">Close</button>
255276
}
256-
<button class="btn btn-sm btn-secondary mt-2" @onclick="ToggleSavedModal">Close</button>
257277
</div>
258278
</div>
259279
}
@@ -311,6 +331,7 @@
311331
private string _saveLabel = string.Empty;
312332
private bool _showSavedModal;
313333
private IReadOnlyList<SavedResponseSummary>? _savedItems;
334+
private GetSavedResponseResponse? _viewingItem;
314335

315336
private static readonly MarkdownPipeline MarkdownPipeline = new MarkdownPipelineBuilder()
316337
.UseAdvancedExtensions()
@@ -583,6 +604,7 @@
583604
private async Task ToggleSavedModal()
584605
{
585606
_showSavedModal = !_showSavedModal;
607+
_viewingItem = null;
586608
if (_showSavedModal)
587609
{
588610
_savedItems = null;
@@ -602,6 +624,7 @@
602624
private async Task LoadSavedItem(string id)
603625
{
604626
_showSavedModal = false;
627+
_viewingItem = null;
605628
try
606629
{
607630
var response = await ProxyService.GetSavedResponseAsync(id);
@@ -614,6 +637,33 @@
614637
}
615638
}
616639

640+
private async Task ViewSavedItem(string id)
641+
{
642+
try
643+
{
644+
var response = await ProxyService.GetSavedResponseAsync(id);
645+
if (response is not null && response.Found)
646+
_viewingItem = response;
647+
}
648+
catch (Exception ex)
649+
{
650+
ChatState.AddError($"Could not load saved response: {ex.Message}");
651+
}
652+
}
653+
654+
private void BackToSavedList()
655+
{
656+
_viewingItem = null;
657+
}
658+
659+
private void InsertViewedItem()
660+
{
661+
if (_viewingItem is null) return;
662+
ChatState.AddSavedReference(_viewingItem.Id, _viewingItem.Label, _viewingItem.Content, _viewingItem.AgentName);
663+
_viewingItem = null;
664+
_showSavedModal = false;
665+
}
666+
617667
private async Task DeleteSavedItem(string id)
618668
{
619669
try

src/RockBot.UserProxy.Blazor/wwwroot/css/app.css

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,27 @@ body.dark-mode .chevron-indicator {
668668
color: #6b7280;
669669
}
670670

671+
.saved-view-header {
672+
margin-bottom: 0.75rem;
673+
padding-bottom: 0.5rem;
674+
border-bottom: 1px solid #e5e7eb;
675+
}
676+
677+
.saved-view-header h5 {
678+
margin-bottom: 0.25rem;
679+
}
680+
681+
.saved-view-content {
682+
max-height: 50vh;
683+
overflow-y: auto;
684+
padding: 0.75rem;
685+
background: #f9fafb;
686+
border-radius: 0.375rem;
687+
border: 1px solid #e5e7eb;
688+
font-size: 0.9rem;
689+
line-height: 1.6;
690+
}
691+
671692
/* ── Dark mode: saved reference ─────────────────────────────────────── */
672693

673694
body.dark-mode .saved-reference-bubble {
@@ -697,6 +718,16 @@ body.dark-mode .saved-item-meta {
697718
color: #9ca3af;
698719
}
699720

721+
body.dark-mode .saved-view-header {
722+
border-bottom-color: #4b5563;
723+
}
724+
725+
body.dark-mode .saved-view-content {
726+
background: #1f2937;
727+
border-color: #4b5563;
728+
color: #e5e7eb;
729+
}
730+
700731
body.dark-mode .save-label-input {
701732
border-top-color: rgba(255, 255, 255, 0.1);
702733
}

0 commit comments

Comments
 (0)