2383 lines
72 KiB
HTML
2383 lines
72 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<!-- 权限策略:禁止 unload 事件(不影响 beforeunload) -->
|
||
<!-- 基本 SEO Meta 标签 -->
|
||
<title>TrueGrowth | 让 AI 接管增长</title>
|
||
<meta
|
||
name="description"
|
||
content="TrueGrowth 是一个以应用中心为入口的桌面 AI 创作平台,统一图片、视频、数字人、声音、素材、任务和无限画布。"
|
||
/>
|
||
<meta
|
||
name="keywords"
|
||
content="TrueGrowth,AI创作平台,AI图片生成,AI视频生成,数字人,声音克隆,无限画布,素材库,任务中心,本地模型"
|
||
/>
|
||
<meta name="application-name" content="TrueGrowth" />
|
||
<meta name="author" content="TrueGrowth Team" />
|
||
<meta name="theme-color" content="#ff5b00" />
|
||
<meta
|
||
name="robots"
|
||
content="index,follow,max-image-preview:large,max-snippet:-1,max-video-preview:-1"
|
||
/>
|
||
<!-- Open Graph Meta 标签 -->
|
||
<meta property="og:title" content="TrueGrowth | 桌面 AI 创作平台" />
|
||
<meta
|
||
property="og:description"
|
||
content="以应用中心进入图片、视频、数字人、声音、素材、任务和无限画布。"
|
||
/>
|
||
<meta property="og:type" content="website" />
|
||
<meta property="og:url" content="https://truegrowth.ai" />
|
||
<meta property="og:site_name" content="TrueGrowth" />
|
||
<meta property="og:locale" content="zh_CN" />
|
||
<meta property="og:image" content="/icons/icon-512x512.png" />
|
||
<meta
|
||
property="og:image:alt"
|
||
content="TrueGrowth 桌面 AI 创作平台界面预览"
|
||
/>
|
||
<!-- Twitter Card Meta 标签 -->
|
||
<meta name="twitter:card" content="summary_large_image" />
|
||
<meta name="twitter:title" content="TrueGrowth | 桌面 AI 创作平台" />
|
||
<meta
|
||
name="twitter:description"
|
||
content="一个把 AI 图片、视频、数字人、声音、素材、任务和无限画布统一到应用中心的 AI 工作台。"
|
||
/>
|
||
<meta name="twitter:image" content="/icons/icon-512x512.png" />
|
||
<meta
|
||
name="twitter:image:alt"
|
||
content="TrueGrowth 桌面 AI 创作平台界面预览"
|
||
/>
|
||
<link rel="canonical" href="https://truegrowth.ai" />
|
||
<!-- 语言替代链接 -->
|
||
<link rel="alternate" hreflang="zh-CN" href="https://truegrowth.ai" />
|
||
<link rel="alternate" hreflang="x-default" href="https://truegrowth.ai" />
|
||
<!-- 结构化数据 (JSON-LD) -->
|
||
<script type="application/ld+json">
|
||
{
|
||
"@context": "https://schema.org",
|
||
"@graph": [
|
||
{
|
||
"@type": "WebSite",
|
||
"@id": "https://truegrowth.ai/#website",
|
||
"url": "https://truegrowth.ai",
|
||
"name": "TrueGrowth",
|
||
"inLanguage": "zh-CN",
|
||
"description": "TrueGrowth 是一个以应用中心为入口的桌面 AI 创作平台。"
|
||
},
|
||
{
|
||
"@type": "Organization",
|
||
"@id": "https://truegrowth.ai/#organization",
|
||
"name": "TrueGrowth Team",
|
||
"url": "https://truegrowth.ai",
|
||
"logo": "https://truegrowth.ai/icons/icon-512x512.png"
|
||
},
|
||
{
|
||
"@type": "SoftwareApplication",
|
||
"@id": "https://truegrowth.ai/#app",
|
||
"name": "TrueGrowth",
|
||
"alternateName": [
|
||
"TrueGrowth AI 创作平台",
|
||
"TrueGrowth Canvas Workspace"
|
||
],
|
||
"description": "TrueGrowth 集成图片、视频、数字人、声音、统一素材库、任务队列和无限画布,将生成、编辑与交付串联在一个工作台里。",
|
||
"url": "https://truegrowth.ai",
|
||
"image": "https://truegrowth.ai/icons/icon-512x512.png",
|
||
"applicationCategory": "DesignApplication",
|
||
"operatingSystem": "Any",
|
||
"offers": {
|
||
"@type": "Offer",
|
||
"price": "0",
|
||
"priceCurrency": "USD"
|
||
},
|
||
"publisher": {
|
||
"@id": "https://TrueGrowth.ai/#organization"
|
||
},
|
||
"keywords": [
|
||
"TrueGrowth",
|
||
"AI应用平台",
|
||
"AI图片生成",
|
||
"流程图",
|
||
"思维导图",
|
||
"画布工作区",
|
||
"AI工作流",
|
||
"素材库",
|
||
"知识库",
|
||
"任务队列"
|
||
],
|
||
"inLanguage": "zh-CN"
|
||
},
|
||
{
|
||
"@type": "FAQPage",
|
||
"@id": "https://TrueGrowth.ai/#faq",
|
||
"mainEntity": [
|
||
{
|
||
"@type": "Question",
|
||
"name": "TrueGrowth 适合做什么?",
|
||
"acceptedAnswer": {
|
||
"@type": "Answer",
|
||
"text": "TrueGrowth 适合做 AI 图片生成、流程图、思维导图、画布整理、素材管理和任务推进。你可以在同一块画布里完成创意生成、编辑、整理与交付。"
|
||
}
|
||
},
|
||
{
|
||
"@type": "Question",
|
||
"name": "TrueGrowth 和普通白板工具有什么区别?",
|
||
"acceptedAnswer": {
|
||
"@type": "Answer",
|
||
"text": "TrueGrowth 不只是白板,还把 AI 生成、素材库、知识与任务队列整合到同一个工作区,减少在多个应用间来回切换。"
|
||
}
|
||
},
|
||
{
|
||
"@type": "Question",
|
||
"name": "TrueGrowth 是否支持流程图和思维导图?",
|
||
"acceptedAnswer": {
|
||
"@type": "Answer",
|
||
"text": "支持。TrueGrowth 提供流程图、思维导图、文本和图形编辑能力,也支持通过自然语言或 Mermaid 语法快速生成结构化图示。"
|
||
}
|
||
}
|
||
]
|
||
}
|
||
]
|
||
}
|
||
</script>
|
||
<!-- base href 由构建工具处理,CDN 模式下使用相对路径 -->
|
||
<meta
|
||
name="viewport"
|
||
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
|
||
/>
|
||
<meta name="app-version" content="0.9.1" />
|
||
<style>
|
||
:root {
|
||
--app-boot-bg: #000000;
|
||
--app-boot-text: #f8fafc;
|
||
--app-boot-muted: rgba(248, 250, 252, 0.58);
|
||
--app-boot-quiet: rgba(248, 250, 252, 0.22);
|
||
--app-boot-accent: #ff5b00;
|
||
--app-boot-accent-soft: rgba(255, 91, 0, 0.18);
|
||
--app-boot-line: rgba(255, 255, 255, 0.065);
|
||
--app-boot-panel: rgba(14, 14, 15, 0.78);
|
||
--app-boot-shadow: 0 22px 70px rgba(0, 0, 0, 0.48);
|
||
--app-boot-progress: 0%;
|
||
--app-boot-flight-speed: 3.2s;
|
||
--app-boot-flight-blur: 1px;
|
||
--app-boot-flight-opacity: 0.62;
|
||
}
|
||
|
||
html,
|
||
body {
|
||
width: 100%;
|
||
height: 100%;
|
||
min-width: 100%;
|
||
min-height: 100%;
|
||
margin: 0;
|
||
background: #000 !important;
|
||
overflow: hidden;
|
||
}
|
||
|
||
#app-boot-loading {
|
||
position: fixed;
|
||
inset: 0;
|
||
width: 100vw;
|
||
height: 100vh;
|
||
height: 100dvh;
|
||
z-index: 9999;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 24px;
|
||
overflow: hidden;
|
||
background: radial-gradient(
|
||
ellipse at 50% 50%,
|
||
rgba(255, 91, 0, 0.2) 0,
|
||
rgba(255, 91, 0, 0.13) 12%,
|
||
rgba(77, 135, 255, 0.1) 30%,
|
||
rgba(180, 87, 255, 0.055) 48%,
|
||
rgba(0, 0, 0, 0.68) 76%,
|
||
#000 100%
|
||
),
|
||
linear-gradient(180deg, #000 0%, #020203 44%, #000 100%), #000;
|
||
transition: opacity 0.28s ease, visibility 0.28s ease;
|
||
opacity: 1;
|
||
visibility: visible;
|
||
pointer-events: auto;
|
||
}
|
||
|
||
#app-boot-loading::before,
|
||
#app-boot-loading::after {
|
||
content: '';
|
||
position: absolute;
|
||
inset: -18%;
|
||
pointer-events: none;
|
||
}
|
||
|
||
#app-boot-loading::before {
|
||
background: radial-gradient(
|
||
circle at 50% 50%,
|
||
transparent 0 8%,
|
||
rgba(255, 255, 255, 0.12) 8.2% 8.35%,
|
||
transparent 8.7% 18%,
|
||
rgba(255, 91, 0, 0.08) 18.2% 18.35%,
|
||
transparent 18.8% 100%
|
||
),
|
||
linear-gradient(var(--app-boot-line) 1px, transparent 1px),
|
||
linear-gradient(90deg, var(--app-boot-line) 1px, transparent 1px);
|
||
background-position: center;
|
||
background-size: 100% 100%, 58px 58px, 58px 58px;
|
||
mask-image: radial-gradient(
|
||
circle at 50% 50%,
|
||
transparent 0 7%,
|
||
#000 13% 70%,
|
||
transparent 92%
|
||
);
|
||
transform: perspective(680px) rotateX(62deg) scale(0.82);
|
||
transform-origin: center;
|
||
animation: appBootGridForward calc(var(--app-boot-flight-speed) * 1.22)
|
||
cubic-bezier(0.22, 0.04, 0.12, 1) infinite;
|
||
opacity: 0.5;
|
||
}
|
||
|
||
#app-boot-loading::after {
|
||
inset: 0;
|
||
background: radial-gradient(
|
||
circle at 50% 50%,
|
||
transparent 0 38%,
|
||
rgba(0, 0, 0, 0.22) 66%,
|
||
rgba(0, 0, 0, 0.9) 100%
|
||
),
|
||
linear-gradient(
|
||
180deg,
|
||
rgba(0, 0, 0, 0.76) 0%,
|
||
transparent 22%,
|
||
transparent 70%,
|
||
rgba(0, 0, 0, 0.92) 100%
|
||
);
|
||
opacity: 1;
|
||
}
|
||
|
||
.app-boot-tunnel {
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 0;
|
||
overflow: hidden;
|
||
opacity: var(--app-boot-flight-opacity);
|
||
filter: saturate(1.18) blur(var(--app-boot-flight-blur));
|
||
pointer-events: none;
|
||
}
|
||
|
||
.app-boot-tunnel::before {
|
||
content: '';
|
||
position: absolute;
|
||
inset: -10%;
|
||
background: radial-gradient(
|
||
circle at 50% 50%,
|
||
rgba(255, 255, 255, 0.2) 0 1px,
|
||
transparent 2px 5%,
|
||
rgba(255, 91, 0, 0.18) 5.6% 6.1%,
|
||
transparent 6.8% 12%,
|
||
rgba(86, 153, 255, 0.11) 12.4% 12.8%,
|
||
transparent 13.6% 100%
|
||
),
|
||
radial-gradient(
|
||
ellipse at 50% 50%,
|
||
transparent 0 10%,
|
||
rgba(255, 91, 0, 0.1) 16%,
|
||
rgba(79, 143, 255, 0.055) 31%,
|
||
transparent 62%
|
||
);
|
||
mask-image: radial-gradient(
|
||
ellipse at 50% 50%,
|
||
transparent 0 9%,
|
||
#000 15% 72%,
|
||
transparent 94%
|
||
);
|
||
opacity: 0.88;
|
||
pointer-events: none;
|
||
transform: scale(0.72);
|
||
animation: appBootTunnelDepth calc(var(--app-boot-flight-speed) * 1.08)
|
||
cubic-bezier(0.22, 0.04, 0.12, 1) infinite;
|
||
}
|
||
|
||
.app-boot-tunnel::after {
|
||
content: '';
|
||
position: absolute;
|
||
left: 50%;
|
||
top: 50%;
|
||
width: min(24vw, 280px);
|
||
aspect-ratio: 1;
|
||
border-radius: 50%;
|
||
background: radial-gradient(
|
||
circle at 50% 50%,
|
||
rgba(255, 255, 255, 0.16) 0,
|
||
rgba(255, 91, 0, 0.12) 22%,
|
||
rgba(71, 117, 255, 0.08) 46%,
|
||
transparent 74%
|
||
);
|
||
box-shadow: 0 0 70px rgba(255, 91, 0, 0.22),
|
||
0 0 110px rgba(71, 117, 255, 0.12);
|
||
transform: translate(-50%, -50%);
|
||
opacity: 0.68;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.app-boot-tunnel span {
|
||
--angle: 0deg;
|
||
--ray-height: 1.4px;
|
||
--delay: 0s;
|
||
--ray: rgba(255, 255, 255, 0.78);
|
||
position: absolute;
|
||
left: 50%;
|
||
top: 50%;
|
||
width: min(50vw, 860px);
|
||
height: var(--ray-height);
|
||
border-radius: 999px;
|
||
background: linear-gradient(
|
||
90deg,
|
||
transparent 0,
|
||
rgba(255, 255, 255, 0.12) 8%,
|
||
var(--ray) 40%,
|
||
rgba(255, 255, 255, 0.38) 72%,
|
||
transparent 100%
|
||
);
|
||
box-shadow: 0 0 22px var(--ray);
|
||
transform-origin: left center;
|
||
animation: appBootWarpRay var(--app-boot-flight-speed)
|
||
cubic-bezier(0.24, 0.02, 0.16, 1) infinite;
|
||
animation-delay: var(--delay);
|
||
opacity: 0;
|
||
}
|
||
|
||
.app-boot-tunnel span:nth-child(3n) {
|
||
width: min(62vw, 1080px);
|
||
}
|
||
|
||
.app-boot-tunnel span:nth-child(4n) {
|
||
width: min(40vw, 720px);
|
||
}
|
||
|
||
.app-boot-tunnel span:nth-child(5n) {
|
||
opacity: 0.12;
|
||
animation-duration: calc(var(--app-boot-flight-speed) * 0.78);
|
||
}
|
||
|
||
#app-boot-loading.is-visible {
|
||
opacity: 1;
|
||
visibility: visible;
|
||
pointer-events: auto;
|
||
}
|
||
|
||
#app-boot-loading.is-leaving {
|
||
opacity: 0;
|
||
visibility: hidden;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.app-boot-card {
|
||
position: relative;
|
||
z-index: 2;
|
||
width: min(720px, calc(100vw - 48px));
|
||
padding: 0;
|
||
border: 0;
|
||
background: transparent;
|
||
box-shadow: none;
|
||
text-align: center;
|
||
}
|
||
|
||
.app-boot-card::before {
|
||
content: '';
|
||
position: absolute;
|
||
left: 50%;
|
||
top: 50%;
|
||
width: min(720px, 88vw);
|
||
height: min(520px, 68vw);
|
||
border: 0;
|
||
border-radius: 50%;
|
||
background: radial-gradient(
|
||
circle at 50% 50%,
|
||
rgba(255, 91, 0, 0.16) 0,
|
||
rgba(255, 91, 0, 0.075) 20%,
|
||
rgba(71, 117, 255, 0.04) 36%,
|
||
transparent 68%
|
||
);
|
||
transform: translate(-50%, -52%);
|
||
opacity: 0.74;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.app-boot-card::after {
|
||
content: '';
|
||
position: absolute;
|
||
left: 50%;
|
||
top: -4%;
|
||
width: min(520px, 78vw);
|
||
height: 1px;
|
||
background: linear-gradient(
|
||
90deg,
|
||
transparent,
|
||
rgba(255, 91, 0, 0.38),
|
||
rgba(255, 255, 255, 0.34),
|
||
transparent
|
||
);
|
||
filter: none;
|
||
transform: translateX(-50%);
|
||
animation: appBootScan 4.2s ease-in-out infinite;
|
||
opacity: 0.28;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.app-boot-mark {
|
||
position: relative;
|
||
z-index: 1;
|
||
width: 76px;
|
||
height: 76px;
|
||
padding: 0;
|
||
margin: 0 auto 30px;
|
||
border-radius: 20px;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
overflow: hidden;
|
||
text-decoration: none;
|
||
background: linear-gradient(
|
||
180deg,
|
||
rgba(255, 255, 255, 0.1),
|
||
rgba(255, 255, 255, 0.025)
|
||
),
|
||
var(--app-boot-panel);
|
||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||
box-shadow: 0 16px 42px rgba(0, 0, 0, 0.34),
|
||
0 0 34px rgba(255, 91, 0, 0.16),
|
||
inset 0 1px 0 rgba(255, 255, 255, 0.16);
|
||
animation: appBootCorePulse 4.8s ease-in-out infinite;
|
||
}
|
||
|
||
.app-boot-mark::before {
|
||
content: '';
|
||
position: absolute;
|
||
inset: 8px;
|
||
border-radius: 14px;
|
||
border: 1px solid rgba(255, 91, 0, 0.22);
|
||
background: linear-gradient(
|
||
135deg,
|
||
rgba(255, 91, 0, 0.08),
|
||
transparent 48%,
|
||
rgba(255, 255, 255, 0.05)
|
||
);
|
||
opacity: 0.9;
|
||
}
|
||
|
||
.app-boot-mark__base,
|
||
.app-boot-mark__fill {
|
||
position: absolute;
|
||
inset: 14px;
|
||
width: 48px;
|
||
height: 48px;
|
||
display: block;
|
||
object-fit: contain;
|
||
transition: clip-path 0.24s ease, filter 0.24s ease;
|
||
}
|
||
|
||
.app-boot-mark__base {
|
||
filter: grayscale(1) brightness(1.9);
|
||
opacity: 0.22;
|
||
}
|
||
|
||
.app-boot-mark__fill {
|
||
filter: saturate(1.12) drop-shadow(0 0 12px rgba(255, 91, 0, 0.3));
|
||
clip-path: inset(0 calc(100% - var(--app-boot-progress)) 0 0);
|
||
}
|
||
|
||
.app-boot-mark::after {
|
||
content: '';
|
||
position: absolute;
|
||
inset: 0;
|
||
border-radius: inherit;
|
||
background: linear-gradient(
|
||
90deg,
|
||
rgba(255, 91, 0, 0.16) 0 var(--app-boot-progress),
|
||
transparent var(--app-boot-progress) 100%
|
||
);
|
||
pointer-events: none;
|
||
transition: background 0.24s ease;
|
||
}
|
||
|
||
.app-boot-title {
|
||
position: relative;
|
||
z-index: 1;
|
||
margin: 0;
|
||
font-size: clamp(36px, 4.8vw, 66px);
|
||
font-weight: 820;
|
||
line-height: 1.02;
|
||
letter-spacing: 0;
|
||
}
|
||
|
||
.app-boot-slogan {
|
||
position: relative;
|
||
z-index: 1;
|
||
margin: 16px 0 0;
|
||
font-size: clamp(38px, 5.8vw, 76px);
|
||
font-weight: 860;
|
||
line-height: 1.06;
|
||
letter-spacing: 0;
|
||
}
|
||
|
||
.app-boot-fill-text {
|
||
display: inline-block;
|
||
color: transparent;
|
||
background: linear-gradient(
|
||
90deg,
|
||
var(--app-boot-accent) 0 var(--app-boot-progress),
|
||
var(--app-boot-quiet) var(--app-boot-progress) 100%
|
||
);
|
||
background-clip: text;
|
||
-webkit-background-clip: text;
|
||
text-shadow: 0 0 22px rgba(255, 91, 0, 0.16);
|
||
transition: background 0.24s ease;
|
||
}
|
||
|
||
.app-boot-status {
|
||
position: relative;
|
||
z-index: 1;
|
||
margin: 28px auto 0;
|
||
color: var(--app-boot-muted);
|
||
font-size: 13px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.app-boot-status-title {
|
||
display: block;
|
||
color: rgba(248, 250, 252, 0.72);
|
||
font-weight: 700;
|
||
}
|
||
|
||
.app-boot-tip {
|
||
display: block;
|
||
margin-top: 6px;
|
||
}
|
||
|
||
.app-boot-progress {
|
||
position: relative;
|
||
z-index: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 14px;
|
||
margin: 24px auto 0;
|
||
padding: 0;
|
||
border-radius: 999px;
|
||
background: transparent;
|
||
color: var(--app-boot-accent);
|
||
font-size: 11px;
|
||
font-weight: 720;
|
||
}
|
||
|
||
.app-boot-progress-track {
|
||
position: relative;
|
||
width: min(420px, 62vw);
|
||
height: 4px;
|
||
overflow: hidden;
|
||
border-radius: 999px;
|
||
background: rgba(255, 255, 255, 0.12);
|
||
border: 0;
|
||
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.055),
|
||
0 14px 34px rgba(0, 0, 0, 0.32);
|
||
}
|
||
|
||
.app-boot-progress-track::before {
|
||
content: '';
|
||
position: absolute;
|
||
inset: 0;
|
||
background: repeating-linear-gradient(
|
||
90deg,
|
||
transparent 0 46px,
|
||
rgba(255, 255, 255, 0.18) 46px 47px,
|
||
transparent 47px 92px
|
||
);
|
||
opacity: 0.28;
|
||
}
|
||
|
||
.app-boot-progress-fill {
|
||
position: absolute;
|
||
inset: 0 auto 0 0;
|
||
width: 0%;
|
||
border-radius: inherit;
|
||
background: linear-gradient(90deg, #ff5b00, #ff7a24);
|
||
box-shadow: 0 0 14px rgba(255, 91, 0, 0.34);
|
||
transition: width 0.24s ease;
|
||
}
|
||
|
||
.app-boot-progress-fill::after {
|
||
content: '';
|
||
position: absolute;
|
||
inset: 0;
|
||
background: linear-gradient(
|
||
90deg,
|
||
transparent,
|
||
rgba(255, 255, 255, 0.34),
|
||
transparent
|
||
);
|
||
transform: translateX(-100%);
|
||
animation: appBootProgressSweep 2.2s ease-in-out infinite;
|
||
}
|
||
|
||
.app-boot-progress-value {
|
||
min-width: 34px;
|
||
text-align: right;
|
||
font-variant-numeric: tabular-nums;
|
||
color: rgba(248, 250, 252, 0.72);
|
||
text-shadow: none;
|
||
}
|
||
|
||
.app-boot-note {
|
||
position: relative;
|
||
z-index: 1;
|
||
margin-top: 12px;
|
||
color: rgba(248, 250, 252, 0.28);
|
||
font-size: 11px;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.app-boot-node-row {
|
||
position: relative;
|
||
z-index: 1;
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 8px;
|
||
margin: 18px auto 0;
|
||
}
|
||
|
||
.app-boot-node-row span {
|
||
width: 32px;
|
||
height: 1px;
|
||
border-radius: 999px;
|
||
background: rgba(255, 255, 255, 0.16);
|
||
box-shadow: 0 0 0 rgba(255, 91, 0, 0);
|
||
animation: appBootNodePulse 3.6s ease-in-out infinite;
|
||
}
|
||
|
||
.app-boot-node-row span:nth-child(2) {
|
||
animation-delay: 0.18s;
|
||
}
|
||
|
||
.app-boot-node-row span:nth-child(3) {
|
||
animation-delay: 0.36s;
|
||
}
|
||
|
||
.app-boot-node-row span:nth-child(4) {
|
||
animation-delay: 0.54s;
|
||
}
|
||
|
||
.app-boot-node-row span:nth-child(5) {
|
||
animation-delay: 0.72s;
|
||
}
|
||
|
||
@keyframes appBootGridForward {
|
||
0% {
|
||
transform: perspective(680px) rotateX(62deg) scale(0.72);
|
||
opacity: 0;
|
||
}
|
||
16% {
|
||
opacity: 0.56;
|
||
}
|
||
100% {
|
||
transform: perspective(680px) rotateX(62deg) scale(1.62);
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
@keyframes appBootTunnelDepth {
|
||
0% {
|
||
transform: scale(0.66);
|
||
opacity: 0;
|
||
}
|
||
18% {
|
||
opacity: 0.82;
|
||
}
|
||
100% {
|
||
transform: scale(1.18);
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
@keyframes appBootWarpRay {
|
||
0% {
|
||
transform: rotate(var(--angle)) translateX(1vw) scaleX(0.02);
|
||
opacity: 0;
|
||
}
|
||
8% {
|
||
opacity: 1;
|
||
}
|
||
62% {
|
||
opacity: 0.68;
|
||
}
|
||
100% {
|
||
transform: rotate(var(--angle)) translateX(72vw) scaleX(1.5);
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
@keyframes appBootScan {
|
||
0% {
|
||
transform: translateX(-50%) scaleX(0.84);
|
||
opacity: 0.18;
|
||
}
|
||
50% {
|
||
transform: translateX(-50%) scaleX(1);
|
||
opacity: 0.44;
|
||
}
|
||
100% {
|
||
transform: translateX(-50%) scaleX(0.84);
|
||
opacity: 0.18;
|
||
}
|
||
}
|
||
|
||
@keyframes appBootCorePulse {
|
||
0%,
|
||
100% {
|
||
transform: translateY(0) scale(1);
|
||
box-shadow: 0 16px 42px rgba(0, 0, 0, 0.34),
|
||
0 0 34px rgba(255, 91, 0, 0.16),
|
||
inset 0 1px 0 rgba(255, 255, 255, 0.16);
|
||
}
|
||
50% {
|
||
transform: translateY(-1px) scale(1.006);
|
||
box-shadow: 0 18px 48px rgba(0, 0, 0, 0.38),
|
||
0 0 42px rgba(255, 91, 0, 0.22),
|
||
inset 0 1px 0 rgba(255, 255, 255, 0.18);
|
||
}
|
||
}
|
||
|
||
@keyframes appBootNodePulse {
|
||
0%,
|
||
100% {
|
||
background: rgba(255, 255, 255, 0.16);
|
||
box-shadow: 0 0 0 rgba(255, 91, 0, 0);
|
||
transform: scaleX(1);
|
||
}
|
||
42% {
|
||
background: rgba(255, 91, 0, 0.72);
|
||
box-shadow: 0 0 12px rgba(255, 91, 0, 0.32);
|
||
transform: scaleX(1.08);
|
||
}
|
||
}
|
||
|
||
@keyframes appBootProgressSweep {
|
||
0% {
|
||
transform: translateX(-100%);
|
||
}
|
||
100% {
|
||
transform: translateX(100%);
|
||
}
|
||
}
|
||
|
||
#seo-content {
|
||
max-width: 1120px;
|
||
margin: 0 auto;
|
||
padding: 48px 24px 88px;
|
||
color: #1f2937;
|
||
}
|
||
|
||
#seo-content a {
|
||
color: #5a4fcf;
|
||
text-decoration: none;
|
||
}
|
||
|
||
#seo-content a:hover {
|
||
text-decoration: underline;
|
||
}
|
||
|
||
.seo-hero {
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1.2fr) minmax(280px, 0.8fr);
|
||
gap: 24px;
|
||
align-items: start;
|
||
margin-bottom: 36px;
|
||
}
|
||
|
||
.seo-hero-copy h1 {
|
||
margin: 0 0 16px;
|
||
font-size: clamp(32px, 5vw, 54px);
|
||
line-height: 1.05;
|
||
color: #121826;
|
||
}
|
||
|
||
.seo-hero-copy p {
|
||
margin: 0 0 14px;
|
||
font-size: 16px;
|
||
line-height: 1.8;
|
||
color: #3f4a5a;
|
||
}
|
||
|
||
.seo-badges,
|
||
.seo-card-links,
|
||
.seo-faq-list {
|
||
list-style: none;
|
||
margin: 0;
|
||
padding: 0;
|
||
}
|
||
|
||
.seo-badges {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 10px;
|
||
margin-top: 18px;
|
||
}
|
||
|
||
.seo-badges li {
|
||
padding: 8px 12px;
|
||
border-radius: 999px;
|
||
background: #fff4df;
|
||
color: #ad6800;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.seo-panel {
|
||
border: 1px solid rgba(90, 79, 207, 0.14);
|
||
border-radius: 24px;
|
||
padding: 24px;
|
||
background: linear-gradient(180deg, #ffffff 0%, #faf8ff 100%);
|
||
box-shadow: 0 18px 40px rgba(90, 79, 207, 0.08);
|
||
}
|
||
|
||
.seo-panel h2,
|
||
.seo-section h2 {
|
||
margin: 0 0 14px;
|
||
font-size: 24px;
|
||
color: #121826;
|
||
}
|
||
|
||
.seo-panel p,
|
||
.seo-section p {
|
||
margin: 0 0 12px;
|
||
line-height: 1.8;
|
||
color: #475569;
|
||
}
|
||
|
||
.seo-card-links li + li,
|
||
.seo-faq-list li + li {
|
||
margin-top: 12px;
|
||
}
|
||
|
||
.seo-card-links a {
|
||
display: block;
|
||
padding: 14px 16px;
|
||
border-radius: 16px;
|
||
background: #ffffff;
|
||
border: 1px solid rgba(90, 79, 207, 0.12);
|
||
}
|
||
|
||
.seo-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||
gap: 18px;
|
||
margin-bottom: 36px;
|
||
}
|
||
|
||
.seo-grid article,
|
||
.seo-faq-list li {
|
||
border-radius: 20px;
|
||
border: 1px solid #e8ebf3;
|
||
background: #ffffff;
|
||
padding: 20px;
|
||
box-shadow: 0 10px 30px rgba(15, 23, 42, 0.04);
|
||
}
|
||
|
||
.seo-grid h3,
|
||
.seo-faq-list h3 {
|
||
margin: 0 0 10px;
|
||
font-size: 18px;
|
||
color: #121826;
|
||
}
|
||
|
||
.seo-section {
|
||
margin-bottom: 36px;
|
||
}
|
||
|
||
.seo-inline-links {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 10px 16px;
|
||
}
|
||
|
||
html.app-ready #seo-content {
|
||
display: none;
|
||
}
|
||
|
||
html.app-boot-error .app-boot-progress {
|
||
background: rgba(233, 30, 99, 0.1);
|
||
color: #e91e63;
|
||
}
|
||
|
||
html.app-boot-error .app-boot-progress-track {
|
||
background: rgba(233, 30, 99, 0.16);
|
||
}
|
||
|
||
html.app-boot-error .app-boot-progress-fill {
|
||
background: linear-gradient(
|
||
90deg,
|
||
rgba(233, 30, 99, 0.72),
|
||
rgba(233, 30, 99, 1)
|
||
);
|
||
box-shadow: 0 0 16px rgba(233, 30, 99, 0.22);
|
||
}
|
||
|
||
html.app-boot-error {
|
||
--app-boot-accent: #e91e63;
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
#app-boot-loading::before,
|
||
#app-boot-loading::after,
|
||
.app-boot-tunnel,
|
||
.app-boot-tunnel::before,
|
||
.app-boot-tunnel::after,
|
||
.app-boot-tunnel span,
|
||
.app-boot-card::after,
|
||
.app-boot-mark,
|
||
.app-boot-mark::before,
|
||
.app-boot-progress-fill::after,
|
||
.app-boot-node-row span {
|
||
animation: none;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 640px) {
|
||
.app-boot-card {
|
||
width: min(100%, calc(100vw - 40px));
|
||
}
|
||
|
||
.app-boot-mark {
|
||
width: 72px;
|
||
height: 72px;
|
||
margin-bottom: 22px;
|
||
border-radius: 20px;
|
||
}
|
||
|
||
.app-boot-mark__base,
|
||
.app-boot-mark__fill {
|
||
inset: 13px;
|
||
width: 46px;
|
||
height: 46px;
|
||
}
|
||
|
||
.app-boot-status {
|
||
margin-top: 24px;
|
||
}
|
||
|
||
.app-boot-progress {
|
||
gap: 10px;
|
||
}
|
||
|
||
.app-boot-progress-track {
|
||
width: min(280px, 62vw);
|
||
}
|
||
|
||
.seo-hero,
|
||
.seo-grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
#seo-content {
|
||
padding: 32px 20px 64px;
|
||
}
|
||
}
|
||
</style>
|
||
<link
|
||
rel="icon"
|
||
type="image/png"
|
||
sizes="16x16"
|
||
href="./icons/favicon-16x16.png"
|
||
/>
|
||
<link
|
||
rel="icon"
|
||
type="image/png"
|
||
sizes="32x32"
|
||
href="./icons/favicon-32x32.png"
|
||
/>
|
||
<link rel="icon" type="image/png" href="./icons/TrueGrowth-logo.png" />
|
||
|
||
<!-- PWA Manifest -->
|
||
<link rel="manifest" href="./manifest.json?v=0.1.0" />
|
||
|
||
<!-- PWA Meta Tags -->
|
||
<meta name="theme-color" content="#ff5b00" />
|
||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
|
||
<meta name="apple-mobile-web-app-title" content="TrueGrowth" />
|
||
<meta name="mobile-web-app-capable" content="yes" />
|
||
<meta name="msapplication-TileColor" content="#ff5b00" />
|
||
<meta name="msapplication-tap-highlight" content="no" />
|
||
|
||
<!-- Apple Touch Icons -->
|
||
<link
|
||
rel="apple-touch-icon"
|
||
sizes="192x192"
|
||
href="./icons/icon-192x192.png"
|
||
/>
|
||
|
||
<!-- Windows Tiles -->
|
||
<meta name="msapplication-TileImage" content="./icons/icon-192x192.png" />
|
||
<meta
|
||
name="msapplication-square310x310logo"
|
||
content="./icons/icon-512x512.png"
|
||
/>
|
||
|
||
<link rel="stylesheet" href="./src/styles.scss" />
|
||
</head>
|
||
<body>
|
||
<div
|
||
id="app-boot-loading"
|
||
role="status"
|
||
aria-live="polite"
|
||
aria-label="应用正在加载"
|
||
>
|
||
<div class="app-boot-tunnel" aria-hidden="true">
|
||
<span
|
||
style="
|
||
--angle: -178deg;
|
||
--delay: -0.1s;
|
||
--ray-height: 1.6px;
|
||
--ray: rgba(255, 91, 0, 0.82);
|
||
"
|
||
></span>
|
||
<span
|
||
style="
|
||
--angle: -154deg;
|
||
--delay: -1.4s;
|
||
--ray-height: 1.2px;
|
||
--ray: rgba(255, 196, 87, 0.64);
|
||
"
|
||
></span>
|
||
<span
|
||
style="
|
||
--angle: -132deg;
|
||
--delay: -0.7s;
|
||
--ray-height: 1px;
|
||
--ray: rgba(88, 156, 255, 0.54);
|
||
"
|
||
></span>
|
||
<span
|
||
style="
|
||
--angle: -109deg;
|
||
--delay: -2.3s;
|
||
--ray-height: 1.4px;
|
||
--ray: rgba(207, 116, 255, 0.48);
|
||
"
|
||
></span>
|
||
<span
|
||
style="
|
||
--angle: -88deg;
|
||
--delay: -1s;
|
||
--ray-height: 1px;
|
||
--ray: rgba(255, 255, 255, 0.58);
|
||
"
|
||
></span>
|
||
<span
|
||
style="
|
||
--angle: -63deg;
|
||
--delay: -2.8s;
|
||
--ray-height: 1.7px;
|
||
--ray: rgba(255, 91, 0, 0.74);
|
||
"
|
||
></span>
|
||
<span
|
||
style="
|
||
--angle: -38deg;
|
||
--delay: -0.4s;
|
||
--ray-height: 1.1px;
|
||
--ray: rgba(94, 217, 255, 0.5);
|
||
"
|
||
></span>
|
||
<span
|
||
style="
|
||
--angle: -14deg;
|
||
--delay: -1.9s;
|
||
--ray-height: 1.5px;
|
||
--ray: rgba(255, 146, 84, 0.68);
|
||
"
|
||
></span>
|
||
<span
|
||
style="
|
||
--angle: 11deg;
|
||
--delay: -0.9s;
|
||
--ray-height: 1px;
|
||
--ray: rgba(255, 255, 255, 0.5);
|
||
"
|
||
></span>
|
||
<span
|
||
style="
|
||
--angle: 34deg;
|
||
--delay: -2.5s;
|
||
--ray-height: 1.6px;
|
||
--ray: rgba(255, 91, 0, 0.78);
|
||
"
|
||
></span>
|
||
<span
|
||
style="
|
||
--angle: 57deg;
|
||
--delay: -1.2s;
|
||
--ray-height: 1.2px;
|
||
--ray: rgba(96, 133, 255, 0.56);
|
||
"
|
||
></span>
|
||
<span
|
||
style="
|
||
--angle: 82deg;
|
||
--delay: -3.1s;
|
||
--ray-height: 1px;
|
||
--ray: rgba(255, 211, 120, 0.6);
|
||
"
|
||
></span>
|
||
<span
|
||
style="
|
||
--angle: 108deg;
|
||
--delay: -0.2s;
|
||
--ray-height: 1.4px;
|
||
--ray: rgba(184, 91, 255, 0.5);
|
||
"
|
||
></span>
|
||
<span
|
||
style="
|
||
--angle: 136deg;
|
||
--delay: -1.7s;
|
||
--ray-height: 1.7px;
|
||
--ray: rgba(255, 91, 0, 0.82);
|
||
"
|
||
></span>
|
||
<span
|
||
style="
|
||
--angle: 163deg;
|
||
--delay: -2.1s;
|
||
--ray-height: 1px;
|
||
--ray: rgba(255, 255, 255, 0.52);
|
||
"
|
||
></span>
|
||
<span
|
||
style="
|
||
--angle: -169deg;
|
||
--delay: -2.9s;
|
||
--ray-height: 0.9px;
|
||
--ray: rgba(92, 152, 255, 0.5);
|
||
"
|
||
></span>
|
||
<span
|
||
style="
|
||
--angle: -122deg;
|
||
--delay: -1.1s;
|
||
--ray-height: 1.3px;
|
||
--ray: rgba(255, 91, 0, 0.68);
|
||
"
|
||
></span>
|
||
<span
|
||
style="
|
||
--angle: -76deg;
|
||
--delay: -2.6s;
|
||
--ray-height: 0.9px;
|
||
--ray: rgba(255, 255, 255, 0.48);
|
||
"
|
||
></span>
|
||
<span
|
||
style="
|
||
--angle: -24deg;
|
||
--delay: -0.6s;
|
||
--ray-height: 1.2px;
|
||
--ray: rgba(255, 174, 92, 0.62);
|
||
"
|
||
></span>
|
||
<span
|
||
style="
|
||
--angle: 24deg;
|
||
--delay: -2.2s;
|
||
--ray-height: 0.9px;
|
||
--ray: rgba(116, 211, 255, 0.48);
|
||
"
|
||
></span>
|
||
<span
|
||
style="
|
||
--angle: 71deg;
|
||
--delay: -1.6s;
|
||
--ray-height: 1.3px;
|
||
--ray: rgba(255, 91, 0, 0.72);
|
||
"
|
||
></span>
|
||
<span
|
||
style="
|
||
--angle: 119deg;
|
||
--delay: -3.3s;
|
||
--ray-height: 0.9px;
|
||
--ray: rgba(191, 103, 255, 0.46);
|
||
"
|
||
></span>
|
||
<span
|
||
style="
|
||
--angle: 151deg;
|
||
--delay: -0.8s;
|
||
--ray-height: 1.2px;
|
||
--ray: rgba(255, 255, 255, 0.5);
|
||
"
|
||
></span>
|
||
</div>
|
||
<div class="app-boot-card">
|
||
<span class="app-boot-mark" data-official-site-link>
|
||
<img
|
||
class="app-boot-mark__base"
|
||
src="./icons/icon-96x96.png"
|
||
alt=""
|
||
aria-hidden="true"
|
||
/>
|
||
<img
|
||
class="app-boot-mark__fill"
|
||
src="./icons/icon-96x96.png"
|
||
alt="TrueGrowth logo"
|
||
/>
|
||
</span>
|
||
<p class="app-boot-title">
|
||
<span class="app-boot-fill-text">TrueGrowth</span>
|
||
</p>
|
||
<p class="app-boot-slogan">
|
||
<span class="app-boot-fill-text">让 AI 接管增长</span>
|
||
</p>
|
||
<p class="app-boot-status">
|
||
<span class="app-boot-status-title" data-app-boot-title
|
||
>正在唤起 AI 工作台</span
|
||
>
|
||
<span class="app-boot-tip" data-app-boot-tip
|
||
>正在加载桌面端创作引擎...</span
|
||
>
|
||
</p>
|
||
<div
|
||
class="app-boot-progress"
|
||
role="progressbar"
|
||
aria-valuemin="0"
|
||
aria-valuemax="100"
|
||
aria-valuenow="0"
|
||
data-app-boot-progress
|
||
>
|
||
<span class="app-boot-progress-track" aria-hidden="true">
|
||
<span
|
||
class="app-boot-progress-fill"
|
||
data-app-boot-progress-fill
|
||
></span>
|
||
</span>
|
||
<span class="app-boot-progress-value" data-app-boot-progress-value>
|
||
0%
|
||
</span>
|
||
</div>
|
||
<div class="app-boot-node-row" aria-hidden="true">
|
||
<span></span>
|
||
<span></span>
|
||
<span></span>
|
||
<span></span>
|
||
<span></span>
|
||
</div>
|
||
<div class="app-boot-note" data-app-boot-note>TrueGrowth Studio</div>
|
||
</div>
|
||
</div>
|
||
<main id="seo-content">
|
||
<section class="seo-hero">
|
||
<div class="seo-hero-copy">
|
||
<h1>TrueGrowth 桌面 AI 创作工作台</h1>
|
||
<p>
|
||
TrueGrowth 是一个面向图片、视频、数字人和声音创作的 AI
|
||
应用平台。你可以在同一个工作台里调度云端模型、管理素材与任务,并把结果继续送入无限画布。
|
||
</p>
|
||
<p>
|
||
相比把提示词、素材库、知识卡片和任务列表拆散在多个工具里,TrueGrowth
|
||
更适合需要边生成、边编辑、边整理的人。首页即工作台,打开就能开始产出。
|
||
</p>
|
||
<ul class="seo-badges">
|
||
<li>AI 图片生成</li>
|
||
<li>流程图</li>
|
||
<li>思维导图</li>
|
||
<li>画布工作区</li>
|
||
<li>素材管理</li>
|
||
</ul>
|
||
</div>
|
||
<aside class="seo-panel">
|
||
<h2>从这些入口开始</h2>
|
||
<p>
|
||
如果你想先了解产品能力,可以查看 TrueGrowth
|
||
介绍页;如果已经准备创作,直接进入工作台即可。
|
||
</p>
|
||
<ul class="seo-card-links">
|
||
<li>
|
||
<a href="/home.html"
|
||
>TrueGrowth 介绍页:了解 AI 画布、作品流和核心能力</a
|
||
>
|
||
</li>
|
||
<li>
|
||
<a href="/user-manual/index.html"
|
||
>用户手册:查看完整功能与操作路径</a
|
||
>
|
||
</li>
|
||
</ul>
|
||
</aside>
|
||
</section>
|
||
|
||
<section class="seo-section">
|
||
<h2>为什么适合搜索用户</h2>
|
||
<div class="seo-grid">
|
||
<article>
|
||
<h3>不是单点工具</h3>
|
||
<p>
|
||
TrueGrowth
|
||
把生成、编辑、整理和交付放在一起,适合需要持续推进任务,而不是只做一次性生成的人。
|
||
</p>
|
||
</article>
|
||
<article>
|
||
<h3>图文结构兼容</h3>
|
||
<p>
|
||
既能做 AI
|
||
图片生成,也能用流程图和思维导图承接方案、脑图、产品结构和知识整理。
|
||
</p>
|
||
</article>
|
||
<article>
|
||
<h3>更少切换</h3>
|
||
<p>
|
||
素材、知识、任务和画布相互打通,减少从聊天窗口、白板、网盘和文档来回跳转的摩擦。
|
||
</p>
|
||
</article>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="seo-section">
|
||
<h2>常见使用场景</h2>
|
||
<p>
|
||
适合做营销海报、社媒配图、灵感整理、产品方案梳理、业务流程图、项目脑图,以及需要边生成边归档的工作流。
|
||
</p>
|
||
<p class="seo-inline-links">
|
||
<a href="/user-manual/ai-generation-image-generation.html"
|
||
>AI 图片生成指南</a
|
||
>
|
||
<a href="/user-manual/ai-generation-video-generation.html"
|
||
>AI 视频生成指南</a
|
||
>
|
||
<a href="/user-manual/drawing-flowchart.html">流程图指南</a>
|
||
<a href="/user-manual/drawing-mindmap.html">思维导图指南</a>
|
||
<a href="/versions.html">版本日志</a>
|
||
</p>
|
||
</section>
|
||
|
||
<section class="seo-section">
|
||
<h2>常见问题</h2>
|
||
<ul class="seo-faq-list">
|
||
<li>
|
||
<h3>TrueGrowth 是什么?</h3>
|
||
<p>
|
||
它是一个以画布工作区为核心的 AI 应用平台,支持 AI
|
||
图片生成、流程图、思维导图、素材管理与任务推进。
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<h3>TrueGrowth 适合谁?</h3>
|
||
<p>
|
||
适合需要把创意生成、结构整理和任务交付放在一起的人,比如内容创作者、产品经理、运营、设计和独立开发者。
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<h3>TrueGrowth 支持哪些图示能力?</h3>
|
||
<p>
|
||
支持流程图、思维导图、文本、形状、自由绘制,也支持通过自然语言或
|
||
Mermaid 语法快速生成结构化图示。
|
||
</p>
|
||
</li>
|
||
</ul>
|
||
</section>
|
||
</main>
|
||
<script>
|
||
(function () {
|
||
var bootReady = false;
|
||
var bootProgress = 0;
|
||
var simulationStartAt = Date.now();
|
||
var simulationTimer = 0;
|
||
var slowTipTimer = window.setTimeout(function () {
|
||
var tip = document.querySelector('[data-app-boot-tip]');
|
||
if (tip) {
|
||
tip.textContent =
|
||
'首次打开或网络较慢时会多等一会,正在努力准备中...';
|
||
}
|
||
}, 4000);
|
||
var removeTimer = 0;
|
||
var cdnConfigLoadStarted = false;
|
||
var resolveMainEntryReady = null;
|
||
var BOOT_CDN_PACKAGE_NAME = 'aitu-app';
|
||
var BOOT_CDN_BASE_URL = 'https://cdn.jsdelivr.net/npm/';
|
||
var BOOT_LOCAL_RETRY_STORAGE_KEY = '__OPENTU_BOOT_LOCAL_RETRY_AT__';
|
||
var BOOT_IMPORT_RETRY_STORAGE_KEY = '__OPENTU_BOOT_IMPORT_RETRY_AT__';
|
||
var BOOT_LOCAL_RETRY_TTL_MS = 2 * 60 * 1000;
|
||
var forceLocalBootAssets = false;
|
||
window.__OPENTU_START_MAIN_ENTRY__ = new Promise(function (resolve) {
|
||
resolveMainEntryReady = resolve;
|
||
});
|
||
|
||
(function initializeBootLocalRetryFlag() {
|
||
try {
|
||
var retryAtRaw = window.sessionStorage.getItem(
|
||
BOOT_LOCAL_RETRY_STORAGE_KEY
|
||
);
|
||
if (!retryAtRaw) {
|
||
return;
|
||
}
|
||
|
||
var retryAt = Number(retryAtRaw);
|
||
if (
|
||
!isFinite(retryAt) ||
|
||
Date.now() - retryAt > BOOT_LOCAL_RETRY_TTL_MS
|
||
) {
|
||
window.sessionStorage.removeItem(BOOT_LOCAL_RETRY_STORAGE_KEY);
|
||
return;
|
||
}
|
||
|
||
forceLocalBootAssets = true;
|
||
} catch (_error) {
|
||
forceLocalBootAssets = false;
|
||
}
|
||
})();
|
||
|
||
function clearBootLocalRetryFlag() {
|
||
forceLocalBootAssets = false;
|
||
try {
|
||
window.sessionStorage.removeItem(BOOT_LOCAL_RETRY_STORAGE_KEY);
|
||
window.sessionStorage.removeItem(BOOT_IMPORT_RETRY_STORAGE_KEY);
|
||
} catch (_error) {
|
||
// ignore sessionStorage cleanup failures
|
||
}
|
||
}
|
||
|
||
function scheduleBootSelfHealReload() {
|
||
if (isBootLocalDevelopment() || forceLocalBootAssets) {
|
||
return false;
|
||
}
|
||
|
||
try {
|
||
window.sessionStorage.setItem(
|
||
BOOT_LOCAL_RETRY_STORAGE_KEY,
|
||
String(Date.now())
|
||
);
|
||
} catch (_error) {
|
||
return false;
|
||
}
|
||
|
||
var tip = document.querySelector('[data-app-boot-tip]');
|
||
if (tip) {
|
||
tip.textContent = '首屏资源回退中,正在自动重试...';
|
||
}
|
||
window.location.reload();
|
||
return true;
|
||
}
|
||
|
||
function isRecoverableBootImportError(error) {
|
||
var message =
|
||
error && typeof error.message === 'string'
|
||
? error.message
|
||
: String(error || '');
|
||
|
||
return (
|
||
/Failed to fetch dynamically imported module/i.test(message) ||
|
||
/Importing a module script failed/i.test(message) ||
|
||
/Loading chunk [\w-]+ failed/i.test(message) ||
|
||
/ChunkLoadError/i.test(message)
|
||
);
|
||
}
|
||
|
||
function hasBootImportRetryParam() {
|
||
try {
|
||
return new URLSearchParams(window.location.search).has(
|
||
'_lazy_chunk_retry'
|
||
);
|
||
} catch (_error) {
|
||
return window.location.search.indexOf('_lazy_chunk_retry=') !== -1;
|
||
}
|
||
}
|
||
|
||
function createBootImportRecoveryUrl() {
|
||
var recoveryUrl = new URL(window.location.href);
|
||
recoveryUrl.searchParams.set('_lazy_chunk_retry', '1');
|
||
recoveryUrl.searchParams.set('_t', String(Date.now()));
|
||
return recoveryUrl.toString();
|
||
}
|
||
|
||
function prepareBootImportRecovery() {
|
||
var tasks = [];
|
||
|
||
try {
|
||
if (navigator.serviceWorker && navigator.serviceWorker.controller) {
|
||
navigator.serviceWorker.controller.postMessage({
|
||
type: 'RECOVER_DYNAMIC_IMPORT_FAILURE',
|
||
appVersion: getBootAppVersion(),
|
||
moduleKey: 'boot-main-entry',
|
||
});
|
||
}
|
||
} catch (_error) {
|
||
// ignore best-effort SW notification failures
|
||
}
|
||
|
||
try {
|
||
if (window.caches && typeof window.caches.keys === 'function') {
|
||
tasks.push(
|
||
window.caches.keys().then(function (cacheNames) {
|
||
return Promise.all(
|
||
cacheNames
|
||
.filter(function (name) {
|
||
return name.indexOf('drawnix-static-v') === 0;
|
||
})
|
||
.map(function (name) {
|
||
return window.caches.delete(name);
|
||
})
|
||
);
|
||
})
|
||
);
|
||
}
|
||
} catch (_error) {
|
||
// ignore cache cleanup failures
|
||
}
|
||
|
||
return Promise.allSettled(tasks);
|
||
}
|
||
|
||
window.__OPENTU_BOOT_DYNAMIC_IMPORT_RECOVERY__ = function (error) {
|
||
if (
|
||
!isRecoverableBootImportError(error) ||
|
||
isBootLocalDevelopment()
|
||
) {
|
||
return false;
|
||
}
|
||
|
||
try {
|
||
var lastRetryAt = Number(
|
||
window.sessionStorage.getItem(BOOT_IMPORT_RETRY_STORAGE_KEY)
|
||
);
|
||
if (
|
||
isFinite(lastRetryAt) &&
|
||
Date.now() - lastRetryAt <= BOOT_LOCAL_RETRY_TTL_MS
|
||
) {
|
||
return false;
|
||
}
|
||
window.sessionStorage.setItem(
|
||
BOOT_IMPORT_RETRY_STORAGE_KEY,
|
||
String(Date.now())
|
||
);
|
||
} catch (_error) {
|
||
if (hasBootImportRetryParam()) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
var recoveryUrl = createBootImportRecoveryUrl();
|
||
var didReload = false;
|
||
var reload = function () {
|
||
if (didReload) {
|
||
return;
|
||
}
|
||
didReload = true;
|
||
window.location.replace(recoveryUrl);
|
||
};
|
||
|
||
var tip = document.querySelector('[data-app-boot-tip]');
|
||
if (tip) {
|
||
tip.textContent = '启动资源已更新,正在自动刷新恢复...';
|
||
}
|
||
|
||
window.setTimeout(reload, 700);
|
||
prepareBootImportRecovery().then(reload, reload);
|
||
return true;
|
||
};
|
||
|
||
function clearBootTimers() {
|
||
if (slowTipTimer) {
|
||
window.clearTimeout(slowTipTimer);
|
||
slowTipTimer = 0;
|
||
}
|
||
if (removeTimer) {
|
||
window.clearTimeout(removeTimer);
|
||
removeTimer = 0;
|
||
}
|
||
if (simulationTimer) {
|
||
window.clearInterval(simulationTimer);
|
||
simulationTimer = 0;
|
||
}
|
||
}
|
||
|
||
function getBootRoot() {
|
||
return document.getElementById('app-boot-loading');
|
||
}
|
||
|
||
function getBootAppVersion() {
|
||
var versionMeta = document.querySelector('meta[name="app-version"]');
|
||
return versionMeta
|
||
? versionMeta.getAttribute('content') || '0.0.0'
|
||
: '0.0.0';
|
||
}
|
||
|
||
function isBootLocalDevelopment() {
|
||
return (
|
||
window.location.hostname === 'localhost' ||
|
||
window.location.hostname === '127.0.0.1' ||
|
||
window.location.hostname.endsWith('.localhost') ||
|
||
window.location.protocol === 'file:' ||
|
||
window.__OPENTU_DESKTOP__ === true ||
|
||
!!window.trueGrowthRuntime
|
||
);
|
||
}
|
||
|
||
function toPreferredBootAssetUrl(localPath) {
|
||
if (!localPath || isBootLocalDevelopment() || forceLocalBootAssets) {
|
||
return localPath;
|
||
}
|
||
|
||
var normalizedPath = String(localPath).trim();
|
||
|
||
if (/^https?:\/\//i.test(normalizedPath)) {
|
||
return normalizedPath;
|
||
}
|
||
|
||
normalizedPath = normalizedPath
|
||
.replace(/^\.?\//, '')
|
||
.replace(/^npm\/aitu-app@[^/]+\//, '')
|
||
.replace(/^aitu-app@[^/]+\//, '');
|
||
|
||
return (
|
||
BOOT_CDN_BASE_URL +
|
||
BOOT_CDN_PACKAGE_NAME +
|
||
'@' +
|
||
getBootAppVersion() +
|
||
'/' +
|
||
normalizedPath
|
||
);
|
||
}
|
||
|
||
function isManagedBootFallbackTarget(target) {
|
||
return !!(
|
||
target &&
|
||
typeof target.getAttribute === 'function' &&
|
||
target.getAttribute('data-cdn-fallback-managed') === '1'
|
||
);
|
||
}
|
||
|
||
function pinManagedBootAssetsToLocal() {
|
||
if (!isBootLocalDevelopment()) {
|
||
return;
|
||
}
|
||
|
||
var targets = document.querySelectorAll(
|
||
'[data-cdn-fallback-managed="1"]'
|
||
);
|
||
targets.forEach(function (target) {
|
||
var localSrc = target.getAttribute('data-local-src');
|
||
var localHref = target.getAttribute('data-local-href');
|
||
|
||
if (localSrc && target.getAttribute('src') !== localSrc) {
|
||
target.setAttribute('src', localSrc);
|
||
}
|
||
|
||
if (localHref && target.getAttribute('href') !== localHref) {
|
||
target.setAttribute('href', localHref);
|
||
}
|
||
});
|
||
}
|
||
|
||
function createLocalBootAssetNode(target, localUrl) {
|
||
var replacement = target.cloneNode(false);
|
||
replacement.setAttribute('data-fallback-attempted', '1');
|
||
replacement.removeAttribute('onerror');
|
||
|
||
var failureMessage =
|
||
target.tagName === 'SCRIPT'
|
||
? '首屏脚本加载失败,请刷新页面后重试。'
|
||
: target.getAttribute('rel') === 'stylesheet'
|
||
? '首屏样式加载失败,请刷新页面后重试。'
|
||
: '首屏资源加载失败,请刷新页面后重试。';
|
||
|
||
if (target.tagName === 'SCRIPT') {
|
||
replacement.setAttribute('src', localUrl);
|
||
replacement.onerror = function () {
|
||
markError(failureMessage);
|
||
};
|
||
return replacement;
|
||
}
|
||
|
||
replacement.setAttribute('href', localUrl);
|
||
replacement.onerror = function () {
|
||
markError(failureMessage);
|
||
};
|
||
return replacement;
|
||
}
|
||
|
||
window.__OPENTU_BOOT_ASSET_FALLBACK__ = function (target) {
|
||
if (!target) {
|
||
return false;
|
||
}
|
||
|
||
var localUrl =
|
||
target.getAttribute('data-local-src') ||
|
||
target.getAttribute('data-local-href');
|
||
|
||
if (!localUrl) {
|
||
return false;
|
||
}
|
||
|
||
if (target.getAttribute('data-fallback-attempted') === '1') {
|
||
markError(
|
||
target.tagName === 'SCRIPT'
|
||
? '首屏脚本加载失败,请刷新页面后重试。'
|
||
: target.getAttribute('rel') === 'stylesheet'
|
||
? '首屏样式加载失败,请刷新页面后重试。'
|
||
: '首屏资源加载失败,请刷新页面后重试。'
|
||
);
|
||
return true;
|
||
}
|
||
|
||
if (
|
||
target.tagName === 'SCRIPT' &&
|
||
target.getAttribute('type') === 'module' &&
|
||
scheduleBootSelfHealReload()
|
||
) {
|
||
return true;
|
||
}
|
||
|
||
var replacement = createLocalBootAssetNode(target, localUrl);
|
||
if (target.parentNode) {
|
||
target.parentNode.replaceChild(replacement, target);
|
||
}
|
||
return true;
|
||
};
|
||
|
||
function appendManagedBootScript(localPath, onload, onfinalerror) {
|
||
var preferredUrl = toPreferredBootAssetUrl(localPath);
|
||
var script = document.createElement('script');
|
||
script.async = true;
|
||
script.setAttribute('data-cdn-fallback-managed', '1');
|
||
script.setAttribute('data-local-src', localPath);
|
||
|
||
var handleFinalFailure = function () {
|
||
if (typeof onfinalerror === 'function') {
|
||
onfinalerror();
|
||
}
|
||
};
|
||
|
||
if (!preferredUrl || preferredUrl === localPath) {
|
||
script.src = localPath;
|
||
script.onload = onload;
|
||
script.onerror = handleFinalFailure;
|
||
document.head.appendChild(script);
|
||
return;
|
||
}
|
||
|
||
script.src = preferredUrl;
|
||
script.onload = onload;
|
||
script.onerror = function () {
|
||
var localScript = document.createElement('script');
|
||
localScript.async = true;
|
||
localScript.src = localPath;
|
||
localScript.setAttribute('data-cdn-fallback-managed', '1');
|
||
localScript.setAttribute('data-fallback-attempted', '1');
|
||
localScript.onload = onload;
|
||
localScript.onerror = handleFinalFailure;
|
||
if (script.parentNode) {
|
||
script.parentNode.removeChild(script);
|
||
}
|
||
document.head.appendChild(localScript);
|
||
};
|
||
|
||
document.head.appendChild(script);
|
||
}
|
||
|
||
pinManagedBootAssetsToLocal();
|
||
|
||
function clampProgress(value) {
|
||
if (!isFinite(value)) {
|
||
return bootProgress;
|
||
}
|
||
return Math.max(0, Math.min(100, value));
|
||
}
|
||
|
||
function renderProgress() {
|
||
var progressRoot = document.querySelector('[data-app-boot-progress]');
|
||
var progressFill = document.querySelector(
|
||
'[data-app-boot-progress-fill]'
|
||
);
|
||
var progressValue = document.querySelector(
|
||
'[data-app-boot-progress-value]'
|
||
);
|
||
var safeProgress = Math.round(clampProgress(bootProgress));
|
||
|
||
if (progressRoot) {
|
||
progressRoot.setAttribute('aria-valuenow', String(safeProgress));
|
||
}
|
||
document.documentElement.style.setProperty(
|
||
'--app-boot-progress',
|
||
safeProgress + '%'
|
||
);
|
||
document.documentElement.style.setProperty(
|
||
'--app-boot-flight-speed',
|
||
Math.max(0.52, 3.2 - safeProgress * 0.027).toFixed(2) + 's'
|
||
);
|
||
document.documentElement.style.setProperty(
|
||
'--app-boot-flight-blur',
|
||
(2 + safeProgress * 0.018).toFixed(2) + 'px'
|
||
);
|
||
document.documentElement.style.setProperty(
|
||
'--app-boot-flight-opacity',
|
||
Math.min(0.9, 0.48 + safeProgress * 0.004).toFixed(2)
|
||
);
|
||
if (progressFill) {
|
||
progressFill.style.width = safeProgress + '%';
|
||
}
|
||
if (progressValue) {
|
||
progressValue.textContent = safeProgress + '%';
|
||
}
|
||
}
|
||
|
||
function startSimulatedProgress() {
|
||
if (simulationTimer) {
|
||
return;
|
||
}
|
||
|
||
simulationTimer = window.setInterval(function () {
|
||
if (bootReady) {
|
||
window.clearInterval(simulationTimer);
|
||
simulationTimer = 0;
|
||
return;
|
||
}
|
||
|
||
var elapsed = Date.now() - simulationStartAt;
|
||
var simulatedProgress = Math.min(82, (elapsed / 4200) * 82);
|
||
if (simulatedProgress > bootProgress) {
|
||
bootProgress = simulatedProgress;
|
||
renderProgress();
|
||
}
|
||
|
||
if (simulatedProgress >= 82) {
|
||
window.clearInterval(simulationTimer);
|
||
simulationTimer = 0;
|
||
}
|
||
}, 60);
|
||
}
|
||
|
||
function setProgress(value, options) {
|
||
if (bootReady) {
|
||
return;
|
||
}
|
||
|
||
if (typeof value === 'number' && isFinite(value)) {
|
||
bootProgress = Math.max(bootProgress, clampProgress(value));
|
||
}
|
||
|
||
var title = document.querySelector('[data-app-boot-title]');
|
||
var tip = document.querySelector('[data-app-boot-tip]');
|
||
var note = document.querySelector('[data-app-boot-note]');
|
||
|
||
if (options && typeof options.title === 'string' && title) {
|
||
title.textContent = options.title;
|
||
}
|
||
if (options && typeof options.tip === 'string' && tip) {
|
||
tip.textContent = options.tip;
|
||
}
|
||
if (options && typeof options.note === 'string' && note) {
|
||
note.textContent = options.note;
|
||
}
|
||
|
||
renderProgress();
|
||
}
|
||
|
||
function markReady() {
|
||
if (bootReady) {
|
||
return;
|
||
}
|
||
document.documentElement.classList.add('app-ready');
|
||
clearBootLocalRetryFlag();
|
||
setProgress(100, {
|
||
tip: '启动完成,正在进入工作台...',
|
||
});
|
||
bootReady = true;
|
||
clearBootTimers();
|
||
var bootRoot = getBootRoot();
|
||
if (!bootRoot) {
|
||
return;
|
||
}
|
||
bootRoot.classList.add('is-leaving');
|
||
var removeBootRoot = function () {
|
||
if (bootRoot.parentNode) {
|
||
bootRoot.parentNode.removeChild(bootRoot);
|
||
}
|
||
};
|
||
bootRoot.addEventListener('transitionend', removeBootRoot, {
|
||
once: true,
|
||
});
|
||
removeTimer = window.setTimeout(removeBootRoot, 360);
|
||
}
|
||
|
||
function markError(message) {
|
||
if (bootReady) {
|
||
return;
|
||
}
|
||
setProgress(100);
|
||
clearBootTimers();
|
||
document.documentElement.classList.add('app-boot-error');
|
||
var title = document.querySelector('[data-app-boot-title]');
|
||
var tip = document.querySelector('[data-app-boot-tip]');
|
||
if (title) {
|
||
title.textContent = '启动中遇到问题';
|
||
}
|
||
if (tip) {
|
||
tip.textContent = message || '资源加载失败,请刷新页面后重试。';
|
||
}
|
||
}
|
||
|
||
window.__OPENTU_BOOT__ = {
|
||
markReady: markReady,
|
||
markError: markError,
|
||
setProgress: setProgress,
|
||
};
|
||
|
||
function getRuntimeCDNPreference() {
|
||
var preference = window.__OPENTU_CDN__ || window.__AITU_CDN__;
|
||
if (!preference || typeof preference !== 'object') {
|
||
return null;
|
||
}
|
||
|
||
var cdn = preference.cdn;
|
||
if (cdn !== 'jsdelivr' && cdn !== 'unpkg' && cdn !== 'local') {
|
||
return null;
|
||
}
|
||
|
||
return {
|
||
cdn: cdn,
|
||
latency:
|
||
typeof preference.latency === 'number' &&
|
||
isFinite(preference.latency)
|
||
? Math.max(0, preference.latency)
|
||
: 0,
|
||
timestamp:
|
||
typeof preference.timestamp === 'number' &&
|
||
isFinite(preference.timestamp) &&
|
||
preference.timestamp > 0
|
||
? preference.timestamp
|
||
: Date.now(),
|
||
};
|
||
}
|
||
|
||
function postCDNPreferenceToRegistration(registration, preference) {
|
||
if (!registration || !preference) {
|
||
return;
|
||
}
|
||
|
||
var payload = {
|
||
type: 'SW_CDN_SET_PREFERENCE',
|
||
cdn: preference.cdn,
|
||
latency: preference.latency,
|
||
timestamp: preference.timestamp,
|
||
version: (function () {
|
||
var versionMeta = document.querySelector(
|
||
'meta[name="app-version"]'
|
||
);
|
||
return versionMeta
|
||
? versionMeta.getAttribute('content') || '0.0.0'
|
||
: '0.0.0';
|
||
})(),
|
||
};
|
||
|
||
var targets = [];
|
||
var maybeWorkers = [
|
||
navigator.serviceWorker.controller,
|
||
registration.active,
|
||
registration.waiting,
|
||
registration.installing,
|
||
];
|
||
|
||
for (var i = 0; i < maybeWorkers.length; i++) {
|
||
var worker = maybeWorkers[i];
|
||
if (worker && targets.indexOf(worker) === -1) {
|
||
targets.push(worker);
|
||
}
|
||
}
|
||
|
||
for (var j = 0; j < targets.length; j++) {
|
||
targets[j].postMessage(payload);
|
||
}
|
||
}
|
||
|
||
function syncEarlyCDNPreference(registration) {
|
||
var cachedPreference = getRuntimeCDNPreference();
|
||
if (cachedPreference) {
|
||
postCDNPreferenceToRegistration(registration, cachedPreference);
|
||
}
|
||
|
||
var api = window.__OPENTU_CDN_API__ || window.__AITU_CDN_API__;
|
||
if (!api || typeof api.selectBestCDN !== 'function') {
|
||
return;
|
||
}
|
||
|
||
api
|
||
.selectBestCDN()
|
||
.then(function (preference) {
|
||
if (!preference || typeof preference !== 'object') {
|
||
return;
|
||
}
|
||
|
||
window.__OPENTU_CDN__ = preference;
|
||
window.__AITU_CDN__ = preference;
|
||
postCDNPreferenceToRegistration(registration, {
|
||
cdn: preference.cdn,
|
||
latency:
|
||
typeof preference.latency === 'number' &&
|
||
isFinite(preference.latency)
|
||
? Math.max(0, preference.latency)
|
||
: 0,
|
||
timestamp:
|
||
typeof preference.timestamp === 'number' &&
|
||
isFinite(preference.timestamp) &&
|
||
preference.timestamp > 0
|
||
? preference.timestamp
|
||
: Date.now(),
|
||
});
|
||
})
|
||
.catch(function (error) {
|
||
console.warn('[Boot] Early CDN preference sync failed:', error);
|
||
});
|
||
}
|
||
|
||
function shouldEnableEarlyServiceWorkerBootstrap() {
|
||
try {
|
||
var swParam = new URLSearchParams(window.location.search).get('sw');
|
||
if (swParam === '0') {
|
||
return false;
|
||
}
|
||
return true;
|
||
} catch {
|
||
if (window.location.search.indexOf('sw=0') !== -1) {
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
}
|
||
|
||
function cleanupExplicitlyDisabledServiceWorker() {
|
||
if (!('serviceWorker' in navigator)) {
|
||
return;
|
||
}
|
||
|
||
navigator.serviceWorker
|
||
.getRegistration()
|
||
.then(function (registration) {
|
||
if (registration) {
|
||
return registration.unregister();
|
||
}
|
||
return false;
|
||
})
|
||
.catch(function (error) {
|
||
console.warn(
|
||
'[Boot] Disabled service worker cleanup failed:',
|
||
error
|
||
);
|
||
});
|
||
}
|
||
|
||
function setupEarlyServiceWorkerBootstrap() {
|
||
if (!('serviceWorker' in navigator)) {
|
||
return;
|
||
}
|
||
|
||
if (!shouldEnableEarlyServiceWorkerBootstrap()) {
|
||
cleanupExplicitlyDisabledServiceWorker();
|
||
setProgress(52, {
|
||
tip: '已按 URL 参数跳过离线缓存服务,正在直接启动工作台...',
|
||
source: 'phase',
|
||
});
|
||
return;
|
||
}
|
||
|
||
if (!window.__OPENTU_SW_REGISTRATION_PROMISE__) {
|
||
setProgress(undefined, {
|
||
tip: '正在连接启动缓存服务...',
|
||
source: 'phase',
|
||
});
|
||
|
||
window.__OPENTU_SW_REGISTRATION_PROMISE__ = navigator.serviceWorker
|
||
.register('./sw.js')
|
||
.then(function (registration) {
|
||
syncEarlyCDNPreference(registration);
|
||
setProgress(54, {
|
||
tip: '启动缓存服务已连接,正在准备资源清单...',
|
||
source: 'phase',
|
||
});
|
||
|
||
if (
|
||
registration.active &&
|
||
!registration.installing &&
|
||
!registration.waiting
|
||
) {
|
||
setProgress(58, {
|
||
tip: '启动缓存服务已就绪,正在恢复工作台状态...',
|
||
source: 'phase',
|
||
});
|
||
}
|
||
|
||
return registration;
|
||
})
|
||
.catch(function (error) {
|
||
console.warn(
|
||
'[Boot] Early service worker registration failed:',
|
||
error
|
||
);
|
||
setProgress(52, {
|
||
tip: '离线加速未启用,正在直接启动工作台...',
|
||
source: 'phase',
|
||
});
|
||
return null;
|
||
});
|
||
} else {
|
||
window.__OPENTU_SW_REGISTRATION_PROMISE__.then(function (
|
||
registration
|
||
) {
|
||
syncEarlyCDNPreference(registration);
|
||
});
|
||
}
|
||
}
|
||
|
||
function loadCDNConfigAndBootstrap() {
|
||
if (cdnConfigLoadStarted) {
|
||
return;
|
||
}
|
||
cdnConfigLoadStarted = true;
|
||
|
||
appendManagedBootScript(
|
||
'./cdn-config.js',
|
||
function () {
|
||
setupEarlyServiceWorkerBootstrap();
|
||
if (resolveMainEntryReady) {
|
||
resolveMainEntryReady();
|
||
resolveMainEntryReady = null;
|
||
}
|
||
},
|
||
function () {
|
||
console.warn(
|
||
'[Boot] CDN config load failed, fallback to local bootstrap'
|
||
);
|
||
setupEarlyServiceWorkerBootstrap();
|
||
if (resolveMainEntryReady) {
|
||
resolveMainEntryReady();
|
||
resolveMainEntryReady = null;
|
||
}
|
||
}
|
||
);
|
||
}
|
||
|
||
renderProgress();
|
||
startSimulatedProgress();
|
||
loadCDNConfigAndBootstrap();
|
||
|
||
window.addEventListener(
|
||
'error',
|
||
function (event) {
|
||
var target = event && event.target;
|
||
if (target && target !== window && (target.src || target.href)) {
|
||
if (isManagedBootFallbackTarget(target)) {
|
||
return;
|
||
}
|
||
var resourceUrl = target.src || target.href || '';
|
||
var resourceName = resourceUrl.split('/').pop() || '资源文件';
|
||
markError(resourceName + ' 加载失败,请刷新页面后重试。');
|
||
return;
|
||
}
|
||
|
||
var message =
|
||
event && typeof event.message === 'string' && event.message.trim()
|
||
? event.message.trim()
|
||
: '启动脚本执行失败,请刷新页面后重试。';
|
||
markError(message);
|
||
},
|
||
{ once: true }
|
||
);
|
||
|
||
window.addEventListener(
|
||
'unhandledrejection',
|
||
function (event) {
|
||
var reason = event && event.reason;
|
||
var message =
|
||
reason && typeof reason.message === 'string'
|
||
? reason.message
|
||
: typeof reason === 'string'
|
||
? reason
|
||
: '启动过程中发生异常,请刷新页面后重试。';
|
||
markError(message);
|
||
},
|
||
{ once: true }
|
||
);
|
||
})();
|
||
</script>
|
||
<div id="root"></div>
|
||
<script type="module">
|
||
(async function () {
|
||
try {
|
||
await (window.__OPENTU_START_MAIN_ENTRY__ || Promise.resolve());
|
||
await import('./src/main.tsx');
|
||
} catch (error) {
|
||
if (
|
||
window.__OPENTU_BOOT_DYNAMIC_IMPORT_RECOVERY__ &&
|
||
window.__OPENTU_BOOT_DYNAMIC_IMPORT_RECOVERY__(error)
|
||
) {
|
||
return;
|
||
}
|
||
throw error;
|
||
}
|
||
})();
|
||
</script>
|
||
</body>
|
||
<!-- PostHog Analytics -->
|
||
<!-- 本地开发环境(localhost/127.0.0.1)默认不加载,除非 URL 带 report=1 参数 -->
|
||
<script>
|
||
(function () {
|
||
var isLocalDev =
|
||
window.location.hostname === 'localhost' ||
|
||
window.location.hostname === '127.0.0.1';
|
||
var forceReport =
|
||
new URLSearchParams(window.location.search).get('report') === '1';
|
||
|
||
// 本地开发环境且没有 report=1 参数时,不加载 PostHog
|
||
if (isLocalDev && !forceReport) {
|
||
return;
|
||
}
|
||
|
||
!(function (t, e) {
|
||
var o, n, p, r;
|
||
e.__SV ||
|
||
(window.posthog && window.posthog.__loaded) ||
|
||
((window.posthog = e),
|
||
(e._i = []),
|
||
(e.init = function (i, s, a) {
|
||
function g(t, e) {
|
||
var o = e.split('.');
|
||
2 == o.length && ((t = t[o[0]]), (e = o[1])),
|
||
(t[e] = function () {
|
||
t.push([e].concat(Array.prototype.slice.call(arguments, 0)));
|
||
});
|
||
}
|
||
((p = t.createElement('script')).type = 'text/javascript'),
|
||
(p.crossOrigin = 'anonymous'),
|
||
(p.async = !0),
|
||
(p.src =
|
||
s.api_host.replace('.i.posthog.com', '-assets.i.posthog.com') +
|
||
'/static/array.js'),
|
||
(r = t.getElementsByTagName('script')[0]).parentNode.insertBefore(
|
||
p,
|
||
r
|
||
);
|
||
var u = e;
|
||
for (
|
||
void 0 !== a ? (u = e[a] = []) : (a = 'posthog'),
|
||
u.people = u.people || [],
|
||
u.toString = function (t) {
|
||
var e = 'posthog';
|
||
return (
|
||
'posthog' !== a && (e += '.' + a), t || (e += ' (stub)'), e
|
||
);
|
||
},
|
||
u.people.toString = function () {
|
||
return u.toString(1) + '.people (stub)';
|
||
},
|
||
o =
|
||
'init Dr Ur fi Lr zr ci Or jr capture Ai calculateEventProperties qr register register_once register_for_session unregister unregister_for_session Jr getFeatureFlag getFeatureFlagPayload isFeatureEnabled reloadFeatureFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSurveysLoaded onSessionId getSurveys getActiveMatchingSurveys renderSurvey displaySurvey cancelPendingSurvey canRenderSurvey canRenderSurveyAsync identify setPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSessionRecording stopSessionRecording sessionRecordingStarted captureException loadToolbar get_property getSessionProperty Gr Br createPersonProfile Vr Cr Kr opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing get_explicit_consent_status is_capturing clear_opt_in_out_capturing Hr debug O Wr getPageViewId captureTraceFeedback captureTraceMetric Rr'.split(
|
||
' '
|
||
),
|
||
n = 0;
|
||
n < o.length;
|
||
n++
|
||
)
|
||
g(u, o[n]);
|
||
e._i.push([i, s, a]);
|
||
}),
|
||
(e.__SV = 1));
|
||
})(document, window.posthog || []);
|
||
var isIgnorableExceptionEvent = function (event) {
|
||
if (!event || event.event !== '$exception') return false;
|
||
var properties = event.properties || {};
|
||
var messages = [];
|
||
|
||
var appendMessages = function (value) {
|
||
if (!value) return;
|
||
if (typeof value === 'string') {
|
||
messages.push(value);
|
||
if (
|
||
value.length < 2000 &&
|
||
(value.charAt(0) === '[' || value.charAt(0) === '{')
|
||
) {
|
||
try {
|
||
appendMessages(JSON.parse(value));
|
||
} catch (_) {}
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (Array.isArray(value)) {
|
||
for (var i = 0; i < value.length; i++) {
|
||
appendMessages(value[i]);
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (typeof value === 'object') {
|
||
if (typeof value.value === 'string') messages.push(value.value);
|
||
if (typeof value.message === 'string') messages.push(value.message);
|
||
if (typeof value.name === 'string') messages.push(value.name);
|
||
}
|
||
};
|
||
|
||
appendMessages(properties.$exception_values);
|
||
appendMessages(properties.$exception_list);
|
||
appendMessages(properties.$exception_message);
|
||
appendMessages(properties.$exception_type);
|
||
appendMessages(properties.$exception_types);
|
||
appendMessages(properties.message);
|
||
appendMessages(properties.name);
|
||
appendMessages(event.exception);
|
||
appendMessages(event.error);
|
||
|
||
var text = messages.join('\n');
|
||
return (
|
||
text.indexOf(
|
||
'ResizeObserver loop completed with undelivered notifications'
|
||
) !== -1 ||
|
||
text.indexOf('ResizeObserver loop limit exceeded') !== -1 ||
|
||
(text.indexOf('WebCodecs API') !== -1 &&
|
||
(text.indexOf('Chrome 94+') !== -1 ||
|
||
text.indexOf('Edge 94+') !== -1)) ||
|
||
(text.indexOf("Failed to execute 'write' on 'Clipboard'") !== -1 &&
|
||
text.indexOf('permissions policy') !== -1) ||
|
||
((text.indexOf('AbortError') !== -1 ||
|
||
text.indexOf('The user aborted a request') !== -1) &&
|
||
(text.indexOf("Failed to execute 'showOpenFilePicker'") !== -1 ||
|
||
text.indexOf("Failed to execute 'showSaveFilePicker'") !== -1 ||
|
||
text.indexOf('The user aborted a request') !== -1)) ||
|
||
text.indexOf('No key or key range specified') !== -1
|
||
);
|
||
};
|
||
|
||
posthog.init('phc_13URtidKwTR9POlwnB153NsN99shRykvPguKWf3vZVn', {
|
||
api_host: 'https://us.i.posthog.com',
|
||
defaults: '2025-11-30',
|
||
person_profiles: 'identified_only',
|
||
// 关闭 Session Recording,避免 rrweb 主线程开销(wheel/setInterval)与 413
|
||
disable_session_recording: true,
|
||
// 限制单次 flush 体积,避免 413 Content Too Large
|
||
rate_limiting: { events_per_second: 10, events_burst_limit: 30 },
|
||
request_queue: { flush_interval_ms: 2000 },
|
||
// 白板 SPA 点击多,关闭 autocapture 减少事件量与请求体
|
||
autocapture: false,
|
||
// 静默网络错误;避免 413 重试风暴干扰
|
||
on_xhr_error: function (err) {
|
||
var code =
|
||
err &&
|
||
(typeof err.statusCode !== 'undefined'
|
||
? err.statusCode
|
||
: err.status);
|
||
void code;
|
||
},
|
||
before_send: function (event) {
|
||
if (isIgnorableExceptionEvent(event)) return false;
|
||
return event;
|
||
},
|
||
});
|
||
})();
|
||
</script>
|
||
</html>
|