feat: add new feature images and update localization strings

This commit is contained in:
JOYCEQL
2025-02-23 23:22:32 +08:00
parent 6735ad7ddc
commit f789684fe9
9 changed files with 75 additions and 273 deletions
-68
View File
@@ -1,68 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="600" height="400" viewBox="0 0 600 400" fill="none" xmlns="http://www.w3.org/2000/svg">
<style>
@media (prefers-color-scheme: dark) {
.bg { fill: #0F172A !important; }
.card { fill: #1E293B !important; stroke: #475569 !important; }
.toolbar { fill: #2D3B55 !important; }
.text-block { fill: #475569 !important; }
.error-bg { fill: #FCA5A5 !important; opacity: 0.15 !important; }
.error-text { fill: #FCA5A5 !important; }
.suggestion-bg { fill: #FCA5A5 !important; opacity: 0.1 !important; }
.ai-circle { fill: #60A5FA !important; }
.ai-text { fill: #EFF6FF !important; }
.error-mark { stroke: #FCA5A5 !important; }
}
@keyframes pulse {
0% { opacity: 0.6; }
50% { opacity: 1; }
100% { opacity: 0.6; }
}
@keyframes slideIn {
from { transform: translateX(20px); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
@keyframes highlight {
0% { stroke-dashoffset: 100; }
100% { stroke-dashoffset: 0; }
}
.ai-icon { animation: pulse 2s infinite; }
.suggestion { animation: slideIn 0.5s ease-out; }
.error-mark {
stroke-dasharray: 100;
animation: highlight 1.5s ease-in-out infinite;
}
.glow {
filter: drop-shadow(0 0 8px rgba(96, 165, 250, 0.3));
}
</style>
<!-- 背景 -->
<rect class="bg" width="600" height="400" fill="#F0F9FF"/>
<!-- 编辑器界面模拟 -->
<rect class="card glow" x="50" y="50" width="500" height="300" rx="8" fill="white" stroke="#93C5FD" stroke-width="2"/>
<!-- 工具栏 -->
<rect class="toolbar" x="50" y="50" width="500" height="40" rx="8" fill="#EFF6FF"/>
<!-- 文本区域 -->
<rect class="text-block" x="70" y="110" width="460" height="20" rx="4" fill="#E0F2FE"/>
<rect class="text-block" x="70" y="140" width="380" height="20" rx="4" fill="#E0F2FE"/>
<!-- AI 纠错标记 -->
<rect class="error-bg" x="70" y="180" width="200" height="20" rx="4" fill="#FEE2E2"/>
<path class="error-mark" d="M260 190 L280 200 L260 210" stroke="#F87171" stroke-width="2"/>
<!-- AI 建议气泡 -->
<g class="suggestion">
<rect class="suggestion-bg" x="300" y="170" width="200" height="60" rx="8" fill="#F87171" opacity="0.1"/>
<text class="error-text" x="320" y="200" fill="#F87171" font-family="Arial" font-size="12">建议修改:表达更专业</text>
</g>
<!-- AI 图标 -->
<g class="ai-icon glow">
<circle class="ai-circle" cx="550" cy="70" r="15" fill="#60A5FA"/>
<text class="ai-text" x="540" y="75" fill="white" font-family="Arial" font-size="12">AI</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

-64
View File
@@ -1,64 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="600" height="400" viewBox="0 0 600 400" fill="none" xmlns="http://www.w3.org/2000/svg">
<style>
@media (prefers-color-scheme: dark) {
.bg { fill: #0F172A !important; }
.device { fill: #1E293B !important; stroke: #A855F7 !important; }
.screen { fill: #2D3B55 !important; }
.storage-circle { fill: #7C3AED !important; opacity: 0.2 !important; }
.storage-icon { stroke: #C4B5FD !important; }
.lock-bg { fill: #7C3AED !important; }
.lock-fg { fill: #EDE9FE !important; }
.data-flow { stroke: #A855F7 !important; }
.data-point { fill: #A855F7 !important; }
}
@keyframes secure {
0% { transform: scale(1); }
50% { transform: scale(1.1); }
100% { transform: scale(1); }
}
@keyframes flow {
0% { stroke-dashoffset: 24; }
100% { stroke-dashoffset: 0; }
}
@keyframes fade {
0% { opacity: 0.4; }
50% { opacity: 0.8; }
100% { opacity: 0.4; }
}
.secure-icon { animation: secure 2s ease-in-out infinite; }
.data-flow { animation: flow 1.5s linear infinite; }
.storage-indicator { animation: fade 2s ease-in-out infinite; }
.glow {
filter: drop-shadow(0 0 8px rgba(167, 139, 250, 0.3));
}
</style>
<!-- 背景 -->
<rect class="bg" width="600" height="400" fill="#F5F3FF"/>
<!-- 设备图标 -->
<rect class="device glow" x="150" y="100" width="300" height="200" rx="20" fill="white" stroke="#A855F7" stroke-width="2"/>
<!-- 屏幕 -->
<rect class="screen" x="170" y="120" width="260" height="140" rx="8" fill="#EEF2FF"/>
<!-- 本地存储图标 -->
<g class="storage-indicator glow">
<circle class="storage-circle" cx="300" cy="180" r="40" fill="#A855F7" opacity="0.1"/>
<path class="storage-icon" d="M280 180 L320 180 M300 160 L300 200" stroke="#8B5CF6" stroke-width="3"/>
</g>
<!-- 锁图标 -->
<g class="secure-icon glow">
<circle class="lock-bg" cx="300" cy="280" r="15" fill="#8B5CF6"/>
<rect class="lock-fg" x="293" y="275" width="14" height="12" rx="2" fill="white"/>
<rect class="lock-fg" x="298" y="272" width="4" height="6" rx="1" fill="white"/>
</g>
<!-- 数据流动效果 -->
<path class="data-flow glow" d="M200 320 C200 350, 400 350, 400 320"
stroke="#8B5CF6" stroke-width="2" stroke-dasharray="4 4"/>
<circle class="data-point glow" cx="200" cy="320" r="4" fill="#8B5CF6"/>
<circle class="data-point glow" cx="400" cy="320" r="4" fill="#8B5CF6"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

-89
View File
@@ -1,89 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="600" height="400" viewBox="0 0 600 400" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<clipPath id="typing-clip">
<rect x="40" y="70" width="230" height="50" />
</clipPath>
</defs>
<style>
@media (prefers-color-scheme: dark) {
.bg { fill: #0F172A !important; }
.editor { fill: #1E293B !important; stroke: #34D399 !important; }
.text-block { fill: #475569 !important; }
.preview-block { fill: #059669 !important; opacity: 0.15 !important; }
.sync-arrow { stroke: #6EE7B7 !important; }
.eye-bg { fill: #059669 !important; }
.eye-fg { fill: #D1FAE5 !important; }
}
@keyframes typing {
0% { transform: translateX(-100%); }
40% { transform: translateX(0); }
60% { transform: translateX(0); }
100% { transform: translateX(-100%); }
}
@keyframes preview {
0% { transform: translateX(20px); opacity: 0; }
20% { transform: translateX(0); opacity: 1; }
80% { transform: translateX(0); opacity: 1; }
100% { transform: translateX(20px); opacity: 0; }
}
@keyframes sync {
0% { transform: translateX(-3px); }
50% { transform: translateX(3px); }
100% { transform: translateX(-3px); }
}
@keyframes eye-blink {
0%, 90%, 100% { transform: scaleY(1); }
95% { transform: scaleY(0.1); }
}
.typing-content {
animation: typing 4s linear infinite;
}
.preview-content {
animation: preview 4s linear infinite;
}
.sync-arrow {
animation: sync 1s ease-in-out infinite;
}
.eye-icon {
animation: eye-blink 2.5s linear infinite;
}
.glow {
filter: drop-shadow(0 0 8px rgba(52, 211, 153, 0.3));
}
</style>
<!-- 背景 -->
<rect class="bg" width="600" height="400" fill="#ECFDF5"/>
<!-- 分屏效果 -->
<rect class="editor glow" x="20" y="50" width="270" height="300" rx="8" fill="white" stroke="#34D399" stroke-width="2"/>
<rect class="editor glow" x="310" y="50" width="270" height="300" rx="8" fill="white" stroke="#34D399" stroke-width="2"/>
<!-- 编辑区域 -->
<g clip-path="url(#typing-clip)">
<g class="typing-content">
<rect class="text-block" x="40" y="70" width="200" height="20" rx="4" fill="#D1FAE5"/>
<rect class="text-block" x="40" y="100" width="150" height="20" rx="4" fill="#D1FAE5"/>
</g>
</g>
<!-- 预览区域 -->
<g class="preview-content">
<rect class="preview-block" x="330" y="70" width="200" height="20" rx="4" fill="#34D399" opacity="0.2"/>
<rect class="preview-block" x="330" y="100" width="150" height="20" rx="4" fill="#34D399" opacity="0.2"/>
</g>
<!-- 同步指示箭头 -->
<g class="sync-arrow glow">
<path d="M290 200 L310 200" stroke="#10B981" stroke-width="2"/>
<path d="M305 195 L310 200 L305 205" stroke="#10B981" stroke-width="2"/>
</g>
<!-- 实时预览图标 -->
<circle class="eye-bg glow" cx="300" cy="200" r="15" fill="#10B981"/>
<g class="eye-icon">
<text class="eye-fg" x="293" y="205" fill="white" font-family="Arial" font-size="12">👁️</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.1 KiB

+10 -9
View File
@@ -19,14 +19,15 @@ export async function POST(req: NextRequest) {
body: JSON.stringify({
model: modelConfig.requiresModelId ? model : modelConfig.defaultModel,
response_format: {
type: "json_object",
type: "json_object"
},
messages: [
{
role: "system",
content: `你是一个专业的中文简历错别字检查助手。请帮助检查以下文本,以下是要求:
1.仅需考虑组词中的错别字,不要考虑语法和格式错误。
2.对每个发现的问题,请按JSON格式返回
content: `你是一个专业的中文简历错别字检查助手。请完整检查以下文本,不要遗漏,以下是要求:
1.所有考虑中文语境下的语法组词错误,包括拼写错误,语境用词错误,专业术语错误。
2.验证是否有遗漏文字未检查
3.对每个发现的问题,请按JSON格式返回
以下是示例格式:
{
@@ -40,14 +41,14 @@ export async function POST(req: NextRequest) {
]
}
请确保返回的格式可以正常解析`,
请确保返回的格式可以正常解析`
},
{
role: "user",
content: content,
},
],
}),
content: content
}
]
})
});
const data = await response.json();
+62 -38
View File
@@ -1,37 +1,37 @@
"use client";
import { CheckCircle2 } from "lucide-react";
import { useTranslations } from "next-intl";
import Image from "next/image";
import { useState } from "react";
import AnimatedFeature from "./client/AnimatedFeature";
const features = [
{
title: "features.ai.title",
description: "features.ai.description",
items: ["features.ai.item1", "features.ai.item2", "features.ai.item3"],
},
{
title: "features.storage.title",
description: "features.storage.description",
items: [
"features.storage.item1",
"features.storage.item2",
"features.storage.item3",
],
},
{
title: "features.preview.title",
description: "features.preview.description",
items: ["features.preview.item1", "features.preview.item2"],
},
];
{
title: "features.ai.item1",
image: "/features/polish.png"
},
{
title: "features.ai.item2",
image: "/features/grammar.png"
}
]
}
] as const;
export default function FeaturesSection() {
const t = useTranslations("home");
const [activeFeature, setActiveFeature] = useState(0);
return (
<section className="py-20 md:py-32 bg-gradient-to-b from-background to-primary/5">
<div className="mx-auto max-w-[1200px] px-4">
<AnimatedFeature>
<div className="text-center mb-12 md:mb-20">
<div className="text-center mb-16 md:mb-24">
<h2 className="text-3xl md:text-4xl font-bold mb-4 md:mb-6">
{t("features.title")}
</h2>
@@ -41,28 +41,52 @@ export default function FeaturesSection() {
</div>
</AnimatedFeature>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 md:gap-12">
{features.map((feature, index) => (
<AnimatedFeature key={index} delay={index * 0.2}>
<div className="relative group">
<div className="absolute inset-0 bg-gradient-to-b from-primary/5 to-transparent rounded-2xl -z-10 transition-transform duration-300 group-hover:scale-105" />
<div className="h-[360px] p-8 bg-white dark:bg-gray-900 rounded-2xl border border-gray-100 dark:border-gray-800 shadow-[0_0_18px_1px_rgba(140,140,140,0.12)] transition-all duration-300 group-hover:border-[#A700FF] dark:group-hover:border-[#A700FF] flex flex-col">
<h3 className="text-2xl font-bold mb-12">
{t(feature.title)}
</h3>
<ul className="space-y-3">
{feature.items.map((item, i) => (
<li key={i} className="flex items-start gap-3">
<CheckCircle2 className="w-5 h-5 text-primary mt-1" />
<span>{t(item)}</span>
</li>
))}
</ul>
<div className="space-y-24 md:space-y-32">
<div className="flex flex-col md:flex-row gap-8 md:gap-16 items-center">
<div className="w-full md:w-[400px] space-y-6 md:mr-16">
<h3 className="text-2xl md:text-3xl font-bold text-center md:text-left">
{t(features[0].title)}
</h3>
<ul className="space-y-4">
{features[0].items.map((item, index) => (
<li
key={index}
onClick={() => setActiveFeature(index)}
className={`flex items-center gap-3 cursor-pointer relative p-2 rounded-lg transition-all ${
activeFeature === index ? "bg-primary/10" : ""
}`}
>
<span className="text-sm font-medium text-muted-foreground w-6">
{String(index + 1).padStart(2, "0")}
</span>
<span
className={`${
activeFeature === index
? "text-primary font-medium"
: "text-muted-foreground"
}`}
>
{t(item.title)}
</span>
</li>
))}
</ul>
</div>
<div className="flex-1 w-full md:w-auto relative">
<AnimatedFeature key={activeFeature}>
<div className="relative aspect-[3/2] w-full overflow-hidden rounded-xl border border-gray-100 dark:border-gray-800 bg-white dark:bg-gray-900">
<Image
src={features[0].items[activeFeature].image}
alt={t(features[0].items[activeFeature].title)}
fill
className="object-contain p-4 md:p-8"
sizes="(max-width: 768px) 100vw, 50vw"
/>
</div>
</div>
</AnimatedFeature>
))}
<div className="absolute inset-0 bg-gradient-to-tr from-primary/5 via-blue-500/5 to-purple-500/5 rounded-xl -z-10 blur-2xl" />
</AnimatedFeature>
</div>
</div>
</div>
</div>
</section>
+1 -2
View File
@@ -32,8 +32,7 @@
"badge": "AI Smart Check",
"title": "Smart Detection, Professional Advice",
"item1": "Intelligent enhancement recommendations",
"item2": "Automatic error correction and grammar check",
"item3": "Personalized content recommendations"
"item2": "Automatic error correction and grammar check"
},
"storage": {
"badge": "Local Storage",
+2 -3
View File
@@ -32,9 +32,8 @@
"badge": "AI 智能纠错",
"title": "智能识别,专业建议",
"description": "内置智能语法检查,自动识别不恰当的表达,提供专业的修改建议,让您的简历更加出色。",
"item1": "智能润色和优化建议",
"item2": "自动纠错和语法检查",
"item3": "个性化内容推荐"
"item1": "智能润色",
"item2": "语法检查"
},
"storage": {
"badge": "本地存储",