Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@

**Learning:** Asynchronous typeahead searches must implement a request ID mechanism. Without it, stale responses can overwrite newer ones, leading to correct search terms displaying incorrect results.
**Action:** Always use a request ID or cancellation token pattern when implementing async search/filter operations.

## 2025-02-13 - Reactive Statements for Input Debouncing

**Learning:** Using `on:keyup` for search input debouncing is inefficient and incomplete; it triggers on non-character keys (e.g., Shift, Arrow keys) and misses alternative input methods like paste or drag-and-drop. Svelte's reactive statements (e.g., `$: debounce(value)`) provide a robust, comprehensive way to trigger side effects only when the value actually changes.
**Action:** Prefer reactive statements over key event listeners for debouncing bound inputs to improve performance and UX consistency.
1 change: 1 addition & 0 deletions .jules/palette.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
## 2024-10-24 - Accessible Icon Props and Loading Button State

**Learning:** Svelte wrapper components (like `Icon.svelte`) must spread `$$restProps` to allow passing accessibility attributes (e.g., `aria-label`) from parent components. Without this, icons remain inaccessible to screen readers. Also, persistent "Success" states on buttons can be confusing; auto-resetting them after a timeout improves clarity.
**Action:** Always include `{...$$restProps}` in wrapper components and implement auto-reset logic for temporary success states in interactive elements.
10 changes: 8 additions & 2 deletions src/routes/DomainSearch.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@
$: invalid = domainName !== '' && !validator.validate(domainName, { raiseError: false });
$: nameSearchedLabel = nameSearched ? `${nameSearched}.${$metaNamesSdk.config.tld}` : null;

function debounce() {
// Reactive debounce: triggers on any value change (typing, paste, etc.)
// and avoids unnecessary triggers on non-input keys (unlike on:keyup).
$: debounce(domainName);

function debounce(_name?: string) {
void _name;
clearTimeout(debounceTimer);
debounceTimer = setTimeout(async () => await search(), 400);
}
Expand Down Expand Up @@ -50,6 +55,8 @@
}

async function submit() {
// Clear pending debounce timer to prevent double submission
clearTimeout(debounceTimer);
await search(true);
}
</script>
Expand All @@ -60,7 +67,6 @@
class="domain-input"
variant="outlined"
bind:value={domainName}
on:keyup={() => debounce()}
bind:invalid
label="Domain name"
withTrailingIcon
Expand Down
2 changes: 1 addition & 1 deletion vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default defineConfig({
}),
sveltekit(),
nodePolyfills({
include: ['buffer', 'crypto', 'stream']
include: ['buffer', 'crypto', 'stream', 'util']
}),
tsconfigPaths()
],
Expand Down