diff --git a/.gitignore b/.gitignore index 8a535885..fdd7bb78 100644 --- a/.gitignore +++ b/.gitignore @@ -32,5 +32,6 @@ ui/web/pnpm-lock.yaml .mcp.json tmp +.claude/ deploy-*.sh \ No newline at end of file diff --git a/ui/web/src/components/shared/loading-skeleton.tsx b/ui/web/src/components/shared/loading-skeleton.tsx index db7408cf..ed827831 100644 --- a/ui/web/src/components/shared/loading-skeleton.tsx +++ b/ui/web/src/components/shared/loading-skeleton.tsx @@ -1,3 +1,4 @@ +import { useState, useEffect } from "react"; import { Skeleton } from "@/components/ui/skeleton"; export function CardSkeleton() { @@ -37,3 +38,21 @@ export function DetailSkeleton() { ); } + +/** Spinner that only appears after a delay (default 500ms) to avoid flicker on fast loads. */ +export function DeferredSpinner({ delay = 500 }: { delay?: number }) { + const [show, setShow] = useState(false); + + useEffect(() => { + const timer = setTimeout(() => setShow(true), delay); + return () => clearTimeout(timer); + }, [delay]); + + if (!show) return null; + + return ( +
+
+
+ ); +} diff --git a/ui/web/src/hooks/use-deferred-loading.ts b/ui/web/src/hooks/use-deferred-loading.ts new file mode 100644 index 00000000..d9e272e2 --- /dev/null +++ b/ui/web/src/hooks/use-deferred-loading.ts @@ -0,0 +1,58 @@ +import { useState, useEffect, useRef } from "react"; + +/** + * Anti-flicker loading state for skeleton displays. + * + * Phase 1 (delay): waits `delayMs` before returning true. + * If loading finishes within this window, skeleton is never shown. + * + * Phase 2 (min display): once shown, stays visible for at least `minDisplayMs`. + */ +export function useDeferredLoading( + loading: boolean, + delayMs = 200, + minDisplayMs = 400, +): boolean { + const [show, setShow] = useState(false); + const delayTimer = useRef | null>(null); + const minTimer = useRef | null>(null); + const shownAt = useRef(null); + + useEffect(() => { + if (loading) { + if (!show && !delayTimer.current) { + delayTimer.current = setTimeout(() => { + delayTimer.current = null; + shownAt.current = Date.now(); + setShow(true); + }, delayMs); + } + } else { + if (delayTimer.current) { + clearTimeout(delayTimer.current); + delayTimer.current = null; + } + + if (show && shownAt.current !== null) { + const remaining = minDisplayMs - (Date.now() - shownAt.current); + if (remaining > 0) { + minTimer.current = setTimeout(() => { + minTimer.current = null; + shownAt.current = null; + setShow(false); + }, remaining); + } else { + shownAt.current = null; + setShow(false); + } + } + } + + return () => { + if (delayTimer.current) clearTimeout(delayTimer.current); + if (minTimer.current) clearTimeout(minTimer.current); + }; + }, [loading, delayMs, minDisplayMs, show]); + + return show; +} diff --git a/ui/web/src/pages/agents/agent-detail/agent-detail-page.tsx b/ui/web/src/pages/agents/agent-detail/agent-detail-page.tsx index ccb1f04b..6d9cfcd5 100644 --- a/ui/web/src/pages/agents/agent-detail/agent-detail-page.tsx +++ b/ui/web/src/pages/agents/agent-detail/agent-detail-page.tsx @@ -8,7 +8,7 @@ import { AgentGeneralTab } from "./agent-general-tab"; import { AgentConfigTab } from "./agent-config-tab"; import { AgentFilesTab } from "./agent-files-tab"; import { AgentSharesTab } from "./agent-shares-tab"; -import { DetailSkeleton } from "@/components/shared/loading-skeleton"; +import { DeferredSpinner } from "@/components/shared/loading-skeleton"; interface AgentDetailPageProps { agentId: string; @@ -41,7 +41,7 @@ export function AgentDetailPage({ agentId, onBack }: AgentDetailPageProps) { - +
); } diff --git a/ui/web/src/pages/agents/agents-page.tsx b/ui/web/src/pages/agents/agents-page.tsx index d598ee8b..fe7bb937 100644 --- a/ui/web/src/pages/agents/agents-page.tsx +++ b/ui/web/src/pages/agents/agents-page.tsx @@ -6,6 +6,7 @@ import { EmptyState } from "@/components/shared/empty-state"; import { SearchInput } from "@/components/shared/search-input"; import { Pagination } from "@/components/shared/pagination"; import { CardSkeleton } from "@/components/shared/loading-skeleton"; +import { useDeferredLoading } from "@/hooks/use-deferred-loading"; import { Button } from "@/components/ui/button"; import { ConfirmDialog } from "@/components/shared/confirm-dialog"; import { useAgents } from "./hooks/use-agents"; @@ -18,6 +19,7 @@ export function AgentsPage() { const { id: detailId } = useParams<{ id: string }>(); const navigate = useNavigate(); const { agents, loading, createAgent, deleteAgent, refresh } = useAgents(); + const showSkeleton = useDeferredLoading(loading && agents.length === 0); const [search, setSearch] = useState(""); const [createOpen, setCreateOpen] = useState(false); @@ -67,7 +69,7 @@ export function AgentsPage() {
- {loading && agents.length === 0 ? ( + {showSkeleton ? (
{Array.from({ length: 6 }).map((_, i) => ( diff --git a/ui/web/src/pages/approvals/approvals-page.tsx b/ui/web/src/pages/approvals/approvals-page.tsx index 473d18a4..390ee2a5 100644 --- a/ui/web/src/pages/approvals/approvals-page.tsx +++ b/ui/web/src/pages/approvals/approvals-page.tsx @@ -9,10 +9,12 @@ import { ConfirmDialog } from "@/components/shared/confirm-dialog"; import { formatRelativeTime } from "@/lib/format"; import { useApprovals, type PendingApproval } from "./hooks/use-approvals"; import { useMinLoading } from "@/hooks/use-min-loading"; +import { useDeferredLoading } from "@/hooks/use-deferred-loading"; export function ApprovalsPage() { const { pending, loading, refresh, approve, deny } = useApprovals(); const spinning = useMinLoading(loading); + const showSkeleton = useDeferredLoading(loading && pending.length === 0); const [denyTarget, setDenyTarget] = useState(null); const [approveTarget, setApproveTarget] = useState<{ approval: PendingApproval; always: boolean } | null>(null); @@ -34,7 +36,7 @@ export function ApprovalsPage() { />
- {loading && pending.length === 0 ? ( + {showSkeleton ? ( ) : pending.length === 0 ? (
- {loading && instances.length === 0 ? ( + {showSkeleton ? ( ) : filtered.length === 0 ? ( = { @@ -25,6 +26,7 @@ interface ChannelsStatusViewProps { export function ChannelsStatusView({ channels, loading, spinning, refresh }: ChannelsStatusViewProps) { const entries = Object.entries(channels); + const showSkeleton = useDeferredLoading(loading && entries.length === 0); return (
@@ -39,7 +41,7 @@ export function ChannelsStatusView({ channels, loading, spinning, refresh }: Cha />
- {loading && entries.length === 0 ? ( + {showSkeleton ? (
{[1, 2, 3].map((i) => ( diff --git a/ui/web/src/pages/config/config-page.tsx b/ui/web/src/pages/config/config-page.tsx index 1aed5dd9..bd47af7c 100644 --- a/ui/web/src/pages/config/config-page.tsx +++ b/ui/web/src/pages/config/config-page.tsx @@ -11,6 +11,7 @@ import { EmptyState } from "@/components/shared/empty-state"; import { DetailSkeleton } from "@/components/shared/loading-skeleton"; import { useConfig } from "./hooks/use-config"; import { useMinLoading } from "@/hooks/use-min-loading"; +import { useDeferredLoading } from "@/hooks/use-deferred-loading"; import { ROUTES } from "@/lib/constants"; import { GatewaySection } from "./sections/gateway-section"; import { ProvidersSection } from "./sections/providers-section"; @@ -26,6 +27,7 @@ import { BindingsSection } from "./sections/bindings-section"; export function ConfigPage() { const { config, hash, configPath, loading, saving, error, refresh, applyRaw, patch } = useConfig(); const spinning = useMinLoading(loading); + const showSkeleton = useDeferredLoading(loading && !config); const [rawText, setRawText] = useState(""); const [dirty, setDirty] = useState(false); const [saveError, setSaveError] = useState(null); @@ -49,7 +51,7 @@ export function ConfigPage() { } }; - if (loading && !config) { + if (showSkeleton) { return (
diff --git a/ui/web/src/pages/cron/cron-page.tsx b/ui/web/src/pages/cron/cron-page.tsx index 46ef60d0..31580fe5 100644 --- a/ui/web/src/pages/cron/cron-page.tsx +++ b/ui/web/src/pages/cron/cron-page.tsx @@ -12,6 +12,7 @@ import { useCron, type CronJob, type CronRunLogEntry } from "./hooks/use-cron"; import { CronFormDialog } from "./cron-form-dialog"; import { CronRunLogDialog } from "./cron-run-log-dialog"; import { useMinLoading } from "@/hooks/use-min-loading"; +import { useDeferredLoading } from "@/hooks/use-deferred-loading"; import { usePagination } from "@/hooks/use-pagination"; function formatSchedule(job: CronJob): string { @@ -30,6 +31,7 @@ function formatSchedule(job: CronJob): string { export function CronPage() { const { jobs, loading, refresh, createJob, toggleJob, deleteJob, runJob, getRunLog } = useCron(); const spinning = useMinLoading(loading); + const showSkeleton = useDeferredLoading(loading && jobs.length === 0); const [showCreate, setShowCreate] = useState(false); const [deleteTarget, setDeleteTarget] = useState(null); const [runLogTarget, setRunLogTarget] = useState(null); @@ -68,7 +70,7 @@ export function CronPage() { />
- {loading && jobs.length === 0 ? ( + {showSkeleton ? ( ) : jobs.length === 0 ? ( (null); @@ -79,7 +81,7 @@ export function CustomToolsPage() {
- {loading && tools.length === 0 ? ( + {showSkeleton ? ( ) : filtered.length === 0 ? ( = { @@ -23,6 +24,7 @@ const transportBadge: Record = { export function MCPPage() { const { servers, loading, refresh, createServer, updateServer, deleteServer, grantAgent, revokeAgent, listGrantsByAgent } = useMCP(); const spinning = useMinLoading(loading); + const showSkeleton = useDeferredLoading(loading && servers.length === 0); const [search, setSearch] = useState(""); const [formOpen, setFormOpen] = useState(false); const [editServer, setEditServer] = useState(null); @@ -87,7 +89,7 @@ export function MCPPage() {
- {loading && servers.length === 0 ? ( + {showSkeleton ? ( ) : filtered.length === 0 ? ( (null); const [approveTarget, setApproveTarget] = useState(null); - const isEmpty = pendingPairings.length === 0 && pairedDevices.length === 0; - return (
- {loading && isEmpty ? ( + {showSkeleton ? ( ) : isEmpty ? ( = { @@ -24,6 +25,7 @@ export function ProvidersPage() { createProvider, updateProvider, deleteProvider, } = useProviders(); const spinning = useMinLoading(loading); + const showSkeleton = useDeferredLoading(loading && providers.length === 0); const [search, setSearch] = useState(""); const [formOpen, setFormOpen] = useState(false); const [editProvider, setEditProvider] = useState(null); @@ -87,7 +89,7 @@ export function ProvidersPage() {
- {loading && providers.length === 0 ? ( + {showSkeleton ? ( ) : filtered.length === 0 ? ( (); const navigate = useNavigate(); const { sessions, total, loading, refresh, preview, deleteSession, resetSession } = useSessions(); + const showSkeleton = useDeferredLoading(loading && sessions.length === 0); const [search, setSearch] = useState(""); const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(20); @@ -65,7 +67,7 @@ export function SessionsPage() {
- {loading && sessions.length === 0 ? ( + {showSkeleton ? ( ) : filtered.length === 0 ? ( (null); const [detailLoading, setDetailLoading] = useState(false); @@ -88,7 +90,7 @@ export function SkillsPage() {
- {loading && skills.length === 0 ? ( + {showSkeleton ? ( ) : filtered.length === 0 ? ( (null); const [page, setPage] = useState(1); @@ -105,7 +107,7 @@ export function TracesPage() {
- {loading && traces.length === 0 ? ( + {showSkeleton ? ( ) : traces.length === 0 ? ( (tts); + const showSkeleton = useDeferredLoading(loading && !draft.provider); const [dirty, setDirty] = useState(false); useEffect(() => { @@ -58,7 +60,7 @@ export function TtsPage() { setDirty(false); }; - if (loading && !draft.provider) { + if (showSkeleton) { return (
diff --git a/ui/web/src/pages/usage/usage-page.tsx b/ui/web/src/pages/usage/usage-page.tsx index 92a775b5..2d3236f1 100644 --- a/ui/web/src/pages/usage/usage-page.tsx +++ b/ui/web/src/pages/usage/usage-page.tsx @@ -9,10 +9,12 @@ import { TableSkeleton } from "@/components/shared/loading-skeleton"; import { formatTokens } from "@/lib/format"; import { useUsage } from "./hooks/use-usage"; import { useMinLoading } from "@/hooks/use-min-loading"; +import { useDeferredLoading } from "@/hooks/use-deferred-loading"; export function UsagePage() { const { records, total, summary, loading, loadRecords, loadSummary } = useUsage(); const spinning = useMinLoading(loading); + const showSkeleton = useDeferredLoading(loading && records.length === 0); const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(20); @@ -45,7 +47,7 @@ export function UsagePage() { } /> - {loading && records.length === 0 ? ( + {showSkeleton ? (
diff --git a/ui/web/src/routes.tsx b/ui/web/src/routes.tsx index 3398dd63..becd7029 100644 --- a/ui/web/src/routes.tsx +++ b/ui/web/src/routes.tsx @@ -85,16 +85,16 @@ export function AppRoutes() { } /> } /> } /> - } /> - } /> - } /> - } /> - } /> - } /> + } /> + } /> + } /> + } /> + } /> + } /> } /> } /> - } /> - } /> + } /> + } /> } /> } /> } /> diff --git a/ui/web/tsconfig.tsbuildinfo b/ui/web/tsconfig.tsbuildinfo index 44580054..d55003bc 100644 --- a/ui/web/tsconfig.tsbuildinfo +++ b/ui/web/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/app.tsx","./src/main.tsx","./src/routes.tsx","./src/vite-env.d.ts","./src/api/errors.ts","./src/api/http-client.ts","./src/api/protocol.ts","./src/api/ws-client.ts","./src/components/chat/agent-selector.tsx","./src/components/chat/chat-input.tsx","./src/components/chat/message-bubble.tsx","./src/components/chat/message-content.tsx","./src/components/chat/rich-content.tsx","./src/components/chat/session-switcher.tsx","./src/components/chat/streaming-text.tsx","./src/components/chat/thinking-indicator.tsx","./src/components/chat/tool-call-card.tsx","./src/components/layout/app-layout.tsx","./src/components/layout/connection-status.tsx","./src/components/layout/sidebar-group.tsx","./src/components/layout/sidebar-item.tsx","./src/components/layout/sidebar.tsx","./src/components/layout/topbar.tsx","./src/components/providers/app-providers.tsx","./src/components/providers/theme-provider.tsx","./src/components/providers/ws-provider.tsx","./src/components/shared/confirm-dialog.tsx","./src/components/shared/empty-state.tsx","./src/components/shared/loading-skeleton.tsx","./src/components/shared/markdown-renderer.tsx","./src/components/shared/page-header.tsx","./src/components/shared/pagination.tsx","./src/components/shared/require-auth.tsx","./src/components/shared/search-input.tsx","./src/components/shared/status-badge.tsx","./src/components/ui/alert.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/card.tsx","./src/components/ui/dialog.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/scroll-area.tsx","./src/components/ui/select.tsx","./src/components/ui/separator.tsx","./src/components/ui/skeleton.tsx","./src/components/ui/switch.tsx","./src/components/ui/tabs.tsx","./src/components/ui/textarea.tsx","./src/components/ui/tooltip.tsx","./src/hooks/use-auto-scroll.ts","./src/hooks/use-clipboard.ts","./src/hooks/use-debounce.ts","./src/hooks/use-debounced-callback.ts","./src/hooks/use-media-query.ts","./src/hooks/use-min-loading.ts","./src/hooks/use-pagination.ts","./src/hooks/use-ws-call.ts","./src/hooks/use-ws-event.ts","./src/hooks/use-ws.ts","./src/lib/constants.ts","./src/lib/format.ts","./src/lib/session-key.ts","./src/lib/slug.ts","./src/lib/utils.ts","./src/pages/agents/agent-card.tsx","./src/pages/agents/agent-create-dialog.tsx","./src/pages/agents/agents-page.tsx","./src/pages/agents/agent-detail/agent-config-tab.tsx","./src/pages/agents/agent-detail/agent-detail-page.tsx","./src/pages/agents/agent-detail/agent-files-tab.tsx","./src/pages/agents/agent-detail/agent-general-tab.tsx","./src/pages/agents/agent-detail/agent-shares-tab.tsx","./src/pages/agents/agent-detail/config-sections/compaction-section.tsx","./src/pages/agents/agent-detail/config-sections/config-section.tsx","./src/pages/agents/agent-detail/config-sections/context-pruning-section.tsx","./src/pages/agents/agent-detail/config-sections/index.ts","./src/pages/agents/agent-detail/config-sections/memory-section.tsx","./src/pages/agents/agent-detail/config-sections/other-config-section.tsx","./src/pages/agents/agent-detail/config-sections/sandbox-section.tsx","./src/pages/agents/agent-detail/config-sections/subagents-section.tsx","./src/pages/agents/agent-detail/config-sections/tool-policy-section.tsx","./src/pages/agents/hooks/use-agent-detail.ts","./src/pages/agents/hooks/use-agents.ts","./src/pages/approvals/approvals-page.tsx","./src/pages/approvals/hooks/use-approvals.ts","./src/pages/channels/channel-fields.tsx","./src/pages/channels/channel-instance-form-dialog.tsx","./src/pages/channels/channel-schemas.ts","./src/pages/channels/channels-page.tsx","./src/pages/channels/channels-status-view.tsx","./src/pages/channels/hooks/use-channel-instances.ts","./src/pages/channels/hooks/use-channels.ts","./src/pages/chat/chat-page.tsx","./src/pages/chat/chat-sidebar.tsx","./src/pages/chat/chat-thread.tsx","./src/pages/chat/hooks/use-chat-messages.ts","./src/pages/chat/hooks/use-chat-send.ts","./src/pages/chat/hooks/use-chat-sessions.ts","./src/pages/config/config-page.tsx","./src/pages/config/hooks/use-config.ts","./src/pages/config/sections/agents-defaults-section.tsx","./src/pages/config/sections/bindings-section.tsx","./src/pages/config/sections/channels-section.tsx","./src/pages/config/sections/cron-section.tsx","./src/pages/config/sections/gateway-section.tsx","./src/pages/config/sections/providers-section.tsx","./src/pages/config/sections/sessions-section.tsx","./src/pages/config/sections/telemetry-section.tsx","./src/pages/config/sections/tools-section.tsx","./src/pages/config/sections/tts-section.tsx","./src/pages/cron/cron-form-dialog.tsx","./src/pages/cron/cron-page.tsx","./src/pages/cron/cron-run-log-dialog.tsx","./src/pages/cron/hooks/use-cron.ts","./src/pages/custom-tools/custom-tool-form-dialog.tsx","./src/pages/custom-tools/custom-tools-page.tsx","./src/pages/custom-tools/hooks/use-custom-tools.ts","./src/pages/login/login-layout.tsx","./src/pages/login/login-page.tsx","./src/pages/login/login-tabs.tsx","./src/pages/login/pairing-form.tsx","./src/pages/login/token-form.tsx","./src/pages/logs/logs-page.tsx","./src/pages/logs/hooks/use-logs.ts","./src/pages/mcp/mcp-form-dialog.tsx","./src/pages/mcp/mcp-grants-dialog.tsx","./src/pages/mcp/mcp-page.tsx","./src/pages/mcp/hooks/use-mcp.ts","./src/pages/nodes/nodes-page.tsx","./src/pages/nodes/hooks/use-nodes.ts","./src/pages/overview/overview-page.tsx","./src/pages/providers/provider-form-dialog.tsx","./src/pages/providers/providers-page.tsx","./src/pages/providers/hooks/use-providers.ts","./src/pages/sessions/session-detail-page.tsx","./src/pages/sessions/sessions-page.tsx","./src/pages/sessions/hooks/use-sessions.ts","./src/pages/skills/skill-detail-dialog.tsx","./src/pages/skills/skill-upload-dialog.tsx","./src/pages/skills/skills-page.tsx","./src/pages/skills/hooks/use-skills.ts","./src/pages/traces/trace-detail-dialog.tsx","./src/pages/traces/traces-page.tsx","./src/pages/traces/hooks/use-traces.ts","./src/pages/tts/tts-page.tsx","./src/pages/tts/hooks/use-tts-config.ts","./src/pages/usage/usage-page.tsx","./src/pages/usage/hooks/use-usage.ts","./src/stores/use-auth-store.ts","./src/stores/use-ui-store.ts","./src/types/agent.ts","./src/types/chat.ts","./src/types/session.ts"],"version":"5.7.3"} \ No newline at end of file +{"root":["./src/app.tsx","./src/main.tsx","./src/routes.tsx","./src/vite-env.d.ts","./src/api/errors.ts","./src/api/http-client.ts","./src/api/protocol.ts","./src/api/ws-client.ts","./src/components/chat/agent-selector.tsx","./src/components/chat/chat-input.tsx","./src/components/chat/message-bubble.tsx","./src/components/chat/message-content.tsx","./src/components/chat/rich-content.tsx","./src/components/chat/session-switcher.tsx","./src/components/chat/streaming-text.tsx","./src/components/chat/thinking-indicator.tsx","./src/components/chat/tool-call-card.tsx","./src/components/layout/app-layout.tsx","./src/components/layout/connection-status.tsx","./src/components/layout/sidebar-group.tsx","./src/components/layout/sidebar-item.tsx","./src/components/layout/sidebar.tsx","./src/components/layout/topbar.tsx","./src/components/providers/app-providers.tsx","./src/components/providers/theme-provider.tsx","./src/components/providers/ws-provider.tsx","./src/components/shared/confirm-dialog.tsx","./src/components/shared/empty-state.tsx","./src/components/shared/loading-skeleton.tsx","./src/components/shared/markdown-renderer.tsx","./src/components/shared/page-header.tsx","./src/components/shared/pagination.tsx","./src/components/shared/require-auth.tsx","./src/components/shared/search-input.tsx","./src/components/shared/status-badge.tsx","./src/components/ui/alert.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/card.tsx","./src/components/ui/dialog.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/scroll-area.tsx","./src/components/ui/select.tsx","./src/components/ui/separator.tsx","./src/components/ui/skeleton.tsx","./src/components/ui/switch.tsx","./src/components/ui/tabs.tsx","./src/components/ui/textarea.tsx","./src/components/ui/tooltip.tsx","./src/hooks/use-auto-scroll.ts","./src/hooks/use-clipboard.ts","./src/hooks/use-debounce.ts","./src/hooks/use-debounced-callback.ts","./src/hooks/use-deferred-loading.ts","./src/hooks/use-media-query.ts","./src/hooks/use-min-loading.ts","./src/hooks/use-pagination.ts","./src/hooks/use-ws-call.ts","./src/hooks/use-ws-event.ts","./src/hooks/use-ws.ts","./src/lib/constants.ts","./src/lib/format.ts","./src/lib/session-key.ts","./src/lib/slug.ts","./src/lib/utils.ts","./src/pages/agents/agent-card.tsx","./src/pages/agents/agent-create-dialog.tsx","./src/pages/agents/agents-page.tsx","./src/pages/agents/agent-detail/agent-config-tab.tsx","./src/pages/agents/agent-detail/agent-detail-page.tsx","./src/pages/agents/agent-detail/agent-files-tab.tsx","./src/pages/agents/agent-detail/agent-general-tab.tsx","./src/pages/agents/agent-detail/agent-shares-tab.tsx","./src/pages/agents/agent-detail/config-sections/compaction-section.tsx","./src/pages/agents/agent-detail/config-sections/config-section.tsx","./src/pages/agents/agent-detail/config-sections/context-pruning-section.tsx","./src/pages/agents/agent-detail/config-sections/index.ts","./src/pages/agents/agent-detail/config-sections/memory-section.tsx","./src/pages/agents/agent-detail/config-sections/other-config-section.tsx","./src/pages/agents/agent-detail/config-sections/sandbox-section.tsx","./src/pages/agents/agent-detail/config-sections/subagents-section.tsx","./src/pages/agents/agent-detail/config-sections/tool-policy-section.tsx","./src/pages/agents/hooks/use-agent-detail.ts","./src/pages/agents/hooks/use-agents.ts","./src/pages/approvals/approvals-page.tsx","./src/pages/approvals/hooks/use-approvals.ts","./src/pages/channels/channel-fields.tsx","./src/pages/channels/channel-instance-form-dialog.tsx","./src/pages/channels/channel-schemas.ts","./src/pages/channels/channels-page.tsx","./src/pages/channels/channels-status-view.tsx","./src/pages/channels/hooks/use-channel-instances.ts","./src/pages/channels/hooks/use-channels.ts","./src/pages/chat/chat-page.tsx","./src/pages/chat/chat-sidebar.tsx","./src/pages/chat/chat-thread.tsx","./src/pages/chat/hooks/use-chat-messages.ts","./src/pages/chat/hooks/use-chat-send.ts","./src/pages/chat/hooks/use-chat-sessions.ts","./src/pages/config/config-page.tsx","./src/pages/config/hooks/use-config.ts","./src/pages/config/sections/agents-defaults-section.tsx","./src/pages/config/sections/bindings-section.tsx","./src/pages/config/sections/channels-section.tsx","./src/pages/config/sections/cron-section.tsx","./src/pages/config/sections/gateway-section.tsx","./src/pages/config/sections/providers-section.tsx","./src/pages/config/sections/sessions-section.tsx","./src/pages/config/sections/telemetry-section.tsx","./src/pages/config/sections/tools-section.tsx","./src/pages/config/sections/tts-section.tsx","./src/pages/cron/cron-form-dialog.tsx","./src/pages/cron/cron-page.tsx","./src/pages/cron/cron-run-log-dialog.tsx","./src/pages/cron/hooks/use-cron.ts","./src/pages/custom-tools/custom-tool-form-dialog.tsx","./src/pages/custom-tools/custom-tools-page.tsx","./src/pages/custom-tools/hooks/use-custom-tools.ts","./src/pages/login/login-layout.tsx","./src/pages/login/login-page.tsx","./src/pages/login/login-tabs.tsx","./src/pages/login/pairing-form.tsx","./src/pages/login/token-form.tsx","./src/pages/logs/logs-page.tsx","./src/pages/logs/hooks/use-logs.ts","./src/pages/mcp/mcp-form-dialog.tsx","./src/pages/mcp/mcp-grants-dialog.tsx","./src/pages/mcp/mcp-page.tsx","./src/pages/mcp/hooks/use-mcp.ts","./src/pages/nodes/nodes-page.tsx","./src/pages/nodes/hooks/use-nodes.ts","./src/pages/overview/overview-page.tsx","./src/pages/providers/provider-form-dialog.tsx","./src/pages/providers/providers-page.tsx","./src/pages/providers/hooks/use-providers.ts","./src/pages/sessions/session-detail-page.tsx","./src/pages/sessions/sessions-page.tsx","./src/pages/sessions/hooks/use-sessions.ts","./src/pages/skills/skill-detail-dialog.tsx","./src/pages/skills/skill-upload-dialog.tsx","./src/pages/skills/skills-page.tsx","./src/pages/skills/hooks/use-skills.ts","./src/pages/traces/trace-detail-dialog.tsx","./src/pages/traces/traces-page.tsx","./src/pages/traces/hooks/use-traces.ts","./src/pages/tts/tts-page.tsx","./src/pages/tts/hooks/use-tts-config.ts","./src/pages/usage/usage-page.tsx","./src/pages/usage/hooks/use-usage.ts","./src/stores/use-auth-store.ts","./src/stores/use-ui-store.ts","./src/types/agent.ts","./src/types/chat.ts","./src/types/session.ts"],"version":"5.7.3"} \ No newline at end of file