...
This commit is contained in:
@@ -50,9 +50,26 @@ else
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-flex mb-2 justify-content-end">
|
<div class="d-flex mb-2 justify-content-end">
|
||||||
|
|
||||||
|
@if (msg.responseText=="null" && msg.dateTime==new DateTime(1400,01,01))
|
||||||
|
{
|
||||||
|
<Spinner Type="SpinnerType.Dots" Class="me-3" Color="SpinnerColor.Info" />
|
||||||
|
}
|
||||||
|
else{
|
||||||
<div class="chat-bubble chat-mine">
|
<div class="chat-bubble chat-mine">
|
||||||
|
@if (DelayShowResponse && messages.Last()==msg)
|
||||||
|
{
|
||||||
|
<TypewriterComponent Text="@msg.responseText" />
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
@msg.responseText
|
@msg.responseText
|
||||||
|
}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -98,13 +115,14 @@ else
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
public bool DelayShowResponse { get; set; } = false;
|
||||||
ReadANDUpdate_CompanyDto? CompanyInfo = new();
|
ReadANDUpdate_CompanyDto? CompanyInfo = new();
|
||||||
[Parameter] public int CompanyID { get; set; }
|
[Parameter] public int CompanyID { get; set; }
|
||||||
List<aiResponseDto> messages = new();
|
List<aiResponseDto> messages = new();
|
||||||
string inputText = string.Empty;
|
string inputText = string.Empty;
|
||||||
bool isReady = false;
|
bool isReady = false;
|
||||||
bool isError= false;
|
bool isError = false;
|
||||||
string msgError= "";
|
string msgError = "";
|
||||||
public string aikeyUser { get; set; } = "";
|
public string aikeyUser { get; set; } = "";
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
@@ -146,10 +164,23 @@ else
|
|||||||
if (string.IsNullOrWhiteSpace(inputText)) return;
|
if (string.IsNullOrWhiteSpace(inputText)) return;
|
||||||
disSend = true;
|
disSend = true;
|
||||||
var dto = new aiNewResponseDto { companyId = CompanyID, aiKeyUser = aikeyUser, requestText = inputText };
|
var dto = new aiNewResponseDto { companyId = CompanyID, aiKeyUser = aikeyUser, requestText = inputText };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var waiting = new aiResponseDto()
|
||||||
|
{
|
||||||
|
requestText = inputText,
|
||||||
|
responseText = "null",
|
||||||
|
dateTime = new(1400, 01, 01)
|
||||||
|
};
|
||||||
|
messages.Add(waiting);
|
||||||
|
await JS.InvokeVoidAsync("scrollToBottom", "ai-chat");
|
||||||
var okmodel = await conversationService.AiNewResponse(dto);
|
var okmodel = await conversationService.AiNewResponse(dto);
|
||||||
if (okmodel != null)
|
if (okmodel != null)
|
||||||
{
|
{
|
||||||
|
DelayShowResponse = true;
|
||||||
messages.Add(okmodel);
|
messages.Add(okmodel);
|
||||||
|
|
||||||
inputText = string.Empty;
|
inputText = string.Empty;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
await JS.InvokeVoidAsync("scrollToBottom", "ai-chat");
|
await JS.InvokeVoidAsync("scrollToBottom", "ai-chat");
|
||||||
@@ -158,6 +189,7 @@ else
|
|||||||
{
|
{
|
||||||
toastService.Notify(new ToastMessage(ToastType.Danger, "ارسال ناموفق بود"));
|
toastService.Notify(new ToastMessage(ToastType.Danger, "ارسال ناموفق بود"));
|
||||||
}
|
}
|
||||||
|
messages.Remove(waiting);
|
||||||
disSend = false;
|
disSend = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,7 +319,6 @@ else
|
|||||||
-webkit-text-fill-color: transparent;
|
-webkit-text-fill-color: transparent;
|
||||||
background-clip: text;
|
background-clip: text;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -325,5 +356,4 @@ else
|
|||||||
color: #353;
|
color: #353;
|
||||||
border-top-right-radius: 0;
|
border-top-right-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
90
Presentation/HushianWebApp/Pages/TypewriterComponent.razor
Normal file
90
Presentation/HushianWebApp/Pages/TypewriterComponent.razor
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
|
||||||
|
@* File: Typewriter.razor *@
|
||||||
|
@inherits ComponentBase
|
||||||
|
|
||||||
|
@* <span>@_display</span> *@
|
||||||
|
@_display
|
||||||
|
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter] public string Text { get; set; }
|
||||||
|
|
||||||
|
public int WordDelay { get; set; } = 100;
|
||||||
|
public bool StartOnRender { get; set; } = true;
|
||||||
|
// [Parameter] public EventCallback OnFinished { get; set; }
|
||||||
|
|
||||||
|
private string _display = string.Empty;
|
||||||
|
private string[] _words = Array.Empty<string>();
|
||||||
|
private int _index = 0;
|
||||||
|
private CancellationTokenSource? _cts;
|
||||||
|
private bool _isRunning = false;
|
||||||
|
|
||||||
|
protected override async Task OnParametersSetAsync()
|
||||||
|
{
|
||||||
|
// Prepare tokenized words once per parameter set
|
||||||
|
_words = string.IsNullOrWhiteSpace(Text)
|
||||||
|
? Array.Empty<string>()
|
||||||
|
: System.Text.RegularExpressions.Regex.Split(Text.Trim(), "\\s+");
|
||||||
|
|
||||||
|
if (StartOnRender && !_isRunning && _index == 0)
|
||||||
|
{
|
||||||
|
await StartAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Start typing from the current index.
|
||||||
|
/// </summary>
|
||||||
|
public async Task StartAsync()
|
||||||
|
{
|
||||||
|
if (_isRunning) return;
|
||||||
|
_cts?.Cancel();
|
||||||
|
_cts = new CancellationTokenSource();
|
||||||
|
var token = _cts.Token;
|
||||||
|
_isRunning = true;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (_index < _words.Length && !token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
if (_index > 0)
|
||||||
|
{
|
||||||
|
_display += " ";
|
||||||
|
}
|
||||||
|
_display += _words[_index++];
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
await Task.Delay(Math.Max(0, WordDelay), token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException) { }
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isRunning = false;
|
||||||
|
// if (_index >= _words.Length && OnFinished.HasDelegate)
|
||||||
|
// {
|
||||||
|
// await OnFinished.InvokeAsync();
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pause typing (can be resumed with StartAsync()).
|
||||||
|
/// </summary>
|
||||||
|
public void Pause()
|
||||||
|
{
|
||||||
|
_cts?.Cancel();
|
||||||
|
_isRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stop and reset the text to the beginning.
|
||||||
|
/// </summary>
|
||||||
|
public async Task ResetAsync()
|
||||||
|
{
|
||||||
|
Pause();
|
||||||
|
_display = string.Empty;
|
||||||
|
_index = 0;
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Reference in New Issue
Block a user