diff --git a/client/src/pages/credential/CredentialDetailPage.tsx b/client/src/pages/credential/CredentialDetailPage.tsx index 431c85c..33189aa 100644 --- a/client/src/pages/credential/CredentialDetailPage.tsx +++ b/client/src/pages/credential/CredentialDetailPage.tsx @@ -11,6 +11,7 @@ import { Button, Card, DescriptionList, + Modal, Timestamp, UuidBadge, } from '../../ui'; @@ -23,6 +24,8 @@ export function CredentialDetailPage() { const [credential, setCredential] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + const [confirmDeleteOpen, setConfirmDeleteOpen] = useState(false); + const [deleting, setDeleting] = useState(false); useEffect(() => { if (!id) return; @@ -35,8 +38,14 @@ export function CredentialDetailPage() { const handleDelete = async () => { if (!credential) return; - await credentials.remove(credential.id); - navigate('/credentials'); + setDeleting(true); + try { + await credentials.remove(credential.id); + navigate('/credentials'); + } finally { + setDeleting(false); + setConfirmDeleteOpen(false); + } }; const handleExport = async () => { @@ -74,7 +83,7 @@ export function CredentialDetailPage() { {t('credentials.action_edit')} - @@ -126,6 +135,24 @@ export function CredentialDetailPage() { )} )} + + !deleting && setConfirmDeleteOpen(false)} + footer={( + <> + + + + )} + > + Are you sure you want to delete this credential? + ); } diff --git a/client/src/pages/credential/CredentialsPage.tsx b/client/src/pages/credential/CredentialsPage.tsx index 7889a4e..b7a511b 100644 --- a/client/src/pages/credential/CredentialsPage.tsx +++ b/client/src/pages/credential/CredentialsPage.tsx @@ -11,6 +11,7 @@ import { Card, ContextMenu, DescriptionList, + Modal, Timestamp, UuidBadge, } from '../../ui'; @@ -103,6 +104,8 @@ export function CredentialsPage() { const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + const [deleteId, setDeleteId] = useState(null); + const [deleting, setDeleting] = useState(false); const fileInputRef = useRef(null); const load = () => { @@ -117,9 +120,20 @@ export function CredentialsPage() { load(); }, []); - const handleDelete = async (id: string) => { - await credentials.remove(id); - setItems((prev) => prev.filter((c) => c.id !== id)); + const requestDelete = (id: string) => { + setDeleteId(id); + }; + + const handleDelete = async () => { + if (!deleteId) return; + setDeleting(true); + try { + await credentials.remove(deleteId); + setItems((prev) => prev.filter((c) => c.id !== deleteId)); + setDeleteId(null); + } finally { + setDeleting(false); + } }; const handleImport = async (e: React.ChangeEvent) => { @@ -159,11 +173,29 @@ export function CredentialsPage() { ) : (
{items.map((credential) => ( - + ))}
)} + + !deleting && setDeleteId(null)} + footer={( + <> + + + + )} + > + Are you sure you want to delete this credential? + ); } diff --git a/client/src/pages/environment/EnvironmentDetailPage.tsx b/client/src/pages/environment/EnvironmentDetailPage.tsx index 0a2d1ae..f4ebc65 100644 --- a/client/src/pages/environment/EnvironmentDetailPage.tsx +++ b/client/src/pages/environment/EnvironmentDetailPage.tsx @@ -5,7 +5,7 @@ import { Upload, Pencil, Trash2, Globe } from 'lucide-react'; import { stringify as yamlStringify } from 'yaml'; import { environments } from '../../api'; import type { Environment } from '../../api'; -import { Breadcrumbs, Button, Card, DescriptionList, Timestamp, UuidBadge } from '../../ui'; +import { Breadcrumbs, Button, Card, DescriptionList, Modal, Timestamp, UuidBadge } from '../../ui'; import styles from '../Page.module.css'; export function EnvironmentDetailPage() { @@ -15,6 +15,8 @@ export function EnvironmentDetailPage() { const [env, setEnv] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + const [confirmDeleteOpen, setConfirmDeleteOpen] = useState(false); + const [deleting, setDeleting] = useState(false); const dataEntries = Object.entries(env?.data ?? {}).filter(([, v]) => v); useEffect(() => { @@ -28,8 +30,14 @@ export function EnvironmentDetailPage() { const handleDelete = async () => { if (!env) return; - await environments.remove(env.id); - navigate('/environments'); + setDeleting(true); + try { + await environments.remove(env.id); + navigate('/environments'); + } finally { + setDeleting(false); + setConfirmDeleteOpen(false); + } }; const handleExport = async () => { @@ -67,7 +75,7 @@ export function EnvironmentDetailPage() { {t('environments.action_edit')} - @@ -118,6 +126,24 @@ export function EnvironmentDetailPage() { )} + + !deleting && setConfirmDeleteOpen(false)} + footer={( + <> + + + + )} + > + Are you sure you want to delete this environment? + ); } diff --git a/client/src/pages/environment/EnvironmentsPage.tsx b/client/src/pages/environment/EnvironmentsPage.tsx index 44fc6d0..25156d6 100644 --- a/client/src/pages/environment/EnvironmentsPage.tsx +++ b/client/src/pages/environment/EnvironmentsPage.tsx @@ -12,6 +12,7 @@ import { Card, ContextMenu, DescriptionList, + Modal, Timestamp, UuidBadge, } from '../../ui'; @@ -120,6 +121,8 @@ export function EnvironmentsPage() { const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + const [deleteId, setDeleteId] = useState(null); + const [deleting, setDeleting] = useState(false); const fileInputRef = useRef(null); const load = () => { @@ -134,9 +137,20 @@ export function EnvironmentsPage() { load(); }, []); - const handleDelete = async (id: string) => { - await environments.remove(id); - setItems((prev) => prev.filter((e) => e.id !== id)); + const requestDelete = (id: string) => { + setDeleteId(id); + }; + + const handleDelete = async () => { + if (!deleteId) return; + setDeleting(true); + try { + await environments.remove(deleteId); + setItems((prev) => prev.filter((e) => e.id !== deleteId)); + setDeleteId(null); + } finally { + setDeleting(false); + } }; const handleImport = async (e: React.ChangeEvent) => { @@ -176,11 +190,29 @@ export function EnvironmentsPage() { ) : (
{items.map((env) => ( - + ))}
)} + + !deleting && setDeleteId(null)} + footer={( + <> + + + + )} + > + Are you sure you want to delete this environment? + ); } diff --git a/client/src/pages/scenario/ScenarioDetailPage.tsx b/client/src/pages/scenario/ScenarioDetailPage.tsx index 3b9e91e..73e831b 100644 --- a/client/src/pages/scenario/ScenarioDetailPage.tsx +++ b/client/src/pages/scenario/ScenarioDetailPage.tsx @@ -11,6 +11,7 @@ import { Card, DescriptionList, Input, + Modal, Select, Table, Timestamp, @@ -41,6 +42,10 @@ export function ScenarioDetailPage() { const [draggedStepId, setDraggedStepId] = useState(null); const [dragOverStepId, setDragOverStepId] = useState(null); const [reorderingSteps, setReorderingSteps] = useState(false); + const [pendingDelete, setPendingDelete] = useState< + { type: 'scenario' } | { type: 'step'; id: string } | { type: 'credential'; id: string } | null + >(null); + const [deleting, setDeleting] = useState(false); useEffect(() => { if (!id) return; @@ -148,6 +153,26 @@ export function ScenarioDetailPage() { setScenarioCreds((prev) => prev.filter((sc) => sc.id !== scCredId)); }; + const confirmDelete = async () => { + if (!pendingDelete) return; + setDeleting(true); + try { + if (pendingDelete.type === 'scenario') { + await handleDelete(); + return; + } + if (pendingDelete.type === 'step') { + await handleDeleteStep(pendingDelete.id); + } + if (pendingDelete.type === 'credential') { + await handleRemoveCredential(pendingDelete.id); + } + setPendingDelete(null); + } finally { + setDeleting(false); + } + }; + const stepColumns: TableColumn[] = [ { key: 'drag', @@ -226,7 +251,7 @@ export function ScenarioDetailPage() { title={t('steps.action_delete')} onClick={(e) => { e.stopPropagation(); - handleDeleteStep(s.id); + setPendingDelete({ type: 'step', id: s.id }); }} > @@ -261,7 +286,7 @@ export function ScenarioDetailPage() { title={t('scenarios.cred_action_remove')} onClick={(e) => { e.stopPropagation(); - handleRemoveCredential(sc.id); + setPendingDelete({ type: 'credential', id: sc.id }); }} > @@ -297,7 +322,7 @@ export function ScenarioDetailPage() { {t('scenarios.action_edit')} - @@ -456,6 +481,32 @@ export function ScenarioDetailPage() { )} + + !deleting && setPendingDelete(null)} + footer={( + <> + + + + )} + > + {pendingDelete?.type === 'step' && 'Are you sure you want to delete this step?'} + {pendingDelete?.type === 'credential' && 'Are you sure you want to remove this credential from scenario?'} + {pendingDelete?.type === 'scenario' && 'Are you sure you want to delete this scenario?'} + ); } diff --git a/client/src/pages/scenario/ScenariosPage.tsx b/client/src/pages/scenario/ScenariosPage.tsx index fad86ea..3658beb 100644 --- a/client/src/pages/scenario/ScenariosPage.tsx +++ b/client/src/pages/scenario/ScenariosPage.tsx @@ -5,7 +5,16 @@ import { Play, Plus, Trash2, Download, ClipboardList } from 'lucide-react'; import { parse as yamlParse } from 'yaml'; import { scenarios } from '../../api'; import type { Scenario } from '../../api'; -import { Breadcrumbs, Button, Table, type TableColumn, Timestamp, UuidBadge, useToast } from '../../ui'; +import { + Breadcrumbs, + Button, + Modal, + Table, + type TableColumn, + Timestamp, + UuidBadge, + useToast, +} from '../../ui'; import styles from '../Page.module.css'; export function ScenariosPage() { @@ -15,6 +24,8 @@ export function ScenariosPage() { const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + const [deleteId, setDeleteId] = useState(null); + const [deleting, setDeleting] = useState(false); const fileInputRef = useRef(null); @@ -40,10 +51,17 @@ export function ScenariosPage() { } }; - const handleDelete = async (id: string) => { - await scenarios.remove(id); - setLoading(true); - load(); + const handleDelete = async () => { + if (!deleteId) return; + setDeleting(true); + try { + await scenarios.remove(deleteId); + setLoading(true); + load(); + setDeleteId(null); + } finally { + setDeleting(false); + } }; const handleImport = async (e: React.ChangeEvent) => { @@ -83,7 +101,10 @@ export function ScenariosPage() { variant="danger" size="sm" title={t('scenarios.action_delete')} - onClick={() => handleDelete(s.id)} + onClick={(e) => { + e.stopPropagation(); + setDeleteId(s.id); + }} > @@ -124,6 +145,24 @@ export function ScenariosPage() { pageSizeOptions={[10, 25, 50]} onRowClick={(s) => navigate(`/scenarios/${s.id}`)} /> + + !deleting && setDeleteId(null)} + footer={( + <> + + + + )} + > + Are you sure you want to delete this scenario? + ); } diff --git a/client/src/pages/session/SessionDetailPage.tsx b/client/src/pages/session/SessionDetailPage.tsx index d785dba..9a0fad2 100644 --- a/client/src/pages/session/SessionDetailPage.tsx +++ b/client/src/pages/session/SessionDetailPage.tsx @@ -10,6 +10,7 @@ import { Button, Card, DescriptionList, + Modal, Timestamp, UuidBadge, } from '../../ui'; @@ -22,6 +23,8 @@ export function SessionDetailPage() { const [session, setSession] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + const [confirmDeleteOpen, setConfirmDeleteOpen] = useState(false); + const [deleting, setDeleting] = useState(false); useEffect(() => { if (!id) return; @@ -34,8 +37,14 @@ export function SessionDetailPage() { const handleDelete = async () => { if (!session) return; - await sessions.remove(session.id); - navigate('/sessions'); + setDeleting(true); + try { + await sessions.remove(session.id); + navigate('/sessions'); + } finally { + setDeleting(false); + setConfirmDeleteOpen(false); + } }; return ( @@ -48,7 +57,7 @@ export function SessionDetailPage() { ]} /> {session && ( - @@ -92,6 +101,24 @@ export function SessionDetailPage() { /> )} + + !deleting && setConfirmDeleteOpen(false)} + footer={( + <> + + + + )} + > + Are you sure you want to delete this session? + ); } diff --git a/client/src/pages/session/SessionsPage.tsx b/client/src/pages/session/SessionsPage.tsx index c4a51c7..18ca7eb 100644 --- a/client/src/pages/session/SessionsPage.tsx +++ b/client/src/pages/session/SessionsPage.tsx @@ -8,6 +8,7 @@ import { Badge, Breadcrumbs, Button, + Modal, Table, type TableColumn, Timestamp, @@ -21,6 +22,8 @@ export function SessionsPage() { const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + const [deleteId, setDeleteId] = useState(null); + const [deleting, setDeleting] = useState(false); const load = useCallback(() => { sessions @@ -34,10 +37,17 @@ export function SessionsPage() { load(); }, [load]); - const handleDelete = async (id: string) => { - await sessions.remove(id); - setLoading(true); - load(); + const handleDelete = async () => { + if (!deleteId) return; + setDeleting(true); + try { + await sessions.remove(deleteId); + setLoading(true); + load(); + setDeleteId(null); + } finally { + setDeleting(false); + } }; const columns: TableColumn[] = [ @@ -65,7 +75,10 @@ export function SessionsPage() { variant="danger" size="sm" title={t('sessions.action_delete')} - onClick={() => handleDelete(s.id)} + onClick={(e) => { + e.stopPropagation(); + setDeleteId(s.id); + }} > @@ -88,6 +101,24 @@ export function SessionsPage() { pageSizeOptions={[10, 25, 50]} onRowClick={(s) => navigate(`/sessions/${s.id}`)} /> + + !deleting && setDeleteId(null)} + footer={( + <> + + + + )} + > + Are you sure you want to delete this session? + ); } diff --git a/client/src/pages/snippet/SnippetDetailPage.tsx b/client/src/pages/snippet/SnippetDetailPage.tsx index 01115bb..e86fb14 100644 --- a/client/src/pages/snippet/SnippetDetailPage.tsx +++ b/client/src/pages/snippet/SnippetDetailPage.tsx @@ -12,6 +12,7 @@ import { Card, DescriptionList, MarkdownContent, + Modal, Timestamp, UuidBadge, } from '../../ui'; @@ -24,6 +25,8 @@ export function SnippetDetailPage() { const [snippet, setSnippet] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + const [confirmDeleteOpen, setConfirmDeleteOpen] = useState(false); + const [deleting, setDeleting] = useState(false); useEffect(() => { if (!id) return; @@ -36,8 +39,14 @@ export function SnippetDetailPage() { const handleDelete = async () => { if (!snippet) return; - await snippets.remove(snippet.id); - navigate('/snippets'); + setDeleting(true); + try { + await snippets.remove(snippet.id); + navigate('/snippets'); + } finally { + setDeleting(false); + setConfirmDeleteOpen(false); + } }; const handleExport = async () => { @@ -75,7 +84,7 @@ export function SnippetDetailPage() { {t('snippets.action_edit')} - @@ -127,6 +136,24 @@ export function SnippetDetailPage() { )} + + !deleting && setConfirmDeleteOpen(false)} + footer={( + <> + + + + )} + > + Are you sure you want to delete this snippet? + ); } diff --git a/client/src/pages/snippet/SnippetsPage.tsx b/client/src/pages/snippet/SnippetsPage.tsx index bb50560..fd55e9f 100644 --- a/client/src/pages/snippet/SnippetsPage.tsx +++ b/client/src/pages/snippet/SnippetsPage.tsx @@ -5,7 +5,16 @@ import { Settings, Pencil, Trash2, Plus, Download, Braces } from 'lucide-react'; import { parse as yamlParse } from 'yaml'; import { snippets } from '../../api'; import type { Snippet } from '../../api'; -import { Breadcrumbs, Button, Card, ContextMenu, MarkdownContent, Timestamp, UuidBadge } from '../../ui'; +import { + Breadcrumbs, + Button, + Card, + ContextMenu, + MarkdownContent, + Modal, + Timestamp, + UuidBadge, +} from '../../ui'; import styles from '../Page.module.css'; function firstParagraphBlocks(markdown: string, limit = 2): { preview: string; truncated: boolean } { @@ -105,6 +114,8 @@ export function SnippetsPage() { const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + const [deleteId, setDeleteId] = useState(null); + const [deleting, setDeleting] = useState(false); const fileInputRef = useRef(null); const load = () => { @@ -119,9 +130,20 @@ export function SnippetsPage() { load(); }, []); - const handleDelete = async (id: string) => { - await snippets.remove(id); - load(); + const requestDelete = (id: string) => { + setDeleteId(id); + }; + + const handleDelete = async () => { + if (!deleteId) return; + setDeleting(true); + try { + await snippets.remove(deleteId); + load(); + setDeleteId(null); + } finally { + setDeleting(false); + } }; const handleImport = async (e: React.ChangeEvent) => { @@ -162,12 +184,30 @@ export function SnippetsPage() { {!loading && (
{items.map((s) => ( - + ))} {items.length === 0 &&

{t('snippets.empty')}

}
)} + + !deleting && setDeleteId(null)} + footer={( + <> + + + + )} + > + Are you sure you want to delete this snippet? + ); } diff --git a/client/src/ui/Modal/Modal.module.css b/client/src/ui/Modal/Modal.module.css new file mode 100644 index 0000000..dc9dedc --- /dev/null +++ b/client/src/ui/Modal/Modal.module.css @@ -0,0 +1,41 @@ +.overlay { + position: fixed; + inset: 0; + z-index: 2000; + background: color-mix(in srgb, #000 45%, transparent); + display: flex; + align-items: center; + justify-content: center; + padding: var(--space-4); +} + +.dialog { + width: min(520px, 100%); + background-color: var(--color-bg-subtle, #ffffff); + border: var(--border-width) solid var(--color-border); + border-radius: var(--radius-lg); + box-shadow: 0 20px 48px color-mix(in srgb, #000 28%, transparent); + overflow: hidden; +} + +.header { + padding: var(--space-4) var(--space-5) var(--space-2); +} + +.title { + margin: 0; + font-size: var(--font-size-md); + color: var(--color-text); +} + +.body { + padding: var(--space-2) var(--space-5) var(--space-4); + color: var(--color-text-muted); +} + +.footer { + padding: var(--space-3) var(--space-5) var(--space-4); + display: flex; + justify-content: flex-end; + gap: var(--space-2); +} diff --git a/client/src/ui/Modal/Modal.tsx b/client/src/ui/Modal/Modal.tsx new file mode 100644 index 0000000..d15b971 --- /dev/null +++ b/client/src/ui/Modal/Modal.tsx @@ -0,0 +1,41 @@ +import { useEffect } from 'react'; +import styles from './Modal.module.css'; + +export interface ModalProps { + open: boolean; + title: string; + children?: React.ReactNode; + onClose: () => void; + footer?: React.ReactNode; +} + +export function Modal({ open, title, children, onClose, footer }: ModalProps) { + useEffect(() => { + if (!open) return; + const onKeyDown = (event: KeyboardEvent) => { + if (event.key === 'Escape') onClose(); + }; + window.addEventListener('keydown', onKeyDown); + return () => window.removeEventListener('keydown', onKeyDown); + }, [open, onClose]); + + if (!open) return null; + + return ( +
+
event.stopPropagation()} + > +
+

{title}

+
+
{children}
+ {footer &&
{footer}
} +
+
+ ); +} diff --git a/client/src/ui/index.ts b/client/src/ui/index.ts index d6811de..dd04e47 100644 --- a/client/src/ui/index.ts +++ b/client/src/ui/index.ts @@ -57,6 +57,9 @@ export type { DescriptionListProps, DescriptionListItem } from './DescriptionLis export { Notification } from './Notification/Notification'; export type { NotificationProps, NotificationVariant } from './Notification/Notification'; +export { Modal } from './Modal/Modal'; +export type { ModalProps } from './Modal/Modal'; + export { Table } from './Table/Table'; export type { TableProps, TableColumn } from './Table/Table'; diff --git a/client/tsconfig.tsbuildinfo b/client/tsconfig.tsbuildinfo index cddbcfb..9bd4b25 100644 --- a/client/tsconfig.tsbuildinfo +++ b/client/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/App.tsx","./src/declarations.d.ts","./src/main.tsx","./src/api/client.ts","./src/api/index.ts","./src/api/types.ts","./src/hooks/useTheme.ts","./src/i18n/index.ts","./src/lib/hljs.ts","./src/lib/toast-events.ts","./src/pages/credential/CreateCredentialPage.tsx","./src/pages/credential/CredentialDetailPage.tsx","./src/pages/credential/CredentialsPage.tsx","./src/pages/credential/EditCredentialPage.tsx","./src/pages/environment/CreateEnvironmentPage.tsx","./src/pages/environment/EditEnvironmentPage.tsx","./src/pages/environment/EnvironmentDetailPage.tsx","./src/pages/environment/EnvironmentsPage.tsx","./src/pages/run/AllRunsPage.tsx","./src/pages/run/RunDetailPage.tsx","./src/pages/run/RunsPage.tsx","./src/pages/scenario/CreateScenarioPage.tsx","./src/pages/scenario/CreateStepPage.tsx","./src/pages/scenario/EditScenarioPage.tsx","./src/pages/scenario/EditStepPage.tsx","./src/pages/scenario/ScenarioDetailPage.tsx","./src/pages/scenario/ScenariosPage.tsx","./src/pages/session/SessionDetailPage.tsx","./src/pages/session/SessionsPage.tsx","./src/pages/snippet/CreateSnippetPage.tsx","./src/pages/snippet/EditSnippetPage.tsx","./src/pages/snippet/SnippetDetailPage.tsx","./src/pages/snippet/SnippetsPage.tsx","./src/ui/index.ts","./src/ui/AutoRefreshIndicator/AutoRefreshIndicator.tsx","./src/ui/Badge/Badge.tsx","./src/ui/Breadcrumbs/Breadcrumbs.tsx","./src/ui/Button/Button.tsx","./src/ui/Card/Card.tsx","./src/ui/CodeBlock/CodeBlock.tsx","./src/ui/CodeEditor/CodeEditor.tsx","./src/ui/ContextMenu/ContextMenu.tsx","./src/ui/DescriptionList/DescriptionList.tsx","./src/ui/Input/Input.tsx","./src/ui/MarkdownContent/MarkdownContent.tsx","./src/ui/Notification/Notification.tsx","./src/ui/Pagination/Pagination.tsx","./src/ui/Search/Search.tsx","./src/ui/Select/Select.tsx","./src/ui/SidePanel/SidePanel.tsx","./src/ui/Table/Table.tsx","./src/ui/Textarea/Textarea.tsx","./src/ui/ThemeSwitcher/ThemeSwitcher.tsx","./src/ui/Timestamp/Timestamp.tsx","./src/ui/Toast/ToastProvider.tsx","./src/ui/UuidBadge/UuidBadge.tsx"],"version":"5.8.3"} \ No newline at end of file +{"root":["./src/App.tsx","./src/declarations.d.ts","./src/main.tsx","./src/api/client.ts","./src/api/index.ts","./src/api/types.ts","./src/hooks/useTheme.ts","./src/i18n/index.ts","./src/lib/hljs.ts","./src/lib/toast-events.ts","./src/pages/credential/CreateCredentialPage.tsx","./src/pages/credential/CredentialDetailPage.tsx","./src/pages/credential/CredentialsPage.tsx","./src/pages/credential/EditCredentialPage.tsx","./src/pages/environment/CreateEnvironmentPage.tsx","./src/pages/environment/EditEnvironmentPage.tsx","./src/pages/environment/EnvironmentDetailPage.tsx","./src/pages/environment/EnvironmentsPage.tsx","./src/pages/run/AllRunsPage.tsx","./src/pages/run/RunDetailPage.tsx","./src/pages/run/RunsPage.tsx","./src/pages/scenario/CreateScenarioPage.tsx","./src/pages/scenario/CreateStepPage.tsx","./src/pages/scenario/EditScenarioPage.tsx","./src/pages/scenario/EditStepPage.tsx","./src/pages/scenario/ScenarioDetailPage.tsx","./src/pages/scenario/ScenariosPage.tsx","./src/pages/session/SessionDetailPage.tsx","./src/pages/session/SessionsPage.tsx","./src/pages/snippet/CreateSnippetPage.tsx","./src/pages/snippet/EditSnippetPage.tsx","./src/pages/snippet/SnippetDetailPage.tsx","./src/pages/snippet/SnippetsPage.tsx","./src/ui/index.ts","./src/ui/AutoRefreshIndicator/AutoRefreshIndicator.tsx","./src/ui/Badge/Badge.tsx","./src/ui/Breadcrumbs/Breadcrumbs.tsx","./src/ui/Button/Button.tsx","./src/ui/Card/Card.tsx","./src/ui/CodeBlock/CodeBlock.tsx","./src/ui/CodeEditor/CodeEditor.tsx","./src/ui/ContextMenu/ContextMenu.tsx","./src/ui/DescriptionList/DescriptionList.tsx","./src/ui/Input/Input.tsx","./src/ui/MarkdownContent/MarkdownContent.tsx","./src/ui/Modal/Modal.tsx","./src/ui/Notification/Notification.tsx","./src/ui/Pagination/Pagination.tsx","./src/ui/Search/Search.tsx","./src/ui/Select/Select.tsx","./src/ui/SidePanel/SidePanel.tsx","./src/ui/Table/Table.tsx","./src/ui/Textarea/Textarea.tsx","./src/ui/ThemeSwitcher/ThemeSwitcher.tsx","./src/ui/Timestamp/Timestamp.tsx","./src/ui/Toast/ToastProvider.tsx","./src/ui/UuidBadge/UuidBadge.tsx"],"version":"5.8.3"} \ No newline at end of file