Sync latest TrueGrowth updates
Some checks failed
CI / main (push) Has been cancelled
CI / release-e2e (push) Has been cancelled

This commit is contained in:
jiam
2026-07-08 02:03:18 +08:00
parent 52636c91ae
commit 5119ac0ef8
102 changed files with 20985 additions and 1760 deletions

25
official-site/README.md Normal file
View File

@@ -0,0 +1,25 @@
# TrueGrowth Official Site
这是给 TrueGrowth 独立官网准备的静态单页项目,位于 `/Users/jammy/TrueGrowth/official-site`,不依赖现有工作台应用代码。
## 本地预览
```bash
cd /Users/jammy/TrueGrowth/official-site
pnpm dev
```
默认地址是 `http://127.0.0.1:7330`
## 文件结构
- `index.html`:官网单页结构与中文文案
- `src/styles.css`:暗色/亮色主题、响应式布局、动效
- `src/main.js`导航、主题切换、功能预览、灵感筛选、Canvas 背景互动
- `assets/`:从 TrueGrowth 产品展示素材复制出的官网本地资产
## 设计方向
- 公司主体:广州本初数字科技有限公司
- 产品表达:一人公司的 AI 工作台
- 视觉关键词AI 作战室、指挥大屏、橙色能量线、真实产品展示、静态单页可部署

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

412
official-site/index.html Normal file
View File

@@ -0,0 +1,412 @@
<!doctype html>
<html lang="zh-CN" data-site-theme="dark">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>TrueGrowth | 一人公司的 AI 工作台</title>
<meta
name="description"
content="TrueGrowth 是广州本初数字科技有限公司打造的一人公司 AI 工作台,把趋势洞察、图片视频生成、数字人、剪辑、资产管理、自动发布和工作流编排放进同一座 AI 作战室。"
/>
<meta
name="keywords"
content="TrueGrowth,广州本初数字科技有限公司,AI工作台,一人公司,AI图片生成,AI视频生成,数字人,AI剪辑,自动发布,AI工作流"
/>
<meta name="theme-color" content="#050506" />
<meta property="og:type" content="website" />
<meta property="og:title" content="TrueGrowth | 一人公司的 AI 工作台" />
<meta
property="og:description"
content="从选题、生成、剪辑、发布到资产沉淀,一块工作台承接完整增长链路。"
/>
<meta property="og:image" content="./assets/hero-aivideo.jpg" />
<link rel="icon" href="./assets/truegrowth-logo.png" />
<link rel="stylesheet" href="./src/styles.css" />
</head>
<body>
<canvas id="signal-canvas" aria-hidden="true"></canvas>
<a class="skip-link" href="#main">跳到主要内容</a>
<header class="site-header" data-elevated="false">
<a class="brand" href="#hero" aria-label="TrueGrowth 首页">
<img src="./assets/truegrowth-logo.png" alt="" />
<span>TrueGrowth</span>
</a>
<button class="nav-toggle" type="button" aria-expanded="false" aria-label="打开导航">
<span></span>
<span></span>
</button>
<nav class="site-nav" aria-label="主导航">
<a href="#command">作战大屏</a>
<a href="#capabilities">功能矩阵</a>
<a href="#inspiration">灵感广场</a>
<a href="#workflow">交付链路</a>
<a href="#contact">联系团队</a>
</nav>
<div class="header-actions">
<button class="theme-toggle" type="button" aria-label="切换亮暗主题" title="切换亮暗主题">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M12 3v2.2M12 18.8V21M4.2 4.2l1.6 1.6M18.2 18.2l1.6 1.6M3 12h2.2M18.8 12H21M4.2 19.8l1.6-1.6M18.2 5.8l1.6-1.6" />
<circle cx="12" cy="12" r="4.2" />
</svg>
</button>
<a class="small-cta" href="#contact">预约演示</a>
</div>
</header>
<main id="main">
<section class="hero" id="hero" aria-labelledby="hero-title">
<video
class="hero-video"
autoplay
muted
loop
playsinline
poster="./assets/hero-aivideo.jpg"
>
<source src="./assets/hero-aivideo.mp4" type="video/mp4" />
</video>
<div class="hero-mesh" aria-hidden="true"></div>
<div class="hero-content reveal">
<h1 id="hero-title">TrueGrowth</h1>
<p class="hero-subtitle">一人公司的 AI 工作台</p>
<p class="hero-copy">
广州本初数字科技有限公司打造的 AI 作战室,把趋势洞察、图片视频生成、数字人、剪辑、资产管理、自动发布和工作流编排压缩到同一张桌面。
</p>
<div class="hero-actions">
<a class="primary-action" href="#command">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M5 12h13M13 6l6 6-6 6" />
</svg>
进入作战大屏
</a>
<a class="ghost-action" href="#capabilities">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M4 6h16M4 12h16M4 18h10" />
</svg>
查看功能矩阵
</a>
</div>
</div>
<div class="hero-hud reveal" aria-label="实时工作台状态">
<div class="hud-line">
<span>Trend Radar</span>
<strong>18 条选题已聚类</strong>
</div>
<div class="hud-line">
<span>Creative Queue</span>
<strong>42 个素材待复用</strong>
</div>
<div class="hud-line">
<span>Publish Runway</span>
<strong>6 个渠道排程中</strong>
</div>
</div>
<div class="scroll-cue" aria-hidden="true">SCROLL</div>
</section>
<section class="command-section section-pad" id="command" aria-labelledby="command-title">
<div class="section-heading reveal">
<span class="section-index">01</span>
<h2 id="command-title">一块大屏,接管一家公司的增长节奏</h2>
<p>
TrueGrowth 不把 AI 能力拆成孤岛。它像一座单人指挥舱,把选题、生产、资产、任务和发布状态同步到同一个视野里。
</p>
</div>
<div class="war-room reveal">
<div class="war-room__screen">
<div class="screen-topbar">
<span></span>
<span></span>
<span></span>
<strong>TRUEGROWTH COMMAND WALL</strong>
</div>
<div class="screen-grid">
<div class="screen-map">
<div class="orbit-ring ring-a"></div>
<div class="orbit-ring ring-b"></div>
<div class="hub-node">TG</div>
<button class="map-node node-research" type="button">情报</button>
<button class="map-node node-image" type="button">图片</button>
<button class="map-node node-video" type="button">视频</button>
<button class="map-node node-avatar" type="button">数字人</button>
<button class="map-node node-publish" type="button">发布</button>
<div class="map-beam beam-a"></div>
<div class="map-beam beam-b"></div>
<div class="map-beam beam-c"></div>
</div>
<div class="signal-panel">
<span class="panel-label">Live Brief</span>
<p id="typing-line">读取全网趋势,拆成可执行内容战役...</p>
<div class="signal-bars" aria-hidden="true">
<span style="--h: 42%"></span>
<span style="--h: 78%"></span>
<span style="--h: 56%"></span>
<span style="--h: 92%"></span>
<span style="--h: 64%"></span>
<span style="--h: 86%"></span>
<span style="--h: 48%"></span>
</div>
</div>
<div class="mission-stack">
<article>
<span>今日主线</span>
<strong>用 1 个主题生成 12 条内容资产</strong>
</article>
<article>
<span>模型路由</span>
<strong>文本、图片、视频、本地模型分层执行</strong>
</article>
<article>
<span>资产沉淀</span>
<strong>生成结果自动进入资产中心</strong>
</article>
</div>
</div>
</div>
<div class="war-room__metrics" aria-label="平台能力概览">
<div>
<strong data-count-to="12">0</strong>
<span>核心工作台模块</span>
</div>
<div>
<strong data-count-to="7">0</strong>
<span>内容生产链路</span>
</div>
<div>
<strong data-count-to="1">0</strong>
<span>一人公司指挥面板</span>
</div>
</div>
</div>
</section>
<section class="capabilities section-pad" id="capabilities" aria-labelledby="capability-title">
<div class="section-heading reveal">
<span class="section-index">02</span>
<h2 id="capability-title">把“一个人做不完”的事,拆给一组 AI 工位</h2>
<p>
从万象智枢到自动发布,每个功能都像一个可调度的岗位。你负责判断方向,工作台负责把动作串起来。
</p>
</div>
<div class="capability-lab reveal">
<div class="feature-dock" role="tablist" aria-label="功能选择">
<button class="feature-tab is-active" type="button" role="tab" aria-selected="true" data-feature="research">
<span>情报策源</span>
<small>趋势到选题</small>
</button>
<button class="feature-tab" type="button" role="tab" aria-selected="false" data-feature="image">
<span>图片创作</span>
<small>单图与批量</small>
</button>
<button class="feature-tab" type="button" role="tab" aria-selected="false" data-feature="video">
<span>视频创作</span>
<small>脚本到镜头</small>
</button>
<button class="feature-tab" type="button" role="tab" aria-selected="false" data-feature="avatar">
<span>数字人口播</span>
<small>形象与声音</small>
</button>
<button class="feature-tab" type="button" role="tab" aria-selected="false" data-feature="clip">
<span>AI 剪辑</span>
<small>拆条与字幕</small>
</button>
<button class="feature-tab" type="button" role="tab" aria-selected="false" data-feature="publish">
<span>自动发布</span>
<small>多渠道排程</small>
</button>
</div>
<div class="feature-stage">
<video id="feature-video" autoplay muted loop playsinline poster="./assets/showcase-research.jpg"></video>
<div class="feature-copy">
<span id="feature-kicker">万象智枢</span>
<h3 id="feature-title">从全网信号里找到今天值得做的内容战役</h3>
<p id="feature-text">
聚合网页、视频、社区和开源项目,把趋势、用户痛点和内容机会转成脚本、分镜和提示词。
</p>
<ul id="feature-points">
<li>趋势研究与需求洞察</li>
<li>脚本、分镜、提示词生成</li>
<li>结果可保存到知识与资产链路</li>
</ul>
</div>
</div>
</div>
<div class="module-grid reveal" aria-label="完整功能列表">
<article><span>AI 图片生成</span><p>文生图、图生图、多图参考、局部重绘、批量出图。</p></article>
<article><span>AI 视频生成</span><p>文生视频、图生视频、首尾帧、爆款视频和 MV 流程。</p></article>
<article><span>数字人口播</span><p>形象、音色、文案、背景组合成可发布视频。</p></article>
<article><span>无限画布</span><p>素材整理、分镜设计、多结果对比和连续创作。</p></article>
<article><span>资产中心</span><p>统一管理图片、视频、音频、数字人和剪辑资产。</p></article>
<article><span>可视化工作流</span><p>把模型、工具、素材和任务编排成可复用流水线。</p></article>
<article><span>本地模型中心</span><p>导入和检查数字人、声音、字幕等本地模型状态。</p></article>
<article><span>任务中心</span><p>云端生成、本地模型、下载、转码和渲染统一排队。</p></article>
</div>
</section>
<section class="inspiration section-pad" id="inspiration" aria-labelledby="inspiration-title">
<div class="section-heading reveal">
<span class="section-index">03</span>
<h2 id="inspiration-title">像逛灵感广场一样,启动一条生产线</h2>
<p>
官网的灵感区不是普通卡片墙。每个创意都直接指向工作台里的动作:研究、生成、剪辑、发布或复盘。
</p>
</div>
<div class="plaza reveal">
<div class="plaza-controls" role="tablist" aria-label="灵感分类">
<button class="plaza-filter is-active" type="button" data-filter="all">全部</button>
<button class="plaza-filter" type="button" data-filter="growth">增长</button>
<button class="plaza-filter" type="button" data-filter="media">内容</button>
<button class="plaza-filter" type="button" data-filter="avatar">数字人</button>
<button class="plaza-filter" type="button" data-filter="workflow">工作流</button>
</div>
<div class="plaza-grid">
<button class="prompt-card" type="button" data-group="growth" data-prompt="分析本周 AI 创作工具热榜,输出 6 条适合一人公司的短视频选题。">
<span>趋势雷达</span>
<strong>本周热榜,拆成可拍选题</strong>
<em>万象智枢</em>
</button>
<button class="prompt-card" type="button" data-group="media" data-prompt="围绕新品发布生成 9 张视觉方向图,并给每张图写出可复用提示词。">
<span>视觉方向</span>
<strong>一组主题,生成九宫格灵感图</strong>
<em>AI 图片</em>
</button>
<button class="prompt-card" type="button" data-group="media" data-prompt="把这段素材拆成 5 条 30 秒短视频,保留金句、字幕和封面建议。">
<span>长视频拆条</span>
<strong>把素材切成连续发布弹药</strong>
<em>AI 剪辑</em>
</button>
<button class="prompt-card" type="button" data-group="avatar" data-prompt="用我的形象和音色生成一条 60 秒产品讲解口播,风格专业但不端着。">
<span>数字分身</span>
<strong>让创始人同时出现在多个渠道</strong>
<em>数字人</em>
</button>
<button class="prompt-card" type="button" data-group="workflow" data-prompt="把选题研究、脚本生成、分镜图、视频生成和发布排程编排成一个可复用工作流。">
<span>自动流水线</span>
<strong>从一句目标到整套内容战役</strong>
<em>工作流</em>
</button>
<button class="prompt-card" type="button" data-group="growth" data-prompt="整理最近 30 天发布数据,找出值得加码的题材、封面风格和发布窗口。">
<span>复盘增长</span>
<strong>让每次发布都变成下一次判断</strong>
<em>资产中心</em>
</button>
</div>
<aside class="prompt-console" aria-live="polite">
<span>Selected Prompt</span>
<p id="selected-prompt">分析本周 AI 创作工具热榜,输出 6 条适合一人公司的短视频选题。</p>
<button class="console-run" type="button">
<svg viewBox="0 0 24 24" aria-hidden="true">
<path d="M7 5v14l11-7z" />
</svg>
模拟执行
</button>
</aside>
</div>
</section>
<section class="workflow section-pad" id="workflow" aria-labelledby="workflow-title">
<div class="section-heading reveal">
<span class="section-index">04</span>
<h2 id="workflow-title">从一个念头,到一套可发布资产</h2>
<p>
TrueGrowth 的核心不是“又多一个 AI 工具”,而是让创始人把重复生产动作沉淀成可复用路径。
</p>
</div>
<div class="flowline reveal">
<article>
<span>洞察</span>
<strong>搜集趋势、痛点和素材线索</strong>
<p>先看市场和用户,再决定今天做什么。</p>
</article>
<article>
<span>生成</span>
<strong>生成图片、视频、声音和脚本</strong>
<p>多模型协同,把创意变成可筛选结果。</p>
</article>
<article>
<span>编排</span>
<strong>在画布和工作流中组织任务</strong>
<p>把素材、分镜、任务和发布计划放在一个视野。</p>
</article>
<article>
<span>分发</span>
<strong>进入资产中心并自动发布</strong>
<p>发布不是终点,复盘数据会回到下一轮选题。</p>
</article>
</div>
</section>
<section class="company section-pad" aria-labelledby="company-title">
<div class="company-panel reveal">
<div>
<span class="section-index">05</span>
<h2 id="company-title">广州本初数字科技有限公司</h2>
<p>
本初要做的不是“炫技式 AI 展示”,而是一套能被一人公司、内容团队和轻量创业组织真正每天打开的生产系统。
</p>
</div>
<ul>
<li>面向增长、内容、私域和交付的真实工作流。</li>
<li>云端模型与本地模型可以按任务路由。</li>
<li>媒体资产、任务历史和发布动作形成闭环。</li>
</ul>
</div>
</section>
<section class="contact section-pad" id="contact" aria-labelledby="contact-title">
<div class="contact-shell reveal">
<div>
<h2 id="contact-title">把 TrueGrowth 部署成你的公司作战室</h2>
<p>
适合需要持续产出内容、管理多模型资产、连接数字人和自动发布的一人公司与小团队。
</p>
</div>
<div class="contact-guide" aria-label="联系与演示说明">
<span>Demo Access</span>
<h3>当前官网先做产品展示,不开放在线提交。</h3>
<p>
如果需要演示、部署或合作,请通过广州本初数字科技有限公司的正式联系方式沟通;后续上线官网表单后,再接入真实预约与通知流程。
</p>
<div class="guide-steps">
<div>
<strong>01</strong>
<small>浏览功能矩阵,确认适合的业务链路。</small>
</div>
<div>
<strong>02</strong>
<small>准备内容生产、数字人或自动发布场景。</small>
</div>
<div>
<strong>03</strong>
<small>通过正式渠道预约产品演示。</small>
</div>
</div>
</div>
</div>
</section>
</main>
<footer class="site-footer">
<span>TrueGrowth</span>
<span>广州本初数字科技有限公司</span>
<span>一人公司的 AI 工作台</span>
</footer>
<script src="./src/main.js" type="module"></script>
</body>
</html>

View File

@@ -0,0 +1,14 @@
{
"name": "truegrowth-official-site",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite --host 127.0.0.1 --port 7330",
"build": "vite build",
"preview": "vite preview --host 127.0.0.1 --port 7331"
},
"devDependencies": {
"vite": "^6.4.1"
}
}

348
official-site/src/main.js Normal file
View File

@@ -0,0 +1,348 @@
const root = document.documentElement;
const header = document.querySelector(".site-header");
const nav = document.querySelector(".site-nav");
const navToggle = document.querySelector(".nav-toggle");
const themeToggle = document.querySelector(".theme-toggle");
const typingLine = document.querySelector("#typing-line");
const selectedPrompt = document.querySelector("#selected-prompt");
const prefersReduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
const media = {
researchVideo: new URL("../assets/showcase-research.mp4", import.meta.url).href,
researchPoster: new URL("../assets/showcase-research.jpg", import.meta.url).href,
imageVideo: new URL("../assets/showcase-ai-image.mp4", import.meta.url).href,
imagePoster: new URL("../assets/showcase-ai-image.jpg", import.meta.url).href,
videoVideo: new URL("../assets/showcase-ai-video.mp4", import.meta.url).href,
videoPoster: new URL("../assets/showcase-ai-video.jpg", import.meta.url).href,
avatarVideo: new URL("../assets/showcase-avatar.mp4", import.meta.url).href,
avatarPoster: new URL("../assets/showcase-avatar.jpg", import.meta.url).href,
clipVideo: new URL("../assets/showcase-clip.mp4", import.meta.url).href,
clipPoster: new URL("../assets/showcase-clip.jpg", import.meta.url).href,
publishVideo: new URL("../assets/showcase-publish.mp4", import.meta.url).href,
publishPoster: new URL("../assets/showcase-publish.jpg", import.meta.url).href
};
const featureData = {
research: {
kicker: "万象智枢",
title: "从全网信号里找到今天值得做的内容战役",
text: "聚合网页、视频、社区和开源项目,把趋势、用户痛点和内容机会转成脚本、分镜和提示词。",
video: media.researchVideo,
poster: media.researchPoster,
points: ["趋势研究与需求洞察", "脚本、分镜、提示词生成", "结果可保存到知识与资产链路"]
},
image: {
kicker: "AI 图片生成",
title: "从一句方向到一组可筛选、可复用的视觉资产",
text: "支持文生图、图生图、多图参考、局部重绘和批量提示词,适合产品图、封面、视觉风格探索。",
video: media.imageVideo,
poster: media.imagePoster,
points: ["单图与批量出图", "参考图和参数统一管理", "结果进入资产中心与画布"]
},
video: {
kicker: "AI 视频生成",
title: "把选题、脚本和分镜快速推到镜头生产",
text: "支持文生视频、图生视频、首尾帧和爆款视频流程,让一人团队也能连续产出内容素材。",
video: media.videoVideo,
poster: media.videoPoster,
points: ["脚本到镜头连续生成", "热门视频与 MV 模式", "多结果对比与二次编辑"]
},
avatar: {
kicker: "数字人口播",
title: "让创始人形象、声音和脚本成为可调度资产",
text: "形象、音色、文案和背景组合生成口播视频,适合课程、产品讲解、私域种草和批量答疑。",
video: media.avatarVideo,
poster: media.avatarPoster,
points: ["形象与音色管理", "TTS 配音和字幕生成", "本地模型状态可检查"]
},
clip: {
kicker: "AI 剪辑中心",
title: "把长素材拆成高密度短视频发布弹药",
text: "长视频拆条、文字剪辑、说话人选段、字幕和商业短视频包装集中在同一个剪辑工作台。",
video: media.clipVideo,
poster: media.clipPoster,
points: ["长视频智能拆条", "字幕、封面和包装建议", "任务历史与资产中心同步"]
},
publish: {
kicker: "自动发布",
title: "让内容从资产中心直接进入渠道排程",
text: "选择图片视频资产,授权多平台账号,批量发布到社交渠道,并把发布动作沉淀回下一轮复盘。",
video: media.publishVideo,
poster: media.publishPoster,
points: ["多平台账号与排程", "素材库批量选择", "发布结果回流到增长复盘"]
}
};
const typingPhrases = [
"读取全网趋势,拆成可执行内容战役...",
"正在为一人公司生成选题、脚本、分镜和发布排程...",
"图片、视频、声音、数字人任务已进入统一队列...",
"资产中心已同步生成结果,等待下一轮复盘..."
];
function setHeaderState() {
header?.setAttribute("data-elevated", String(window.scrollY > 24));
}
window.addEventListener("scroll", setHeaderState, { passive: true });
setHeaderState();
navToggle?.addEventListener("click", () => {
const isOpen = nav?.classList.toggle("is-open") ?? false;
navToggle.setAttribute("aria-expanded", String(isOpen));
});
nav?.addEventListener("click", (event) => {
if (event.target instanceof HTMLAnchorElement) {
nav.classList.remove("is-open");
navToggle?.setAttribute("aria-expanded", "false");
}
});
const savedTheme = localStorage.getItem("truegrowth-official-theme");
if (savedTheme === "light" || savedTheme === "dark") {
root.dataset.siteTheme = savedTheme;
}
themeToggle?.addEventListener("click", () => {
const next = root.dataset.siteTheme === "dark" ? "light" : "dark";
root.dataset.siteTheme = next;
localStorage.setItem("truegrowth-official-theme", next);
});
const revealObserver = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add("is-visible");
revealObserver.unobserve(entry.target);
}
});
},
{ threshold: 0.14 }
);
document.querySelectorAll(".reveal").forEach((element) => revealObserver.observe(element));
const counters = document.querySelectorAll("[data-count-to]");
const counterObserver = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) return;
const element = entry.target;
const target = Number(element.getAttribute("data-count-to") || "0");
const start = performance.now();
const duration = prefersReduced ? 1 : 900;
const tick = (time) => {
const progress = Math.min(1, (time - start) / duration);
element.textContent = String(Math.round(target * progress));
if (progress < 1) requestAnimationFrame(tick);
};
requestAnimationFrame(tick);
counterObserver.unobserve(element);
});
},
{ threshold: 0.4 }
);
counters.forEach((counter) => counterObserver.observe(counter));
let phraseIndex = 0;
let charIndex = 0;
let deleting = false;
function typeNext() {
if (!typingLine || prefersReduced) return;
const phrase = typingPhrases[phraseIndex];
typingLine.textContent = phrase.slice(0, charIndex);
if (!deleting && charIndex < phrase.length) {
charIndex += 1;
window.setTimeout(typeNext, 34);
return;
}
if (!deleting && charIndex === phrase.length) {
deleting = true;
window.setTimeout(typeNext, 1300);
return;
}
if (deleting && charIndex > 0) {
charIndex -= 1;
window.setTimeout(typeNext, 18);
return;
}
deleting = false;
phraseIndex = (phraseIndex + 1) % typingPhrases.length;
window.setTimeout(typeNext, 220);
}
typeNext();
const featureVideo = document.querySelector("#feature-video");
const featureKicker = document.querySelector("#feature-kicker");
const featureTitle = document.querySelector("#feature-title");
const featureText = document.querySelector("#feature-text");
const featurePoints = document.querySelector("#feature-points");
function setFeatureVideo(data) {
if (!featureVideo || !data) return;
featureVideo.setAttribute("poster", data.poster);
if (featureVideo.currentSrc !== data.video) {
featureVideo.setAttribute("src", data.video);
featureVideo.load();
}
featureVideo.play().catch(() => {});
}
setFeatureVideo(featureData.research);
document.querySelectorAll(".feature-tab").forEach((button) => {
button.addEventListener("click", () => {
const key = button.getAttribute("data-feature");
const data = key ? featureData[key] : null;
if (!data) return;
document.querySelectorAll(".feature-tab").forEach((tab) => {
const active = tab === button;
tab.classList.toggle("is-active", active);
tab.setAttribute("aria-selected", String(active));
});
if (featureKicker) featureKicker.textContent = data.kicker;
if (featureTitle) featureTitle.textContent = data.title;
if (featureText) featureText.textContent = data.text;
if (featurePoints) {
featurePoints.innerHTML = data.points.map((point) => `<li>${point}</li>`).join("");
}
setFeatureVideo(data);
});
});
document.querySelectorAll(".plaza-filter").forEach((button) => {
button.addEventListener("click", () => {
const filter = button.getAttribute("data-filter") || "all";
document.querySelectorAll(".plaza-filter").forEach((item) => item.classList.toggle("is-active", item === button));
document.querySelectorAll(".prompt-card").forEach((card) => {
const group = card.getAttribute("data-group");
card.classList.toggle("is-hidden", filter !== "all" && group !== filter);
});
});
});
document.querySelectorAll(".prompt-card").forEach((card) => {
card.addEventListener("click", () => {
document.querySelectorAll(".prompt-card").forEach((item) => item.classList.toggle("is-selected", item === card));
selectedPrompt.textContent = card.getAttribute("data-prompt") || "";
});
});
document.querySelector(".prompt-card")?.classList.add("is-selected");
document.querySelector(".console-run")?.addEventListener("click", () => {
if (!selectedPrompt) return;
selectedPrompt.textContent = `已创建模拟任务:${selectedPrompt.textContent.replace(/^已创建模拟任务:/, "")}`;
});
function initSignalCanvas() {
const canvas = document.querySelector("#signal-canvas");
if (!(canvas instanceof HTMLCanvasElement) || prefersReduced) return;
const context = canvas.getContext("2d");
if (!context) return;
const pointer = { x: window.innerWidth / 2, y: window.innerHeight / 2 };
const nodes = [];
const maxNodes = Math.min(82, Math.max(38, Math.floor(window.innerWidth / 22)));
function resize() {
const dpr = Math.min(2, window.devicePixelRatio || 1);
canvas.width = Math.floor(window.innerWidth * dpr);
canvas.height = Math.floor(window.innerHeight * dpr);
canvas.style.width = `${window.innerWidth}px`;
canvas.style.height = `${window.innerHeight}px`;
context.setTransform(dpr, 0, 0, dpr, 0, 0);
}
function seed() {
nodes.length = 0;
for (let i = 0; i < maxNodes; i += 1) {
nodes.push({
x: Math.random() * window.innerWidth,
y: Math.random() * window.innerHeight,
vx: (Math.random() - 0.5) * 0.36,
vy: (Math.random() - 0.5) * 0.36,
r: 1 + Math.random() * 1.8
});
}
}
function draw() {
context.clearRect(0, 0, window.innerWidth, window.innerHeight);
nodes.forEach((node) => {
node.x += node.vx;
node.y += node.vy;
if (node.x < -20 || node.x > window.innerWidth + 20) node.vx *= -1;
if (node.y < -20 || node.y > window.innerHeight + 20) node.vy *= -1;
});
for (let i = 0; i < nodes.length; i += 1) {
for (let j = i + 1; j < nodes.length; j += 1) {
const a = nodes[i];
const b = nodes[j];
const dx = a.x - b.x;
const dy = a.y - b.y;
const distance = Math.hypot(dx, dy);
if (distance < 132) {
const alpha = (1 - distance / 132) * 0.18;
context.strokeStyle = `rgba(255, 91, 0, ${alpha})`;
context.lineWidth = 1;
context.beginPath();
context.moveTo(a.x, a.y);
context.lineTo(b.x, b.y);
context.stroke();
}
}
}
nodes.forEach((node) => {
const pointerDistance = Math.hypot(node.x - pointer.x, node.y - pointer.y);
if (pointerDistance < 190) {
context.strokeStyle = `rgba(80, 230, 255, ${(1 - pointerDistance / 190) * 0.28})`;
context.beginPath();
context.moveTo(node.x, node.y);
context.lineTo(pointer.x, pointer.y);
context.stroke();
}
context.fillStyle = pointerDistance < 190 ? "rgba(80, 230, 255, 0.72)" : "rgba(255, 91, 0, 0.58)";
context.beginPath();
context.arc(node.x, node.y, node.r, 0, Math.PI * 2);
context.fill();
});
requestAnimationFrame(draw);
}
window.addEventListener("resize", () => {
resize();
seed();
});
window.addEventListener("pointermove", (event) => {
pointer.x = event.clientX;
pointer.y = event.clientY;
}, { passive: true });
resize();
seed();
draw();
}
initSignalCanvas();

1367
official-site/src/styles.css Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
import { defineConfig } from 'vite';
export default defineConfig({
base: './'
});