diff --git a/.Jules/palette.md b/.Jules/palette.md
index d6d129b..d974090 100644
--- a/.Jules/palette.md
+++ b/.Jules/palette.md
@@ -2,3 +2,8 @@
**Learning:** Async buttons that handle errors often fail to reset their error state on subsequent attempts. This leads to a confusing UX where a successful retry still displays the error icon, making the user believe the action failed again.
**Action:** Always ensure that error flags (e.g., `hasError`) are reset at the _start_ of the async operation, not just set in the `catch` block.
+
+## 2024-10-24 - In-Place Loading Indicators
+
+**Learning:** Replacing static icons (like search) with loading spinners within the same container (e.g., input field) prevents layout shifts and provides immediate, contextual feedback.
+**Action:** Use conditional rendering to swap icons for spinners in `trailingIcon` slots or button contents during async operations.
diff --git a/.jules/palette.md b/.jules/palette.md
index 00f41cd..9cc85e1 100644
--- a/.jules/palette.md
+++ b/.jules/palette.md
@@ -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.
diff --git a/src/routes/DomainSearch.svelte b/src/routes/DomainSearch.svelte
index 48c50fd..1f7e1f5 100644
--- a/src/routes/DomainSearch.svelte
+++ b/src/routes/DomainSearch.svelte
@@ -22,8 +22,17 @@
$: invalid = domainName !== '' && !validator.validate(domainName, { raiseError: false });
$: nameSearchedLabel = nameSearched ? `${nameSearched}.${$metaNamesSdk.config.tld}` : null;
- function debounce() {
+ $: debounce(domainName);
+
+ function debounce(name: string) {
clearTimeout(debounceTimer);
+
+ if (name === '' || invalid) {
+ domain = undefined;
+ isLoading = false;
+ return;
+ }
+
debounceTimer = setTimeout(async () => await search(), 400);
}
@@ -60,7 +69,6 @@
class="domain-input"
variant="outlined"
bind:value={domainName}
- on:keyup={() => debounce()}
bind:invalid
label="Domain name"
withTrailingIcon
@@ -68,9 +76,20 @@
>
-
-
-
+ {#if isLoading}
+
+
+
+ {:else}
+
+
+
+ {/if}
diff --git a/vite.config.ts b/vite.config.ts
index a9322d6..6094564 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -14,7 +14,7 @@ export default defineConfig({
}),
sveltekit(),
nodePolyfills({
- include: ['buffer', 'crypto', 'stream']
+ include: ['buffer', 'crypto', 'stream', 'util']
}),
tsconfigPaths()
],