mirror of
https://github.com/marcominerva/SqlDatabaseVectorSearch.git
synced 2026-08-04 09:48:57 +00:00
Improve copy-to-clipboard UX in Ask.razor
Refactored the copy button to use per-message state, displaying a checkmark icon and "Copied!" tooltip when a message is copied. Simplified state management, removed unused references, and updated the Reset method to clear copied state. Enhanced Ask.razor.css to minimize button chrome and improve interactive states for a cleaner UI.
This commit is contained in:
@@ -77,10 +77,10 @@
|
|||||||
<Icon Class="d-flex" Name="IconName.CashCoin"></Icon>
|
<Icon Class="d-flex" Name="IconName.CashCoin"></Icon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-end bg-transparent">
|
<div class="text-end bg-transparent copy-button">
|
||||||
<Tooltip Title="@toolTipText" Color="TooltipColor.Dark" Placement="TooltipPlacement.Bottom">
|
<Tooltip Title="@(copiedMessage == message ? "Copied!" : "Copy to clipboard")" Color="TooltipColor.Dark" Placement="TooltipPlacement.Bottom">
|
||||||
<Button Type="ButtonType.Button" Outline="false" @onclick="@(async () => await CopyToClipboardAsync(message.Text))">
|
<Button Type="ButtonType.Button" Outline="false" @onclick="@(async () => await CopyToClipboardAsync(message))">
|
||||||
@if (showCopyConfirmation)
|
@if (copiedMessage == message)
|
||||||
{
|
{
|
||||||
<Icon Name="IconName.Check" Class="text-success" />
|
<Icon Name="IconName.Check" Class="text-success" />
|
||||||
}
|
}
|
||||||
@@ -113,11 +113,11 @@
|
|||||||
</span>
|
</span>
|
||||||
<input @ref="askInput" type="text" @bind="@question" @bind:event="oninput" placeholder="Ask a question about your documents..." class="form-control border-0" maxlength="2000" @onkeydown="HandleKeyDown" />
|
<input @ref="askInput" type="text" @bind="@question" @bind:event="oninput" placeholder="Ask a question about your documents..." class="form-control border-0" maxlength="2000" @onkeydown="HandleKeyDown" />
|
||||||
<div class="input-group-text bg-transparent border-0">
|
<div class="input-group-text bg-transparent border-0">
|
||||||
<Button Type="ButtonType.Submit" @ref="askButton" Color="ButtonColor.Primary" Disabled="@(isAsking || string.IsNullOrWhiteSpace(question))" @onclick="AskQuestion">
|
<Button Type="ButtonType.Submit" Color="ButtonColor.Primary" Disabled="@(isAsking || string.IsNullOrWhiteSpace(question))" @onclick="AskQuestion">
|
||||||
<Icon Name="IconName.Send" />
|
<Icon Name="IconName.Send" />
|
||||||
</Button>
|
</Button>
|
||||||
<Tooltip Title="Start a new conversation" Color="TooltipColor.Secondary" Placement="TooltipPlacement.Bottom">
|
<Tooltip Title="Start a new conversation" Color="TooltipColor.Secondary" Placement="TooltipPlacement.Bottom">
|
||||||
<Button Type="ButtonType.Reset" @ref="resetButton" Class="ms-2" Color="ButtonColor.Secondary" Disabled="@isAsking" @onclick="Reset">
|
<Button Type="ButtonType.Reset" Class="ms-2" Color="ButtonColor.Secondary" Disabled="@isAsking" @onclick="Reset">
|
||||||
<Icon CustomIconName="bi bi-x-lg" />
|
<Icon CustomIconName="bi bi-x-lg" />
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -128,8 +128,6 @@
|
|||||||
|
|
||||||
@code
|
@code
|
||||||
{
|
{
|
||||||
private Button askButton = default!;
|
|
||||||
private Button resetButton = default!;
|
|
||||||
private ElementReference askInput = default!;
|
private ElementReference askInput = default!;
|
||||||
private ElementReference chat = default!;
|
private ElementReference chat = default!;
|
||||||
|
|
||||||
@@ -137,10 +135,9 @@
|
|||||||
private string? question;
|
private string? question;
|
||||||
|
|
||||||
private Guid conversationId = Guid.NewGuid();
|
private Guid conversationId = Guid.NewGuid();
|
||||||
private bool isAsking = false;
|
private bool isAsking;
|
||||||
|
|
||||||
private bool showCopyConfirmation = false;
|
private Message? copiedMessage;
|
||||||
private string toolTipText = "Copy to clipboard";
|
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
{
|
{
|
||||||
@@ -222,9 +219,10 @@
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
await EnsureMessageIsVisibleAsync();
|
|
||||||
|
|
||||||
isAsking = false;
|
isAsking = false;
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
|
||||||
|
await EnsureMessageIsVisibleAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,26 +230,30 @@
|
|||||||
{
|
{
|
||||||
question = null;
|
question = null;
|
||||||
conversationId = Guid.NewGuid();
|
conversationId = Guid.NewGuid();
|
||||||
|
copiedMessage = null;
|
||||||
messages.Clear();
|
messages.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CopyToClipboardAsync(string text)
|
private async Task CopyToClipboardAsync(Message message)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(message.Text))
|
||||||
{
|
{
|
||||||
if (text is null)
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await JSRuntime.InvokeVoidAsync("navigator.clipboard.writeText", text);
|
await JSRuntime.InvokeVoidAsync("navigator.clipboard.writeText", message.Text);
|
||||||
|
|
||||||
showCopyConfirmation = true;
|
copiedMessage = message;
|
||||||
toolTipText = "Copied!";
|
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
|
|
||||||
await Task.Delay(3000); // Shows the checkmark for 3 seconds
|
await Task.Delay(3000); // Shows the checkmark for 3 seconds
|
||||||
|
|
||||||
toolTipText = "Copy to clipboard";
|
if (copiedMessage == message)
|
||||||
showCopyConfirmation = false;
|
{
|
||||||
|
copiedMessage = null;
|
||||||
await InvokeAsync(StateHasChanged);
|
await InvokeAsync(StateHasChanged);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static string FormatTokenUsage(TokenUsageResponse? tokenUsageResponse)
|
private static string FormatTokenUsage(TokenUsageResponse? tokenUsageResponse)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -119,6 +119,29 @@ input[type="checkbox"] + label {
|
|||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The copy button only shows an icon, so the default button chrome is removed and just the pointer cursor is kept. */
|
||||||
|
.copy-button ::deep .btn {
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
color: var(--bs-secondary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-button ::deep .btn:hover {
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: transparent;
|
||||||
|
color: var(--bs-body-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-button ::deep .btn:focus,
|
||||||
|
.copy-button ::deep .btn:focus-visible,
|
||||||
|
.copy-button ::deep .btn:active {
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-clipboard {
|
.btn-clipboard {
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
color: var(--bs-body-color);
|
color: var(--bs-body-color);
|
||||||
|
|||||||
Reference in New Issue
Block a user