-
+
+
-
+
+ {@const TriggerIcon = getProviderIcon(formType)}
+
{providerTypeLabel(formType)}
{#each PROVIDER_TYPES as t (t.value)}
+ {@const ItemIcon = getProviderIcon(t.value)}
- {t.label}
+
+
+ {t.label}
+
{/each}
- {#each fields as field (field.key)}
-
-
-
- {#if field.hint}
-
{field.hint}
- {/if}
-
- {/each}
+
+
+ {#each fields as field (field.key)}
+
+
+
+ {#if field.hint}
+
{field.hint}
+ {/if}
+
+ {/each}
+
Configuration is stored encrypted.{#if isEditing}
Leave secret fields blank to keep the existing values.{/if}
@@ -327,12 +377,19 @@
variant="outline"
onclick={testCurrent}
disabled={formTesting || formSaving}
+ class={`transition-colors duration-300 ${testOk ? 'border-green-500/60 text-green-600 dark:text-green-400' : ''}`}
>
- {#if formTesting}
-
- {:else}
-
- {/if}
+
+ {#if formTesting}
+
+ {:else if testOk}
+
+
+
+ {:else}
+
+ {/if}
+
Test connection
diff --git a/src/routes/settings/secrets/SecretsTab.svelte b/src/routes/settings/secrets/SecretsTab.svelte
index 345037d..e8aa0b4 100644
--- a/src/routes/settings/secrets/SecretsTab.svelte
+++ b/src/routes/settings/secrets/SecretsTab.svelte
@@ -12,7 +12,10 @@
KeyRound,
PlugZap,
RefreshCw,
+ Check,
} from 'lucide-svelte';
+ import { scale } from 'svelte/transition';
+ import { backOut, cubicIn } from 'svelte/easing';
import ConfirmPopover from '$lib/components/ConfirmPopover.svelte';
import { canAccess } from '$lib/stores/auth';
import { EmptyState } from '$lib/components/ui/empty-state';
@@ -20,6 +23,7 @@
type SecretProvider,
providerTypeLabel,
} from './ProviderModal.svelte';
+ import { getProviderIcon } from '$lib/components/provider-icons';
let providers = $state
([]);
let loading = $state(true);
@@ -27,6 +31,9 @@
let editing = $state(null);
let confirmDeleteId = $state(null);
let testingId = $state(null);
+ // Brief green tick on the tile's Test button right after a successful test.
+ let testOkId = $state(null);
+ let testOkTimer: ReturnType | undefined;
async function fetchProviders() {
loading = true;
@@ -73,6 +80,9 @@
const data = await response.json();
if (data.ok) {
toast.success(`${provider.name}: connection works`);
+ clearTimeout(testOkTimer);
+ testOkId = provider.id;
+ testOkTimer = setTimeout(() => (testOkId = null), 2000);
} else {
toast.error(
`${provider.name}: ${data.error || 'connection failed'}`,
@@ -121,14 +131,13 @@
{:else}
{#each providers as provider (provider.id)}
+ {@const ProviderIcon = getProviderIcon(provider.type)}
-
+
{provider.name}
@@ -151,14 +160,19 @@
size="sm"
onclick={() => testProvider(provider)}
disabled={testingId === provider.id}
+ class={`transition-colors duration-300 ${testOkId === provider.id ? 'border-green-500/60 text-green-600 dark:text-green-400' : ''}`}
>
- {#if testingId === provider.id}
-
- {:else}
-
- {/if}
+
+ {#if testingId === provider.id}
+
+ {:else if testOkId === provider.id}
+
+
+
+ {:else}
+
+ {/if}
+
Test
{/if}
diff --git a/src/routes/stacks/GitStackModal.svelte b/src/routes/stacks/GitStackModal.svelte
index 9adc3bd..bf10231 100644
--- a/src/routes/stacks/GitStackModal.svelte
+++ b/src/routes/stacks/GitStackModal.svelte
@@ -1188,7 +1188,9 @@
-
+
+ {#if secretProviders.length > 0 || formSecretProviderId !== null}
+ {/if}
-
+
+ {#if secretProviders.length > 0 || formSecretProviderId !== null}
+ {/if}