/* ============================================================ CamTalk — Web 端应用样式 双栏布局:左侧视频面板 + 右侧聊天面板 ============================================================ */ :root { --color-primary: #2563eb; --color-primary-hover: #1d4ed8; --color-bg: #0b0f1a; --color-surface: #151b2b; --color-surface-2: #1c2438; --color-text: #e2e8f0; --color-text-muted: #64748b; --color-border: #1e293b; --color-success: #22c55e; --color-warning: #f59e0b; --color-error: #ef4444; --radius: 10px; --radius-sm: 6px; } [data-theme="light"] { --color-primary: #2563eb; --color-primary-hover: #1d4ed8; --color-bg: #f1f5f9; --color-surface: #ffffff; --color-surface-2: #f8fafc; --color-text: #0f172a; --color-text-muted: #64748b; --color-border: #e2e8f0; --color-success: #16a34a; --color-warning: #d97706; --color-error: #dc2626; } * { margin: 0; padding: 0; box-sizing: border-box; } html, body, #root { height: 100%; } body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif; background: var(--color-bg); color: var(--color-text); } /* ---- App Shell ---- */ .app { display: flex; flex-direction: column; height: 100vh; overflow: hidden; } /* ---- Header ---- */ .header { display: flex; align-items: center; justify-content: space-between; padding: 0 20px; height: 48px; background: var(--color-surface); border-bottom: 1px solid var(--color-border); flex-shrink: 0; } .header__left { display: flex; align-items: baseline; gap: 12px; } .header__title { font-size: 1.2rem; font-weight: 700; color: var(--color-text); } .header__subtitle { font-size: 0.8rem; color: var(--color-text-muted); } .header__right { display: flex; align-items: center; gap: 12px; } .header__stats { font-size: 0.75rem; color: var(--color-text-muted); padding: 4px 10px; background: var(--color-surface-2); border-radius: 12px; } .badge { font-size: 0.75rem; padding: 3px 10px; border-radius: 12px; font-weight: 500; } .badge--connected { color: var(--color-success); background: rgba(34, 197, 94, 0.1); border: 1px solid rgba(34, 197, 94, 0.3); } .badge--connecting { color: var(--color-warning); background: rgba(245, 158, 11, 0.1); border: 1px solid rgba(245, 158, 11, 0.3); } .badge--disconnected { color: var(--color-text-muted); background: var(--color-surface-2); border: 1px solid var(--color-border); } .btn-icon { background: none; border: none; font-size: 1.1rem; cursor: pointer; padding: 6px; border-radius: var(--radius-sm); transition: background 0.15s; color: var(--color-text-muted); } .btn-icon:hover { background: var(--color-surface-2); color: var(--color-text); } /* ---- Workspace (双栏) ---- */ .workspace { flex: 1; display: flex; overflow: hidden; } /* ---- 左侧视频面板 ---- */ .video-panel { width: 400px; flex-shrink: 0; display: flex; flex-direction: column; padding: 16px; gap: 12px; } .video-container { position: relative; aspect-ratio: 4 / 3; background: var(--color-surface-2); border-radius: var(--radius); overflow: hidden; border: 1px solid var(--color-border); } .video-container .video-preview { width: 100%; height: 100%; border-radius: 0; } .video-container .video-preview__video { width: 100%; height: 100%; object-fit: cover; background: #1a1a2e; } .video-placeholder { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; color: var(--color-text-muted); font-size: 0.82rem; } .video-placeholder__icon { font-size: 2.4rem; opacity: 0.2; margin-bottom: 4px; } .video-indicator { position: absolute; bottom: 16px; left: 16px; background: rgba(0, 0, 0, 0.75); backdrop-filter: blur(8px); color: var(--color-success); padding: 6px 14px; border-radius: var(--radius-sm); font-size: 0.8rem; font-weight: 500; animation: pulse 1.5s ease-in-out infinite; } .video-indicator--loading { color: var(--color-warning); } .video-indicator--audio { left: auto; right: 16px; color: var(--color-primary); } .video-indicator--error { color: var(--color-error); animation: none; } .detail-badge { position: absolute; top: 12px; right: 12px; background: rgba(37, 99, 235, 0.85); backdrop-filter: blur(8px); color: white; padding: 3px 10px; border-radius: var(--radius-sm); font-size: 0.7rem; font-weight: 700; letter-spacing: 0.5px; } .observation-badge { position: absolute; top: 12px; left: 12px; background: rgba(34, 197, 94, 0.85); backdrop-filter: blur(8px); color: white; padding: 3px 10px; border-radius: var(--radius-sm); font-size: 0.7rem; font-weight: 700; animation: pulse 2s ease-in-out infinite; } .video-overlay { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.35); display: flex; align-items: center; justify-content: center; backdrop-filter: blur(2px); } .video-overlay__spinner { width: 36px; height: 36px; border: 3px solid rgba(255, 255, 255, 0.2); border-top-color: white; border-radius: 50%; animation: spin 0.8s linear infinite; } /* ---- 视频控制栏 ---- */ .video-controls { flex-shrink: 0; display: flex; justify-content: center; padding: 4px 0; } .video-controls__row { display: flex; gap: 8px; } /* ---- 右侧聊天面板 ---- */ .chat-panel-wrapper { flex: 1; display: flex; flex-direction: column; background: var(--color-surface); border-left: 1px solid var(--color-border); min-width: 0; } .chat-panel-header { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; font-weight: 600; font-size: 0.85rem; border-bottom: 1px solid var(--color-border); flex-shrink: 0; color: var(--color-text-muted); } .chat-panel-header__mode { font-size: 0.7rem; font-weight: 500; color: var(--color-success); background: rgba(34, 197, 94, 0.1); padding: 2px 8px; border-radius: 10px; } .chat-panel-body { flex: 1; overflow: hidden; display: flex; flex-direction: column; } /* ---- Chat Panel (覆盖子组件样式) ---- */ .chat-panel { flex: 1; overflow-y: auto; padding: 12px 16px; display: flex; flex-direction: column; gap: 10px; } .chat-panel--empty { display: flex; align-items: center; justify-content: center; flex: 1; color: var(--color-text-muted); font-size: 0.85rem; } .chat-message { padding: 10px 14px; border-radius: var(--radius-sm); background: var(--color-surface-2); max-width: 92%; } .chat-message--user { border-left: 2px solid var(--color-primary); align-self: flex-end; background: rgba(37, 99, 235, 0.08); } .chat-message--assistant { border-left: 2px solid var(--color-success); } .chat-message--streaming { opacity: 0.85; } .chat-message__role { font-size: 0.65rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; color: var(--color-text-muted); margin-bottom: 3px; } .chat-message__content { font-size: 0.82rem; line-height: 1.55; white-space: pre-wrap; } .chat-message__meta { margin-top: 6px; font-size: 0.65rem; color: var(--color-text-muted); } /* ---- Streaming Cursor ---- */ .cursor { display: inline; animation: blink 0.8s step-end infinite; color: var(--color-success); } /* ---- System Message ---- */ .system-message { text-align: center; padding: 8px 16px; margin: 0 20px; border-radius: var(--radius-sm); font-size: 0.8rem; } .system-message--warning { background: rgba(245, 158, 11, 0.1); color: var(--color-warning); border: 1px solid rgba(245, 158, 11, 0.2); } /* ---- Drawer (Config Panel) ---- */ .drawer-overlay { position: fixed; inset: 0; z-index: 100; background: rgba(0, 0, 0, 0.4); backdrop-filter: blur(2px); animation: fadeIn 0.2s ease; } .drawer { position: absolute; top: 0; right: 0; bottom: 0; width: 320px; background: var(--color-surface); border-left: 1px solid var(--color-border); display: flex; flex-direction: column; animation: slideInRight 0.25s ease; box-shadow: -8px 0 24px rgba(0, 0, 0, 0.3); } .drawer__header { display: flex; align-items: center; justify-content: space-between; padding: 14px 16px; border-bottom: 1px solid var(--color-border); flex-shrink: 0; } .drawer__title { font-weight: 600; font-size: 0.9rem; } .drawer__close { background: none; border: none; color: var(--color-text-muted); cursor: pointer; font-size: 1rem; padding: 4px 8px; border-radius: var(--radius-sm); transition: all 0.15s; } .drawer__close:hover { background: var(--color-surface-2); color: var(--color-text); } .drawer__body { flex: 1; overflow-y: auto; padding: 16px; } /* ---- Config Group ---- */ .config-group { margin-bottom: 20px; } .config-group__title { font-size: 0.7rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.8px; color: var(--color-text-muted); margin-bottom: 12px; padding-bottom: 8px; border-bottom: 1px solid var(--color-border); } .config-row { display: flex; align-items: center; justify-content: space-between; padding: 10px 0; font-size: 0.82rem; color: var(--color-text); cursor: pointer; } .config-row__info { display: flex; flex-direction: column; gap: 2px; } .config-row__label { font-weight: 500; color: var(--color-text); } .config-row__desc { font-size: 0.72rem; color: var(--color-text-muted); } .config-row select, .config-row input[type="checkbox"] { background: var(--color-surface-2); color: var(--color-text); border: 1px solid var(--color-border); border-radius: var(--radius-sm); padding: 5px 10px; font-size: 0.8rem; cursor: pointer; } .config-row input[type="checkbox"] { width: 18px; height: 18px; cursor: pointer; } /* ---- Toast ---- */ .toast-container { position: fixed; top: 68px; right: 20px; z-index: 1000; display: flex; flex-direction: column; gap: 8px; } .toast { padding: 10px 16px; border-radius: var(--radius-sm); font-size: 0.82rem; cursor: pointer; animation: slideIn 0.3s ease-out; max-width: 320px; backdrop-filter: blur(8px); } .toast--error { background: rgba(239, 68, 68, 0.9); color: white; } .toast--warning { background: rgba(245, 158, 11, 0.9); color: #000; } .toast--info { background: rgba(37, 99, 235, 0.9); color: white; } /* ---- Buttons ---- */ .btn { padding: 8px 20px; border: none; border-radius: var(--radius-sm); font-size: 0.85rem; font-weight: 600; cursor: pointer; transition: all 0.15s; display: inline-flex; align-items: center; gap: 6px; } .btn--lg { padding: 12px 32px; font-size: 0.95rem; border-radius: var(--radius); } .btn--primary { background: var(--color-primary); color: white; } .btn--primary:hover { background: var(--color-primary-hover); } .btn--secondary { background: var(--color-surface-2); color: var(--color-text); border: 1px solid var(--color-border); } .btn--secondary:hover { background: var(--color-border); } .btn--warning { background: var(--color-warning); color: #000; } .btn--warning:hover { opacity: 0.9; } .btn--danger { background: rgba(239, 68, 68, 0.15); color: var(--color-error); border: 1px solid rgba(239, 68, 68, 0.3); } .btn--danger:hover { background: rgba(239, 68, 68, 0.25); } .btn--active { background: var(--color-primary); color: white; box-shadow: 0 0 12px rgba(37, 99, 235, 0.4); animation: pulseBtn 2s ease-in-out infinite; } /* ---- Animations ---- */ @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.6; } } @keyframes pulseBtn { 0%, 100% { box-shadow: 0 0 12px rgba(37, 99, 235, 0.4); } 50% { box-shadow: 0 0 20px rgba(37, 99, 235, 0.7); } } @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } } @keyframes spin { to { transform: rotate(360deg); } } @keyframes slideIn { from { opacity: 0; transform: translateX(20px); } to { opacity: 1; transform: translateX(0); } } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes slideInRight { from { transform: translateX(100%); } to { transform: translateX(0); } } /* ---- Scrollbar ---- */ ::-webkit-scrollbar { width: 6px; } ::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-thumb { background: var(--color-border); border-radius: 3px; } ::-webkit-scrollbar-thumb:hover { background: var(--color-text-muted); }