diff --git a/src/components/SupportWidget.astro b/src/components/SupportWidget.astro deleted file mode 100644 index 549969d..0000000 --- a/src/components/SupportWidget.astro +++ /dev/null @@ -1,25 +0,0 @@ ---- -/** - * SupportWidget — top-level wrapper for the SiteAtlas Support Chat Widget. - * - * Imported in BaseLayout.astro. Only renders when PUBLIC_SUPPORT_API_URL is set. - * Passes the configured API URL and product slug to the ChatWidget. - */ -import ChatWidget from './support/ChatWidget.astro'; - -interface Props { - productSlug?: string; -} - -const { productSlug = 'siteatlas' } = Astro.props; - -// Graceful degradation: render nothing if the API URL is not configured. -// The ChatWidget itself also handles the missing-URL case, but we short-circuit -// here to avoid shipping the widget JS/CSS to pages where support isn't wired up. -const apiUrl = import.meta.env.PUBLIC_SUPPORT_API_URL ?? ''; -const shouldRender = apiUrl.length > 0; ---- - -{shouldRender && ( - -)} diff --git a/src/components/support/ChatWidget.astro b/src/components/support/ChatWidget.astro deleted file mode 100644 index 1728e11..0000000 --- a/src/components/support/ChatWidget.astro +++ /dev/null @@ -1,761 +0,0 @@ ---- -/** - * SiteAtlas Support Chat Widget - * Vanilla JS implementation — no React dependency required. - * Connects to the SupportAtlas SSE-based API. - */ -import './ChatWidget.css'; - -interface Props { - apiUrl: string; - productSlug: string; -} - -const { apiUrl, productSlug } = Astro.props; ---- - - - - - diff --git a/src/components/support/ChatWidget.css b/src/components/support/ChatWidget.css deleted file mode 100644 index 48789e0..0000000 --- a/src/components/support/ChatWidget.css +++ /dev/null @@ -1,683 +0,0 @@ -/* ============================================================ - SiteAtlas Support Chat Widget — Styles - Dark theme with amber/gold accents matching SiteAtlas design - ============================================================ */ - -/* ----- Slide-up animation for panel ----- */ -@keyframes sw-slide-up { - from { - opacity: 0; - transform: translateY(12px) scale(0.97); - } - to { - opacity: 1; - transform: translateY(0) scale(1); - } -} - -@keyframes sw-slide-down { - from { - opacity: 1; - transform: translateY(0) scale(1); - } - to { - opacity: 0; - transform: translateY(12px) scale(0.97); - } -} - -/* ----- Pulse for unread indicator ----- */ -@keyframes sw-pulse { - 0%, 100% { - opacity: 1; - transform: scale(1); - } - 50% { - opacity: 0.7; - transform: scale(1.2); - } -} - -/* ----- Typing dots animation ----- */ -@keyframes sw-dot-bounce { - 0%, 60%, 100% { - transform: translateY(0); - opacity: 0.4; - } - 30% { - transform: translateY(-5px); - opacity: 1; - } -} - -/* ----- Token streaming fade-in ----- */ -@keyframes sw-token-in { - from { opacity: 0; } - to { opacity: 1; } -} - -/* ----- Widget container ----- */ -#support-widget { - position: fixed; - bottom: 24px; - right: 24px; - z-index: 9999; - font-family: "Inter", ui-sans-serif, system-ui, sans-serif; - font-size: 14px; - line-height: 1.5; - color: #D4D4D4; -} - -/* ----- Floating bubble button ----- */ -#sw-bubble { - width: 56px; - height: 56px; - border-radius: 50%; - background: linear-gradient(135deg, #D97706, #F59E0B); - border: none; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - box-shadow: 0 4px 20px rgba(245, 158, 11, 0.35), 0 2px 8px rgba(0, 0, 0, 0.5); - transition: transform 0.2s ease, box-shadow 0.2s ease; - position: relative; - outline: none; -} - -#sw-bubble:hover { - transform: scale(1.08); - box-shadow: 0 6px 28px rgba(245, 158, 11, 0.45), 0 2px 10px rgba(0, 0, 0, 0.5); -} - -#sw-bubble:focus-visible { - outline: 2px solid #F59E0B; - outline-offset: 3px; -} - -#sw-bubble:active { - transform: scale(0.96); -} - -/* ----- Unread dot ----- */ -#sw-unread-dot { - position: absolute; - top: 2px; - right: 2px; - width: 12px; - height: 12px; - background: #F85149; - border-radius: 50%; - border: 2px solid #0A0A0A; - animation: sw-pulse 2s ease-in-out infinite; -} - -#sw-unread-dot.hidden { - display: none; -} - -/* ----- Chat panel ----- */ -#sw-panel { - position: absolute; - bottom: 68px; - right: 0; - width: 380px; - height: 520px; - background: #111111; - border: 1px solid #1F1F1F; - border-radius: 16px; - box-shadow: 0 25px 50px rgba(0, 0, 0, 0.7), 0 8px 20px rgba(0, 0, 0, 0.5); - display: flex; - flex-direction: column; - overflow: hidden; - animation: sw-slide-up 0.25s ease forwards; -} - -#sw-panel.sw-closing { - animation: sw-slide-down 0.2s ease forwards; -} - -#sw-panel.hidden { - display: none; -} - -/* Mobile: full-width panel */ -@media (max-width: 480px) { - #support-widget { - bottom: 16px; - right: 16px; - left: 16px; - } - - #sw-panel { - width: 100%; - right: 0; - left: 0; - height: 480px; - bottom: 68px; - } -} - -/* ----- Panel header ----- */ -#sw-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 14px 16px; - background: #0A0A0A; - border-bottom: 1px solid #1F1F1F; - flex-shrink: 0; -} - -.sw-header-title { - display: flex; - align-items: center; - gap: 8px; -} - -.sw-header-icon { - width: 28px; - height: 28px; - background: linear-gradient(135deg, #D97706, #F59E0B); - border-radius: 8px; - display: flex; - align-items: center; - justify-content: center; -} - -.sw-header-icon svg { - width: 16px; - height: 16px; - color: #0A0A0A; -} - -.sw-title-text { - display: flex; - flex-direction: column; - gap: 1px; -} - -.sw-title-name { - font-weight: 600; - font-size: 13px; - color: #E0E0E0; - letter-spacing: 0.01em; -} - -.sw-title-status { - font-size: 11px; - color: #3FB950; - display: flex; - align-items: center; - gap: 4px; -} - -.sw-status-dot { - width: 6px; - height: 6px; - background: #3FB950; - border-radius: 50%; -} - -.sw-status-dot.offline { - background: #8B949E; -} - -#sw-close-btn { - background: none; - border: none; - cursor: pointer; - color: #8B949E; - padding: 4px; - border-radius: 6px; - display: flex; - align-items: center; - justify-content: center; - transition: color 0.15s, background 0.15s; - outline: none; -} - -#sw-close-btn:hover { - color: #D4D4D4; - background: #1F1F1F; -} - -#sw-close-btn:focus-visible { - outline: 2px solid #F59E0B; - outline-offset: 2px; -} - -/* ----- Messages area ----- */ -#sw-messages { - flex: 1; - overflow-y: auto; - padding: 12px 14px; - display: flex; - flex-direction: column; - gap: 8px; - scroll-behavior: smooth; -} - -/* Custom scrollbar */ -#sw-messages::-webkit-scrollbar { - width: 4px; -} - -#sw-messages::-webkit-scrollbar-track { - background: transparent; -} - -#sw-messages::-webkit-scrollbar-thumb { - background: #2A2A2A; - border-radius: 4px; -} - -#sw-messages::-webkit-scrollbar-thumb:hover { - background: #3A3A3A; -} - -/* ----- Message bubbles ----- */ -.sw-msg { - display: flex; - flex-direction: column; - gap: 2px; - max-width: 88%; -} - -.sw-msg-user { - align-self: flex-end; - align-items: flex-end; -} - -.sw-msg-assistant { - align-self: flex-start; - align-items: flex-start; -} - -.sw-msg-system { - align-self: center; - align-items: center; - max-width: 100%; -} - -.sw-bubble-text { - padding: 8px 12px; - border-radius: 12px; - font-size: 13.5px; - line-height: 1.55; - word-break: break-word; -} - -.sw-msg-user .sw-bubble-text { - background: linear-gradient(135deg, #B45309, #D97706); - color: #FEF3C7; - border-bottom-right-radius: 3px; -} - -.sw-msg-assistant .sw-bubble-text { - background: #1A1A1A; - border: 1px solid #252525; - color: #D4D4D4; - border-bottom-left-radius: 3px; -} - -.sw-msg-system .sw-bubble-text { - background: transparent; - color: #8B949E; - font-size: 12px; - text-align: center; - border: none; - padding: 4px 0; -} - -/* Streaming cursor blink */ -.sw-streaming-cursor::after { - content: '▊'; - animation: sw-pulse 1s ease-in-out infinite; - color: #F59E0B; - font-size: 11px; - margin-left: 1px; -} - -/* ----- Timestamp ----- */ -.sw-msg-time { - font-size: 10px; - color: #4A4A4A; - padding: 0 4px; -} - -/* ----- Agent dispatch badges ----- */ -.sw-badges { - display: flex; - flex-wrap: wrap; - gap: 4px; - margin-top: 4px; - padding: 0 2px; -} - -.sw-badge { - display: inline-flex; - align-items: center; - gap: 4px; - padding: 2px 8px; - border-radius: 20px; - font-size: 11px; - font-weight: 500; - border: 1px solid; -} - -.sw-badge-running { - background: rgba(245, 158, 11, 0.1); - border-color: rgba(245, 158, 11, 0.3); - color: #F59E0B; -} - -.sw-badge-done { - background: rgba(63, 185, 80, 0.1); - border-color: rgba(63, 185, 80, 0.3); - color: #3FB950; -} - -.sw-badge-spinner { - width: 8px; - height: 8px; - border: 1.5px solid rgba(245, 158, 11, 0.4); - border-top-color: #F59E0B; - border-radius: 50%; - animation: spin 0.8s linear infinite; - flex-shrink: 0; -} - -@keyframes spin { - to { transform: rotate(360deg); } -} - -.sw-badge-check { - width: 8px; - height: 8px; - flex-shrink: 0; -} - -/* ----- Action cards (write approval) ----- */ -.sw-action-card { - margin-top: 6px; - padding: 10px 12px; - background: #0F0F0F; - border: 1px solid rgba(245, 158, 11, 0.25); - border-radius: 10px; - display: flex; - flex-direction: column; - gap: 8px; -} - -.sw-action-label { - font-size: 11px; - font-weight: 600; - color: #F59E0B; - text-transform: uppercase; - letter-spacing: 0.06em; -} - -.sw-action-desc { - font-size: 12.5px; - color: #B0B0B0; - line-height: 1.4; -} - -.sw-action-buttons { - display: flex; - gap: 8px; -} - -.sw-action-btn { - padding: 5px 14px; - border-radius: 6px; - font-size: 12px; - font-weight: 500; - border: none; - cursor: pointer; - transition: opacity 0.15s, transform 0.1s; - outline: none; -} - -.sw-action-btn:active { - transform: scale(0.97); -} - -.sw-action-btn:focus-visible { - outline: 2px solid #F59E0B; - outline-offset: 2px; -} - -.sw-action-approve { - background: linear-gradient(135deg, #D97706, #F59E0B); - color: #0A0A0A; -} - -.sw-action-approve:hover { - opacity: 0.9; -} - -.sw-action-reject { - background: #1F1F1F; - color: #8B949E; - border: 1px solid #2A2A2A; -} - -.sw-action-reject:hover { - color: #D4D4D4; - background: #252525; -} - -.sw-action-approved-label { - font-size: 12px; - color: #3FB950; - display: flex; - align-items: center; - gap: 4px; -} - -/* ----- Typing indicator ----- */ -#sw-typing { - display: flex; - align-items: center; - gap: 10px; - padding: 4px 2px; - align-self: flex-start; -} - -#sw-typing.hidden { - display: none; -} - -.sw-typing-dots { - display: flex; - align-items: center; - gap: 3px; - background: #1A1A1A; - border: 1px solid #252525; - padding: 8px 12px; - border-radius: 12px; - border-bottom-left-radius: 3px; -} - -.sw-typing-dot { - width: 6px; - height: 6px; - background: #8B949E; - border-radius: 50%; - animation: sw-dot-bounce 1.2s ease-in-out infinite; -} - -.sw-typing-dot:nth-child(2) { animation-delay: 0.2s; } -.sw-typing-dot:nth-child(3) { animation-delay: 0.4s; } - -/* ----- Unavailable state ----- */ -#sw-unavailable { - flex: 1; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 10px; - padding: 24px; - text-align: center; -} - -#sw-unavailable svg { - color: #3A3A3A; - width: 40px; - height: 40px; -} - -#sw-unavailable .sw-unavail-title { - font-weight: 600; - color: #8B949E; - font-size: 14px; -} - -#sw-unavailable .sw-unavail-sub { - font-size: 12px; - color: #4A4A4A; - line-height: 1.5; -} - -/* ----- Empty state ----- */ -#sw-empty { - flex: 1; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 10px; - padding: 24px; - text-align: center; -} - -.sw-empty-icon { - width: 48px; - height: 48px; - background: linear-gradient(135deg, rgba(217, 119, 6, 0.15), rgba(245, 158, 11, 0.1)); - border: 1px solid rgba(245, 158, 11, 0.2); - border-radius: 12px; - display: flex; - align-items: center; - justify-content: center; -} - -.sw-empty-icon svg { - color: #F59E0B; - width: 24px; - height: 24px; -} - -.sw-empty-title { - font-weight: 600; - font-size: 14px; - color: #D4D4D4; -} - -.sw-empty-sub { - font-size: 12px; - color: #8B949E; - line-height: 1.5; - max-width: 240px; -} - -/* ----- Input area ----- */ -#sw-input-area { - display: flex; - align-items: flex-end; - gap: 8px; - padding: 12px 14px; - border-top: 1px solid #1F1F1F; - background: #0A0A0A; - flex-shrink: 0; -} - -#sw-input { - flex: 1; - background: #1A1A1A; - border: 1px solid #2A2A2A; - border-radius: 10px; - padding: 8px 12px; - color: #D4D4D4; - font-family: inherit; - font-size: 13.5px; - line-height: 1.5; - resize: none; - min-height: 36px; - max-height: 100px; - outline: none; - transition: border-color 0.15s; - overflow-y: auto; -} - -#sw-input::placeholder { - color: #4A4A4A; -} - -#sw-input:focus { - border-color: rgba(245, 158, 11, 0.4); -} - -#sw-input::-webkit-scrollbar { - width: 3px; -} - -#sw-input::-webkit-scrollbar-thumb { - background: #2A2A2A; - border-radius: 3px; -} - -#sw-send-btn { - width: 36px; - height: 36px; - flex-shrink: 0; - background: linear-gradient(135deg, #D97706, #F59E0B); - border: none; - border-radius: 8px; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - transition: opacity 0.15s, transform 0.1s; - outline: none; -} - -#sw-send-btn:hover:not(:disabled) { - opacity: 0.9; -} - -#sw-send-btn:active:not(:disabled) { - transform: scale(0.93); -} - -#sw-send-btn:disabled { - opacity: 0.4; - cursor: not-allowed; -} - -#sw-send-btn:focus-visible { - outline: 2px solid #F59E0B; - outline-offset: 2px; -} - -#sw-send-btn svg { - width: 16px; - height: 16px; - color: #0A0A0A; -} - -/* ----- Error toast ----- */ -.sw-error-toast { - padding: 8px 12px; - background: rgba(248, 81, 73, 0.12); - border: 1px solid rgba(248, 81, 73, 0.3); - border-radius: 8px; - color: #F85149; - font-size: 12px; - align-self: center; - max-width: 100%; - text-align: center; -} diff --git a/src/components/support/types.ts b/src/components/support/types.ts deleted file mode 100644 index f958816..0000000 --- a/src/components/support/types.ts +++ /dev/null @@ -1,93 +0,0 @@ -// SSE event types from SupportAtlas API - -export type SSEEventType = - | 'token' - | 'tool_start' - | 'tool_end' - | 'action_card' - | 'done' - | 'error'; - -export interface TokenEvent { - type: 'token'; - content: string; -} - -export interface ToolStartEvent { - type: 'tool_start'; - agentName: string; - toolName: string; - input: Record; -} - -export interface ToolEndEvent { - type: 'tool_end'; - result: Record; -} - -export interface ActionCardEvent { - type: 'action_card'; - actionId: string; - description: string; - payload: Record; -} - -export interface DoneEvent { - type: 'done'; -} - -export interface ErrorEvent { - type: 'error'; - message: string; -} - -export type SSEEvent = - | TokenEvent - | ToolStartEvent - | ToolEndEvent - | ActionCardEvent - | DoneEvent - | ErrorEvent; - -// Message types for chat history - -export type MessageRole = 'user' | 'assistant' | 'system'; - -export interface AgentBadge { - agentName: string; - toolName: string; - status: 'running' | 'done'; -} - -export interface ActionCard { - actionId: string; - description: string; - payload: Record; - approved?: boolean; -} - -export interface ChatMessage { - id: string; - role: MessageRole; - content: string; - timestamp: number; - streaming?: boolean; - agentBadges?: AgentBadge[]; - actionCards?: ActionCard[]; -} - -// Session persistence - -export interface ChatSession { - sessionId: string; - messages: ChatMessage[]; - createdAt: number; - lastActiveAt: number; -} - -// Widget config - -export interface WidgetConfig { - apiUrl: string; - productSlug: string; -} diff --git a/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro index c8fe0a6..6a0bb7b 100644 --- a/src/layouts/BaseLayout.astro +++ b/src/layouts/BaseLayout.astro @@ -2,7 +2,6 @@ import Header from '../components/Header.astro'; import Footer from '../components/Footer.astro'; import SEO from '../components/SEO.astro'; -import SupportWidget from '../components/SupportWidget.astro'; import '../styles/global.css'; interface Props { @@ -77,7 +76,5 @@ const { title, description, ogImage, ogType, article, jsonLd } = Astro.props;