Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3b3a187835 |
@@ -1,10 +1,13 @@
|
||||
# 王元有 - 前端工程师简历网站
|
||||
# Wang Yuanyou Fluid Portfolio
|
||||
|
||||
基于 Astro 的个人简历网站,内容整理自 `王元有-前端工程师.pdf`。
|
||||
Independent Vite + Three.js portfolio site for 王元有, focused on a more grounded frontend-engineering resume.
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run dev
|
||||
npm run dev -- --port 5177
|
||||
npm run build
|
||||
```
|
||||
|
||||
The page uses a restrained scroll-aware WebGL backdrop, critical first-paint styling, grounded resume summaries, responsive layout, subtle scroll reveals, project cards, skills, experience, and contact sections.
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import { defineConfig } from "astro/config";
|
||||
|
||||
export default defineConfig({
|
||||
output: "static",
|
||||
});
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta
|
||||
name="description"
|
||||
content="王元有个人网站,前端工程师,7 年 Web 与跨端经验,近一年参与 SeaBuzz、SeaCloud 等 AI 产品前端,覆盖对话、内容流、计费、权限和国际化。"
|
||||
/>
|
||||
<script>
|
||||
(() => {
|
||||
const saved = localStorage.getItem("wy-fluid-theme");
|
||||
if (saved) document.documentElement.dataset.theme = saved;
|
||||
})();
|
||||
</script>
|
||||
<style>
|
||||
:root {
|
||||
background: #07080d;
|
||||
color: #f5f7fb;
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
html[data-theme="light"] {
|
||||
background: #f2f5f7;
|
||||
color: #11131a;
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
background: inherit;
|
||||
color: inherit;
|
||||
font-family:
|
||||
Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
|
||||
"Microsoft YaHei", sans-serif;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="/src/styles.css" />
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||
<title>王元有 - 前端工程师</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="progress" id="scroll-progress"></div>
|
||||
<canvas id="fluid-canvas" aria-hidden="true"></canvas>
|
||||
<div class="scene-backdrop" id="scene-backdrop" aria-hidden="true"></div>
|
||||
<div class="grain" aria-hidden="true"></div>
|
||||
|
||||
<header class="site-header">
|
||||
<a class="brand-mark" href="#top" aria-label="王元有个人网站首页">
|
||||
<span>WY</span>
|
||||
<strong>王元有</strong>
|
||||
</a>
|
||||
<nav class="site-nav" aria-label="页面导航">
|
||||
<a href="#ai-fit">方向</a>
|
||||
<a href="#proof">证据</a>
|
||||
<a href="#projects">项目</a>
|
||||
<a href="#skills">能力</a>
|
||||
<a href="#experience">经历</a>
|
||||
<a href="#contact">联系</a>
|
||||
</nav>
|
||||
<div class="header-actions">
|
||||
<a class="icon-link" href="https://github.com/zhanBoss" target="_blank" rel="noreferrer" aria-label="GitHub">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M12 2C6.48 2 2 6.58 2 12.23c0 4.52 2.87 8.35 6.84 9.7.5.1.68-.22.68-.5v-1.75c-2.78.62-3.37-1.37-3.37-1.37-.45-1.19-1.11-1.5-1.11-1.5-.91-.64.07-.63.07-.63 1 .07 1.53 1.06 1.53 1.06.9 1.56 2.35 1.11 2.92.85.09-.66.35-1.11.63-1.37-2.22-.26-4.56-1.14-4.56-5.07 0-1.12.39-2.03 1.03-2.75-.1-.26-.45-1.3.1-2.71 0 0 .84-.28 2.75 1.05A9.35 9.35 0 0 1 12 6.9c.85 0 1.71.12 2.51.34 1.91-1.33 2.75-1.05 2.75-1.05.55 1.41.2 2.45.1 2.71.64.72 1.03 1.63 1.03 2.75 0 3.94-2.34 4.81-4.57 5.06.36.32.68.94.68 1.9v2.82c0 .28.18.6.69.5A10.08 10.08 0 0 0 22 12.23C22 6.58 17.52 2 12 2Z"
|
||||
></path>
|
||||
</svg>
|
||||
</a>
|
||||
<button class="icon-link" id="theme-toggle" type="button" aria-label="切换明暗主题">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path fill="currentColor" d="M12 2a10 10 0 1 0 0 20V2Zm2 2.29A8 8 0 0 1 14 19.71V4.29Z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main id="top">
|
||||
<section class="hero" aria-labelledby="hero-title">
|
||||
<div class="hero-meta reveal" data-drift="-24">
|
||||
<p class="eyebrow">Frontend Engineer / Product UI / Cross-platform</p>
|
||||
<h1 id="hero-title">王元有</h1>
|
||||
<p class="hero-subtitle">前端工程师,近一年在 AI 产品团队做前端</p>
|
||||
<p class="hero-lede">
|
||||
做过 B 端供应链系统、小程序、微前端平台,也参与过 SeaBuzz 和 SeaCloud 这类 AI 产品。现在更擅长把对话流、
|
||||
内容流、计费、权限、国际化这些复杂链路整理成稳定的前端界面。
|
||||
</p>
|
||||
<div class="hero-actions">
|
||||
<a class="button primary" href="#projects">看项目经历</a>
|
||||
<a class="button" href="mailto:419021733@qq.com">419021733@qq.com</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="signal-board reveal" data-drift="28" aria-label="候选人摘要">
|
||||
<div>
|
||||
<span>前端经验</span>
|
||||
<strong>7 年</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>近一年方向</span>
|
||||
<strong>对话与控制台</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>主要场景</span>
|
||||
<strong>对话 / 内容 / 控制台</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>协作方式</span>
|
||||
<strong>能独立推进模块</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="profile-panel reveal" data-drift="20" aria-label="工作方式摘要">
|
||||
<div>
|
||||
<span>最近职责</span>
|
||||
<strong>SeaBuzz 跨端应用、SeaCloud 控制台</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>更擅长</span>
|
||||
<strong>把复杂状态、权限和数据边界梳理清楚</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>常用技术</span>
|
||||
<strong>React / Next.js / React Native / TypeScript</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>工作习惯</span>
|
||||
<strong>先对齐业务边界,再拆组件和状态模型</strong>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="ai-fit" class="ai-fit-section reveal" data-drift="36" aria-labelledby="ai-fit-title">
|
||||
<div class="section-heading">
|
||||
<p>Current Direction</p>
|
||||
<h2 id="ai-fit-title">AI 是最近一年的工作场景,不是拿来堆关键词的标签。</h2>
|
||||
</div>
|
||||
<div class="ai-focus-grid" id="ai-focus-grid"></div>
|
||||
<div class="resume-signal-panel reveal" data-drift="-30">
|
||||
<div>
|
||||
<p>Resume Summary</p>
|
||||
<h3>前端工程师|AI 产品方向|Web / React Native</h3>
|
||||
</div>
|
||||
<ul id="resume-signal-list"></ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="proof" class="proof-strip reveal" data-drift="-40" aria-labelledby="proof-title">
|
||||
<div class="section-kicker">Evidence</div>
|
||||
<h2 id="proof-title">一些可以核对的经历,先放在前面。</h2>
|
||||
<div class="metric-row" id="metric-row"></div>
|
||||
</section>
|
||||
|
||||
<section id="projects" class="work-section" aria-labelledby="projects-title">
|
||||
<div class="section-heading reveal" data-drift="42">
|
||||
<p>Selected Work</p>
|
||||
<h2 id="projects-title">最近两段经历和 AI 产品相关,但履历底子仍然是前端工程交付。</h2>
|
||||
</div>
|
||||
<div class="project-stack" id="project-stack"></div>
|
||||
</section>
|
||||
|
||||
<section id="skills" class="skills-section" aria-labelledby="skills-title">
|
||||
<div class="section-heading reveal" data-drift="-42">
|
||||
<p>Capabilities</p>
|
||||
<h2 id="skills-title">按实际做过的工作来组织技能,而不是按热门词来分类。</h2>
|
||||
</div>
|
||||
<div class="skill-matrix" id="skill-matrix"></div>
|
||||
</section>
|
||||
|
||||
<section id="experience" class="experience-section" aria-labelledby="experience-title">
|
||||
<div class="section-heading reveal" data-drift="34">
|
||||
<p>Experience</p>
|
||||
<h2 id="experience-title">职责从核心交付演进到架构设计和团队规范建设。</h2>
|
||||
</div>
|
||||
<div class="timeline" id="timeline"></div>
|
||||
</section>
|
||||
|
||||
<section id="contact" class="contact-section reveal" data-drift="-26" aria-labelledby="contact-title">
|
||||
<div>
|
||||
<p class="section-kicker">Contact</p>
|
||||
<h2 id="contact-title">正在看前端、跨端、AI 产品前端相关机会。</h2>
|
||||
<span>男 · 29 岁 · 19980439383 · 电子科技大学 信息管理与信息系统 本科</span>
|
||||
</div>
|
||||
<div class="contact-actions">
|
||||
<a class="button primary" href="mailto:419021733@qq.com">发送邮件</a>
|
||||
<a class="button" href="mailto:wmagmgema521@gmail.com">wmagmgema521@gmail.com</a>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer>© 2026 王元有. Built as an independent fluid portfolio.</footer>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Generated
+118
-3819
File diff suppressed because it is too large
Load Diff
+6
-7
@@ -1,17 +1,16 @@
|
||||
{
|
||||
"name": "wang-yuanyou-resume-site",
|
||||
"name": "wang-yuanyou-fluid-portfolio",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"build": "astro build",
|
||||
"build:test": "PUBLIC_APP_ENV_LABEL=测试环境 astro build",
|
||||
"build:prod": "PUBLIC_APP_ENV_LABEL=生产环境 astro build",
|
||||
"preview": "astro preview"
|
||||
"dev": "vite --host 0.0.0.0",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview --host 0.0.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"astro": "^6.3.1"
|
||||
"three": "^0.184.0",
|
||||
"vite": "^7.2.7"
|
||||
},
|
||||
"devDependencies": {}
|
||||
}
|
||||
|
||||
Generated
+649
@@ -0,0 +1,649 @@
|
||||
lockfileVersion: '9.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
three:
|
||||
specifier: ^0.184.0
|
||||
version: 0.184.0
|
||||
vite:
|
||||
specifier: ^7.2.7
|
||||
version: 7.3.5
|
||||
|
||||
packages:
|
||||
|
||||
'@esbuild/aix-ppc64@0.27.7':
|
||||
resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ppc64]
|
||||
os: [aix]
|
||||
|
||||
'@esbuild/android-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-arm@0.27.7':
|
||||
resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-x64@0.27.7':
|
||||
resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/darwin-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/darwin-x64@0.27.7':
|
||||
resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/freebsd-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/freebsd-x64@0.27.7':
|
||||
resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/linux-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-arm@0.27.7':
|
||||
resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ia32@0.27.7':
|
||||
resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ia32]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-loong64@0.27.7':
|
||||
resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-mips64el@0.27.7':
|
||||
resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [mips64el]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ppc64@0.27.7':
|
||||
resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-riscv64@0.27.7':
|
||||
resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-s390x@0.27.7':
|
||||
resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-x64@0.27.7':
|
||||
resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/netbsd-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/netbsd-x64@0.27.7':
|
||||
resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/openbsd-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/openbsd-x64@0.27.7':
|
||||
resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/openharmony-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [openharmony]
|
||||
|
||||
'@esbuild/sunos-x64@0.27.7':
|
||||
resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [sunos]
|
||||
|
||||
'@esbuild/win32-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-ia32@0.27.7':
|
||||
resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-x64@0.27.7':
|
||||
resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.61.1':
|
||||
resolution: {integrity: sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@rollup/rollup-android-arm64@4.61.1':
|
||||
resolution: {integrity: sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@rollup/rollup-darwin-arm64@4.61.1':
|
||||
resolution: {integrity: sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@rollup/rollup-darwin-x64@4.61.1':
|
||||
resolution: {integrity: sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@rollup/rollup-freebsd-arm64@4.61.1':
|
||||
resolution: {integrity: sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
|
||||
'@rollup/rollup-freebsd-x64@4.61.1':
|
||||
resolution: {integrity: sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@rollup/rollup-linux-arm-gnueabihf@4.61.1':
|
||||
resolution: {integrity: sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.61.1':
|
||||
resolution: {integrity: sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.61.1':
|
||||
resolution: {integrity: sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.61.1':
|
||||
resolution: {integrity: sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-loong64-gnu@4.61.1':
|
||||
resolution: {integrity: sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-loong64-musl@4.61.1':
|
||||
resolution: {integrity: sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-ppc64-gnu@4.61.1':
|
||||
resolution: {integrity: sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-ppc64-musl@4.61.1':
|
||||
resolution: {integrity: sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.61.1':
|
||||
resolution: {integrity: sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-musl@4.61.1':
|
||||
resolution: {integrity: sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-s390x-gnu@4.61.1':
|
||||
resolution: {integrity: sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.61.1':
|
||||
resolution: {integrity: sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.61.1':
|
||||
resolution: {integrity: sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@rollup/rollup-openbsd-x64@4.61.1':
|
||||
resolution: {integrity: sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==}
|
||||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
|
||||
'@rollup/rollup-openharmony-arm64@4.61.1':
|
||||
resolution: {integrity: sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==}
|
||||
cpu: [arm64]
|
||||
os: [openharmony]
|
||||
|
||||
'@rollup/rollup-win32-arm64-msvc@4.61.1':
|
||||
resolution: {integrity: sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@rollup/rollup-win32-ia32-msvc@4.61.1':
|
||||
resolution: {integrity: sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@rollup/rollup-win32-x64-gnu@4.61.1':
|
||||
resolution: {integrity: sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@rollup/rollup-win32-x64-msvc@4.61.1':
|
||||
resolution: {integrity: sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@types/estree@1.0.9':
|
||||
resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
|
||||
|
||||
esbuild@0.27.7:
|
||||
resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
fdir@6.5.0:
|
||||
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
picomatch: ^3 || ^4
|
||||
peerDependenciesMeta:
|
||||
picomatch:
|
||||
optional: true
|
||||
|
||||
fsevents@2.3.3:
|
||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
os: [darwin]
|
||||
|
||||
nanoid@3.3.12:
|
||||
resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==}
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
picocolors@1.1.1:
|
||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||
|
||||
picomatch@4.0.4:
|
||||
resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
postcss@8.5.15:
|
||||
resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
||||
rollup@4.61.1:
|
||||
resolution: {integrity: sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==}
|
||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||
hasBin: true
|
||||
|
||||
source-map-js@1.2.1:
|
||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
three@0.184.0:
|
||||
resolution: {integrity: sha512-wtTRjG92pM5eUg/KuUnHsqSAlPM296brTOcLgMRqEeylYTh/CdtvKUvCyyCQTzFuStieWxvZb8mVTMvdPyUpxg==}
|
||||
|
||||
tinyglobby@0.2.17:
|
||||
resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
vite@7.3.5:
|
||||
resolution: {integrity: sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@types/node': ^20.19.0 || >=22.12.0
|
||||
jiti: '>=1.21.0'
|
||||
less: ^4.0.0
|
||||
lightningcss: ^1.21.0
|
||||
sass: ^1.70.0
|
||||
sass-embedded: ^1.70.0
|
||||
stylus: '>=0.54.8'
|
||||
sugarss: ^5.0.0
|
||||
terser: ^5.16.0
|
||||
tsx: ^4.8.1
|
||||
yaml: ^2.4.2
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
jiti:
|
||||
optional: true
|
||||
less:
|
||||
optional: true
|
||||
lightningcss:
|
||||
optional: true
|
||||
sass:
|
||||
optional: true
|
||||
sass-embedded:
|
||||
optional: true
|
||||
stylus:
|
||||
optional: true
|
||||
sugarss:
|
||||
optional: true
|
||||
terser:
|
||||
optional: true
|
||||
tsx:
|
||||
optional: true
|
||||
yaml:
|
||||
optional: true
|
||||
|
||||
snapshots:
|
||||
|
||||
'@esbuild/aix-ppc64@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm64@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-x64@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-arm64@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-x64@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-arm64@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-x64@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm64@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ia32@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-loong64@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-mips64el@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ppc64@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-riscv64@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-s390x@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-x64@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-arm64@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-x64@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-arm64@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-x64@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openharmony-arm64@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@esbuild/sunos-x64@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-arm64@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-ia32@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-x64@0.27.7':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.61.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-android-arm64@4.61.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-darwin-arm64@4.61.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-darwin-x64@4.61.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-freebsd-arm64@4.61.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-freebsd-x64@4.61.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm-gnueabihf@4.61.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.61.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.61.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.61.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-loong64-gnu@4.61.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-loong64-musl@4.61.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-ppc64-gnu@4.61.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-ppc64-musl@4.61.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.61.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-riscv64-musl@4.61.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-s390x-gnu@4.61.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.61.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.61.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-openbsd-x64@4.61.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-openharmony-arm64@4.61.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-arm64-msvc@4.61.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-ia32-msvc@4.61.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-x64-gnu@4.61.1':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-x64-msvc@4.61.1':
|
||||
optional: true
|
||||
|
||||
'@types/estree@1.0.9': {}
|
||||
|
||||
esbuild@0.27.7:
|
||||
optionalDependencies:
|
||||
'@esbuild/aix-ppc64': 0.27.7
|
||||
'@esbuild/android-arm': 0.27.7
|
||||
'@esbuild/android-arm64': 0.27.7
|
||||
'@esbuild/android-x64': 0.27.7
|
||||
'@esbuild/darwin-arm64': 0.27.7
|
||||
'@esbuild/darwin-x64': 0.27.7
|
||||
'@esbuild/freebsd-arm64': 0.27.7
|
||||
'@esbuild/freebsd-x64': 0.27.7
|
||||
'@esbuild/linux-arm': 0.27.7
|
||||
'@esbuild/linux-arm64': 0.27.7
|
||||
'@esbuild/linux-ia32': 0.27.7
|
||||
'@esbuild/linux-loong64': 0.27.7
|
||||
'@esbuild/linux-mips64el': 0.27.7
|
||||
'@esbuild/linux-ppc64': 0.27.7
|
||||
'@esbuild/linux-riscv64': 0.27.7
|
||||
'@esbuild/linux-s390x': 0.27.7
|
||||
'@esbuild/linux-x64': 0.27.7
|
||||
'@esbuild/netbsd-arm64': 0.27.7
|
||||
'@esbuild/netbsd-x64': 0.27.7
|
||||
'@esbuild/openbsd-arm64': 0.27.7
|
||||
'@esbuild/openbsd-x64': 0.27.7
|
||||
'@esbuild/openharmony-arm64': 0.27.7
|
||||
'@esbuild/sunos-x64': 0.27.7
|
||||
'@esbuild/win32-arm64': 0.27.7
|
||||
'@esbuild/win32-ia32': 0.27.7
|
||||
'@esbuild/win32-x64': 0.27.7
|
||||
|
||||
fdir@6.5.0(picomatch@4.0.4):
|
||||
optionalDependencies:
|
||||
picomatch: 4.0.4
|
||||
|
||||
fsevents@2.3.3:
|
||||
optional: true
|
||||
|
||||
nanoid@3.3.12: {}
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
|
||||
picomatch@4.0.4: {}
|
||||
|
||||
postcss@8.5.15:
|
||||
dependencies:
|
||||
nanoid: 3.3.12
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
rollup@4.61.1:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.9
|
||||
optionalDependencies:
|
||||
'@rollup/rollup-android-arm-eabi': 4.61.1
|
||||
'@rollup/rollup-android-arm64': 4.61.1
|
||||
'@rollup/rollup-darwin-arm64': 4.61.1
|
||||
'@rollup/rollup-darwin-x64': 4.61.1
|
||||
'@rollup/rollup-freebsd-arm64': 4.61.1
|
||||
'@rollup/rollup-freebsd-x64': 4.61.1
|
||||
'@rollup/rollup-linux-arm-gnueabihf': 4.61.1
|
||||
'@rollup/rollup-linux-arm-musleabihf': 4.61.1
|
||||
'@rollup/rollup-linux-arm64-gnu': 4.61.1
|
||||
'@rollup/rollup-linux-arm64-musl': 4.61.1
|
||||
'@rollup/rollup-linux-loong64-gnu': 4.61.1
|
||||
'@rollup/rollup-linux-loong64-musl': 4.61.1
|
||||
'@rollup/rollup-linux-ppc64-gnu': 4.61.1
|
||||
'@rollup/rollup-linux-ppc64-musl': 4.61.1
|
||||
'@rollup/rollup-linux-riscv64-gnu': 4.61.1
|
||||
'@rollup/rollup-linux-riscv64-musl': 4.61.1
|
||||
'@rollup/rollup-linux-s390x-gnu': 4.61.1
|
||||
'@rollup/rollup-linux-x64-gnu': 4.61.1
|
||||
'@rollup/rollup-linux-x64-musl': 4.61.1
|
||||
'@rollup/rollup-openbsd-x64': 4.61.1
|
||||
'@rollup/rollup-openharmony-arm64': 4.61.1
|
||||
'@rollup/rollup-win32-arm64-msvc': 4.61.1
|
||||
'@rollup/rollup-win32-ia32-msvc': 4.61.1
|
||||
'@rollup/rollup-win32-x64-gnu': 4.61.1
|
||||
'@rollup/rollup-win32-x64-msvc': 4.61.1
|
||||
fsevents: 2.3.3
|
||||
|
||||
source-map-js@1.2.1: {}
|
||||
|
||||
three@0.184.0: {}
|
||||
|
||||
tinyglobby@0.2.17:
|
||||
dependencies:
|
||||
fdir: 6.5.0(picomatch@4.0.4)
|
||||
picomatch: 4.0.4
|
||||
|
||||
vite@7.3.5:
|
||||
dependencies:
|
||||
esbuild: 0.27.7
|
||||
fdir: 6.5.0(picomatch@4.0.4)
|
||||
picomatch: 4.0.4
|
||||
postcss: 8.5.15
|
||||
rollup: 4.61.1
|
||||
tinyglobby: 0.2.17
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<rect width="64" height="64" rx="14" fill="#07080d"/>
|
||||
<path d="M12 17h8l5 25 7-25h7l7 25 5-25h7L49 50h-8l-6-22-6 22h-8L12 17Z" fill="#56f7c5"/>
|
||||
<path d="M13 50h38" stroke="#ff5ea8" stroke-width="4" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 297 B |
@@ -1,481 +0,0 @@
|
||||
export const resume = {
|
||||
name: "王元有",
|
||||
alias: "湛兮",
|
||||
title: "前端开发工程师",
|
||||
intent: "求职意向:前端开发工程师",
|
||||
profile:
|
||||
"7 年前端与跨端开发经验,长期负责 Web、App、H5、小程序与管理后台的架构设计、工程化建设和核心业务交付。熟悉 AI 产品工程、SSE 流式通信、Next.js/Vue/React Native 跨端体系、微前端和 Monorepo 协作模式。",
|
||||
basics: [
|
||||
{ label: "工作经验", value: "7 年" },
|
||||
{ label: "技术管理", value: "2+ 年" },
|
||||
{ label: "项目用户规模", value: "千万级 MAU" },
|
||||
{ label: "教育背景", value: "电子科技大学 本科" },
|
||||
],
|
||||
contact: {
|
||||
phone: "19980439383",
|
||||
email: "419021733@qq.com",
|
||||
emailAlt: "wmagmgema521@gmail.com",
|
||||
meta: "男 | 29 岁",
|
||||
},
|
||||
highlights: [
|
||||
"主导多款企业级产品从 0 到 1 搭建,覆盖 AI 模型服务、AI 资讯对话、供应链、门店数字化、工业互联网平台等场景。",
|
||||
"掌握 Vue3、React、Next.js、React Native、Expo、UniApp 等技术栈,能独立完成 Web、移动端、App 与管理后台方案落地。",
|
||||
"推动 Micro-Frontend、SSR、pnpm Monorepo、Git Flow、代码审查、CI/CD、监控体系等工程化实践。",
|
||||
"有明确的性能与效率结果:构建体积优化 30%、首屏白屏时间缩短 25%、组件库工程化提升开发效率 50%、重复开发成本减少约 40%。",
|
||||
],
|
||||
metrics: [
|
||||
{ value: "-30%", label: "构建体积优化" },
|
||||
{ value: "-25%", label: "首屏白屏时间" },
|
||||
{ value: "+50%", label: "组件库效率提升" },
|
||||
{ value: "+40%", label: "团队人效提升" },
|
||||
{ value: "40%", label: "重复开发成本降低" },
|
||||
{ value: "30%", label: "消息渲染性能提升" },
|
||||
],
|
||||
skills: [
|
||||
{
|
||||
group: "前端框架",
|
||||
items: [
|
||||
"Next.js 16",
|
||||
"React 19",
|
||||
"Vue 3",
|
||||
"TypeScript 5",
|
||||
"Tailwind CSS 4",
|
||||
"Nuxt/SSR",
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "跨端开发",
|
||||
items: [
|
||||
"React Native 0.79",
|
||||
"Expo 53",
|
||||
"UniApp",
|
||||
"H5",
|
||||
"微信/飞书小程序",
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "AI 与通信",
|
||||
items: [
|
||||
"Vercel AI SDK v6",
|
||||
"SSE 流式通信",
|
||||
"多 Agent 协作",
|
||||
"Markdown/LaTeX 渲染",
|
||||
"AI 对话",
|
||||
"AI 生图/生视频",
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "工程化",
|
||||
items: [
|
||||
"pnpm Monorepo",
|
||||
"Git Submodules",
|
||||
"Qiankun",
|
||||
"Lerna",
|
||||
"Git Flow",
|
||||
"Playwright",
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "状态与 UI",
|
||||
items: [
|
||||
"Zustand",
|
||||
"MobX",
|
||||
"Pinia",
|
||||
"Radix UI",
|
||||
"Ant Design",
|
||||
"Element Plus",
|
||||
"ProComponents",
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "基础设施",
|
||||
items: [
|
||||
"Alova",
|
||||
"next-intl",
|
||||
"i18next",
|
||||
"ARMS 监控",
|
||||
"CDN 优化",
|
||||
"Adyen 支付",
|
||||
],
|
||||
},
|
||||
],
|
||||
experiences: [
|
||||
{
|
||||
company: "成都海艺互娱科技有限公司",
|
||||
role: "React Native 开发工程师",
|
||||
period: "2025.05 - 至今",
|
||||
points: [
|
||||
"参与多款 AI 产品从 0 到 1 搭建与架构设计,覆盖前端性能、交互体验、国际化体系与多端组件复用。",
|
||||
"在 AI 对话、生图、生视频与多 Agent 协作领域沉淀工程经验,负责流式通信与前端渲染体验优化。",
|
||||
"支撑千万级海外用户产品,推动多端组件库统一建设,减少重复开发成本约 40%。",
|
||||
],
|
||||
},
|
||||
{
|
||||
company: "四川茶姬企业管理有限公司",
|
||||
role: "高级 Web 前端开发",
|
||||
period: "2024.03 - 2025.03",
|
||||
points: [
|
||||
"主导供应链管理系统前端开发,实现采购、仓储、配送等核心模块。",
|
||||
"独立负责门店报损与食安管理核心功能搭建及迭代,覆盖国内、东南亚与北美业务逻辑。",
|
||||
"引入阿里云 ARMS 前端监控体系,落地 CDN 静态资源优化方案,并制定代码审查与 Git 操作规范。",
|
||||
],
|
||||
},
|
||||
{
|
||||
company: "中钧科技有限公司四川分公司",
|
||||
role: "前端开发组长",
|
||||
period: "2022.04 - 2024.03",
|
||||
points: [
|
||||
"负责经营帮 PC 端微前端、门户和商管核心模块开发,完成 UI 还原、接口联调及性能优化。",
|
||||
"从零搭建新 E 畅行小程序基础框架,完成技术方案制定与全流程开发。",
|
||||
"主导经营帮小程序、H5、Admin 后台迭代开发与跨端技术方案设计,建立 Git Flow、代码审查和新人培训机制。",
|
||||
],
|
||||
},
|
||||
],
|
||||
projects: [
|
||||
{
|
||||
id: "vtrix",
|
||||
name: "SeaCloud / Vtrix",
|
||||
subtitle: "AI 模型服务平台(Web 端)",
|
||||
period: "2025.09 - 至今",
|
||||
tech: [
|
||||
"Next.js 16",
|
||||
"React 19",
|
||||
"TypeScript",
|
||||
"Tailwind CSS",
|
||||
"Zustand",
|
||||
"Alova",
|
||||
"Vercel AI SDK",
|
||||
"Radix UI",
|
||||
"next-intl",
|
||||
],
|
||||
summary:
|
||||
"面向全球用户的 AI 模型聚合服务平台,聚合 LLM、图像、视频、音频、3D 等多模态模型能力,覆盖 C 端调用与 B 端组织/分销管理。",
|
||||
modules: [
|
||||
"分销商客户邀请、折扣模板、额度分配、销售配置与利润率计算。",
|
||||
"多币种与汇率体系,封装 useCurrency Hook 并贯穿 Pricing、Billing、API Keys 等模块。",
|
||||
"组织成员、角色权限守卫、配额设置、支出限额、账单报表、交易筛选与 Excel 导出。",
|
||||
"i18n Submodule 架构迁移、企业微信同步工作流、通用表格/筛选器/分页组件增强。",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "seabuzz",
|
||||
name: "SeaBuzz",
|
||||
subtitle: "AI 智能资讯与对话平台",
|
||||
period: "2025.05 - 至今",
|
||||
tech: [
|
||||
"React Native",
|
||||
"Expo 53",
|
||||
"Expo Router",
|
||||
"Zustand",
|
||||
"NativeWind",
|
||||
"SSE",
|
||||
"i18next",
|
||||
"Adyen",
|
||||
"Lerna",
|
||||
],
|
||||
summary:
|
||||
"海艺 AI 旗下 AI 新闻聚合、智能搜索与多模态对话平台,基于 Expo 实现 iOS、Android、Web 三端统一开发。",
|
||||
modules: [
|
||||
"AI Agent 对话完整链路:SSE 流式聊天、打字机渲染、Markdown/LaTeX、思考动画、来源引用与历史同步。",
|
||||
"Discover 发现页与新闻详情,支持瀑布流、大/小卡动态布局、骨架屏与 Smart Image。",
|
||||
"Google、Facebook、Discord、Email 登录与 SeaArt Auth SDK 对接。",
|
||||
"Monorepo 公共包体系、API 层、数据模型、UI 组件、状态管理、OTA 热更新与代码签名机制。",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "bawang",
|
||||
name: "霸王功夫",
|
||||
subtitle: "门店数字化与供应链管理平台",
|
||||
period: "2024.03 - 2025.03",
|
||||
tech: [
|
||||
"React 18",
|
||||
"Vue 3",
|
||||
"UniApp",
|
||||
"MobX",
|
||||
"Pinia",
|
||||
"Element Plus",
|
||||
"Ant Design",
|
||||
"ProComponents",
|
||||
],
|
||||
summary:
|
||||
"服务全球 6000+ 门店及运营伙伴的数字化管理平台,覆盖门店运营、食品安全、供应链协同等核心业务。",
|
||||
modules: [
|
||||
"小程序报损模块:摄像头扫码、在线报损登记,兼容微信小程序与飞书 H5。",
|
||||
"食安管理模块:低频蓝牙连接 TSPL 指令集打印机,支持国内、东南亚、北美三套业务逻辑。",
|
||||
"经营信息模块:多门店经营状态移动端看板,按区域、时间、指标筛选。",
|
||||
"供应链模块:采购系统、供应商合同管理、供应商结算系统,对接云厉、费控等外部系统。",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "jingyingbang",
|
||||
name: "经营帮平台 + 经营帮拉新",
|
||||
subtitle: "工业互联网与微前端平台",
|
||||
period: "2022.04 - 2024.03",
|
||||
tech: [
|
||||
"Vue",
|
||||
"Qiankun",
|
||||
"Element UI",
|
||||
"华为云 OBS",
|
||||
"百度地图",
|
||||
"高德地图",
|
||||
"IM",
|
||||
"UniApp",
|
||||
],
|
||||
summary:
|
||||
"基于信息化设计理念和区块链技术的工业互联网平台,为企业和个人提供数字化运营服务。",
|
||||
modules: [
|
||||
"参与单体前端到 Qiankun 微前端拆分,拆分出 12 个基础项目。",
|
||||
"负责商管、门户、经营帮系列小程序/H5/Admin 后台核心业务交付。",
|
||||
"引入华为云 OBS 直传减轻请求链路性能浪费。",
|
||||
"开发内部 Chrome 插件 zjkj-decryption,提升数据解析效率。",
|
||||
],
|
||||
},
|
||||
],
|
||||
education: {
|
||||
school: "电子科技大学",
|
||||
degree: "本科",
|
||||
major: "信息管理与信息系统",
|
||||
period: "2023 - 2025",
|
||||
},
|
||||
};
|
||||
|
||||
export const resumeEn = {
|
||||
name: "Wang Yuanyou",
|
||||
alias: "mrZhan",
|
||||
title: "Frontend Engineer",
|
||||
intent: "Target Role: Frontend Engineer",
|
||||
profile:
|
||||
"Frontend and cross-platform engineer with 7 years of experience building Web, App, H5, mini-program and admin systems. Strong in AI product engineering, SSE streaming, Next.js/Vue/React Native ecosystems, micro-frontends and Monorepo collaboration.",
|
||||
basics: [
|
||||
{ label: "Experience", value: "7 years" },
|
||||
{ label: "Tech Leadership", value: "2+ years" },
|
||||
{ label: "Product Scale", value: "10M+ MAU" },
|
||||
{ label: "Education", value: "UESTC Bachelor" },
|
||||
],
|
||||
contact: {
|
||||
phone: "19980439383",
|
||||
email: "419021733@qq.com",
|
||||
emailAlt: "wmagmgema521@gmail.com",
|
||||
meta: "Male | 29",
|
||||
},
|
||||
highlights: [
|
||||
"Led multiple enterprise products from 0 to 1 across AI model services, AI news and chat, supply chain, store digitization and industrial internet platforms.",
|
||||
"Hands-on with Vue3, React, Next.js, React Native, Expo and UniApp, capable of delivering Web, mobile, App and admin products end to end.",
|
||||
"Drove engineering practices including Micro-Frontend, SSR, pnpm Monorepo, Git Flow, code review, CI/CD and frontend observability.",
|
||||
"Delivered measurable outcomes: 30% smaller bundles, 25% faster first screen, 50% faster component-driven delivery and around 40% less duplicated work.",
|
||||
],
|
||||
metrics: [
|
||||
{ value: "-30%", label: "Bundle Size" },
|
||||
{ value: "-25%", label: "First Screen Blank Time" },
|
||||
{ value: "+50%", label: "Component Delivery Efficiency" },
|
||||
{ value: "+40%", label: "Team Productivity" },
|
||||
{ value: "40%", label: "Duplicated Work Reduced" },
|
||||
{ value: "30%", label: "Message Rendering Performance" },
|
||||
],
|
||||
skills: [
|
||||
{
|
||||
group: "Frontend Frameworks",
|
||||
items: [
|
||||
"Next.js 16",
|
||||
"React 19",
|
||||
"Vue 3",
|
||||
"TypeScript 5",
|
||||
"Tailwind CSS 4",
|
||||
"Nuxt/SSR",
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "Cross-platform",
|
||||
items: [
|
||||
"React Native 0.79",
|
||||
"Expo 53",
|
||||
"UniApp",
|
||||
"H5",
|
||||
"WeChat/Lark Mini Programs",
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "AI & Streaming",
|
||||
items: [
|
||||
"Vercel AI SDK v6",
|
||||
"SSE Streaming",
|
||||
"Multi-Agent Collaboration",
|
||||
"Markdown/LaTeX",
|
||||
"AI Chat",
|
||||
"AI Image/Video",
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "Engineering",
|
||||
items: [
|
||||
"pnpm Monorepo",
|
||||
"Git Submodules",
|
||||
"Qiankun",
|
||||
"Lerna",
|
||||
"Git Flow",
|
||||
"Playwright",
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "State & UI",
|
||||
items: [
|
||||
"Zustand",
|
||||
"MobX",
|
||||
"Pinia",
|
||||
"Radix UI",
|
||||
"Ant Design",
|
||||
"Element Plus",
|
||||
"ProComponents",
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "Infrastructure",
|
||||
items: [
|
||||
"Alova",
|
||||
"next-intl",
|
||||
"i18next",
|
||||
"ARMS Monitoring",
|
||||
"CDN Optimization",
|
||||
"Adyen Payments",
|
||||
],
|
||||
},
|
||||
],
|
||||
experiences: [
|
||||
{
|
||||
company: "Chengdu Haiyi Interactive Entertainment Technology Co., Ltd.",
|
||||
role: "React Native Engineer",
|
||||
period: "2025.05 - Present",
|
||||
points: [
|
||||
"Contributed to architecture and 0-to-1 delivery of multiple AI products, covering performance, UX, i18n and multi-platform component reuse.",
|
||||
"Built engineering experience in AI chat, image/video generation and multi-agent collaboration, with focus on streaming and frontend rendering performance.",
|
||||
"Supported products serving 10M+ overseas users and promoted unified multi-platform component libraries, reducing duplicate work by around 40%.",
|
||||
],
|
||||
},
|
||||
{
|
||||
company: "Sichuan Chaji Enterprise Management Co., Ltd.",
|
||||
role: "Senior Web Frontend Developer",
|
||||
period: "2024.03 - 2025.03",
|
||||
points: [
|
||||
"Led frontend development of supply-chain management systems, including procurement, warehousing and delivery modules.",
|
||||
"Owned store loss-reporting and food-safety features across China, Southeast Asia and North America business rules.",
|
||||
"Introduced Alibaba Cloud ARMS frontend monitoring, delivered CDN optimization and established code review and Git workflow standards.",
|
||||
],
|
||||
},
|
||||
{
|
||||
company: "Zhongjun Technology Sichuan Branch",
|
||||
role: "Frontend Team Lead",
|
||||
period: "2022.04 - 2024.03",
|
||||
points: [
|
||||
"Delivered core modules for the Jingyingbang PC micro-frontend platform, portal and business management systems, including UI implementation, API integration and performance optimization.",
|
||||
"Built the New E Travel mini-program foundation from scratch and owned technical planning through delivery.",
|
||||
"Led mini-program, H5 and admin iterations, designed cross-platform solutions, and established Git Flow, code review and onboarding practices.",
|
||||
],
|
||||
},
|
||||
],
|
||||
projects: [
|
||||
{
|
||||
id: "vtrix",
|
||||
name: "SeaCloud / Vtrix",
|
||||
subtitle: "AI Model Service Platform (Web)",
|
||||
period: "2025.09 - Present",
|
||||
tech: [
|
||||
"Next.js 16",
|
||||
"React 19",
|
||||
"TypeScript",
|
||||
"Tailwind CSS",
|
||||
"Zustand",
|
||||
"Alova",
|
||||
"Vercel AI SDK",
|
||||
"Radix UI",
|
||||
"next-intl",
|
||||
],
|
||||
summary:
|
||||
"A global AI model aggregation platform covering LLM, image, video, audio and 3D model capabilities for developers, organizations and distributors.",
|
||||
modules: [
|
||||
"Built distributor invitation, discount templates, credit allocation, sales configuration and profit margin workflows.",
|
||||
"Implemented global currency switching and exchange-rate conversion via a reusable useCurrency hook across Pricing, Billing and API Keys.",
|
||||
"Delivered organization roles, quota controls, spending limits, billing reports, transaction filtering and Excel export flows.",
|
||||
"Supported i18n submodule migration, WeCom translation sync and shared table/filter/pagination component enhancements.",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "seabuzz",
|
||||
name: "SeaBuzz",
|
||||
subtitle: "AI News and Conversation Platform",
|
||||
period: "2025.05 - Present",
|
||||
tech: [
|
||||
"React Native",
|
||||
"Expo 53",
|
||||
"Expo Router",
|
||||
"Zustand",
|
||||
"NativeWind",
|
||||
"SSE",
|
||||
"i18next",
|
||||
"Adyen",
|
||||
"Lerna",
|
||||
],
|
||||
summary:
|
||||
"An AI-powered news aggregation, smart search and multimodal conversation platform under SeaArt AI, built with Expo for iOS, Android and Web.",
|
||||
modules: [
|
||||
"Built the AI Agent chat flow with SSE streaming, typewriter rendering, Markdown/LaTeX, thinking animation, citations and history sync.",
|
||||
"Delivered Discover feed and news detail pages with masonry layout, large/small dynamic cards, skeleton loading and Smart Image.",
|
||||
"Integrated Google, Facebook, Discord and Email login with SeaArt Auth SDK.",
|
||||
"Built Monorepo shared packages for API, data models, UI components, state, OTA updates and code signing.",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "bawang",
|
||||
name: "Bawang Kungfu",
|
||||
subtitle: "Store Digitization and Supply Chain Platform",
|
||||
period: "2024.03 - 2025.03",
|
||||
tech: [
|
||||
"React 18",
|
||||
"Vue 3",
|
||||
"UniApp",
|
||||
"MobX",
|
||||
"Pinia",
|
||||
"Element Plus",
|
||||
"Ant Design",
|
||||
"ProComponents",
|
||||
],
|
||||
summary:
|
||||
"A digital operations platform serving 6,000+ stores and partners, covering store operations, food safety and supply-chain collaboration.",
|
||||
modules: [
|
||||
"Built mini-program loss reporting with camera scanning and online registration, compatible with WeChat mini-program and Lark H5.",
|
||||
"Implemented food safety flows with Bluetooth TSPL printers and separate China, Southeast Asia and North America business rules.",
|
||||
"Delivered mobile dashboards for multi-store operation metrics with region, time and KPI filtering.",
|
||||
"Built procurement, supplier contract and settlement modules, integrating multiple external systems.",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "jingyingbang",
|
||||
name: "Jingyingbang Platform",
|
||||
subtitle: "Industrial Internet and Micro-frontend Platform",
|
||||
period: "2022.04 - 2024.03",
|
||||
tech: [
|
||||
"Vue",
|
||||
"Qiankun",
|
||||
"Element UI",
|
||||
"Huawei Cloud OBS",
|
||||
"Baidu Map",
|
||||
"Amap",
|
||||
"IM",
|
||||
"UniApp",
|
||||
],
|
||||
summary:
|
||||
"An industrial internet platform based on informatization and blockchain concepts, providing digital operation services for companies and individuals.",
|
||||
modules: [
|
||||
"Participated in splitting a large frontend monolith into 12 Qiankun-based micro-frontend projects.",
|
||||
"Delivered core business features for portals, business management, mini-program, H5 and admin systems.",
|
||||
"Introduced Huawei Cloud OBS direct upload to reduce request-chain overhead.",
|
||||
"Built an internal Chrome extension, zjkj-decryption, to improve data parsing efficiency.",
|
||||
],
|
||||
},
|
||||
],
|
||||
education: {
|
||||
school: "University of Electronic Science and Technology of China",
|
||||
degree: "Bachelor",
|
||||
major: "Information Management and Information Systems",
|
||||
period: "2023 - 2025",
|
||||
},
|
||||
};
|
||||
+669
@@ -0,0 +1,669 @@
|
||||
import * as THREE from "three";
|
||||
import { experiences, focusAreas, metrics, projects, resumeSignals, skills } from "./resume-data";
|
||||
|
||||
const $ = (selector) => document.querySelector(selector);
|
||||
const $$ = (selector) => [...document.querySelectorAll(selector)];
|
||||
|
||||
const metricRow = $("#metric-row");
|
||||
const aiFocusGrid = $("#ai-focus-grid");
|
||||
const resumeSignalList = $("#resume-signal-list");
|
||||
const projectStack = $("#project-stack");
|
||||
const skillMatrix = $("#skill-matrix");
|
||||
const timeline = $("#timeline");
|
||||
const progress = $("#scroll-progress");
|
||||
const themeToggle = $("#theme-toggle");
|
||||
const root = document.documentElement;
|
||||
|
||||
const clamp = (value, min = 0, max = 1) => Math.min(max, Math.max(min, value));
|
||||
const lerp = (from, to, amount) => from + (to - from) * amount;
|
||||
const smoothstep = (edge0, edge1, value) => {
|
||||
const t = clamp((value - edge0) / (edge1 - edge0));
|
||||
return t * t * (3 - 2 * t);
|
||||
};
|
||||
|
||||
const fluidScenes = [
|
||||
{
|
||||
id: "top",
|
||||
name: "hero",
|
||||
colors: ["#64cbb8", "#8aa4d6", "#d77b91"],
|
||||
desktop: { x: 2.05, y: 0.22, z: -0.78, scale: 1.08 },
|
||||
compact: { x: 1.34, y: 1.14, z: -1.08, scale: 0.38 },
|
||||
flow: 0.62,
|
||||
twist: 0.48,
|
||||
ring: 0.82,
|
||||
particle: 0.78,
|
||||
angle: 128,
|
||||
},
|
||||
{
|
||||
id: "ai-fit",
|
||||
name: "ai-fit",
|
||||
colors: ["#74c8bd", "#8097d4", "#d4b56c"],
|
||||
desktop: { x: 1.84, y: 0.08, z: -0.88, scale: 0.96 },
|
||||
compact: { x: 1.18, y: 1.05, z: -1.14, scale: 0.34 },
|
||||
flow: 0.48,
|
||||
twist: 0.72,
|
||||
ring: 0.62,
|
||||
particle: 0.58,
|
||||
angle: 154,
|
||||
},
|
||||
{
|
||||
id: "proof",
|
||||
name: "proof",
|
||||
colors: ["#d4b56c", "#6dc4b1", "#c97a8d"],
|
||||
desktop: { x: 2.22, y: -0.04, z: -0.84, scale: 0.9 },
|
||||
compact: { x: 1.48, y: 1.0, z: -1.18, scale: 0.31 },
|
||||
flow: 0.36,
|
||||
twist: 0.34,
|
||||
ring: 0.92,
|
||||
particle: 0.52,
|
||||
angle: 96,
|
||||
},
|
||||
{
|
||||
id: "projects",
|
||||
name: "projects",
|
||||
colors: ["#889fd4", "#c97991", "#68c7b2"],
|
||||
desktop: { x: 1.68, y: 0.26, z: -0.7, scale: 1.18 },
|
||||
compact: { x: 1.22, y: 1.18, z: -1.02, scale: 0.4 },
|
||||
flow: 0.82,
|
||||
twist: 0.92,
|
||||
ring: 0.78,
|
||||
particle: 0.9,
|
||||
angle: 202,
|
||||
},
|
||||
{
|
||||
id: "skills",
|
||||
name: "skills",
|
||||
colors: ["#75bd9a", "#d1b46d", "#879dd0"],
|
||||
desktop: { x: 2.3, y: 0.04, z: -0.96, scale: 0.86 },
|
||||
compact: { x: 1.52, y: 1.08, z: -1.22, scale: 0.31 },
|
||||
flow: 0.42,
|
||||
twist: 0.56,
|
||||
ring: 1.08,
|
||||
particle: 0.46,
|
||||
angle: 72,
|
||||
},
|
||||
{
|
||||
id: "experience",
|
||||
name: "experience",
|
||||
colors: ["#9a8fcb", "#70beb0", "#849bd0"],
|
||||
desktop: { x: 1.96, y: -0.1, z: -0.92, scale: 1.02 },
|
||||
compact: { x: 1.36, y: 1.0, z: -1.14, scale: 0.36 },
|
||||
flow: 0.3,
|
||||
twist: 0.82,
|
||||
ring: 0.72,
|
||||
particle: 0.64,
|
||||
angle: 232,
|
||||
},
|
||||
{
|
||||
id: "contact",
|
||||
name: "contact",
|
||||
colors: ["#c97991", "#d0b56f", "#70bda8"],
|
||||
desktop: { x: 1.46, y: 0.02, z: -0.66, scale: 1.24 },
|
||||
compact: { x: 1.16, y: 1.14, z: -1.02, scale: 0.42 },
|
||||
flow: 0.24,
|
||||
twist: 0.28,
|
||||
ring: 0.5,
|
||||
particle: 0.42,
|
||||
angle: 312,
|
||||
},
|
||||
].map((scene) => ({
|
||||
...scene,
|
||||
colorObjects: scene.colors.map((color) => new THREE.Color(color)),
|
||||
}));
|
||||
|
||||
function getSceneFrame() {
|
||||
const center = window.scrollY + window.innerHeight * 0.28;
|
||||
const points = fluidScenes.map((scene) => ({
|
||||
scene,
|
||||
top: scene.id === "top" ? 0 : document.getElementById(scene.id)?.offsetTop ?? document.documentElement.scrollHeight,
|
||||
}));
|
||||
let index = 0;
|
||||
for (let i = 0; i < points.length - 1; i += 1) {
|
||||
if (center >= points[i + 1].top) index = i + 1;
|
||||
}
|
||||
const current = points[index];
|
||||
const next = points[Math.min(points.length - 1, index + 1)];
|
||||
const span = Math.max(1, next.top - current.top);
|
||||
const blend = current === next ? 0 : smoothstep(0.18, 0.86, (center - current.top) / span);
|
||||
return { current: current.scene, next: next.scene, blend };
|
||||
}
|
||||
|
||||
function mixScene(frame, compact) {
|
||||
const layoutA = compact ? frame.current.compact : frame.current.desktop;
|
||||
const layoutB = compact ? frame.next.compact : frame.next.desktop;
|
||||
return {
|
||||
name: frame.blend > 0.58 ? frame.next.name : frame.current.name,
|
||||
x: lerp(layoutA.x, layoutB.x, frame.blend),
|
||||
y: lerp(layoutA.y, layoutB.y, frame.blend),
|
||||
z: lerp(layoutA.z, layoutB.z, frame.blend),
|
||||
scale: lerp(layoutA.scale, layoutB.scale, frame.blend),
|
||||
flow: lerp(frame.current.flow, frame.next.flow, frame.blend),
|
||||
twist: lerp(frame.current.twist, frame.next.twist, frame.blend),
|
||||
ring: lerp(frame.current.ring, frame.next.ring, frame.blend),
|
||||
particle: lerp(frame.current.particle, frame.next.particle, frame.blend),
|
||||
angle: lerp(frame.current.angle, frame.next.angle, frame.blend),
|
||||
colors: frame.current.colorObjects.map((color, index) => color.clone().lerp(frame.next.colorObjects[index], frame.blend)),
|
||||
};
|
||||
}
|
||||
|
||||
function writeSceneCss(scene, scroll) {
|
||||
root.dataset.scene = scene.name;
|
||||
root.style.setProperty("--scene-a", scene.colors[0].getStyle());
|
||||
root.style.setProperty("--scene-b", scene.colors[1].getStyle());
|
||||
root.style.setProperty("--scene-c", scene.colors[2].getStyle());
|
||||
root.style.setProperty("--scene-angle", `${scene.angle.toFixed(2)}deg`);
|
||||
root.style.setProperty("--scene-opacity", (0.18 + scene.flow * 0.12).toFixed(3));
|
||||
root.style.setProperty("--scene-drift-x", `${(-4 + scene.x * 1.4 - scroll * 3).toFixed(2)}vw`);
|
||||
root.style.setProperty("--scene-drift-y", `${(-2 + scene.y * 2 + scroll * 2.5).toFixed(2)}vh`);
|
||||
root.style.setProperty("--scene-rotate", `${(-1 + scene.twist * 2.5 + scroll * 3).toFixed(2)}deg`);
|
||||
}
|
||||
|
||||
function renderMetrics() {
|
||||
metricRow.innerHTML = metrics
|
||||
.map((item, index) => `<article class="reveal" data-drift="${index % 2 === 0 ? 28 : -28}"><strong>${item.value}</strong><span>${item.label}</span></article>`)
|
||||
.join("");
|
||||
}
|
||||
|
||||
function renderFocusAreas() {
|
||||
aiFocusGrid.innerHTML = focusAreas
|
||||
.map(
|
||||
(item, index) => `
|
||||
<article class="ai-focus-card reveal" data-drift="${index % 2 === 0 ? -34 : 34}">
|
||||
<span>${String(index + 1).padStart(2, "0")}</span>
|
||||
<h3>${item.title}</h3>
|
||||
<p>${item.summary}</p>
|
||||
<div>${item.points.map((point) => `<em>${point}</em>`).join("")}</div>
|
||||
</article>
|
||||
`,
|
||||
)
|
||||
.join("");
|
||||
}
|
||||
|
||||
function renderResumeSignals() {
|
||||
resumeSignalList.innerHTML = resumeSignals.map((signal) => `<li>${signal}</li>`).join("");
|
||||
}
|
||||
|
||||
function renderProjects() {
|
||||
projectStack.innerHTML = projects
|
||||
.map(
|
||||
(project, index) => `
|
||||
<article class="project-card reveal" data-drift="${index % 2 === 0 ? -52 : 52}">
|
||||
<div class="project-index">
|
||||
<span class="logo-frame"><img src="${project.logo}" alt="" loading="lazy" decoding="async" /></span>
|
||||
<span>${String(index + 1).padStart(2, "0")}</span>
|
||||
</div>
|
||||
<div class="project-body">
|
||||
<div class="project-head">
|
||||
<div>
|
||||
<h3>${project.name}</h3>
|
||||
<p>${project.subtitle}</p>
|
||||
</div>
|
||||
<time>${project.period}</time>
|
||||
</div>
|
||||
<p class="project-summary">${project.summary}</p>
|
||||
<ul>${project.modules.map((item) => `<li>${item}</li>`).join("")}</ul>
|
||||
<div class="tech-row">${project.tech.map((item) => `<span>${item}</span>`).join("")}</div>
|
||||
</div>
|
||||
</article>
|
||||
`,
|
||||
)
|
||||
.join("");
|
||||
}
|
||||
|
||||
function renderSkills() {
|
||||
skillMatrix.innerHTML = skills
|
||||
.map(
|
||||
(skill, index) => `
|
||||
<article class="skill-group reveal" data-drift="${index % 3 === 0 ? -36 : index % 3 === 1 ? 18 : 42}">
|
||||
<h3>${skill.group}</h3>
|
||||
<div>${skill.items.map((item) => `<span>${item}</span>`).join("")}</div>
|
||||
</article>
|
||||
`,
|
||||
)
|
||||
.join("");
|
||||
}
|
||||
|
||||
function renderExperience() {
|
||||
timeline.innerHTML = experiences
|
||||
.map(
|
||||
(item, index) => `
|
||||
<article class="reveal" data-drift="${index % 2 === 0 ? 38 : -38}">
|
||||
<div class="timeline-side">
|
||||
<span class="logo-frame"><img src="${item.logo}" alt="" loading="lazy" decoding="async" /></span>
|
||||
<time>${item.period}</time>
|
||||
</div>
|
||||
<div>
|
||||
<h3>${item.company}</h3>
|
||||
<p>${item.role}</p>
|
||||
<ul>${item.points.map((point) => `<li>${point}</li>`).join("")}</ul>
|
||||
</div>
|
||||
</article>
|
||||
`,
|
||||
)
|
||||
.join("");
|
||||
}
|
||||
|
||||
function setupReveal() {
|
||||
$$(".reveal").forEach((node) => {
|
||||
node.style.setProperty("--drift", node.dataset.drift || 0);
|
||||
});
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) entry.target.classList.add("is-visible");
|
||||
});
|
||||
},
|
||||
{ threshold: 0.14 },
|
||||
);
|
||||
|
||||
$$(".reveal").forEach((node) => observer.observe(node));
|
||||
}
|
||||
|
||||
function updateScrollProgress() {
|
||||
const max = document.documentElement.scrollHeight - window.innerHeight;
|
||||
const value = max > 0 ? window.scrollY / max : 0;
|
||||
root.style.setProperty("--scroll-progress", value.toFixed(4));
|
||||
root.style.setProperty("--bg-grid-x", `${(-220 * value).toFixed(2)}px`);
|
||||
root.style.setProperty("--bg-grid-y", `${(150 * value).toFixed(2)}px`);
|
||||
root.style.setProperty("--bg-grid-x-2", `${(140 * value).toFixed(2)}px`);
|
||||
root.style.setProperty("--bg-grid-y-2", `${(-190 * value).toFixed(2)}px`);
|
||||
progress.style.transform = `scaleX(${Math.min(1, Math.max(0, value))})`;
|
||||
|
||||
const frame = getSceneFrame();
|
||||
const activeScene = frame.blend > 0.58 ? frame.next : frame.current;
|
||||
const sections = fluidScenes
|
||||
.filter((scene) => scene.id !== "top")
|
||||
.map((scene) => document.getElementById(scene.id))
|
||||
.filter(Boolean);
|
||||
const current = activeScene.id === "top" ? null : sections.find((section) => section.id === activeScene.id);
|
||||
$$(".site-nav a").forEach((link) => link.classList.toggle("active", current && link.getAttribute("href") === `#${current.id}`));
|
||||
}
|
||||
|
||||
function restoreHashPosition() {
|
||||
const id = decodeURIComponent(location.hash.slice(1));
|
||||
if (!id) return;
|
||||
const target = document.getElementById(id);
|
||||
if (!target) return;
|
||||
requestAnimationFrame(() => {
|
||||
window.scrollTo({ top: target.offsetTop, behavior: "auto" });
|
||||
updateScrollProgress();
|
||||
});
|
||||
}
|
||||
|
||||
function setupTheme() {
|
||||
const saved = localStorage.getItem("wy-fluid-theme");
|
||||
if (saved) document.documentElement.dataset.theme = saved;
|
||||
themeToggle.addEventListener("click", () => {
|
||||
const next = document.documentElement.dataset.theme === "dark" ? "light" : "dark";
|
||||
document.documentElement.dataset.theme = next;
|
||||
localStorage.setItem("wy-fluid-theme", next);
|
||||
});
|
||||
}
|
||||
|
||||
function setupTilts() {
|
||||
$$(".project-card").forEach((card) => {
|
||||
card.addEventListener("pointerenter", () => card.classList.add("is-hovered"));
|
||||
card.addEventListener("pointerleave", () => card.classList.remove("is-hovered"));
|
||||
});
|
||||
}
|
||||
|
||||
function setupFluidScene() {
|
||||
const canvas = $("#fluid-canvas");
|
||||
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||
if (!canvas || reduceMotion) return;
|
||||
|
||||
const renderer = new THREE.WebGLRenderer({
|
||||
canvas,
|
||||
antialias: true,
|
||||
alpha: true,
|
||||
preserveDrawingBuffer: true,
|
||||
powerPreference: "high-performance",
|
||||
});
|
||||
renderer.outputColorSpace = THREE.SRGBColorSpace;
|
||||
renderer.setClearColor(0x000000, 0);
|
||||
|
||||
const scene = new THREE.Scene();
|
||||
const camera = new THREE.PerspectiveCamera(39, 1, 0.1, 100);
|
||||
camera.position.set(0, 0, 6.2);
|
||||
|
||||
const pointer = new THREE.Vector2(0.18, -0.08);
|
||||
const pointerTarget = new THREE.Vector2(0.18, -0.08);
|
||||
const pointerVisual = new THREE.Vector2(58, 46);
|
||||
const pointerVisualTarget = new THREE.Vector2(58, 46);
|
||||
const pointerState = {
|
||||
down: false,
|
||||
lastX: window.innerWidth * 0.58,
|
||||
lastY: window.innerHeight * 0.46,
|
||||
velocity: 0,
|
||||
targetVelocity: 0,
|
||||
impact: 0,
|
||||
pressure: 0,
|
||||
};
|
||||
const group = new THREE.Group();
|
||||
scene.add(group);
|
||||
|
||||
const uniforms = {
|
||||
uTime: { value: 0 },
|
||||
uPointer: { value: pointer },
|
||||
uScroll: { value: 0 },
|
||||
uImpact: { value: 0 },
|
||||
uFlow: { value: fluidScenes[0].flow },
|
||||
uTwist: { value: fluidScenes[0].twist },
|
||||
uColorA: { value: fluidScenes[0].colorObjects[0].clone() },
|
||||
uColorB: { value: fluidScenes[0].colorObjects[1].clone() },
|
||||
uColorC: { value: fluidScenes[0].colorObjects[2].clone() },
|
||||
uTheme: { value: document.documentElement.dataset.theme === "light" ? 1 : 0 },
|
||||
};
|
||||
|
||||
const vertexShader = `
|
||||
uniform float uTime;
|
||||
uniform vec2 uPointer;
|
||||
uniform float uScroll;
|
||||
uniform float uImpact;
|
||||
uniform float uFlow;
|
||||
uniform float uTwist;
|
||||
varying vec3 vNormal;
|
||||
varying vec3 vWorld;
|
||||
varying float vWave;
|
||||
|
||||
float ridge(float value) {
|
||||
return 1.0 - abs(value);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec3 p = position;
|
||||
float t = uTime;
|
||||
float sceneBreath = sin(uScroll * 6.283 + t * (0.18 + uFlow * 0.22));
|
||||
float waveA = sin(p.x * (2.15 + uTwist * 1.25) + t * (0.58 + uFlow * 0.82) + sin(p.y * 1.4 + t * 0.32));
|
||||
float waveB = cos(p.y * (2.6 + uTwist * 1.8) - t * (0.42 + uFlow * 0.74) + p.z * (1.35 + uTwist));
|
||||
float waveC = sin((p.x + p.y + p.z) * (1.58 + uTwist * 1.1) + t * (0.86 + uFlow * 1.05));
|
||||
vec2 pointerPlane = uPointer * vec2(1.55, 1.08);
|
||||
float pointerDistance = distance(p.xy, pointerPlane);
|
||||
float pointerWave = ridge(sin(pointerDistance * (4.8 + uImpact * 1.2) - t * (1.8 + uImpact * 0.8)));
|
||||
float pointerPull = smoothstep(1.62, 0.18, pointerDistance);
|
||||
float scrollWave = sin(uScroll * (5.5 + uFlow * 5.0) + p.z * (2.4 + uTwist * 1.6) + sceneBreath) * (0.045 + uTwist * 0.055);
|
||||
float displacement = waveA * (0.11 + uFlow * 0.065) + waveB * (0.085 + uTwist * 0.04) + waveC * (0.052 + uFlow * 0.035) + pointerWave * (0.055 + uImpact * 0.11) + pointerPull * (0.05 + uImpact * 0.075) + scrollWave;
|
||||
p += normal * displacement;
|
||||
vec2 pointerDir = normalize((p.xy - pointerPlane) + vec2(0.0001, -0.0001));
|
||||
p.xy += pointerDir * pointerPull * (0.024 + uImpact * 0.055);
|
||||
p.x += sin(t * (0.34 + uFlow * 0.28) + p.y * (2.1 + uTwist * 0.9)) * (0.04 + uFlow * 0.03);
|
||||
p.y += cos(t * (0.28 + uFlow * 0.26) + p.x * (1.8 + uTwist * 0.75)) * (0.032 + uTwist * 0.025);
|
||||
vec4 world = modelMatrix * vec4(p, 1.0);
|
||||
vNormal = normalize(normalMatrix * normal);
|
||||
vWorld = world.xyz;
|
||||
vWave = displacement;
|
||||
gl_Position = projectionMatrix * viewMatrix * world;
|
||||
}
|
||||
`;
|
||||
|
||||
const fragmentShader = `
|
||||
uniform float uTime;
|
||||
uniform float uTheme;
|
||||
uniform vec3 uColorA;
|
||||
uniform vec3 uColorB;
|
||||
uniform vec3 uColorC;
|
||||
varying vec3 vNormal;
|
||||
varying vec3 vWorld;
|
||||
varying float vWave;
|
||||
|
||||
void main() {
|
||||
vec3 viewDir = normalize(cameraPosition - vWorld);
|
||||
float fresnel = pow(1.0 - max(dot(normalize(vNormal), viewDir), 0.0), 2.4);
|
||||
float scan = sin((vWorld.x - vWorld.y) * 8.0 + uTime * 1.8) * 0.5 + 0.5;
|
||||
vec3 darkA = uColorA;
|
||||
vec3 darkB = uColorB;
|
||||
vec3 darkC = uColorC;
|
||||
vec3 lightA = mix(uColorA, vec3(0.95, 1.0, 0.96), 0.42);
|
||||
vec3 lightB = mix(uColorB, vec3(0.9, 0.94, 1.0), 0.34);
|
||||
vec3 lightC = mix(uColorC, vec3(1.0, 0.94, 0.9), 0.3);
|
||||
vec3 a = mix(darkA, lightA, uTheme);
|
||||
vec3 b = mix(darkB, lightB, uTheme);
|
||||
vec3 c = mix(darkC, lightC, uTheme);
|
||||
vec3 color = mix(a, b, smoothstep(-0.22, 0.38, vWave));
|
||||
color = mix(color, c, fresnel * 0.86 + scan * 0.12);
|
||||
color += fresnel * 0.16;
|
||||
float alpha = 0.56 + fresnel * 0.24 + abs(vWave) * 0.06;
|
||||
gl_FragColor = vec4(color, alpha);
|
||||
}
|
||||
`;
|
||||
|
||||
const material = new THREE.ShaderMaterial({
|
||||
uniforms,
|
||||
vertexShader,
|
||||
fragmentShader,
|
||||
transparent: true,
|
||||
depthWrite: false,
|
||||
});
|
||||
|
||||
const shellMaterial = new THREE.MeshBasicMaterial({
|
||||
color: 0xffffff,
|
||||
transparent: true,
|
||||
opacity: 0.13,
|
||||
wireframe: true,
|
||||
blending: THREE.AdditiveBlending,
|
||||
});
|
||||
|
||||
const blob = new THREE.Mesh(new THREE.SphereGeometry(1.52, 128, 128), material);
|
||||
const shell = new THREE.Mesh(new THREE.SphereGeometry(1.72, 48, 32), shellMaterial);
|
||||
shell.rotation.set(0.6, 0.1, -0.4);
|
||||
group.add(blob, shell);
|
||||
|
||||
const ringMaterial = new THREE.MeshBasicMaterial({
|
||||
color: 0x74a7ff,
|
||||
transparent: true,
|
||||
opacity: 0.16,
|
||||
wireframe: true,
|
||||
blending: THREE.AdditiveBlending,
|
||||
});
|
||||
const rings = Array.from({ length: 4 }, (_, index) => {
|
||||
const ring = new THREE.Mesh(new THREE.TorusGeometry(1.9 + index * 0.16, 0.008, 8, 180), ringMaterial.clone());
|
||||
ring.rotation.set(Math.PI / 2.3 + index * 0.18, index * 0.5, index * 0.8);
|
||||
ring.material.opacity = 0.08 + index * 0.018;
|
||||
group.add(ring);
|
||||
return ring;
|
||||
});
|
||||
|
||||
const rayMaterial = new THREE.LineBasicMaterial({
|
||||
color: 0x56f7c5,
|
||||
transparent: true,
|
||||
opacity: 0.025,
|
||||
blending: THREE.AdditiveBlending,
|
||||
});
|
||||
const rayGeometry = new THREE.BufferGeometry();
|
||||
const rayPositions = new Float32Array(18 * 3);
|
||||
rayGeometry.setAttribute("position", new THREE.BufferAttribute(rayPositions, 3));
|
||||
const interactionRay = new THREE.LineSegments(rayGeometry, rayMaterial);
|
||||
group.add(interactionRay);
|
||||
|
||||
const particleCount = 950;
|
||||
const positions = new Float32Array(particleCount * 3);
|
||||
const colors = new Float32Array(particleCount * 3);
|
||||
const colorA = new THREE.Color("#56f7c5");
|
||||
const colorB = new THREE.Color("#ff5ea8");
|
||||
const colorC = new THREE.Color("#74a7ff");
|
||||
for (let index = 0; index < particleCount; index += 1) {
|
||||
const radius = 2.05 + Math.random() * 2.3;
|
||||
const theta = Math.random() * Math.PI * 2;
|
||||
const phi = Math.acos(Math.random() * 2 - 1);
|
||||
positions[index * 3] = Math.sin(phi) * Math.cos(theta) * radius;
|
||||
positions[index * 3 + 1] = Math.sin(phi) * Math.sin(theta) * radius;
|
||||
positions[index * 3 + 2] = Math.cos(phi) * radius;
|
||||
const color = index % 3 === 0 ? colorA : index % 3 === 1 ? colorB : colorC;
|
||||
colors[index * 3] = color.r;
|
||||
colors[index * 3 + 1] = color.g;
|
||||
colors[index * 3 + 2] = color.b;
|
||||
}
|
||||
const particleGeometry = new THREE.BufferGeometry();
|
||||
particleGeometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
|
||||
particleGeometry.setAttribute("color", new THREE.BufferAttribute(colors, 3));
|
||||
const particles = new THREE.Points(
|
||||
particleGeometry,
|
||||
new THREE.PointsMaterial({
|
||||
size: 0.018,
|
||||
vertexColors: true,
|
||||
transparent: true,
|
||||
opacity: 0.78,
|
||||
depthWrite: false,
|
||||
blending: THREE.AdditiveBlending,
|
||||
}),
|
||||
);
|
||||
group.add(particles);
|
||||
|
||||
let width = 0;
|
||||
let height = 0;
|
||||
const basePosition = new THREE.Vector3(fluidScenes[0].desktop.x, fluidScenes[0].desktop.y, fluidScenes[0].desktop.z);
|
||||
const sceneVisual = {
|
||||
name: fluidScenes[0].name,
|
||||
x: basePosition.x,
|
||||
y: basePosition.y,
|
||||
z: basePosition.z,
|
||||
scale: fluidScenes[0].desktop.scale,
|
||||
flow: fluidScenes[0].flow,
|
||||
twist: fluidScenes[0].twist,
|
||||
ring: fluidScenes[0].ring,
|
||||
particle: fluidScenes[0].particle,
|
||||
angle: fluidScenes[0].angle,
|
||||
colors: fluidScenes[0].colorObjects.map((color) => color.clone()),
|
||||
};
|
||||
function resize() {
|
||||
width = window.innerWidth;
|
||||
height = window.innerHeight;
|
||||
const dpr = Math.min(window.devicePixelRatio || 1, 2);
|
||||
renderer.setPixelRatio(dpr);
|
||||
renderer.setSize(width, height, false);
|
||||
camera.aspect = width / height;
|
||||
camera.updateProjectionMatrix();
|
||||
const compact = width < 760;
|
||||
const target = mixScene(getSceneFrame(), compact);
|
||||
basePosition.set(target.x, target.y, target.z);
|
||||
group.position.copy(basePosition);
|
||||
group.scale.setScalar(target.scale);
|
||||
}
|
||||
|
||||
function onPointerMove(event) {
|
||||
const dx = event.clientX - pointerState.lastX;
|
||||
const dy = event.clientY - pointerState.lastY;
|
||||
pointerState.lastX = event.clientX;
|
||||
pointerState.lastY = event.clientY;
|
||||
const speed = Math.hypot(dx, dy);
|
||||
pointerState.targetVelocity = Math.min(1, speed / 170);
|
||||
pointerState.impact = Math.max(pointerState.impact, Math.min(0.26, pointerState.targetVelocity * 0.2));
|
||||
pointerTarget.x = (event.clientX / window.innerWidth) * 2 - 1;
|
||||
pointerTarget.y = -((event.clientY / window.innerHeight) * 2 - 1);
|
||||
pointerVisualTarget.set((event.clientX / window.innerWidth) * 100, (event.clientY / window.innerHeight) * 100);
|
||||
}
|
||||
|
||||
function onPointerDown(event) {
|
||||
pointerState.down = true;
|
||||
pointerState.impact = Math.max(pointerState.impact, 0.46);
|
||||
root.classList.add("is-pointer-down");
|
||||
onPointerMove(event);
|
||||
}
|
||||
|
||||
function onPointerUp() {
|
||||
pointerState.down = false;
|
||||
root.classList.remove("is-pointer-down");
|
||||
}
|
||||
|
||||
window.addEventListener("resize", resize);
|
||||
window.addEventListener("pointermove", onPointerMove, { passive: true });
|
||||
window.addEventListener("pointerdown", onPointerDown, { passive: true });
|
||||
window.addEventListener("pointerup", onPointerUp, { passive: true });
|
||||
window.addEventListener("pointercancel", onPointerUp, { passive: true });
|
||||
resize();
|
||||
|
||||
const startedAt = performance.now();
|
||||
function animate() {
|
||||
const elapsed = (performance.now() - startedAt) / 1000;
|
||||
const scrollMax = document.documentElement.scrollHeight - window.innerHeight;
|
||||
const scroll = scrollMax > 0 ? window.scrollY / scrollMax : 0;
|
||||
const compact = width < 760;
|
||||
const sceneTarget = mixScene(getSceneFrame(), compact);
|
||||
sceneVisual.name = sceneTarget.name;
|
||||
sceneVisual.x = lerp(sceneVisual.x, sceneTarget.x, 0.045);
|
||||
sceneVisual.y = lerp(sceneVisual.y, sceneTarget.y, 0.045);
|
||||
sceneVisual.z = lerp(sceneVisual.z, sceneTarget.z, 0.045);
|
||||
sceneVisual.scale = lerp(sceneVisual.scale, sceneTarget.scale, 0.045);
|
||||
sceneVisual.flow = lerp(sceneVisual.flow, sceneTarget.flow, 0.04);
|
||||
sceneVisual.twist = lerp(sceneVisual.twist, sceneTarget.twist, 0.04);
|
||||
sceneVisual.ring = lerp(sceneVisual.ring, sceneTarget.ring, 0.04);
|
||||
sceneVisual.particle = lerp(sceneVisual.particle, sceneTarget.particle, 0.04);
|
||||
sceneVisual.angle = lerp(sceneVisual.angle, sceneTarget.angle, 0.035);
|
||||
sceneVisual.colors.forEach((color, index) => color.lerp(sceneTarget.colors[index], 0.045));
|
||||
basePosition.set(sceneVisual.x, sceneVisual.y, sceneVisual.z);
|
||||
writeSceneCss(sceneVisual, scroll);
|
||||
pointer.lerp(pointerTarget, 0.024);
|
||||
pointerVisual.lerp(pointerVisualTarget, 0.07);
|
||||
root.style.setProperty("--cursor-x", `${pointerVisual.x.toFixed(2)}vw`);
|
||||
root.style.setProperty("--cursor-y", `${pointerVisual.y.toFixed(2)}vh`);
|
||||
pointerState.velocity += (pointerState.targetVelocity - pointerState.velocity) * 0.045;
|
||||
pointerState.targetVelocity *= 0.82;
|
||||
pointerState.pressure += ((pointerState.down ? 1 : 0) - pointerState.pressure) * 0.075;
|
||||
pointerState.impact *= pointerState.down ? 0.985 : 0.955;
|
||||
uniforms.uImpact.value = Math.min(0.72, pointerState.velocity * 0.24 + pointerState.impact * 0.72 + pointerState.pressure * 0.12);
|
||||
uniforms.uTime.value = elapsed;
|
||||
uniforms.uScroll.value = scroll;
|
||||
uniforms.uFlow.value = sceneVisual.flow;
|
||||
uniforms.uTwist.value = sceneVisual.twist;
|
||||
uniforms.uColorA.value.copy(sceneVisual.colors[0]);
|
||||
uniforms.uColorB.value.copy(sceneVisual.colors[1]);
|
||||
uniforms.uColorC.value.copy(sceneVisual.colors[2]);
|
||||
uniforms.uTheme.value += ((document.documentElement.dataset.theme === "light" ? 1 : 0) - uniforms.uTheme.value) * 0.08;
|
||||
|
||||
group.position.x = basePosition.x + pointer.x * (0.06 + uniforms.uImpact.value * 0.035);
|
||||
group.position.y = basePosition.y + Math.sin(elapsed * 0.47) * 0.07 + pointer.y * (0.045 + uniforms.uImpact.value * 0.03);
|
||||
group.position.z = basePosition.z + uniforms.uImpact.value * 0.025;
|
||||
group.scale.setScalar(sceneVisual.scale + Math.sin(elapsed * 0.18 + scroll * 5.2) * 0.018);
|
||||
group.rotation.x = Math.sin(elapsed * 0.28) * 0.14 + pointer.y * (0.11 + uniforms.uImpact.value * 0.055);
|
||||
group.rotation.y = elapsed * (0.055 + sceneVisual.flow * 0.065 + uniforms.uImpact.value * 0.018) + pointer.x * (0.18 + uniforms.uImpact.value * 0.075) + scroll * (0.5 + sceneVisual.twist * 0.5);
|
||||
group.rotation.z = Math.cos(elapsed * 0.18) * 0.055 + pointer.x * pointer.y * 0.055 + sceneVisual.twist * 0.06;
|
||||
blob.scale.setScalar(1 + Math.sin(elapsed * 0.62) * 0.025 + uniforms.uImpact.value * 0.014);
|
||||
shell.rotation.y -= 0.0014;
|
||||
shell.rotation.z += 0.001;
|
||||
particles.material.opacity = 0.42 + sceneVisual.particle * 0.36;
|
||||
particles.rotation.y -= 0.00035 + sceneVisual.flow * 0.00045 + uniforms.uImpact.value * 0.00035;
|
||||
particles.rotation.x = Math.sin(elapsed * (0.14 + sceneVisual.flow * 0.12)) * (0.055 + sceneVisual.twist * 0.06);
|
||||
rings.forEach((ring, index) => {
|
||||
ring.material.opacity = (0.04 + index * 0.012) * (0.75 + sceneVisual.ring * 0.7);
|
||||
ring.rotation.z += 0.0007 + sceneVisual.ring * 0.00075 + index * 0.00035 + uniforms.uImpact.value * 0.00055;
|
||||
ring.rotation.x += Math.sin(elapsed * 0.24 + index) * 0.00048 + pointer.y * 0.00022;
|
||||
});
|
||||
|
||||
for (let index = 0; index < 9; index += 1) {
|
||||
const offset = index * 6;
|
||||
const angle = elapsed * (0.28 + sceneVisual.flow * 0.22 + index * 0.025) + index * 0.7 + scroll * sceneVisual.twist * 2.4;
|
||||
const length = 0.42 + sceneVisual.ring * 0.18 + uniforms.uImpact.value * 0.72;
|
||||
rayPositions[offset] = pointer.x * 0.82;
|
||||
rayPositions[offset + 1] = pointer.y * 0.58;
|
||||
rayPositions[offset + 2] = 0.38;
|
||||
rayPositions[offset + 3] = pointer.x * 0.82 + Math.cos(angle) * length;
|
||||
rayPositions[offset + 4] = pointer.y * 0.58 + Math.sin(angle) * length * 0.42;
|
||||
rayPositions[offset + 5] = 0.12 + Math.sin(angle * 0.7) * 0.28;
|
||||
}
|
||||
rayGeometry.attributes.position.needsUpdate = true;
|
||||
rayMaterial.opacity = 0.025 + uniforms.uImpact.value * 0.13;
|
||||
|
||||
renderer.render(scene, camera);
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
animate();
|
||||
}
|
||||
|
||||
renderMetrics();
|
||||
renderFocusAreas();
|
||||
renderResumeSignals();
|
||||
renderProjects();
|
||||
renderSkills();
|
||||
renderExperience();
|
||||
setupTheme();
|
||||
setupReveal();
|
||||
setupTilts();
|
||||
setupFluidScene();
|
||||
updateScrollProgress();
|
||||
restoreHashPosition();
|
||||
window.addEventListener("scroll", () => requestAnimationFrame(updateScrollProgress), { passive: true });
|
||||
window.addEventListener("hashchange", () => {
|
||||
restoreHashPosition();
|
||||
updateScrollProgress();
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,173 @@
|
||||
export const metrics = [
|
||||
{ value: "7 年", label: "Web 与跨端前端经验" },
|
||||
{ value: "2 条", label: "近一年参与的产品线" },
|
||||
{ value: "SSE", label: "对话流、思考态、消息重试" },
|
||||
{ value: "12 个", label: "微前端拆分基础项目" },
|
||||
{ value: "30%", label: "消息渲染链路优化结果" },
|
||||
{ value: "6000+", label: "门店业务系统服务规模" },
|
||||
];
|
||||
|
||||
export const focusAreas = [
|
||||
{
|
||||
title: "对话链路",
|
||||
summary: "做过流式消息、思考态、来源引用、历史同步和失败重试,重点是让对话过程稳定、可恢复。",
|
||||
points: ["SSE", "Markdown/LaTeX", "来源引用", "消息重试"],
|
||||
},
|
||||
{
|
||||
title: "控制台业务",
|
||||
summary: "参与过 API Key、账单、组织权限、额度、交易筛选和导出,知道复杂后台最怕状态不清和边界不稳。",
|
||||
points: ["API Key", "Billing", "RBAC", "Excel 导出"],
|
||||
},
|
||||
{
|
||||
title: "跨端内容流",
|
||||
summary: "在 Expo / React Native 项目里做过新闻流、详情页、瀑布流、图片组件和游客转登录数据处理。",
|
||||
points: ["Expo", "瀑布流", "Smart Image", "游客绑定"],
|
||||
},
|
||||
{
|
||||
title: "工程习惯",
|
||||
summary: "经历过微前端拆分、跨项目公共包、i18n 协作、监控接入和代码审查,能把模块交付和团队维护放在一起考虑。",
|
||||
points: ["Qiankun", "Monorepo", "i18n", "Code Review"],
|
||||
},
|
||||
];
|
||||
|
||||
export const resumeSignals = [
|
||||
"前端经验覆盖 PC 管理后台、小程序、H5、React Native 和微前端,最近一年主要在 AI 产品团队做业务前端。",
|
||||
"SeaBuzz 侧更偏用户体验:对话、内容流、新闻详情、游客数据、分享和反馈链路。",
|
||||
"SeaCloud / Vtrix 侧更偏控制台:Pricing、Billing、API Keys、组织权限、交易筛选和分销配置。",
|
||||
"技术栈以 React / Next.js / React Native / TypeScript 为主,也有 Vue、UniApp 和 Qiankun 项目经验。",
|
||||
];
|
||||
|
||||
export const skills = [
|
||||
{
|
||||
group: "对话与内容",
|
||||
items: ["SSE 流式通信", "打字机响应", "Markdown/LaTeX", "来源引用", "对话历史", "新闻详情"],
|
||||
},
|
||||
{
|
||||
group: "控制台",
|
||||
items: ["Pricing", "Billing", "API Keys", "组织权限", "额度限制", "交易筛选", "Excel 导出"],
|
||||
},
|
||||
{
|
||||
group: "跨端体验",
|
||||
items: ["React Native", "Expo Router", "NativeWind", "游客转登录", "只读分享", "Smart Image"],
|
||||
},
|
||||
{
|
||||
group: "Web 主栈",
|
||||
items: ["Next.js", "React", "Vue 3", "TypeScript", "Tailwind CSS", "Zustand", "Alova"],
|
||||
},
|
||||
{
|
||||
group: "小程序与 H5",
|
||||
items: ["UniApp", "微信小程序", "飞书小程序", "H5", "摄像头扫码", "低功耗蓝牙"],
|
||||
},
|
||||
{
|
||||
group: "工程协作",
|
||||
items: ["pnpm Monorepo", "Git Submodules", "Qiankun", "Lerna", "i18n 同步", "Code Review"],
|
||||
},
|
||||
{
|
||||
group: "工程化与质量",
|
||||
items: ["ARMS 监控", "CDN 优化", "Playwright", "Git Flow", "分支规范", "新人培训"],
|
||||
},
|
||||
];
|
||||
|
||||
export const projects = [
|
||||
{
|
||||
id: "vtrix",
|
||||
name: "SeaCloud / Vtrix",
|
||||
logo: "/logos/vtrix.png",
|
||||
period: "2025.09 - 至今",
|
||||
subtitle: "模型服务平台控制台",
|
||||
summary:
|
||||
"参与模型服务平台的前端建设,主要处理 Pricing、Billing、API Keys、组织权限、额度和分销相关页面。项目复杂度不在单个页面,而在状态、权限和账务边界。",
|
||||
modules: [
|
||||
"建设分销控制台:客户邀请、折扣模板、额度分配、销售配置与利润率计算。",
|
||||
"封装 useCurrency Hook,贯穿 Pricing、Billing、API Keys 等模型调用和费用模块。",
|
||||
"落地组织角色权限、成员配额、支出限额、账单报表、交易筛选与 Excel 导出。",
|
||||
"参与 i18n Submodule 架构迁移、企业微信翻译同步和通用表格/筛选器/分页组件增强。",
|
||||
],
|
||||
tech: ["Next.js 16", "React 19", "TypeScript", "Tailwind CSS", "Zustand", "Alova", "Radix UI", "next-intl"],
|
||||
},
|
||||
{
|
||||
id: "seabuzz",
|
||||
name: "SeaBuzz",
|
||||
logo: "/logos/seabuzz.webp",
|
||||
period: "2025.05 - 至今",
|
||||
subtitle: "资讯、搜索与对话应用",
|
||||
summary:
|
||||
"基于 Expo 的跨端应用,覆盖 iOS、Android、Web。我的工作集中在对话链路、内容流、登录绑定、分享和公共包沉淀。",
|
||||
modules: [
|
||||
"实现对话链路:SSE 流式聊天、打字机渲染、Markdown/LaTeX、思考动画、来源引用、消息重写与历史同步。",
|
||||
"处理游客聊天记录本地存储、登录后自动绑定、分享链接只读模式和反馈机制。",
|
||||
"交付 Discover 发现页、新闻详情、瀑布流动态布局、骨架屏、Smart Image 与富文本章节渲染。",
|
||||
"集成 Google、Facebook、Discord、Email 登录与 SeaArt Auth SDK。",
|
||||
"封装 Monorepo 公共包体系,覆盖 API、数据模型、UI 组件、状态、OTA 热更新与代码签名。",
|
||||
],
|
||||
tech: ["React Native", "Expo 53", "Expo Router", "Zustand", "NativeWind", "SSE", "i18next", "Adyen"],
|
||||
},
|
||||
{
|
||||
id: "bawang",
|
||||
name: "霸王功夫",
|
||||
logo: "/logos/chagee.png",
|
||||
period: "2024.03 - 2025.03",
|
||||
subtitle: "门店、食安与供应链系统",
|
||||
summary:
|
||||
"服务全球 6000+ 门店及运营伙伴,覆盖门店运营、食品安全、供应链协同等核心业务。",
|
||||
modules: [
|
||||
"搭建小程序报损模块,调用摄像头扫码并兼容微信小程序与飞书 H5。",
|
||||
"实现食安管理模块,通过低频蓝牙连接 TSPL 指令集打印机,覆盖国内、东南亚、北美三套业务逻辑。",
|
||||
"交付多门店经营状态移动看板,支持区域、时间、指标筛选。",
|
||||
"负责采购、供应商合同、供应商结算等供应链系统,对接云厉、费控等外部系统。",
|
||||
],
|
||||
tech: ["React 18", "Vue 3", "UniApp", "MobX", "Pinia", "Element Plus", "Ant Design"],
|
||||
},
|
||||
{
|
||||
id: "jingyingbang",
|
||||
name: "经营帮平台 + 经营帮拉新",
|
||||
logo: "/logos/jingyingbang.png",
|
||||
period: "2022.04 - 2024.03",
|
||||
subtitle: "微前端平台与小程序",
|
||||
summary:
|
||||
"基于信息化设计理念和区块链技术的工业互联网平台,为企业和个人提供数字化运营服务。",
|
||||
modules: [
|
||||
"参与单体前端到 Qiankun 微前端拆分,共拆分出 12 个基础项目。",
|
||||
"负责商管、门户、经营帮小程序、H5、Admin 后台核心业务交付。",
|
||||
"引入华为云 OBS 直传,降低请求链路性能浪费。",
|
||||
"开发内部 Chrome 插件 zjkj-decryption,提升数据解析效率。",
|
||||
],
|
||||
tech: ["Vue", "Qiankun", "Element UI", "华为云 OBS", "百度地图", "高德地图", "UniApp"],
|
||||
},
|
||||
];
|
||||
|
||||
export const experiences = [
|
||||
{
|
||||
company: "成都海艺互娱科技有限公司",
|
||||
logo: "/logos/seaart.webp",
|
||||
role: "React Native 开发工程师",
|
||||
period: "2025.05 - 至今",
|
||||
points: [
|
||||
"参与 SeaBuzz、SeaCloud / Vtrix 等项目,主要负责 React Native 跨端页面和 Web 控制台模块。",
|
||||
"负责对话流、内容流、分享、游客数据绑定、账单、API Keys、组织权限等业务页面开发。",
|
||||
"优化消息渲染链路和跨端公共包复用,消息渲染性能提升约 30%。",
|
||||
],
|
||||
},
|
||||
{
|
||||
company: "四川茶姬企业管理有限公司",
|
||||
logo: "/logos/chagee.png",
|
||||
role: "高级 Web 前端开发",
|
||||
period: "2024.03 - 2025.03",
|
||||
points: [
|
||||
"主导供应链管理系统前端开发,实现采购、仓储、配送等核心模块功能。",
|
||||
"独立负责门店报损与食安核心功能搭建及迭代。",
|
||||
"完成前端监控体系引入、CDN 静态资源优化、代码审查机制与 Git 操作规范建设。",
|
||||
],
|
||||
},
|
||||
{
|
||||
company: "中钧科技有限公司四川分公司",
|
||||
logo: "/logos/zhongjun.png",
|
||||
role: "前端开发组长",
|
||||
period: "2022.04 - 2024.03",
|
||||
points: [
|
||||
"负责经营帮 PC 端微前端、门户和商管核心模块开发,完成 UI 还原、接口联调与性能优化。",
|
||||
"从零搭建新 E 畅行小程序基础框架,制定技术方案并完成全流程开发。",
|
||||
"设计 Git Flow、代码审查与分支管理机制,主导新人培训与项目分工协调。",
|
||||
],
|
||||
},
|
||||
];
|
||||
+1010
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"extends": "astro/tsconfigs/strict"
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { defineConfig } from "vite";
|
||||
|
||||
export default defineConfig({
|
||||
build: {
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: {
|
||||
three: ["three"],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user