refactor: 将情景图标和设备选择器的 emoji 统一替换为 SVG 图标
- 新增 scenarioIcons.tsx 提取情景图标渲染逻辑 - 情景芯片、设备选择器、警告提示等 emoji 全部改为 Feather-style SVG - 调整 CSS 确保 SVG 图标居中对齐
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
// 增强:空状态情景选择卡片、语音输入按钮
|
||||
// ============================================================
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { ReactNode, useEffect, useRef, useState } from "react";
|
||||
import { useI18n } from "../../lib/i18n";
|
||||
import { scenarios } from "../../lib/scenarios";
|
||||
import type { ChatMessage } from "../../types";
|
||||
@@ -28,12 +28,13 @@ interface ChatPanelProps {
|
||||
}
|
||||
|
||||
/** 场景卡片数据(视觉分析快捷) */
|
||||
function getSceneCards(t: (key: string) => string) {
|
||||
function getSceneCards(t: (key: string) => string): { icon: ReactNode; titleKey: string; descKey: string; prompt: string }[] {
|
||||
const svgProps = { width: 14, height: 14, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round" as const, strokeLinejoin: "round" as const };
|
||||
return [
|
||||
{ icon: "👁", titleKey: "scene.describe", descKey: "scene.describe.desc", prompt: t("scene.describe") },
|
||||
{ icon: "🔤", titleKey: "scene.text", descKey: "scene.text.desc", prompt: t("scene.text") },
|
||||
{ icon: "📦", titleKey: "scene.object", descKey: "scene.object.desc", prompt: t("scene.object") },
|
||||
{ icon: "💡", titleKey: "scene.suggest", descKey: "scene.suggest.desc", prompt: t("scene.suggest") },
|
||||
{ icon: <svg {...svgProps}><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>, titleKey: "scene.describe", descKey: "scene.describe.desc", prompt: t("scene.describe") },
|
||||
{ icon: <svg {...svgProps}><polyline points="4 7 4 4 20 4 20 7"/><line x1="9" y1="20" x2="15" y2="20"/><line x1="12" y1="4" x2="12" y2="20"/></svg>, titleKey: "scene.text", descKey: "scene.text.desc", prompt: t("scene.text") },
|
||||
{ icon: <svg {...svgProps}><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></svg>, titleKey: "scene.object", descKey: "scene.object.desc", prompt: t("scene.object") },
|
||||
{ icon: <svg {...svgProps}><line x1="9" y1="18" x2="15" y2="18"/><line x1="10" y1="22" x2="14" y2="22"/><path d="M15.09 14c.18-.98.65-1.74 1.41-2.5A4.65 4.65 0 0 0 18 8 6 6 0 0 0 6 8c0 1 .23 2.23 1.5 3.5A4.61 4.61 0 0 1 8.91 14"/></svg>, titleKey: "scene.suggest", descKey: "scene.suggest.desc", prompt: t("scene.suggest") },
|
||||
];
|
||||
}
|
||||
|
||||
@@ -124,7 +125,7 @@ export function ChatPanel({
|
||||
{/* 空状态:欢迎信息 */}
|
||||
{isEmpty && (
|
||||
<div className="chat-panel__welcome">
|
||||
<span className="chat-panel__welcome-icon">{scenarios.find(s => s.id === activeScenario)?.icon || "💬"}</span>
|
||||
<span className="chat-panel__welcome-icon">{scenarios.find(s => s.id === activeScenario)?.icon || <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>}</span>
|
||||
<p>{isConnected ? t("chat.welcome.prompt") : t("chat.empty.prompt")}</p>
|
||||
<span className="chat-panel__welcome-hint">{isConnected ? t("chat.welcome.hint") : t("chat.empty.hint")}</span>
|
||||
|
||||
@@ -242,7 +243,7 @@ export function ChatPanel({
|
||||
onClick={onToggleMic}
|
||||
title={t("chat.input.voice")}
|
||||
>
|
||||
🎤
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/></svg>
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
@@ -251,7 +252,7 @@ export function ChatPanel({
|
||||
disabled={!inputText.trim() || connectionStatus === "connecting"}
|
||||
title={t("chat.send")}
|
||||
>
|
||||
➤
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>
|
||||
</button>
|
||||
</form>
|
||||
)}
|
||||
|
||||
@@ -197,7 +197,7 @@ export function ConfigPanel({
|
||||
onClick={() => onEditScenario(sc as unknown as UserScenario)}
|
||||
title={t("common.edit")}
|
||||
>
|
||||
✏️
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
|
||||
</button>
|
||||
<button
|
||||
className={`scenario-action-btn scenario-action-btn--delete ${
|
||||
@@ -206,7 +206,7 @@ export function ConfigPanel({
|
||||
onClick={() => handleDelete(sc.id)}
|
||||
title={deleteConfirm === sc.id ? t("common.confirmDelete") : t("common.delete")}
|
||||
>
|
||||
{deleteConfirm === sc.id ? "✓" : "🗑"}
|
||||
{deleteConfirm === sc.id ? "✓" : <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,24 +6,17 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { useI18n } from "../../lib/i18n";
|
||||
import type { CreateScenarioRequest } from "../../lib/api/scenarios";
|
||||
import { SCENARIO_ICON_SET, DEFAULT_SCENARIO_ICON_ID } from "../../lib/scenarioIcons";
|
||||
|
||||
interface CreateScenarioModalProps {
|
||||
onClose: () => void;
|
||||
onSubmit: (data: CreateScenarioRequest) => Promise<void>;
|
||||
}
|
||||
|
||||
// 预设常用图标
|
||||
const PRESET_ICONS = [
|
||||
"✨", "🎭", "🎨", "🎯", "🎪", "🎬",
|
||||
"📖", "📚", "📝", "📋", "📌", "📍",
|
||||
"🔬", "🔭", "🔮", "💡", "💼", "💻",
|
||||
"🎓", "🎤", "🎵", "🎸", "🎹", "🎺",
|
||||
];
|
||||
|
||||
export function CreateScenarioModal({ onClose, onSubmit }: CreateScenarioModalProps) {
|
||||
const { t } = useI18n();
|
||||
const [name, setName] = useState("");
|
||||
const [icon, setIcon] = useState("✨");
|
||||
const [icon, setIcon] = useState(DEFAULT_SCENARIO_ICON_ID);
|
||||
const [description, setDescription] = useState("");
|
||||
const [prompt, setPrompt] = useState("");
|
||||
const [greeting, setGreeting] = useState("");
|
||||
@@ -134,14 +127,15 @@ export function CreateScenarioModal({ onClose, onSubmit }: CreateScenarioModalPr
|
||||
<div className="form-group">
|
||||
<label className="form-label">{t("scenario.create.icon")}</label>
|
||||
<div className="icon-picker">
|
||||
{PRESET_ICONS.map((ic) => (
|
||||
{SCENARIO_ICON_SET.map((ic) => (
|
||||
<button
|
||||
key={ic}
|
||||
key={ic.id}
|
||||
type="button"
|
||||
className={`icon-picker__item ${icon === ic ? "icon-picker__item--active" : ""}`}
|
||||
onClick={() => setIcon(ic)}
|
||||
className={`icon-picker__item ${icon === ic.id ? "icon-picker__item--active" : ""}`}
|
||||
onClick={() => setIcon(ic.id)}
|
||||
title={ic.label}
|
||||
>
|
||||
{ic}
|
||||
{ic.render(22)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { useI18n } from "../../lib/i18n";
|
||||
import type { UpdateScenarioRequest, UserScenario } from "../../lib/api/scenarios";
|
||||
import { SCENARIO_ICON_SET } from "../../lib/scenarioIcons";
|
||||
|
||||
interface EditScenarioModalProps {
|
||||
scenario: UserScenario;
|
||||
@@ -13,14 +14,6 @@ interface EditScenarioModalProps {
|
||||
onSubmit: (id: string, data: UpdateScenarioRequest) => Promise<void>;
|
||||
}
|
||||
|
||||
// 预设常用图标
|
||||
const PRESET_ICONS = [
|
||||
"✨", "🎭", "🎨", "🎯", "🎪", "🎬",
|
||||
"📖", "📚", "📝", "📋", "📌", "📍",
|
||||
"🔬", "🔭", "🔮", "💡", "💼", "💻",
|
||||
"🎓", "🎤", "🎵", "🎸", "🎹", "🎺",
|
||||
];
|
||||
|
||||
export function EditScenarioModal({ scenario, onClose, onSubmit }: EditScenarioModalProps) {
|
||||
const { t } = useI18n();
|
||||
const [name, setName] = useState(scenario.name);
|
||||
@@ -135,14 +128,27 @@ export function EditScenarioModal({ scenario, onClose, onSubmit }: EditScenarioM
|
||||
<div className="form-group">
|
||||
<label className="form-label">{t("scenario.create.icon")}</label>
|
||||
<div className="icon-picker">
|
||||
{PRESET_ICONS.map((ic) => (
|
||||
{/* 如果当前图标是旧版 emoji(不在 SVG 图标集中),显示为可选项 */}
|
||||
{!SCENARIO_ICON_SET.some((ic) => ic.id === icon) && (
|
||||
<button
|
||||
key={ic}
|
||||
key="__legacy"
|
||||
type="button"
|
||||
className={`icon-picker__item ${icon === ic ? "icon-picker__item--active" : ""}`}
|
||||
onClick={() => setIcon(ic)}
|
||||
className={`icon-picker__item icon-picker__item--active`}
|
||||
onClick={() => {/* already selected, no-op */}}
|
||||
title="当前图标(旧版)"
|
||||
>
|
||||
{ic}
|
||||
{icon}
|
||||
</button>
|
||||
)}
|
||||
{SCENARIO_ICON_SET.map((ic) => (
|
||||
<button
|
||||
key={ic.id}
|
||||
type="button"
|
||||
className={`icon-picker__item ${icon === ic.id ? "icon-picker__item--active" : ""}`}
|
||||
onClick={() => setIcon(ic.id)}
|
||||
title={ic.label}
|
||||
>
|
||||
{ic.render(22)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -180,13 +180,13 @@ export function LandingPage() {
|
||||
<div className="lp-flow-wrapper lp-fade-in">
|
||||
<div className="lp-flow-steps">
|
||||
{[
|
||||
{ icon: "📷", label: "摄像头采集" },
|
||||
{ icon: "🧠", label: "边缘预处理" },
|
||||
{ icon: "🔌", label: "WebSocket" },
|
||||
{ icon: "⚡", label: "Eino 编排" },
|
||||
{ icon: "👁️", label: "视觉理解" },
|
||||
{ icon: "💬", label: "流式回复" },
|
||||
{ icon: "🔊", label: "语音输出" },
|
||||
{ icon: <svg width={16} height={16} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/><circle cx="12" cy="13" r="4"/></svg>, label: "摄像头采集" },
|
||||
{ icon: <svg width={16} height={16} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"><path d="M12 2a5 5 0 0 1 5 5c0 1.5-.7 2.8-1.7 3.7L12 14l-3.3-3.3A5 5 0 0 1 12 2z"/><path d="M12 14v8"/><path d="M8 18h8"/></svg>, label: "边缘预处理" },
|
||||
{ icon: <svg width={16} height={16} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"><path d="M5 12.55a11 11 0 0 1 14.08 0"/><path d="M1.42 9a16 16 0 0 1 21.16 0"/><path d="M8.53 16.11a6 6 0 0 1 6.95 0"/><line x1="12" y1="20" x2="12.01" y2="20"/></svg>, label: "WebSocket" },
|
||||
{ icon: <svg width={16} height={16} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>, label: "Eino 编排" },
|
||||
{ icon: <svg width={16} height={16} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>, label: "视觉理解" },
|
||||
{ icon: <svg width={16} height={16} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>, label: "流式回复" },
|
||||
{ icon: <svg width={16} height={16} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/></svg>, label: "语音输出" },
|
||||
].map((step, i, arr) => (
|
||||
<div key={i} style={{ display: "contents" }}>
|
||||
<div className="lp-flow-step">
|
||||
@@ -212,27 +212,27 @@ export function LandingPage() {
|
||||
<div className="lp-features-grid">
|
||||
{[
|
||||
{
|
||||
num: "01", icon: "⚡",
|
||||
num: "01", icon: <svg width={24} height={24} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>,
|
||||
title: "流式并行推送",
|
||||
desc: "LLM 文本流与 TTS 音频流并行输出。用户先看到文字,紧接着听到语音,感知延迟低于 0.5 秒,接近真人对话节奏。",
|
||||
},
|
||||
{
|
||||
num: "02", icon: "🧠",
|
||||
num: "02", icon: <svg width={24} height={24} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"><path d="M12 2a5 5 0 0 1 5 5c0 1.5-.7 2.8-1.7 3.7L12 14l-3.3-3.3A5 5 0 0 1 12 2z"/><path d="M12 14v8"/><path d="M8 18h8"/></svg>,
|
||||
title: "声明式 AI 编排",
|
||||
desc: "基于 CloudWeGo Eino Graph 的 7 节点 DAG 流水线(STT → History → ChatModel → Splitter → TTS),类型安全、可扩展、易测试。",
|
||||
},
|
||||
{
|
||||
num: "03", icon: "💰",
|
||||
num: "03", icon: <svg width={24} height={24} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"><line x1="12" y1="1" x2="12" y2="23"/><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/></svg>,
|
||||
title: "端云协同降本",
|
||||
desc: "浏览器端 VAD 语音检测 + 关键帧像素比较 + 混合采样策略,节省 70% 带宽,月成本从 $5,000 降至 $300,降幅 90%。",
|
||||
},
|
||||
{
|
||||
num: "04", icon: "🎯",
|
||||
num: "04", icon: <svg width={24} height={24} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/></svg>,
|
||||
title: "多场景智能模式",
|
||||
desc: "5 种 AI 角色(自由对话 / 模拟面试 / 英语老师 / 辩论对手 / 同声翻译)× 3 种视觉模式 × 观察模式,灵活覆盖学习与工作。",
|
||||
},
|
||||
{
|
||||
num: "05", icon: "🏗️",
|
||||
num: "05", icon: <svg width={24} height={24} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"><rect x="4" y="2" width="16" height="20" rx="2" ry="2"/><line x1="12" y1="18" x2="12" y2="18"/><line x1="12" y1="6" x2="12" y2="6"/><line x1="4" y1="10" x2="20" y2="10"/><line x1="8" y1="2" x2="8" y2="10"/><line x1="16" y1="10" x2="16" y2="18"/></svg>,
|
||||
title: "生产级工程架构",
|
||||
desc: "三级存储自动降级(Memory → Redis → PostgreSQL)、JWT 双 token 认证、Docker Compose 一键部署、完善的错误处理与降级策略。",
|
||||
},
|
||||
@@ -259,11 +259,11 @@ export function LandingPage() {
|
||||
</div>
|
||||
<div className="lp-users-grid">
|
||||
{[
|
||||
{ avatar: "🧑🎓", title: "语言学习者", desc: "对着课本或实物,与 AI 英语外教用英语自由对话,实时纠正语法和发音" },
|
||||
{ avatar: "💼", title: "面试准备者", desc: "开启模拟面试模式,AI 面试官通过摄像头观察你的表情与状态,给出针对性反馈" },
|
||||
{ avatar: "🌍", title: "跨境交流者", desc: "出国旅行时对着外文菜单、路牌实时翻译,AI 语音播报翻译结果" },
|
||||
{ avatar: "👁️", title: "视障人士", desc: "AI 实时描述摄像头画面中的环境、障碍物和文字,提供无障碍信息辅助" },
|
||||
{ avatar: "🔬", title: "学生 / 教师", desc: "对着题目问「怎么做?」,AI 看到画面后逐步讲解,就像身边有一位私教" },
|
||||
{ avatar: <svg width={20} height={20} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>, title: "语言学习者", desc: "对着课本或实物,与 AI 英语外教用英语自由对话,实时纠正语法和发音" },
|
||||
{ avatar: <svg width={20} height={20} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"><rect x="2" y="7" width="20" height="14" rx="2" ry="2"/><path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"/></svg>, title: "面试准备者", desc: "开启模拟面试模式,AI 面试官通过摄像头观察你的表情与状态,给出针对性反馈" },
|
||||
{ avatar: <svg width={20} height={20} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>, title: "跨境交流者", desc: "出国旅行时对着外文菜单、路牌实时翻译,AI 语音播报翻译结果" },
|
||||
{ avatar: <svg width={20} height={20} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>, title: "视障人士", desc: "AI 实时描述摄像头画面中的环境、障碍物和文字,提供无障碍信息辅助" },
|
||||
{ avatar: <svg width={20} height={20} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>, title: "学生 / 教师", desc: "对着题目问「怎么做?」,AI 看到画面后逐步讲解,就像身边有一位私教" },
|
||||
].map((u) => (
|
||||
<div className="lp-user-card lp-fade-in" key={u.title}>
|
||||
<div className="lp-user-card__avatar">{u.avatar}</div>
|
||||
@@ -283,11 +283,11 @@ export function LandingPage() {
|
||||
</div>
|
||||
<div className="lp-scenes-list">
|
||||
{[
|
||||
{ icon: "💬", title: "自由对话", desc: "对着摄像头随意聊天,AI 实时理解画面并语音回答", tag: "通用" },
|
||||
{ icon: "🗣️", title: "英语老师", desc: "AI 外教结合摄像头场景进行英语口语教学,实时纠正语法", tag: "学习" },
|
||||
{ icon: "🎤", title: "模拟面试", desc: "AI 面试官根据你的回答追问,通过摄像头观察你的表现", tag: "求职" },
|
||||
{ icon: "⚔️", title: "辩论对手", desc: "AI 反驳你的观点,锻炼你的逻辑思维和表达能力", tag: "思维" },
|
||||
{ icon: "🌐", title: "同声翻译", desc: "实时识别画面中的外语文字并语音翻译,口语化输出", tag: "工具" },
|
||||
{ icon: <svg width={16} height={16} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>, title: "自由对话", desc: "对着摄像头随意聊天,AI 实时理解画面并语音回答", tag: "通用" },
|
||||
{ icon: <svg width={16} height={16} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"><path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/></svg>, title: "英语老师", desc: "AI 外教结合摄像头场景进行英语口语教学,实时纠正语法", tag: "学习" },
|
||||
{ icon: <svg width={16} height={16} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"><path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/></svg>, title: "模拟面试", desc: "AI 面试官根据你的回答追问,通过摄像头观察你的表现", tag: "求职" },
|
||||
{ icon: <svg width={16} height={16} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/><path d="M8 10h8"/><path d="M8 14h4"/></svg>, title: "辩论对手", desc: "AI 反驳你的观点,锻炼你的逻辑思维和表达能力", tag: "思维" },
|
||||
{ icon: <svg width={16} height={16} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>, title: "同声翻译", desc: "实时识别画面中的外语文字并语音翻译,口语化输出", tag: "工具" },
|
||||
].map((s) => (
|
||||
<div className="lp-scene-row lp-fade-in" key={s.title}>
|
||||
<div className="lp-scene-row__icon">{s.icon}</div>
|
||||
|
||||
@@ -187,7 +187,7 @@ export function SessionSidebar({
|
||||
<>
|
||||
{/* 视频标记图标 */}
|
||||
{session.messageCount > 5 && (
|
||||
<span className="sidebar__item-icon" title={t("sidebar.video")}>📹</span>
|
||||
<span className="sidebar__item-icon" title={t("sidebar.video")}><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2" ry="2"/></svg></span>
|
||||
)}
|
||||
<div className="sidebar__item-content">
|
||||
<div className="sidebar__item-title">{session.title}</div>
|
||||
@@ -207,7 +207,7 @@ export function SessionSidebar({
|
||||
}}
|
||||
title={t("sidebar.rename")}
|
||||
>
|
||||
✏
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
|
||||
</button>
|
||||
<button
|
||||
className="sidebar__action-btn sidebar__action-btn--danger"
|
||||
@@ -217,7 +217,7 @@ export function SessionSidebar({
|
||||
}}
|
||||
title={t("sidebar.delete")}
|
||||
>
|
||||
🗑
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user