diff --git a/app/public/icons.svg b/app/public/icons.svg deleted file mode 100644 index e952219..0000000 --- a/app/public/icons.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/assets/allTools.png b/app/src/assets/allTools.png new file mode 100644 index 0000000..54fe37d Binary files /dev/null and b/app/src/assets/allTools.png differ diff --git a/app/src/assets/hero.png b/app/src/assets/hero.png deleted file mode 100644 index cc51a3d..0000000 Binary files a/app/src/assets/hero.png and /dev/null differ diff --git a/app/src/assets/react.svg b/app/src/assets/react.svg deleted file mode 100644 index 6c87de9..0000000 --- a/app/src/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/app/src/assets/vite.svg b/app/src/assets/vite.svg deleted file mode 100644 index 5101b67..0000000 --- a/app/src/assets/vite.svg +++ /dev/null @@ -1 +0,0 @@ -Vite diff --git a/app/src/components/layouts/components/ToolHeader/ToolHeader.tsx b/app/src/components/layouts/components/ToolHeader/ToolHeader.tsx index 7425a30..9830638 100644 --- a/app/src/components/layouts/components/ToolHeader/ToolHeader.tsx +++ b/app/src/components/layouts/components/ToolHeader/ToolHeader.tsx @@ -1,12 +1,10 @@ -import { useEffect, useState } from 'react' +import { useState } from 'react' import { useNavigate } from 'react-router-dom' import { Box, Menu, Star, X } from 'lucide-react' +import { APP_TITLE_UP, githubRepoUrl } from '@/constants' +import { useGithubStars } from '@/hooks/useGithubStars' import './ToolHeader.scss' -const APP_NAME = import.meta.env.VITE_APP_TITLE_UP -const GITHUB_REPO = 'zeMinng/quick-kit' -const githubRepoUrl = `https://github.com/${GITHUB_REPO}` - const navLinks = [ { label: '首页', to: '/' }, { label: '工具台', to: '/tools' }, @@ -15,27 +13,9 @@ const navLinks = [ const ToolHeader: React.FC = () => { const navigate = useNavigate() - const [starCount, setStarCount] = useState('-') + const starCount = useGithubStars() const [menuOpen, setMenuOpen] = useState(false) - useEffect(() => { - const loadStars = async () => { - try { - const res = await fetch(`https://api.github.com/repos/${GITHUB_REPO}`, { - headers: { Accept: 'application/vnd.github.v3+json' }, - }) - const data = await res.json() - setStarCount( - typeof data.stargazers_count === 'number' ? String(data.stargazers_count) : '-', - ) - } catch { - setStarCount('-') - } - } - - void loadStars() - }, []) - const go = (to: string) => { setMenuOpen(false) if (to.startsWith('/#')) { @@ -57,7 +37,7 @@ const ToolHeader: React.FC = () => { - {APP_NAME} + {APP_TITLE_UP} diff --git a/app/src/configs/home/index.ts b/app/src/configs/home/index.ts index 1fdf792..9eab292 100644 --- a/app/src/configs/home/index.ts +++ b/app/src/configs/home/index.ts @@ -1,5 +1,3 @@ -import { Braces, Cpu, Image } from 'lucide-react' - export const HeroConfig = { heading: '文件留在本地,\n常用能力不打折。', description: '是一套纯前端的实用工具集合:无自有后端、无强制上传。适合对隐私敏感、又希望「点开即用」的团队与个人。', @@ -9,7 +7,7 @@ export const ProductSignals = [ { label: '使用门槛', value: '开箱即用,无需安装' }, { label: '交付形态', value: '支持离线独立使用' }, { label: '开源许可', value: 'MIT 协议,支持 Fork 自建' }, - { label: '数据路径', value: '本地浏览器运行,默认不上传数据' }, + { label: '数据路径', value: '本地运行,默认不上传数据' }, ] // const CapabilityCards = [ @@ -29,40 +27,3 @@ export const ProductSignals = [ // icon: Globe, // }, // ] - -export const CapabilityCards = [ - { - title: '图像与媒体', - description: '面向日常素材流:格式互转、尺寸调整、智能压缩与网格拼接。处理管线走 Canvas,预览与参数调整同步可见。', - icon: Image, - }, - { - title: 'JSON 与结构化数据', - description: '格式化、校验、压缩与展示模式切换;结果支持一键复制。逻辑在浏览器内完成,适合接口联调与日志整理。', - icon: Braces, - }, - { - title: '性能与工程底座', - description: '重计算放入 Web Worker,避免阻塞主线程交互。后续将把解析与转换热点逐步迁到 Rust + WebAssembly,以接近原生的吞吐。', - icon: Cpu, - }, -] - -export const ToolCatalog = [ - { - id: 'json', - name: 'JSON 工作台', - description: '格式化、校验、压缩、转义与美化选项;适合在发布前快速过一遍 payload。', - href: '/tools/json', - actionLabel: '打开工具', - available: true, - }, - { - id: 'image', - name: '图像工具组', - description: '互转、裁切、拼接与压缩等能力正在按模块合入主线;进度与需求可在 GitHub 跟踪。', - href: null, - actionLabel: null, - available: false, - }, -] as const \ No newline at end of file diff --git a/app/src/constants/index.ts b/app/src/constants/index.ts new file mode 100644 index 0000000..471e11a --- /dev/null +++ b/app/src/constants/index.ts @@ -0,0 +1,9 @@ +// 应用相关常量 +export const APP_TITLE = import.meta.env.VITE_APP_TITLE as string +export const APP_TITLE_UP = import.meta.env.VITE_APP_TITLE_UP as string + +// GitHub 仓库相关常量 +export const GITHUB_REPO = 'zeMinng/quick-kit' +export const githubRepoUrl = `https://github.com/${GITHUB_REPO}` +export const GITHUB_ISSUES_URL = `https://github.com/${GITHUB_REPO}/issues` +export const GITHUB_AVATAR_URL = 'https://avatars.githubusercontent.com/u/75962652?v=4' diff --git a/app/src/hooks/useGithubStars.ts b/app/src/hooks/useGithubStars.ts new file mode 100644 index 0000000..9ea6819 --- /dev/null +++ b/app/src/hooks/useGithubStars.ts @@ -0,0 +1,37 @@ +import { useEffect, useState } from 'react' +import { GITHUB_REPO } from '@/constants' + +export function useGithubStars() { + const [stars, setStars] = useState(null) + + useEffect(() => { + let mounted = true + + async function load() { + try { + const res = await fetch(`https://api.github.com/repos/${GITHUB_REPO}`, { + headers: { Accept: 'application/vnd.github.v3+json' }, + }) + + if (!res.ok) throw new Error('Failed to fetch stars') + + const data = await res.json() + if (mounted) { + setStars(data.stargazers_count ?? '-') + } + } catch { + if (mounted) { + setStars('-') + } + } + } + + load() + + return () => { + mounted = false + } + }, []) + + return stars +} diff --git a/app/src/pages/Home/Home.scss b/app/src/pages/Home/Home.scss index 328e63b..83d5540 100644 --- a/app/src/pages/Home/Home.scss +++ b/app/src/pages/Home/Home.scss @@ -5,18 +5,13 @@ flex: 1; } -.home__divider { - width: 100%; - height: 0; - max-width: var(--layout-max); - margin: 0 auto; - border: 0; - border-top: 1px solid var(--color-hairline); -} - .home__hero { + position: relative; padding: clamp(3.5rem, 10vw, 5.5rem) var(--space-lg) clamp(3rem, 8vw, var(--space-section)); - background-color: var(--color-canvas); + overflow: hidden; + background: + radial-gradient(120% 80% at 50% 0%, rgb(250 250 252) 0%, var(--color-canvas) 52%), + linear-gradient(180deg, rgb(255 255 255) 0%, var(--color-canvas) 100%); } .home__hero-wrap { @@ -24,21 +19,14 @@ margin: 0 auto; } -.home__hero-grid { - display: grid; - gap: clamp(var(--space-xl), 4vw, var(--space-xxl)); - align-items: start; -} - -@media (width >= 900px) { - .home__hero-grid { - grid-template-columns: minmax(0, 1.12fr) minmax(220px, 300px); - gap: clamp(var(--space-xl), 4vw, 2.75rem); - } +.home__hero-stack { + width: 100%; } .home__hero-main { - max-width: 38rem; + max-width: 44rem; + margin-inline: auto; + text-align: center; } .home__eyebrow { @@ -71,514 +59,81 @@ color: var(--color-body); } -.home__hero-panel { - display: flex; - flex-direction: column; - padding: 0; - margin: 0; - background: none; - border: none; - border-radius: 0; -} - .home__hero-cta { display: flex; flex-wrap: wrap; gap: var(--space-sm); + justify-content: center; margin-top: var(--space-xl); } -.home__signals { - display: flex; - flex-direction: column; - gap: 0; - padding: 0; - margin: 0; -} - -.home__signal { - padding: var(--space-md) 0; - margin: 0; - text-align: left; - border-bottom: 1px solid var(--color-hairline-soft); +.home__signals-row { + width: 100%; + max-width: 72rem; + margin-top: var(--space-md); } -.home__signal:last-child { - padding-bottom: 0; - border-bottom: none; +.home__signal-card .ant-card-body { + padding: var(--space-md) var(--space-base); } -.home__signal dt { - margin: 0 0 var(--space-xxs); +.home__signal-label { + display: block; + margin-bottom: var(--space-xxs); font-size: var(--type-caption-size); - font-weight: 500; - letter-spacing: 0.06em; - color: var(--color-muted); + letter-spacing: 0.04em; } -.home__signal dd { - margin: 0; +.home__signal-value { + margin-bottom: 0 !important; font-size: var(--type-body-sm-size); - font-weight: 400; - line-height: 1.47; - letter-spacing: 0.01em; - color: var(--color-body-strong); -} - -@media (width >= 900px) { - .home__hero-panel { - padding-left: clamp(var(--space-lg), 3vw, var(--space-xl)); - border-left: 1px solid var(--color-hairline-soft); - } - - .home__signals { - gap: var(--space-lg); - } - - .home__signal { - padding: 0 0 var(--space-lg); - border-bottom: none; - } - - .home__signal:not(:last-child) { - border-bottom: 1px solid var(--color-hairline-soft); - } - - .home__signal:last-child { - padding-bottom: 0; - } -} - -@media (width <= 899px) { - .home__hero-main { - max-width: none; - text-align: center; - } - - .home__hero-panel { - width: 100%; - max-width: none; - padding-top: var(--space-xl); - margin-top: var(--space-md); - border-top: 1px solid var(--color-hairline-soft); - } - - .home__hero-cta { - justify-content: center; - } - - .home__signal { - text-align: center; - } -} - -/* —— 章节头:左侧序号 + 右侧标题/描述 —— */ - -.home__features, -.home__tools, -.home__about { - padding: clamp(3rem, 8vw, var(--space-section)) var(--space-lg); - background-color: var(--color-canvas); -} - -.home__section-head { - margin-bottom: clamp(var(--space-xl), 5vw, var(--space-xxl)); -} - -.home__section-head--split { - display: grid; - max-width: var(--layout-max); - margin-right: auto; - margin-left: auto; - gap: var(--space-lg); + line-height: 1.45; + white-space: nowrap; } -@media (width >= 720px) { - .home__section-head--split { - grid-template-columns: auto minmax(0, 1fr); - gap: var(--space-xxl); - align-items: start; - } -} - -.home__section-kicker, -.home__about-rail { - display: flex; - flex-direction: column; - gap: var(--space-xs); - align-items: flex-start; -} - -@media (width <= 719px) { - .home__section-kicker, - .home__about-rail { - flex-flow: row wrap; - align-items: baseline; - gap: var(--space-md); - } -} - -.home__section-index { - font-size: clamp(2.5rem, 6vw, 3.5rem); - font-weight: 400; - line-height: 1; - letter-spacing: -0.04em; - color: var(--color-hairline-strong); -} - -.home__section-label { - margin: 0; - font-size: var(--type-caption-upper-size); - font-weight: 600; - line-height: 1.4; - letter-spacing: 0.08em; - color: var(--color-muted); - text-transform: uppercase; -} - -.home__section-copy { - max-width: 40rem; -} - -.home__section-title { - margin: 0 0 var(--space-md); - font-size: var(--type-display-lg-size); - font-weight: 500; - line-height: 1.17; - letter-spacing: -0.02em; - color: var(--color-ink); -} - -.home__section-desc { - margin: 0; - font-size: var(--type-body-md-size); - font-weight: 400; - line-height: 1.6; - letter-spacing: 0.01em; - color: var(--color-body); -} - -@media (width <= 719px) { - .home__section-head--split { - text-align: center; - } - - .home__section-kicker { - justify-content: center; - width: 100%; - } - - .home__section-copy { - margin: 0 auto; - } -} - -/* —— 能力卡片:白底线框 + 通栏第三张 —— */ - -.home__card-grid { - display: grid; - gap: var(--space-lg); - max-width: var(--layout-max); - margin: 0 auto; -} - -.home__card-grid--bento { - grid-template-columns: 1fr; -} - -@media (width >= 720px) { - .home__card-grid--bento { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - .home__feature-card[data-bento='wide'] { - grid-column: 1 / -1; - } +.home__visual { + max-width: min(var(--layout-max), 1120px); + padding: 0 var(--space-lg) clamp(2rem, 6vw, 3.5rem); + margin-inline: auto; } -.home__feature-card { - position: relative; - display: flex; - flex-direction: column; - padding: var(--space-xl) var(--space-lg); - background-color: var(--color-canvas); - border: 1px solid var(--color-hairline); +.home__visual img { + display: block; + width: 100%; + height: auto; border-radius: var(--radius-xl); - transition: - border-color 0.2s ease, - box-shadow 0.2s ease; -} - -.home__feature-card:hover { - border-color: var(--color-hairline-strong); box-shadow: var(--shadow-soft); } -.home__feature-index { - position: absolute; - top: var(--space-lg); - right: var(--space-lg); - font-size: var(--type-caption-upper-size); - font-weight: 500; - line-height: 1; - letter-spacing: 0.12em; - color: var(--color-hairline-strong); -} - -.home__feature-icon { - display: flex; - align-items: center; - justify-content: center; - width: 40px; - height: 40px; - margin-bottom: var(--space-md); - color: var(--color-ink); - background-color: var(--color-canvas); - border: 1px solid var(--color-hairline); - border-radius: var(--radius-full); -} - -.home__feature-title { - padding-right: 2.5rem; - margin: 0 0 var(--space-sm); - font-size: var(--type-title-md-size); - font-weight: 500; - line-height: 1.35; - color: var(--color-ink); -} - -.home__feature-card[data-bento='wide'] .home__feature-title { - padding-right: 0; -} - -.home__feature-desc { - margin: 0; - font-size: var(--type-body-md-size); - font-weight: 400; - line-height: 1.55; - letter-spacing: 0.01em; - color: var(--color-body); -} - -@media (width >= 720px) { - .home__feature-card[data-bento='wide'] { - display: grid; - grid-template-columns: auto 1fr; - grid-template-rows: auto auto; - gap: var(--space-sm) var(--space-xl); - align-items: start; - } - - .home__feature-card[data-bento='wide'] .home__feature-index { - position: static; - grid-column: 1; - grid-row: 1; - margin-bottom: var(--space-xs); - font-size: var(--type-display-sm-size); - font-weight: 400; - letter-spacing: -0.02em; - } - - .home__feature-card[data-bento='wide'] .home__feature-icon { - grid-column: 1; - grid-row: 2; - margin-bottom: 0; - } - - .home__feature-card[data-bento='wide'] .home__feature-title { - grid-column: 2; - grid-row: 1; - margin-bottom: var(--space-xs); - } - - .home__feature-card[data-bento='wide'] .home__feature-desc { - grid-column: 2; - grid-row: 2; - } -} - -/* —— 工具表:无灰底容器,行号 + 发丝行 —— */ - -.home__tool-list { +.home__section-inner { max-width: var(--layout-max); - margin: 0 auto; - overflow: hidden; - background-color: var(--color-canvas); - border: 1px solid var(--color-hairline); - border-radius: var(--radius-xl); + margin-inline: auto; } -.home__tool-row { - display: grid; - grid-template-columns: auto auto minmax(0, 1fr) auto; - gap: var(--space-md); - align-items: center; - padding: var(--space-lg) var(--space-lg); - border-bottom: 1px solid var(--color-hairline-soft); +.home__social { + padding: clamp(2.5rem, 8vw, 4rem) var(--space-lg) clamp(3rem, 10vw, 5rem); + text-align: center; + background: linear-gradient(180deg, var(--color-canvas) 0%, rgb(250 250 252) 100%); } -.home__tool-row:last-child { - border-bottom: none; -} - -.home__tool-line { - min-width: 1.5rem; - font-size: var(--type-caption-size); - font-weight: 400; - color: var(--color-muted-soft); +.home__social-lead { + margin-bottom: var(--space-lg) !important; + font-size: var(--type-body-sm-size); + letter-spacing: 0.02em; } -.home__tool-icon { - display: flex; - flex-shrink: 0; - align-items: center; +.home__social-inner { justify-content: center; - width: 36px; - height: 36px; - color: var(--color-ink); - background-color: var(--color-canvas); - border: 1px solid var(--color-hairline); - border-radius: var(--radius-full); } -.home__tool-copy { - min-width: 0; -} - -.home__tool-name-row { - display: flex; - flex-wrap: wrap; - gap: var(--space-sm); - align-items: center; - margin-bottom: var(--space-xs); -} - -.home__tool-name { - margin: 0; - font-size: var(--type-title-sm-size); - font-weight: 500; - line-height: 1.44; - letter-spacing: 0.012em; - color: var(--color-ink); -} - -.home__badge { - display: inline-block; - padding: 4px 10px; - font-size: var(--type-caption-upper-size); +.home__social-tag { + padding-inline: 12px; font-weight: 600; - line-height: 1.4; - letter-spacing: 0.08em; - color: var(--color-ink); - text-transform: uppercase; - background-color: var(--color-canvas); - border: 1px solid var(--color-hairline); border-radius: var(--radius-pill); } -.home__tool-desc { - margin: 0; - font-size: var(--type-body-md-size); - font-weight: 400; - line-height: 1.5; - letter-spacing: 0.01em; - color: var(--color-body); -} - -.home__tool-action { - flex-shrink: 0; -} - -.home__tool-placeholder { - display: inline-flex; - align-items: center; - justify-content: center; - height: 40px; - padding: 0 var(--space-base); - font-size: var(--type-body-sm-size); - font-weight: 400; - line-height: 1.47; - letter-spacing: 0.01em; - color: var(--color-muted-soft); -} - -@media (width <= 639px) { - .home__tool-row { - grid-template-columns: auto auto 1fr; - grid-template-rows: auto auto; - } - - .home__tool-action { - grid-column: 1 / -1; - justify-self: start; - } -} - -/* —— About:双栏 + 文末动作 —— */ - -.home__about-grid { - display: grid; - gap: var(--space-xl); - max-width: var(--layout-max); - margin: 0 auto; -} - -@media (width >= 720px) { - .home__about-grid { - grid-template-columns: auto minmax(0, 1fr); - gap: var(--space-xxl); - align-items: start; - } -} - -.home__about-title { - margin: 0 0 var(--space-lg); - font-size: var(--type-display-md-size); - font-weight: 500; - line-height: 1.13; - letter-spacing: -0.02em; - color: var(--color-ink); -} - -.home__about-text { - margin: 0 0 var(--space-xl); - font-size: var(--type-body-md-size); - font-weight: 400; - line-height: 1.65; - letter-spacing: 0.01em; - color: var(--color-body); -} - -.home__about-actions { - display: flex; - flex-wrap: wrap; - gap: var(--space-sm); -} - -@media (width <= 719px) { - .home__about-grid { - text-align: center; - } - - .home__about-rail { - justify-content: center; - width: 100%; - } - - .home__about-body { - max-width: 40rem; - margin: 0 auto; - } - - .home__about-actions { - justify-content: center; - } -} - -@media (width <= 639px) { - .home__features, - .home__tools, - .home__about { - padding: var(--space-xxl) var(--space-base); +@media (width <= 899px) { + .home__hero-main { + max-width: none; } } diff --git a/app/src/pages/Home/Home.tsx b/app/src/pages/Home/Home.tsx index db35643..7537ab3 100644 --- a/app/src/pages/Home/Home.tsx +++ b/app/src/pages/Home/Home.tsx @@ -1,31 +1,30 @@ -import { Link } from 'react-router-dom' -import { Braces, Image } from 'lucide-react' -import { HeroConfig, ProductSignals, CapabilityCards, ToolCatalog } from '@/configs/home' +import { Button, Flex, Card, Row, Col, Typography, Avatar, Space, Tag, } from 'antd' +import { HeroConfig, ProductSignals } from '@/configs/home' +import { APP_TITLE_UP, githubRepoUrl, GITHUB_AVATAR_URL } from '@/constants' +import allTools from '@/assets/allTools.png' import './Home.scss' -import { Button } from 'antd' - -const APP_NAME = import.meta.env.VITE_APP_TITLE_UP as string +const { Text, Paragraph } = Typography const Home: React.FC = () => { return ( - + - {APP_NAME} + {APP_TITLE_UP} {HeroConfig.heading} - {APP_NAME}{HeroConfig.description} + {APP_TITLE_UP}{HeroConfig.description} 打开工具台 - { - - - - - - - - - - - 01 - - 产品主轴 - - - - 三类能力,覆盖日常交付 - - - 与仓库 README 中的路线图一致:先把「本地、可预览、可重复」做扎实,再按模块扩展格式与算法,而不是堆叠营销话术。 - - - - - {CapabilityCards.map(({ title, description, icon: Icon }, i) => ( - - - {String(i + 1).padStart(2, '0')} - - - - - {title} - {description} - - ))} - - - - - - - - - - 02 - - 工具目录 - - - - 当前开放与进行中的模块 - - - 下列条目与代码路由一致:已挂载的路径可直接进入;未挂载的能力在仓库内迭代,避免占位外链。 - - - - - {ToolCatalog.map((tool, row) => ( - - - {String(row + 1).padStart(2, '0')} - - - {tool.id === 'json' ? : } - - - - {tool.name} - {!tool.available ? 筹备中 : null} - - {tool.description} - - - {tool.available && tool.href ? ( - - {tool.actionLabel} - - ) : ( - - 敬请期待 - - )} - - - ))} + + {ProductSignals.map(s => ( + + + + {s.label} + + + {s.value} + + + + ))} + + - + + + - - - - - 03 - - 关于与协作 - - - - 开源维护,按真实排期演进 - - - 本仓库由 zeMinng 发起,采用 MIT - 协议。若你在日常工作中依赖本地工具链,欢迎通过 Issue 描述场景、通过 PR 改进交互或补齐算法;Star - 也会帮助项目被更多人看到。当前主线已接通 JSON 工作台路由;图像与更多格式转换将按模块合入,不在首页伪造「立即体验」入口。 - - + + + + 深受开发者与创作者信赖 + + + + + zeMinng + + + +1 +
{APP_NAME}
{APP_TITLE_UP}
- {APP_NAME}{HeroConfig.description} + {APP_TITLE_UP}{HeroConfig.description}
产品主轴
- 与仓库 README 中的路线图一致:先把「本地、可预览、可重复」做扎实,再按模块扩展格式与算法,而不是堆叠营销话术。 -
{description}
工具目录
- 下列条目与代码路由一致:已挂载的路径可直接进入;未挂载的能力在仓库内迭代,避免占位外链。 -
{tool.description}
关于与协作
- 本仓库由 zeMinng 发起,采用 MIT - 协议。若你在日常工作中依赖本地工具链,欢迎通过 Issue 描述场景、通过 PR 改进交互或补齐算法;Star - 也会帮助项目被更多人看到。当前主线已接通 JSON 工作台路由;图像与更多格式转换将按模块合入,不在首页伪造「立即体验」入口。 -