import { useEffect, useMemo, useState } from 'react'; import type { DemoLocale } from './types'; type ChatDemoScene = { id: 'prompt' | 'workflow' | 'output'; title: string; caption: string; prompt: string; tool: string; result: string; durationMs: number; }; type ChatFeatureDemoProps = { locale: DemoLocale; isBusy: boolean; }; const COPY = { en: { title: 'Feature Walkthrough', subtitle: 'Video-style preview of how the Chat Agent works', stateBusy: 'Live run active', statePlaying: 'Playing demo', statePaused: 'Demo paused', play: 'Play', pause: 'Pause', replay: 'Replay', hide: 'Hide', show: 'Show Demo', hiddenBusy: 'Walkthrough hidden while the agent is running.', hiddenIdle: 'Walkthrough hidden.', reducedMotion: 'Autoplay is disabled because reduced motion is enabled.', videoName: 'chat_agent_walkthrough.mp4', lanePrompt: 'Prompt', laneWorkflow: 'Workflow', laneOutput: 'Output', quickPromptLabel: 'quick prompt', workflowProgress: 'progress panel update', workflowTrace: 'trace event recorded', resultCardTitle: 'Result Card', payloadCardTitle: 'Structured Payload', payloadCardText: 'JSON preview + action chips', }, 'zh-TW': { title: '功能導覽', subtitle: '以影片風格快速示範 Chat Agent 使用方式', stateBusy: '執行中', statePlaying: '導覽播放中', statePaused: '導覽已暫停', play: '播放', pause: '暫停', replay: '重播', hide: '隱藏', show: '顯示導覽', hiddenBusy: '模型執行中,已暫時隱藏導覽。', hiddenIdle: '導覽已隱藏。', reducedMotion: '你啟用了減少動畫,已停用自動播放。', videoName: '聊天導覽影片.mp4', lanePrompt: '輸入', laneWorkflow: '流程', laneOutput: '輸出', quickPromptLabel: '快速提示', workflowProgress: '進度面板更新', workflowTrace: '追蹤事件記錄', resultCardTitle: '結果卡片', payloadCardTitle: '結構化載荷', payloadCardText: 'JSON 預覽 + 動作標籤', }, } as const; const SCENES = { en: [ { id: 'prompt', title: 'Step 1: Prompt and context', caption: 'User chooses a quick prompt or types intent in natural language.', prompt: 'Find eggs under NT$200 and rank by freshness.', tool: 'Prompt parser prepares query tokens and constraints', result: 'Input normalized and queued', durationMs: 1700, }, { id: 'workflow', title: 'Step 2: Tool orchestration', caption: 'Planner selects tools and the progress panel updates in real time.', prompt: 'Routing: product_search_public + score ranking', tool: 'Planner validates filters and runs tool chain', result: 'Structured candidate set is ready', durationMs: 1750, }, { id: 'output', title: 'Step 3: Structured answer', caption: 'Assistant responds with cards, metrics, actions, and JSON payload.', prompt: 'Rendering response bundle for UI cards', tool: 'Renderer composes summary and recommendation cards', result: 'Chat output card with payload attached', durationMs: 1750, }, ], 'zh-TW': [ { id: 'prompt', title: '步驟 1:輸入需求', caption: '使用者可點擊快速提示,或直接輸入自然語句。', prompt: '找出 200 元以下雞蛋,並依新鮮度排序。', tool: '系統先解析條件與語意', result: '已建立查詢上下文', durationMs: 1700, }, { id: 'workflow', title: '步驟 2:工具編排', caption: '規劃器決定工具路徑,進度面板同步顯示執行狀態。', prompt: '路由:product_search_public + 排名計算', tool: '規劃器驗證條件並串接工具', result: '已取得結構化候選結果', durationMs: 1750, }, { id: 'output', title: '步驟 3:輸出答案', caption: '最後輸出摘要、卡片與 JSON 結構化資料。', prompt: '生成可視化回覆內容', tool: 'Renderer 整理摘要與推薦卡片', result: '可直接展示的結果已完成', durationMs: 1750, }, ], } as const satisfies Record; function ChatFeatureDemo({ locale, isBusy }: ChatFeatureDemoProps) { const copy = COPY[locale]; const scenes = useMemo(() => SCENES[locale], [locale]); const [sceneIndex, setSceneIndex] = useState(0); const [elapsedMs, setElapsedMs] = useState(0); const [isPlaying, setIsPlaying] = useState(true); const [isCollapsed, setIsCollapsed] = useState(false); const [reducedMotion, setReducedMotion] = useState(false); useEffect(() => { const media = window.matchMedia('(prefers-reduced-motion: reduce)'); const apply = () => setReducedMotion(media.matches); apply(); if (media.addEventListener) { media.addEventListener('change', apply); return () => media.removeEventListener('change', apply); } media.addListener(apply); return () => media.removeListener(apply); }, []); useEffect(() => { if (reducedMotion) { setIsPlaying(false); } }, [reducedMotion]); useEffect(() => { setSceneIndex(0); setElapsedMs(0); setIsCollapsed(false); if (!reducedMotion) { setIsPlaying(true); } }, [locale, reducedMotion]); useEffect(() => { if (!isBusy) return; setIsCollapsed(true); setIsPlaying(false); }, [isBusy]); useEffect(() => { if (!isPlaying || isCollapsed || isBusy || reducedMotion) return; const timer = window.setInterval(() => { setElapsedMs((prev) => { const scene = scenes[sceneIndex]; if (!scene) return 0; const next = prev + 120; if (next < scene.durationMs) return next; setSceneIndex((current) => (current + 1) % scenes.length); return 0; }); }, 120); return () => window.clearInterval(timer); }, [isPlaying, isCollapsed, isBusy, reducedMotion, scenes, sceneIndex]); const activeScene = scenes[sceneIndex] ?? scenes[0]; const totalDuration = scenes.reduce((sum, scene) => sum + scene.durationMs, 0); const previousDuration = scenes.slice(0, sceneIndex).reduce((sum, scene) => sum + scene.durationMs, 0); const progress = totalDuration > 0 ? Math.round(((previousDuration + elapsedMs) / totalDuration) * 100) : 0; const replay = () => { setSceneIndex(0); setElapsedMs(0); if (!reducedMotion && !isBusy) { setIsPlaying(true); } }; if (isCollapsed) { return (

{isBusy ? copy.hiddenBusy : copy.hiddenIdle}

{!isBusy ? ( ) : null}
); } return (

{copy.title}

{copy.subtitle}

{reducedMotion ? {copy.reducedMotion} : null}
{isBusy ? copy.stateBusy : isPlaying ? copy.statePlaying : copy.statePaused}
{copy.videoName}

{copy.lanePrompt}

{copy.quickPromptLabel}
{activeScene.prompt}

{copy.laneWorkflow}

{activeScene.tool}
{copy.workflowProgress}
{copy.workflowTrace}

{copy.laneOutput}

{copy.resultCardTitle}

{activeScene.result}

{copy.payloadCardTitle}

{copy.payloadCardText}

); } export default ChatFeatureDemo;