using Microsoft.AspNetCore.Components; using Timer = System.Timers.Timer; namespace AIStudio.Components; /// /// Debounced multi-line text input built on . /// Keeps the base API while adding a debounce timer. /// Callers can override any property as usual. /// public class UserPromptComponent : MudTextField { [Parameter] public TimeSpan DebounceTime { get; set; } = TimeSpan.FromMilliseconds(800); [Parameter] public Func WhenTextChangedAsync { get; set; } = _ => Task.CompletedTask; private readonly Timer debounceTimer = new(); private string text = string.Empty; private string lastParameterText = string.Empty; private string lastNotifiedText = string.Empty; private bool isInitialized; protected override async Task OnInitializedAsync() { this.text = this.Text ?? string.Empty; this.lastParameterText = this.text; this.lastNotifiedText = this.text; this.debounceTimer.AutoReset = false; this.debounceTimer.Interval = this.DebounceTime.TotalMilliseconds; this.debounceTimer.Elapsed += (_, _) => { this.debounceTimer.Stop(); if (this.text == this.lastNotifiedText) return; this.lastNotifiedText = this.text; this.InvokeAsync(async () => await this.TextChanged.InvokeAsync(this.text)); this.InvokeAsync(async () => await this.WhenTextChangedAsync(this.text)); }; this.isInitialized = true; await base.OnInitializedAsync(); } protected override async Task OnParametersSetAsync() { // Ensure the timer uses the latest debouncing interval: if (!this.isInitialized) return; if(Math.Abs(this.debounceTimer.Interval - this.DebounceTime.TotalMilliseconds) > 1) this.debounceTimer.Interval = this.DebounceTime.TotalMilliseconds; // Only sync when the parent's parameter actually changed since the last change: if (this.Text != this.lastParameterText) { this.text = this.Text ?? string.Empty; this.lastParameterText = this.text; } this.debounceTimer.Stop(); this.debounceTimer.Start(); await base.OnParametersSetAsync(); } }