414 lines
14 KiB
JavaScript
414 lines
14 KiB
JavaScript
// 全局状态
|
||
let currentStep = 0;
|
||
let isExpanded = false;
|
||
let demoRunning = false;
|
||
|
||
// 步骤配置
|
||
const steps = [
|
||
{
|
||
type: 'thinking',
|
||
icon: 'brain',
|
||
summary: '我需要搜索最新的AI技术发展趋势。首先,我应该使用搜索工具来查找相关信息...',
|
||
fullText: '我需要搜索最新的AI技术发展趋势。首先,我应该使用搜索工具来查找相关信息。搜索关键词应该包括"AI技术"、"人工智能"、"2026"、"发展趋势"等。',
|
||
duration: 2000
|
||
},
|
||
{
|
||
type: 'tool',
|
||
icon: 'search',
|
||
summary: '搜索:AI技术发展趋势 2026',
|
||
duration: 2500
|
||
},
|
||
{
|
||
type: 'thinking',
|
||
icon: 'brain',
|
||
summary: '搜索结果显示了一些有价值的信息。现在我需要执行一个命令来获取更详细的数据分析...',
|
||
fullText: '搜索结果显示了一些有价值的信息。现在我需要执行一个命令来获取更详细的数据分析。我将使用curl命令来获取第一个链接的内容,然后进行分析。这将帮助我提供更准确和详细的答案。让我执行这个命令...\n\n在执行之前,我需要确保命令的安全性和有效性。curl命令是一个常用的网络请求工具,可以帮助我获取网页内容。我会添加适当的参数来确保请求的稳定性,比如设置超时时间、用户代理等。\n\n同时,我还需要考虑如何解析返回的HTML内容,提取出关键信息。可能需要使用一些文本处理工具,如grep、sed或awk来过滤和格式化数据。',
|
||
duration: 2000
|
||
},
|
||
{
|
||
type: 'tool',
|
||
icon: 'terminal',
|
||
summary: '执行命令:获取网页内容并分析',
|
||
duration: 2500
|
||
},
|
||
{
|
||
type: 'thinking',
|
||
icon: 'brain',
|
||
summary: '很好,我已经收集到了足够的信息。现在我可以整理这些内容,为用户提供一个清晰、全面的答案...',
|
||
fullText: '很好,我已经收集到了足够的信息。现在我可以整理这些内容,为用户提供一个清晰、全面的答案。我会按照趋势的重要性和影响力来组织内容。',
|
||
duration: 2000
|
||
}
|
||
];
|
||
|
||
// 最终输出文本
|
||
const finalOutputText = `根据我的搜索和分析,2026年AI技术的主要发展趋势包括:
|
||
|
||
1. **多模态AI模型的普及**:能够同时处理文本、图像、音频等多种数据类型的AI模型将成为主流。
|
||
|
||
2. **AI安全性和可解释性**:随着AI应用的广泛部署,确保AI系统的安全性和决策的可解释性变得越来越重要。
|
||
|
||
3. **边缘AI计算**:将AI计算能力部署到边缘设备,实现更快的响应速度和更好的隐私保护。
|
||
|
||
这些趋势将深刻影响各行各业的数字化转型进程。`;
|
||
|
||
// 开始演示
|
||
async function startDemo() {
|
||
if (demoRunning) return;
|
||
|
||
demoRunning = true;
|
||
resetDemo();
|
||
|
||
// 显示等待动画
|
||
const placeholder = document.getElementById('generatingPlaceholder');
|
||
placeholder.style.display = 'flex';
|
||
|
||
await sleep(1500);
|
||
|
||
// 隐藏等待动画,显示摘要包装器
|
||
placeholder.style.display = 'none';
|
||
const summaryWrapper = document.getElementById('summaryWrapper');
|
||
summaryWrapper.style.display = 'flex';
|
||
summaryWrapper.classList.add('running');
|
||
|
||
// 执行每个步骤
|
||
for (let i = 0; i < steps.length; i++) {
|
||
currentStep = i;
|
||
await executeStep(i);
|
||
}
|
||
|
||
// 完成后移除运行状态
|
||
await sleep(500);
|
||
summaryWrapper.classList.remove('running');
|
||
|
||
// 如果展开状态,显示最后一步
|
||
if (isExpanded) {
|
||
await showDetailStep(currentStep, true);
|
||
} else {
|
||
// 折叠状态下,摘要显示第一步内容
|
||
updateSummary(steps[0]);
|
||
}
|
||
|
||
await sleep(800);
|
||
|
||
// 显示最终输出(流式)
|
||
const textOutput = document.getElementById('textOutput');
|
||
textOutput.style.display = 'block';
|
||
setTimeout(() => {
|
||
textOutput.classList.add('show');
|
||
}, 50);
|
||
|
||
// 流式输出最终文本
|
||
await streamText(document.querySelector('.text-content'), finalOutputText, 20);
|
||
|
||
demoRunning = false;
|
||
}
|
||
|
||
// 执行单个步骤
|
||
async function executeStep(stepIndex) {
|
||
const step = steps[stepIndex];
|
||
|
||
// 更新摘要(仅在折叠状态下)
|
||
if (!isExpanded) {
|
||
// 流式输出摘要文本
|
||
await streamSummary(step);
|
||
}
|
||
|
||
// 如果展开状态,显示详细内容
|
||
if (isExpanded) {
|
||
await showDetailStep(stepIndex, true);
|
||
|
||
// 如果是思考类型,流式输出完整内容
|
||
if (step.type === 'thinking' && step.fullText) {
|
||
const stepBody = document.querySelector(`.step-item[data-step="${stepIndex + 1}"] .step-body`);
|
||
if (stepBody) {
|
||
stepBody.textContent = '';
|
||
await streamText(stepBody, step.fullText, 30);
|
||
// 更新竖线高度
|
||
updateLineHeight(stepIndex + 1);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 显示下一个步骤的竖线(如果有下一个步骤)
|
||
if (stepIndex < steps.length - 1) {
|
||
if (isExpanded) {
|
||
// 展开状态:显示当前步骤的竖线
|
||
const currentStepItem = document.querySelector(`.step-item[data-step="${stepIndex + 1}"]`);
|
||
if (currentStepItem) {
|
||
const line = currentStepItem.querySelector('.step-line');
|
||
if (line) {
|
||
updateLineHeight(stepIndex + 1);
|
||
}
|
||
}
|
||
} else {
|
||
// 折叠状态:显示摘要的竖线(暂时不需要,因为只在展开时才有步骤)
|
||
}
|
||
}
|
||
|
||
await sleep(step.duration);
|
||
}
|
||
|
||
// 流式输出摘要文本
|
||
async function streamSummary(step) {
|
||
const summaryIcon = document.getElementById('summaryIcon');
|
||
const summaryText = document.getElementById('summaryText');
|
||
|
||
// 更新图标
|
||
summaryIcon.innerHTML = getIconSVG(step.icon);
|
||
|
||
// 流式输出文本
|
||
summaryText.textContent = '';
|
||
await streamText(summaryText, step.summary, 30);
|
||
}
|
||
|
||
// 流式输出文本
|
||
async function streamText(element, text, speed = 30) {
|
||
element.textContent = '';
|
||
for (let i = 0; i < text.length; i++) {
|
||
element.textContent += text[i];
|
||
await sleep(speed);
|
||
}
|
||
}
|
||
|
||
// 更新摘要
|
||
function updateSummary(step) {
|
||
const summaryIcon = document.getElementById('summaryIcon');
|
||
const summaryText = document.getElementById('summaryText');
|
||
|
||
// 更新图标
|
||
summaryIcon.innerHTML = getIconSVG(step.icon);
|
||
|
||
// 更新文本
|
||
summaryText.textContent = step.summary;
|
||
}
|
||
|
||
// 显示详细步骤
|
||
async function showDetailStep(stepIndex, isNewStep = false) {
|
||
const stepItem = document.querySelector(`.step-item[data-step="${stepIndex + 1}"]`);
|
||
if (!stepItem) return;
|
||
|
||
// 添加动画类
|
||
if (isNewStep) {
|
||
stepItem.classList.add('animated');
|
||
} else {
|
||
stepItem.classList.add('instant');
|
||
}
|
||
|
||
// 显示步骤
|
||
stepItem.style.display = 'flex';
|
||
|
||
// 等待动画
|
||
await sleep(isNewStep ? 100 : 10);
|
||
|
||
// 如果有下一个步骤,显示当前步骤的竖线
|
||
if (stepIndex < steps.length - 1) {
|
||
updateLineHeight(stepIndex + 1);
|
||
}
|
||
|
||
await sleep(isNewStep ? 250 : 10);
|
||
}
|
||
|
||
// 更新竖线高度
|
||
function updateLineHeight(stepIndex) {
|
||
const stepItem = document.querySelector(`.step-item[data-step="${stepIndex}"]`);
|
||
if (!stepItem) return;
|
||
|
||
const line = stepItem.querySelector('.step-line');
|
||
const stepContent = stepItem.querySelector('.step-content');
|
||
if (!line || !stepContent) return;
|
||
|
||
// 获取内容的实际高度
|
||
const contentHeight = stepContent.offsetHeight;
|
||
|
||
// 如果是最后一个步骤,不需要竖线
|
||
if (stepIndex >= steps.length) {
|
||
line.style.height = '0';
|
||
return;
|
||
}
|
||
|
||
// 计算竖线高度:内容高度 + 间距
|
||
const gap = 16;
|
||
const lineHeight = Math.max(contentHeight + gap, 40);
|
||
line.style.height = `${lineHeight}px`;
|
||
}
|
||
|
||
// 切换展开/折叠
|
||
async function toggleExpand() {
|
||
const summaryWrapper = document.getElementById('summaryWrapper');
|
||
if (summaryWrapper.style.display === 'none') return;
|
||
|
||
isExpanded = !isExpanded;
|
||
|
||
if (isExpanded) {
|
||
// 展开:显示完整文本,隐藏摘要文本
|
||
summaryWrapper.classList.add('expanded');
|
||
|
||
const summaryText = document.getElementById('summaryText');
|
||
const summaryTextFull = document.getElementById('summaryTextFull');
|
||
const summaryLine = document.getElementById('summaryLine');
|
||
const stepsContainer = document.getElementById('stepsContainer');
|
||
|
||
// 切换文本显示
|
||
summaryText.style.display = 'none';
|
||
summaryTextFull.style.display = 'block';
|
||
|
||
// 显示连接线
|
||
await sleep(50);
|
||
summaryLine.style.height = '20px';
|
||
|
||
// 显示步骤容器
|
||
stepsContainer.style.display = 'block';
|
||
await sleep(50);
|
||
stepsContainer.classList.add('show');
|
||
|
||
// 快速显示已完成的步骤
|
||
for (let i = 0; i <= currentStep; i++) {
|
||
const isCurrentStep = i === currentStep && demoRunning;
|
||
await showDetailStep(i, isCurrentStep);
|
||
if (!isCurrentStep) {
|
||
await sleep(30); // 已有内容快速出现
|
||
}
|
||
}
|
||
} else {
|
||
// 折叠:显示摘要文本,隐藏完整文本
|
||
summaryWrapper.classList.remove('expanded');
|
||
|
||
const summaryText = document.getElementById('summaryText');
|
||
const summaryTextFull = document.getElementById('summaryTextFull');
|
||
const summaryLine = document.getElementById('summaryLine');
|
||
const stepsContainer = document.getElementById('stepsContainer');
|
||
|
||
// 隐藏步骤容器
|
||
stepsContainer.classList.remove('show');
|
||
await sleep(250);
|
||
stepsContainer.style.display = 'none';
|
||
|
||
// 隐藏连接线
|
||
summaryLine.style.height = '0';
|
||
|
||
// 切换文本显示
|
||
summaryText.style.display = 'block';
|
||
summaryTextFull.style.display = 'none';
|
||
|
||
// 更新摘要为第一步内容(不是当前步骤)
|
||
updateSummary(steps[0]);
|
||
|
||
// 重置所有步骤显示
|
||
document.querySelectorAll('.step-item').forEach(item => {
|
||
item.style.display = 'none';
|
||
item.classList.remove('instant', 'animated');
|
||
const line = item.querySelector('.step-line');
|
||
if (line) {
|
||
line.style.height = '0';
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
// 展开思考内容
|
||
function expandThinking(stepIndex) {
|
||
const thinkingContent = document.getElementById(`thinkingContent${stepIndex}`);
|
||
const thinkingContentFull = document.getElementById(`thinkingContentFull${stepIndex}`);
|
||
|
||
thinkingContent.style.display = 'none';
|
||
thinkingContentFull.style.display = 'block';
|
||
|
||
// 重新计算并更新竖线高度
|
||
setTimeout(() => {
|
||
updateLineHeight(stepIndex);
|
||
}, 50);
|
||
}
|
||
|
||
// 收起思考内容
|
||
function collapseThinking(stepIndex) {
|
||
const thinkingContent = document.getElementById(`thinkingContent${stepIndex}`);
|
||
const thinkingContentFull = document.getElementById(`thinkingContentFull${stepIndex}`);
|
||
|
||
thinkingContent.style.display = 'block';
|
||
thinkingContentFull.style.display = 'none';
|
||
|
||
// 重新计算并更新竖线高度
|
||
setTimeout(() => {
|
||
updateLineHeight(stepIndex);
|
||
}, 50);
|
||
}
|
||
|
||
// 重置演示
|
||
function resetDemo() {
|
||
currentStep = 0;
|
||
isExpanded = false;
|
||
demoRunning = false;
|
||
|
||
// 隐藏所有元素
|
||
document.getElementById('generatingPlaceholder').style.display = 'none';
|
||
|
||
const summaryWrapper = document.getElementById('summaryWrapper');
|
||
summaryWrapper.style.display = 'none';
|
||
summaryWrapper.classList.remove('running', 'expanded');
|
||
|
||
const summaryText = document.getElementById('summaryText');
|
||
const summaryTextFull = document.getElementById('summaryTextFull');
|
||
summaryText.style.display = 'block';
|
||
summaryTextFull.style.display = 'none';
|
||
|
||
const summaryLine = document.getElementById('summaryLine');
|
||
summaryLine.style.height = '0';
|
||
|
||
const stepsContainer = document.getElementById('stepsContainer');
|
||
stepsContainer.style.display = 'none';
|
||
stepsContainer.classList.remove('show');
|
||
|
||
const textOutput = document.getElementById('textOutput');
|
||
textOutput.style.display = 'none';
|
||
textOutput.classList.remove('show');
|
||
const textContent = textOutput.querySelector('.text-content');
|
||
if (textContent) {
|
||
textContent.innerHTML = `<p>根据我的搜索和分析,2026年AI技术的主要发展趋势包括:</p>
|
||
<ol>
|
||
<li><strong>多模态AI模型的普及</strong>:能够同时处理文本、图像、音频等多种数据类型的AI模型将成为主流。</li>
|
||
<li><strong>AI安全性和可解释性</strong>:随着AI应用的广泛部署,确保AI系统的安全性和决策的可解释性变得越来越重要。</li>
|
||
<li><strong>边缘AI计算</strong>:将AI计算能力部署到边缘设备,实现更快的响应速度和更好的隐私保护。</li>
|
||
</ol>
|
||
<p>这些趋势将深刻影响各行各业的数字化转型进程。</p>`;
|
||
}
|
||
|
||
// 重置所有步骤
|
||
document.querySelectorAll('.step-item').forEach(item => {
|
||
item.style.display = 'none';
|
||
item.classList.remove('instant', 'animated');
|
||
const line = item.querySelector('.step-line');
|
||
if (line) {
|
||
line.style.height = '0';
|
||
}
|
||
});
|
||
|
||
// 重置思考内容
|
||
document.querySelectorAll('.thinking-content').forEach(item => {
|
||
item.style.display = 'block';
|
||
});
|
||
document.querySelectorAll('.thinking-content-full').forEach(item => {
|
||
item.style.display = 'none';
|
||
});
|
||
|
||
// 重置摘要文本
|
||
updateSummary(steps[0]);
|
||
}
|
||
|
||
// 获取图标SVG
|
||
function getIconSVG(iconType) {
|
||
const icons = {
|
||
brain: '<path d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2z"></path><path d="M12 6v6l4 2"></path>',
|
||
search: '<circle cx="11" cy="11" r="8"></circle><path d="m21 21-4.35-4.35"></path>',
|
||
terminal: '<polyline points="4 17 10 11 4 5"></polyline><line x1="12" y1="19" x2="20" y2="19"></line>'
|
||
};
|
||
return icons[iconType] || icons.brain;
|
||
}
|
||
|
||
// 工具函数:延迟
|
||
function sleep(ms) {
|
||
return new Promise(resolve => setTimeout(resolve, ms));
|
||
}
|
||
|
||
// 点击摘要包装器切换展开/折叠
|
||
document.getElementById('summaryWrapper').addEventListener('click', toggleExpand);
|