diff --git a/canvas/src/components/__tests__/TestConnectionButton.test.tsx b/canvas/src/components/__tests__/TestConnectionButton.test.tsx index 15f1dd9cf..207a37a8f 100644 --- a/canvas/src/components/__tests__/TestConnectionButton.test.tsx +++ b/canvas/src/components/__tests__/TestConnectionButton.test.tsx @@ -102,7 +102,7 @@ describe("TestConnectionButton — state machine", () => { expect(screen.getByText("Permission denied")).toBeTruthy(); }); - it("shows generic error message on unexpected exception", async () => { + it("shows honest network error on unexpected exception", async () => { vi.mocked(validateSecret).mockRejectedValue(new Error("timeout")); render(); @@ -110,8 +110,9 @@ describe("TestConnectionButton — state machine", () => { await act(async () => { /* flush */ }); expect(screen.getByRole("alert")).toBeTruthy(); - // The error detail is hardcoded to "Connection timed out. Service may be down." - expect(document.body.querySelector('[role="alert"]')?.textContent).toMatch(/timed out/i); + // The new error detail distinguishes network/abort errors (no server answer) + // from server-answered ApiErrors (404 = not available, other = status code). + expect(document.body.querySelector('[role="alert"]')?.textContent).toMatch(/Could not reach/i); }); }); diff --git a/canvas/src/components/settings/SecretRow.tsx b/canvas/src/components/settings/SecretRow.tsx index 92bb633ea..10339e869 100644 --- a/canvas/src/components/settings/SecretRow.tsx +++ b/canvas/src/components/settings/SecretRow.tsx @@ -3,16 +3,24 @@ import { useState, useCallback, useRef, useEffect } from 'react'; import type { Secret, SecretGroup } from '@/types/secrets'; import { useSecretsStore } from '@/stores/secrets-store'; import { StatusBadge } from '@/components/ui/StatusBadge'; -import { RevealToggle } from '@/components/ui/RevealToggle'; import { KeyValueField } from '@/components/ui/KeyValueField'; import { ValidationHint } from '@/components/ui/ValidationHint'; import { TestConnectionButton } from '@/components/ui/TestConnectionButton'; import { validateSecretValue } from '@/lib/validation/secret-formats'; import { SERVICES } from '@/lib/services'; -const AUTO_HIDE_MS = 30_000; const VALIDATION_DEBOUNCE_MS = 400; +// Secret values are write-only from the browser: the server List endpoint +// "Never exposes values", there is no per-secret decrypt route, and the +// only decrypted path (GET /secrets/values) is bulk + token-gated for +// remote agents. The old eye/RevealToggle was a dead affordance — it +// flipped its own icon but could never reveal anything, which read as +// "this doesn't work" (esp. once clicked → eye-with-slash). We show an +// honest static indicator instead; rotation is via Edit. +const WRITE_ONLY_TITLE = + 'Value is write-only and cannot be revealed — use Edit to replace/rotate it'; + interface SecretRowProps { secret: Secret; workspaceId: string; @@ -31,28 +39,12 @@ export function SecretRow({ secret, workspaceId }: SecretRowProps) { const setSecretStatus = useSecretsStore((s) => s.setSecretStatus); const isEditing = editingKey === secret.name; - const [revealed, setRevealed] = useState(false); const [editValue, setEditValue] = useState(''); const [validationError, setValidationError] = useState(null); const [isSaving, setIsSaving] = useState(false); const [saveError, setSaveError] = useState(null); const debounceRef = useRef>(undefined); const editBtnRef = useRef(null); - const revealTimerRef = useRef>(undefined); - - // Auto-hide revealed value after 30s - useEffect(() => { - if (revealed) { - clearTimeout(revealTimerRef.current); - revealTimerRef.current = setTimeout(() => setRevealed(false), AUTO_HIDE_MS); - return () => clearTimeout(revealTimerRef.current); - } - }, [revealed]); - - // Reset revealed state when panel closes (session-only) - useEffect(() => { - return () => setRevealed(false); - }, []); // Debounced validation useEffect(() => { @@ -133,11 +125,15 @@ export function SecretRow({ secret, workspaceId }: SecretRowProps) { {secret.masked_value}
- setRevealed((r) => !r)} - label={`Toggle reveal ${secret.name}`} - /> + + 🔒 +