feat(site): add standalone marketing website

This commit is contained in:
linshen committed 2026-03-21 21:36:41 +08:00
1 parent 4b0e4c4a8d
commit 9f07be50af
13 files changed
+3666

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
ignore-workspace=true
lockfile-dir=.
+63
View File
@@ -0,0 +1,63 @@
# Prompt Optimizer 官网
本目录是 `always200.com` 对应的独立官网工程。
它与产品站、文档站彻底分离:
- `always200.com`:官网(当前目录)
- `docs.always200.com`:文档站(`mkdocs/`)
- `prompt.always200.com`:产品站(根项目)
## 目录职责
- 技术栈:独立 Vite 静态站
- 部署目标:独立 Vercel 项目
- Vercel Root Directory:`site/`
- 官网不复用根项目的业务代码、UI 组件和构建链路
- 官网保留自己的 `package.json`、`pnpm-lock.yaml`、`vercel.json`
## 本地运行
在 `site/` 目录内执行:
```bash
pnpm install
pnpm dev
```
启动后访问终端输出的地址,默认通常是:
- `http://127.0.0.1:5173/`
如果端口被占用,Vite 会自动切换到其他端口。
## 语言切换
- 默认支持中文与英文两种官网文案
- 首次访问会根据浏览器语言自动选择中文或英文
- 用户手动切换语言后,会记住选择,后续访问优先使用上次手动选择
- 当前官网采用单页静态渲染,不额外引入前端 i18n 框架
## 本地构建与静态预览
```bash
pnpm build
python -m http.server 8011 -d dist
```
访问:
- `http://127.0.0.1:8011/`
## 路由说明
- `/`:官网首页
- `/docs`:跳转到 `https://docs.always200.com/`
- `/docs/:path*`:跳转到 `https://docs.always200.com/:path*`
## 部署
- 独立 Vercel 项目
- Root Directory:`site/`
- 正式域名:`always200.com`
- `www.always200.com` 建议在 Vercel Dashboard 中跳转到 `always200.com`
+27
View File
@@ -0,0 +1,27 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Prompt Optimizer | 提示词优化,不该只靠感觉</title>
<meta
name="description"
content="Prompt Optimizer 把提示词优化、真实测试、结果评估和建议应用放进同一个工作台。"
/>
<link rel="canonical" href="https://always200.com/" />
<link rel="icon" type="image/png" href="/images/favicon.png" />
<meta property="og:title" content="Prompt Optimizer" />
<meta
property="og:description"
content="把提示词优化、真实测试、结果评估和建议应用放进同一个工作台。"
/>
<meta property="og:type" content="website" />
<meta property="og:url" content="https://always200.com/" />
<meta property="og:image" content="https://always200.com/images/og-cover.png" />
<meta name="theme-color" content="#fcfbff" />
<script type="module" src="/src/main.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
+14
View File
@@ -0,0 +1,14 @@
{
"name": "prompt-optimizer-site",
"private": true,
"version": "0.1.0",
"packageManager": "pnpm@10.6.1",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"devDependencies": {
"vite": "^7.1.7"
}
}
+673
View File
@@ -0,0 +1,673 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
devDependencies:
vite:
specifier: ^7.1.7
version: 7.3.1(@types/node@22.19.15)(jiti@2.6.1)(yaml@2.8.2)
packages:
'@esbuild/aix-ppc64@0.27.4':
resolution: {integrity: sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
'@esbuild/android-arm64@0.27.4':
resolution: {integrity: sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
'@esbuild/android-arm@0.27.4':
resolution: {integrity: sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
'@esbuild/android-x64@0.27.4':
resolution: {integrity: sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
'@esbuild/darwin-arm64@0.27.4':
resolution: {integrity: sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-x64@0.27.4':
resolution: {integrity: sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
'@esbuild/freebsd-arm64@0.27.4':
resolution: {integrity: sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-x64@0.27.4':
resolution: {integrity: sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
'@esbuild/linux-arm64@0.27.4':
resolution: {integrity: sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm@0.27.4':
resolution: {integrity: sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
'@esbuild/linux-ia32@0.27.4':
resolution: {integrity: sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
'@esbuild/linux-loong64@0.27.4':
resolution: {integrity: sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
'@esbuild/linux-mips64el@0.27.4':
resolution: {integrity: sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
'@esbuild/linux-ppc64@0.27.4':
resolution: {integrity: sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
'@esbuild/linux-riscv64@0.27.4':
resolution: {integrity: sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
'@esbuild/linux-s390x@0.27.4':
resolution: {integrity: sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
'@esbuild/linux-x64@0.27.4':
resolution: {integrity: sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==}
engines: {node: '>=18'}
cpu: [x64]
os: [linux]
'@esbuild/netbsd-arm64@0.27.4':
resolution: {integrity: sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==}
engines: {node: '>=18'}
cpu: [arm64]
os: [netbsd]
'@esbuild/netbsd-x64@0.27.4':
resolution: {integrity: sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
'@esbuild/openbsd-arm64@0.27.4':
resolution: {integrity: sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openbsd]
'@esbuild/openbsd-x64@0.27.4':
resolution: {integrity: sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
'@esbuild/openharmony-arm64@0.27.4':
resolution: {integrity: sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openharmony]
'@esbuild/sunos-x64@0.27.4':
resolution: {integrity: sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
'@esbuild/win32-arm64@0.27.4':
resolution: {integrity: sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
'@esbuild/win32-ia32@0.27.4':
resolution: {integrity: sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
'@esbuild/win32-x64@0.27.4':
resolution: {integrity: sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
'@rollup/rollup-android-arm-eabi@4.59.0':
resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==}
cpu: [arm]
os: [android]
'@rollup/rollup-android-arm64@4.59.0':
resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==}
cpu: [arm64]
os: [android]
'@rollup/rollup-darwin-arm64@4.59.0':
resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==}
cpu: [arm64]
os: [darwin]
'@rollup/rollup-darwin-x64@4.59.0':
resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==}
cpu: [x64]
os: [darwin]
'@rollup/rollup-freebsd-arm64@4.59.0':
resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==}
cpu: [arm64]
os: [freebsd]
'@rollup/rollup-freebsd-x64@4.59.0':
resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==}
cpu: [x64]
os: [freebsd]
'@rollup/rollup-linux-arm-gnueabihf@4.59.0':
resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==}
cpu: [arm]
os: [linux]
'@rollup/rollup-linux-arm-musleabihf@4.59.0':
resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==}
cpu: [arm]
os: [linux]
'@rollup/rollup-linux-arm64-gnu@4.59.0':
resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==}
cpu: [arm64]
os: [linux]
'@rollup/rollup-linux-arm64-musl@4.59.0':
resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==}
cpu: [arm64]
os: [linux]
'@rollup/rollup-linux-loong64-gnu@4.59.0':
resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==}
cpu: [loong64]
os: [linux]
'@rollup/rollup-linux-loong64-musl@4.59.0':
resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==}
cpu: [loong64]
os: [linux]
'@rollup/rollup-linux-ppc64-gnu@4.59.0':
resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==}
cpu: [ppc64]
os: [linux]
'@rollup/rollup-linux-ppc64-musl@4.59.0':
resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==}
cpu: [ppc64]
os: [linux]
'@rollup/rollup-linux-riscv64-gnu@4.59.0':
resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==}
cpu: [riscv64]
os: [linux]
'@rollup/rollup-linux-riscv64-musl@4.59.0':
resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==}
cpu: [riscv64]
os: [linux]
'@rollup/rollup-linux-s390x-gnu@4.59.0':
resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==}
cpu: [s390x]
os: [linux]
'@rollup/rollup-linux-x64-gnu@4.59.0':
resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==}
cpu: [x64]
os: [linux]
'@rollup/rollup-linux-x64-musl@4.59.0':
resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==}
cpu: [x64]
os: [linux]
'@rollup/rollup-openbsd-x64@4.59.0':
resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==}
cpu: [x64]
os: [openbsd]
'@rollup/rollup-openharmony-arm64@4.59.0':
resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==}
cpu: [arm64]
os: [openharmony]
'@rollup/rollup-win32-arm64-msvc@4.59.0':
resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==}
cpu: [arm64]
os: [win32]
'@rollup/rollup-win32-ia32-msvc@4.59.0':
resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==}
cpu: [ia32]
os: [win32]
'@rollup/rollup-win32-x64-gnu@4.59.0':
resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==}
cpu: [x64]
os: [win32]
'@rollup/rollup-win32-x64-msvc@4.59.0':
resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==}
cpu: [x64]
os: [win32]
'@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
'@types/node@22.19.15':
resolution: {integrity: sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==}
esbuild@0.27.4:
resolution: {integrity: sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==}
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]
jiti@2.6.1:
resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
hasBin: true
nanoid@3.3.11:
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
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.3:
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
engines: {node: '>=12'}
postcss@8.5.8:
resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==}
engines: {node: ^10 || ^12 || >=14}
rollup@4.59.0:
resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==}
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'}
tinyglobby@0.2.15:
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
engines: {node: '>=12.0.0'}
undici-types@6.21.0:
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
vite@7.3.1:
resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==}
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
yaml@2.8.2:
resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==}
engines: {node: '>= 14.6'}
hasBin: true
snapshots:
'@esbuild/aix-ppc64@0.27.4':
optional: true
'@esbuild/android-arm64@0.27.4':
optional: true
'@esbuild/android-arm@0.27.4':
optional: true
'@esbuild/android-x64@0.27.4':
optional: true
'@esbuild/darwin-arm64@0.27.4':
optional: true
'@esbuild/darwin-x64@0.27.4':
optional: true
'@esbuild/freebsd-arm64@0.27.4':
optional: true
'@esbuild/freebsd-x64@0.27.4':
optional: true
'@esbuild/linux-arm64@0.27.4':
optional: true
'@esbuild/linux-arm@0.27.4':
optional: true
'@esbuild/linux-ia32@0.27.4':
optional: true
'@esbuild/linux-loong64@0.27.4':
optional: true
'@esbuild/linux-mips64el@0.27.4':
optional: true
'@esbuild/linux-ppc64@0.27.4':
optional: true
'@esbuild/linux-riscv64@0.27.4':
optional: true
'@esbuild/linux-s390x@0.27.4':
optional: true
'@esbuild/linux-x64@0.27.4':
optional: true
'@esbuild/netbsd-arm64@0.27.4':
optional: true
'@esbuild/netbsd-x64@0.27.4':
optional: true
'@esbuild/openbsd-arm64@0.27.4':
optional: true
'@esbuild/openbsd-x64@0.27.4':
optional: true
'@esbuild/openharmony-arm64@0.27.4':
optional: true
'@esbuild/sunos-x64@0.27.4':
optional: true
'@esbuild/win32-arm64@0.27.4':
optional: true
'@esbuild/win32-ia32@0.27.4':
optional: true
'@esbuild/win32-x64@0.27.4':
optional: true
'@rollup/rollup-android-arm-eabi@4.59.0':
optional: true
'@rollup/rollup-android-arm64@4.59.0':
optional: true
'@rollup/rollup-darwin-arm64@4.59.0':
optional: true
'@rollup/rollup-darwin-x64@4.59.0':
optional: true
'@rollup/rollup-freebsd-arm64@4.59.0':
optional: true
'@rollup/rollup-freebsd-x64@4.59.0':
optional: true
'@rollup/rollup-linux-arm-gnueabihf@4.59.0':
optional: true
'@rollup/rollup-linux-arm-musleabihf@4.59.0':
optional: true
'@rollup/rollup-linux-arm64-gnu@4.59.0':
optional: true
'@rollup/rollup-linux-arm64-musl@4.59.0':
optional: true
'@rollup/rollup-linux-loong64-gnu@4.59.0':
optional: true
'@rollup/rollup-linux-loong64-musl@4.59.0':
optional: true
'@rollup/rollup-linux-ppc64-gnu@4.59.0':
optional: true
'@rollup/rollup-linux-ppc64-musl@4.59.0':
optional: true
'@rollup/rollup-linux-riscv64-gnu@4.59.0':
optional: true
'@rollup/rollup-linux-riscv64-musl@4.59.0':
optional: true
'@rollup/rollup-linux-s390x-gnu@4.59.0':
optional: true
'@rollup/rollup-linux-x64-gnu@4.59.0':
optional: true
'@rollup/rollup-linux-x64-musl@4.59.0':
optional: true
'@rollup/rollup-openbsd-x64@4.59.0':
optional: true
'@rollup/rollup-openharmony-arm64@4.59.0':
optional: true
'@rollup/rollup-win32-arm64-msvc@4.59.0':
optional: true
'@rollup/rollup-win32-ia32-msvc@4.59.0':
optional: true
'@rollup/rollup-win32-x64-gnu@4.59.0':
optional: true
'@rollup/rollup-win32-x64-msvc@4.59.0':
optional: true
'@types/estree@1.0.8': {}
'@types/node@22.19.15':
dependencies:
undici-types: 6.21.0
optional: true
esbuild@0.27.4:
optionalDependencies:
'@esbuild/aix-ppc64': 0.27.4
'@esbuild/android-arm': 0.27.4
'@esbuild/android-arm64': 0.27.4
'@esbuild/android-x64': 0.27.4
'@esbuild/darwin-arm64': 0.27.4
'@esbuild/darwin-x64': 0.27.4
'@esbuild/freebsd-arm64': 0.27.4
'@esbuild/freebsd-x64': 0.27.4
'@esbuild/linux-arm': 0.27.4
'@esbuild/linux-arm64': 0.27.4
'@esbuild/linux-ia32': 0.27.4
'@esbuild/linux-loong64': 0.27.4
'@esbuild/linux-mips64el': 0.27.4
'@esbuild/linux-ppc64': 0.27.4
'@esbuild/linux-riscv64': 0.27.4
'@esbuild/linux-s390x': 0.27.4
'@esbuild/linux-x64': 0.27.4
'@esbuild/netbsd-arm64': 0.27.4
'@esbuild/netbsd-x64': 0.27.4
'@esbuild/openbsd-arm64': 0.27.4
'@esbuild/openbsd-x64': 0.27.4
'@esbuild/openharmony-arm64': 0.27.4
'@esbuild/sunos-x64': 0.27.4
'@esbuild/win32-arm64': 0.27.4
'@esbuild/win32-ia32': 0.27.4
'@esbuild/win32-x64': 0.27.4
fdir@6.5.0(picomatch@4.0.3):
optionalDependencies:
picomatch: 4.0.3
fsevents@2.3.3:
optional: true
jiti@2.6.1:
optional: true
nanoid@3.3.11: {}
picocolors@1.1.1: {}
picomatch@4.0.3: {}
postcss@8.5.8:
dependencies:
nanoid: 3.3.11
picocolors: 1.1.1
source-map-js: 1.2.1
rollup@4.59.0:
dependencies:
'@types/estree': 1.0.8
optionalDependencies:
'@rollup/rollup-android-arm-eabi': 4.59.0
'@rollup/rollup-android-arm64': 4.59.0
'@rollup/rollup-darwin-arm64': 4.59.0
'@rollup/rollup-darwin-x64': 4.59.0
'@rollup/rollup-freebsd-arm64': 4.59.0
'@rollup/rollup-freebsd-x64': 4.59.0
'@rollup/rollup-linux-arm-gnueabihf': 4.59.0
'@rollup/rollup-linux-arm-musleabihf': 4.59.0
'@rollup/rollup-linux-arm64-gnu': 4.59.0
'@rollup/rollup-linux-arm64-musl': 4.59.0
'@rollup/rollup-linux-loong64-gnu': 4.59.0
'@rollup/rollup-linux-loong64-musl': 4.59.0
'@rollup/rollup-linux-ppc64-gnu': 4.59.0
'@rollup/rollup-linux-ppc64-musl': 4.59.0
'@rollup/rollup-linux-riscv64-gnu': 4.59.0
'@rollup/rollup-linux-riscv64-musl': 4.59.0
'@rollup/rollup-linux-s390x-gnu': 4.59.0
'@rollup/rollup-linux-x64-gnu': 4.59.0
'@rollup/rollup-linux-x64-musl': 4.59.0
'@rollup/rollup-openbsd-x64': 4.59.0
'@rollup/rollup-openharmony-arm64': 4.59.0
'@rollup/rollup-win32-arm64-msvc': 4.59.0
'@rollup/rollup-win32-ia32-msvc': 4.59.0
'@rollup/rollup-win32-x64-gnu': 4.59.0
'@rollup/rollup-win32-x64-msvc': 4.59.0
fsevents: 2.3.3
source-map-js@1.2.1: {}
tinyglobby@0.2.15:
dependencies:
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
undici-types@6.21.0:
optional: true
vite@7.3.1(@types/node@22.19.15)(jiti@2.6.1)(yaml@2.8.2):
dependencies:
esbuild: 0.27.4
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
postcss: 8.5.8
rollup: 4.59.0
tinyglobby: 0.2.15
optionalDependencies:
'@types/node': 22.19.15
fsevents: 2.3.3
jiti: 2.6.1
yaml: 2.8.2
yaml@2.8.2:
optional: true
+1
View File
@@ -0,0 +1 @@
packages: []
Binary file not shown.

After

Width:  |  Height:  |  Size: 320 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 KiB

+815
View File
@@ -0,0 +1,815 @@
import './styles.css'
const STORAGE_KEY = 'prompt-optimizer-site-locale'
const THEME_STORAGE_KEY = 'prompt-optimizer-site-theme'
const SUPPORTED_LOCALES = ['zh-CN', 'en']
const THEME_MODES = ['system', 'dark', 'light']
const translations = {
'zh-CN': {
htmlLang: 'zh-CN',
title: 'Prompt Optimizer | 提示词优化,应该基于真实结果',
description: '在一个工作台里完成提示词优化、真实测试、结果评估与建议应用。',
brandAriaLabel: 'Prompt Optimizer 官网首页',
nav: {
product: '产品',
docs: '文档',
github: 'GitHub',
githubLabel: '开源仓库',
themeAria: '切换主题模式',
themeLabels: {
system: '自动',
dark: '深',
light: '浅'
},
locale: 'EN',
localeAria: '切换到英文',
menuAria: '切换导航菜单'
},
hero: {
eyebrow: '开源提示词工作台',
titleTop: '提示词优化',
titleMid: '应该基于',
titleBottom: '真实结果',
lead: '在一个工作台里完成优化、测试、评估,让建议回到当前提示词。',
primary: '打开产品',
secondary: '查看文档',
stats: [
{ value: '4 步闭环', label: '从测试到应用' },
{ value: '多工作区', label: '单条、变量、上下文' },
{ value: '结果证据', label: '改动基于输出' }
],
visualLabel: '结果闭环',
visualTitle: '左改右测,结论回流',
visualSummary: '左侧改提示词,右侧看结果,再把结论带回工作区。',
visualTags: ['左侧工作区', '右侧测试', '单结果评估'],
screenshotBadge: '工作台预览',
screenshotAlt: 'Prompt Optimizer 工作台截图'
},
value: {
kicker: '为什么不同',
title: '不是改字工具,而是结果驱动的工作台',
lead: '先看真实输出,再决定提示词到底要不要改。',
manifestoLabel: '结果优先',
manifestoTitle: '先看结果,再决定修改',
manifestoBody: '它把分析、评估、对比和应用建议收进同一条可回溯的工作流。',
principles: [
{
index: '01',
title: '改动必须回应输出',
body: '不是润色文本,而是修正结果偏差。',
tags: ['分析', '评估', '应用']
},
{
index: '02',
title: '任务边界要清楚',
body: '分析、评估、对比各自独立建模。'
},
{
index: '03',
title: '结论要能继续迭代',
body: '输出结论最终要回到当前工作区。'
}
]
},
workflow: {
kicker: '工作方式',
title: '用一次闭环,把改动落回真实结果',
lead: '左侧设计提示词,右侧验证结果,再把结论收回来。',
steps: [
{
label: '工作区',
title: '设计层',
body: '在左侧持续改写当前可编辑提示词。'
},
{
label: '测试',
title: '执行层',
body: '在右侧用真实输入和模型配置跑结果。'
},
{
label: '回流',
title: '决策层',
body: '把结果收敛成可继续应用的改动建议。'
}
]
},
scenario: {
kicker: '工作区覆盖',
title: '适配不同的提示词结构',
lead: '单条提示词、变量模板和上下文链路,都能放进同一套验证流程。',
columns: {
type: '结构类型',
fit: '适用内容',
capability: '关键能力'
},
cards: [
{
label: '单条提示词',
title: 'system / user 单提示词',
body: '适合优化角色、规则与输出格式。',
tags: ['system', 'user', '聚焦分析']
},
{
label: '变量模板',
title: '变量模板',
body: '把主题、语气和角色收进模板后再验证。',
tags: ['占位变量', '真实输入', '模板复用']
},
{
label: '上下文链路',
title: '多消息上下文',
body: '当 system、user 与上下文共同影响结果时仍可分析。',
tags: ['system + user', '上下文', '多消息']
}
]
},
access: {
kicker: '开放与入口',
title: '开源可信,也能立刻开始',
lead: '看清它的开放能力,再选最顺手的入口。',
proof: {
label: 'GitHub 仓库',
title: 'linshenkx/prompt-optimizer',
body: '同一套产品能力覆盖 Web、桌面版、自托管、Docker 和 MCP。',
facts: ['AGPL-3.0', 'Web / Desktop / Extension', 'Docker / MCP'],
primary: '查看 GitHub',
secondary: '下载 Release'
},
entries: [
{
title: '在线产品',
body: '直接进入工作台开始优化。',
href: 'https://prompt.always200.com'
},
{
title: '下载桌面版',
body: '从 Releases 获取安装包。',
href: 'https://github.com/linshenkx/prompt-optimizer/releases'
},
{
title: 'Chrome 插件',
body: '从 Chrome Web Store 安装入口。',
href: 'https://chromewebstore.google.com/detail/prompt-optimizer/cakkkhboolfnadechdlgdcnjammejlna'
},
{
title: 'Docker / MCP',
body: '适合自托管和外部集成。',
href: 'https://docs.always200.com/deployment/docker-basic/'
}
]
},
footer: {
title: 'Prompt Optimizer',
body: '让提示词优化回到真实结果。',
product: '产品',
docs: '文档',
github: 'GitHub'
}
},
en: {
htmlLang: 'en',
title: 'Prompt Optimizer | Prompt optimization should follow real outputs',
description: 'One workspace for prompt iteration, real testing, result evaluation, and applying improvements.',
brandAriaLabel: 'Prompt Optimizer website home',
nav: {
product: 'Product',
docs: 'Docs',
github: 'GitHub',
githubLabel: 'Open-source repository',
themeAria: 'Switch theme mode',
themeLabels: {
system: 'Auto',
dark: 'Dark',
light: 'Light'
},
locale: '中文',
localeAria: 'Switch to Chinese',
menuAria: 'Toggle navigation menu'
},
hero: {
eyebrow: 'Open-source prompt workspace',
titleTop: 'Prompt optimization',
titleMid: 'should follow',
titleBottom: 'real outputs',
lead: 'Iterate, test, evaluate, and return changes in one workspace.',
primary: 'Open Product',
secondary: 'Read Docs',
stats: [
{ value: '4-Step', label: 'From test to apply' },
{ value: 'Multi-Workspace', label: 'Single, variable, context' },
{ value: 'Evidence-Based', label: 'Edits follow outputs' }
],
visualLabel: 'Closed Loop',
visualTitle: 'Edit left, test right',
visualSummary: 'Refine the prompt, inspect the result, then send the conclusion back.',
visualTags: ['Editable workspace', 'Real test run', 'Result evaluation'],
screenshotBadge: 'Workspace preview',
screenshotAlt: 'Prompt Optimizer workspace screenshot'
},
value: {
kicker: 'Why it is different',
title: 'Not a rewrite toy, but an output-driven workspace',
lead: 'See the output first, then decide whether the prompt should change.',
manifestoLabel: 'Result First',
manifestoTitle: 'Look at the result before the rewrite',
manifestoBody: 'It keeps analysis, evaluation, compare, and apply actions inside one traceable loop.',
principles: [
{
index: '01',
title: 'Edits must answer the output',
body: 'The goal is not prettier wording, but better execution.',
tags: ['Analysis', 'Evaluation', 'Apply']
},
{
index: '02',
title: 'Action boundaries stay clear',
body: 'Analysis, evaluation, and compare are modeled separately.'
},
{
index: '03',
title: 'Findings should keep iterating',
body: 'Every conclusion should flow back into the editable workspace.'
}
]
},
workflow: {
kicker: 'How it works',
title: 'Close the loop from edit to real result',
lead: 'Design on the left, validate on the right, then fold the conclusion back.',
steps: [
{
label: 'Workspace',
title: 'Design layer',
body: 'Keep refining the editable prompt in the left workspace.'
},
{
label: 'Run',
title: 'Execution layer',
body: 'Use real inputs and model settings on the right.'
},
{
label: 'Return',
title: 'Decision layer',
body: 'Turn results into changes that can be applied back.'
}
]
},
scenario: {
kicker: 'Workspace coverage',
title: 'Handle more than a single prompt string',
lead: 'Single prompts, variable templates, and context chains all fit into the same validation loop.',
columns: {
type: 'Structure',
fit: 'Best for',
capability: 'Key capability'
},
cards: [
{
label: 'Single prompt',
title: 'System / user single prompt',
body: 'Best for roles, rules, and output instructions.',
tags: ['system', 'user', 'focused analysis']
},
{
label: 'Variables',
title: 'Variable templates',
body: 'Validate reusable templates with real values.',
tags: ['variables', 'real input', 'reuse']
},
{
label: 'Context chain',
title: 'Multi-message context',
body: 'Useful when system, user, and context all affect the result.',
tags: ['system + user', 'context', 'multi-message']
}
]
},
access: {
kicker: 'Open and access',
title: 'Open-source credibility with immediate entry points',
lead: 'Check the open surface first, then choose the path that fits you.',
proof: {
label: 'GitHub repository',
title: 'linshenkx/prompt-optimizer',
body: 'The same product surface spans web, desktop, self-hosting, Docker, and MCP.',
facts: ['AGPL-3.0', 'Web / Desktop / Extension', 'Docker / MCP'],
primary: 'View on GitHub',
secondary: 'Download Releases'
},
entries: [
{
title: 'Web App',
body: 'Open the workspace and start optimizing.',
href: 'https://prompt.always200.com'
},
{
title: 'Desktop Downloads',
body: 'Get installers and archives from Releases.',
href: 'https://github.com/linshenkx/prompt-optimizer/releases'
},
{
title: 'Chrome Extension',
body: 'Install it from the Chrome Web Store.',
href: 'https://chromewebstore.google.com/detail/prompt-optimizer/cakkkhboolfnadechdlgdcnjammejlna'
},
{
title: 'Docker / MCP',
body: 'Best for self-hosting and integrations.',
href: 'https://docs.always200.com/deployment/docker-basic/'
}
]
},
footer: {
title: 'Prompt Optimizer',
body: 'Bring prompt optimization back to real outputs.',
product: 'Product',
docs: 'Docs',
github: 'GitHub'
}
}
}
function normalizeLocale(locale) {
if (!locale) return 'zh-CN'
if (locale.startsWith('zh')) return 'zh-CN'
return 'en'
}
function getInitialLocale() {
const storedLocale = window.localStorage.getItem(STORAGE_KEY)
if (storedLocale && SUPPORTED_LOCALES.includes(storedLocale)) {
return storedLocale
}
const browserLocale = normalizeLocale(navigator.language || navigator.languages?.[0] || 'zh-CN')
return SUPPORTED_LOCALES.includes(browserLocale) ? browserLocale : 'zh-CN'
}
function normalizeThemeMode(themeMode) {
if (THEME_MODES.includes(themeMode)) return themeMode
return 'system'
}
function getInitialThemeMode() {
return normalizeThemeMode(window.localStorage.getItem(THEME_STORAGE_KEY) || 'system')
}
function resolveTheme(themeMode) {
if (themeMode === 'light' || themeMode === 'dark') return themeMode
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
}
function getNextThemeMode(themeMode) {
if (themeMode === 'system') return 'dark'
if (themeMode === 'dark') return 'light'
return 'system'
}
function renderLocaleButton(copy) {
return `
<button class="locale-switch" type="button" data-locale-toggle="true" aria-label="${copy.nav.localeAria}">
${copy.nav.locale}
</button>
`
}
function renderThemeButton(copy, themeMode) {
return `
<button class="theme-switch" type="button" data-theme-toggle="true" aria-label="${copy.nav.themeAria}" title="${copy.nav.themeAria}">
<span class="theme-switch__icon" aria-hidden="true">${renderThemeIcon(themeMode)}</span>
<span class="theme-switch__label">${copy.nav.themeLabels[themeMode]}</span>
</button>
`
}
function renderThemeIcon(themeMode) {
if (themeMode === 'dark') {
return `
<svg viewBox="0 0 24 24" role="img" focusable="false">
<path d="M21 12.79A9 9 0 0 1 11.21 3a7.5 7.5 0 1 0 9.79 9.79Z" fill="currentColor" />
</svg>
`
}
if (themeMode === 'light') {
return `
<svg viewBox="0 0 24 24" role="img" focusable="false">
<circle cx="12" cy="12" r="4.2" fill="currentColor" />
<path d="M12 1.5v3M12 19.5v3M4.57 4.57l2.12 2.12M17.31 17.31l2.12 2.12M1.5 12h3M19.5 12h3M4.57 19.43l2.12-2.12M17.31 6.69l2.12-2.12" fill="none" stroke="currentColor" stroke-linecap="round" stroke-width="1.8" />
</svg>
`
}
return `
<svg viewBox="0 0 24 24" role="img" focusable="false">
<rect x="4" y="5" width="16" height="11" rx="2.2" fill="none" stroke="currentColor" stroke-width="1.8" />
<path d="M8 19h8" fill="none" stroke="currentColor" stroke-linecap="round" stroke-width="1.8" />
<path d="M10 16.5h4" fill="none" stroke="currentColor" stroke-linecap="round" stroke-width="1.8" />
</svg>
`
}
function renderGithubPill(copy) {
return `
<a class="github-pill" href="https://github.com/linshenkx/prompt-optimizer" target="_blank" rel="noopener" aria-label="${copy.nav.githubLabel}">
<span class="github-pill__icon" aria-hidden="true">
<svg viewBox="0 0 24 24" role="img" focusable="false">
<path d="M12 1.5a10.5 10.5 0 0 0-3.32 20.47c.52.1.7-.23.7-.5v-1.95c-2.86.62-3.46-1.21-3.46-1.21-.47-1.18-1.14-1.5-1.14-1.5-.93-.64.07-.63.07-.63 1.03.07 1.57 1.06 1.57 1.06.91 1.56 2.4 1.1 2.98.84.09-.66.36-1.1.65-1.35-2.28-.26-4.67-1.14-4.67-5.08 0-1.12.4-2.04 1.05-2.76-.1-.26-.46-1.31.1-2.73 0 0 .86-.28 2.82 1.05a9.8 9.8 0 0 1 5.13 0c1.96-1.33 2.82-1.05 2.82-1.05.56 1.42.2 2.47.1 2.73.66.72 1.05 1.64 1.05 2.76 0 3.95-2.39 4.82-4.67 5.08.37.32.7.95.7 1.92v2.84c0 .28.18.61.7.5A10.5 10.5 0 0 0 12 1.5Z" fill="currentColor"/>
</svg>
</span>
<span class="github-pill__text">${copy.nav.github}</span>
<img
class="github-pill__badge"
src="https://img.shields.io/github/stars/linshenkx/prompt-optimizer?style=flat&label=stars&color=2563eb&labelColor=f8fbff&logo=github&logoColor=0f172a"
alt="GitHub stars"
/>
</a>
`
}
function renderNav(copy, themeMode) {
return `
<a href="https://prompt.always200.com" target="_blank" rel="noopener">${copy.nav.product}</a>
<a href="https://docs.always200.com" target="_blank" rel="noopener">${copy.nav.docs}</a>
${renderGithubPill(copy)}
${renderThemeButton(copy, themeMode)}
${renderLocaleButton(copy)}
`
}
function renderStats(stats) {
return stats
.map(
(item) => `
<article class="hero-stat">
<strong>${item.value}</strong>
<span>${item.label}</span>
</article>
`
)
.join('')
}
function renderPills(items, className = '') {
return items.map((item) => `<span class="${className}">${item}</span>`).join('')
}
function renderApp(locale, themeMode) {
const copy = translations[locale]
const resolvedTheme = resolveTheme(themeMode)
document.documentElement.lang = copy.htmlLang
document.documentElement.dataset.locale = locale
document.documentElement.dataset.theme = resolvedTheme
document.documentElement.dataset.themeMode = themeMode
document.title = copy.title
const descriptionMeta = document.querySelector('meta[name="description"]')
const ogDescriptionMeta = document.querySelector('meta[property="og:description"]')
const ogTitleMeta = document.querySelector('meta[property="og:title"]')
const themeColorMeta = document.querySelector('meta[name="theme-color"]')
if (descriptionMeta) descriptionMeta.setAttribute('content', copy.description)
if (ogDescriptionMeta) ogDescriptionMeta.setAttribute('content', copy.description)
if (ogTitleMeta) ogTitleMeta.setAttribute('content', copy.title)
if (themeColorMeta) themeColorMeta.setAttribute('content', resolvedTheme === 'dark' ? '#081120' : '#f8fbfe')
document.querySelector('#app').innerHTML = `
<div class="site-shell">
<div class="site-background" aria-hidden="true">
<div class="site-glow site-glow--left"></div>
<div class="site-glow site-glow--right"></div>
<div class="site-grid"></div>
</div>
<header class="site-header">
<a class="site-brand" href="/" aria-label="${copy.brandAriaLabel}">
<img src="/images/logo.png" alt="" class="site-brand__logo" />
<span class="site-brand__text">Prompt Optimizer</span>
</a>
<button class="site-menu-button" type="button" aria-expanded="false" aria-controls="site-nav" aria-label="${copy.nav.menuAria}">
<span></span>
<span></span>
<span></span>
</button>
<nav class="site-nav" id="site-nav" aria-label="Primary">
${renderNav(copy, themeMode)}
</nav>
</header>
<main>
<section class="hero">
<div class="hero-copy" data-reveal style="--reveal-delay: 20ms">
<div class="eyebrow">
<img src="/images/logo.png" alt="" />
<span>${copy.hero.eyebrow}</span>
</div>
<h1 class="hero-title">
<span class="hero-title__line hero-title__line--top">${copy.hero.titleTop}</span>
<span class="hero-title__line hero-title__line--mid">${copy.hero.titleMid}</span>
<span class="hero-title__line hero-title__line--bottom">${copy.hero.titleBottom}</span>
</h1>
<p class="hero-lead">${copy.hero.lead}</p>
<div class="hero-actions">
<a class="button button--primary" href="https://prompt.always200.com" target="_blank" rel="noopener">${copy.hero.primary}</a>
<a class="button" href="https://docs.always200.com" target="_blank" rel="noopener">${copy.hero.secondary}</a>
</div>
<div class="hero-proof">
${renderStats(copy.hero.stats)}
</div>
</div>
<div class="hero-visual" data-reveal style="--reveal-delay: 120ms">
<div class="hero-visual__meta">
<span class="signal-label">${copy.hero.visualLabel}</span>
<p>${copy.hero.visualTitle}</p>
</div>
<div class="hero-shot">
<div class="hero-shot__badge">${copy.hero.screenshotBadge}</div>
<img src="/images/demo/knowledge-graph-extractor.png" alt="${copy.hero.screenshotAlt}" />
</div>
<div class="hero-caption">
<strong>${copy.hero.visualSummary}</strong>
<div class="hero-caption__tags">
${renderPills(copy.hero.visualTags)}
</div>
</div>
</div>
</section>
<section class="section section--value">
<div class="value-layout">
<div class="section-head value-head" data-reveal>
<p class="section-kicker">${copy.value.kicker}</p>
<h2>${copy.value.title}</h2>
<p class="section-lead">${copy.value.lead}</p>
<div class="value-manifesto">
<span class="value-manifesto__label">${copy.value.manifestoLabel}</span>
<h3>${copy.value.manifestoTitle}</h3>
<p>${copy.value.manifestoBody}</p>
</div>
</div>
<div class="value-principles">
${copy.value.principles
.map(
(principle, index) => `
<article class="value-principle" data-reveal style="--reveal-delay: ${80 + index * 70}ms">
<span class="value-principle__index">${principle.index}</span>
<div class="value-principle__body">
<h3>${principle.title}</h3>
<p>${principle.body}</p>
${
principle.tags
? `
<div class="value-principle__tags">
${renderPills(principle.tags)}
</div>
`
: ''
}
</div>
</article>
`
)
.join('')}
</div>
</div>
</section>
<section class="section section--workflow" data-reveal>
<div class="section-head section-head--center workflow-head">
<p class="section-kicker">${copy.workflow.kicker}</p>
<h2>${copy.workflow.title}</h2>
<p class="section-lead">${copy.workflow.lead}</p>
</div>
<div class="workflow-line">
${copy.workflow.steps
.map(
(step, index) => `
<article class="workflow-node workflow-node--${index + 1}" data-reveal style="--reveal-delay: ${100 + index * 90}ms">
<div class="workflow-node__marker">
<span class="workflow-index">0${index + 1}</span>
</div>
<div class="workflow-node__body">
<span class="workflow-card__label">${step.label}</span>
<h3>${step.title}</h3>
<p>${step.body}</p>
</div>
</article>
`
)
.join('')}
</div>
</section>
<section class="section section--scenario">
<div class="scenario-layout">
<div class="section-head scenario-head" data-reveal>
<p class="section-kicker">${copy.scenario.kicker}</p>
<h2>${copy.scenario.title}</h2>
<p class="section-lead">${copy.scenario.lead}</p>
</div>
<div class="scenario-matrix" data-reveal style="--reveal-delay: 100ms">
<div class="scenario-matrix__head">
<span>${copy.scenario.columns.type}</span>
<span>${copy.scenario.columns.fit}</span>
<span>${copy.scenario.columns.capability}</span>
</div>
${copy.scenario.cards
.map(
(card, index) => `
<article class="scenario-row scenario-row--${index + 1}" data-reveal style="--reveal-delay: ${120 + index * 80}ms">
<div class="scenario-row__meta">
<span class="scenario-row__index">0${index + 1}</span>
<span class="scenario-card__label">${card.label}</span>
</div>
<div class="scenario-row__body">
<h3>${card.title}</h3>
<p>${card.body}</p>
</div>
<div class="scenario-row__tags">
${renderPills(card.tags)}
</div>
</article>
`
)
.join('')}
</div>
</div>
</section>
<section class="section section--access">
<div class="section-head" data-reveal>
<p class="section-kicker">${copy.access.kicker}</p>
<h2>${copy.access.title}</h2>
<p class="section-lead">${copy.access.lead}</p>
</div>
<article class="proof-strip" data-reveal style="--reveal-delay: 80ms">
<div class="proof-strip__main">
<div class="proof-strip__top">
<div class="proof-strip__title">
<span class="proof-strip__label">${copy.access.proof.label}</span>
<strong>${copy.access.proof.title}</strong>
</div>
<img
class="proof-strip__stars"
src="https://img.shields.io/github/stars/linshenkx/prompt-optimizer?style=for-the-badge&label=stars&color=2d6cff&labelColor=0b1633"
alt="GitHub stars"
/>
</div>
<p>${copy.access.proof.body}</p>
<div class="proof-strip__facts">
${renderPills(copy.access.proof.facts)}
</div>
</div>
<div class="proof-strip__actions">
<a class="button button--primary" href="https://github.com/linshenkx/prompt-optimizer" target="_blank" rel="noopener">${copy.access.proof.primary}</a>
<a class="button button--light" href="https://github.com/linshenkx/prompt-optimizer/releases" target="_blank" rel="noopener">${copy.access.proof.secondary}</a>
</div>
</article>
<div class="entry-shelf">
${copy.access.entries
.map(
(entry, index) => `
<a class="entry-card entry-card--${index + 1}" data-reveal style="--reveal-delay: ${120 + index * 70}ms" href="${entry.href}" target="_blank" rel="noopener">
<strong>${entry.title}</strong>
<span>${entry.body}</span>
</a>
`
)
.join('')}
</div>
</section>
</main>
<footer class="site-footer">
<div class="site-footer__copy">
<strong>${copy.footer.title}</strong>
<p>${copy.footer.body}</p>
</div>
<div class="site-footer__links">
<a href="https://prompt.always200.com" target="_blank" rel="noopener">${copy.footer.product}</a>
<a href="https://docs.always200.com" target="_blank" rel="noopener">${copy.footer.docs}</a>
<a href="https://github.com/linshenkx/prompt-optimizer" target="_blank" rel="noopener">${copy.footer.github}</a>
${renderThemeButton(copy, themeMode)}
${renderLocaleButton(copy)}
</div>
</footer>
</div>
`
bindInteractions(locale, themeMode)
bindRevealAnimations()
bindMotionEffects()
}
function bindInteractions(locale, themeMode) {
const menuButton = document.querySelector('.site-menu-button')
const nav = document.querySelector('.site-nav')
if (menuButton && nav) {
menuButton.addEventListener('click', () => {
const expanded = menuButton.getAttribute('aria-expanded') === 'true'
menuButton.setAttribute('aria-expanded', String(!expanded))
nav.classList.toggle('site-nav--open', !expanded)
})
}
document.querySelectorAll('[data-locale-toggle="true"]').forEach((button) => {
button.addEventListener('click', () => {
const nextLocale = locale === 'zh-CN' ? 'en' : 'zh-CN'
window.localStorage.setItem(STORAGE_KEY, nextLocale)
currentLocale = nextLocale
renderApp(currentLocale, currentThemeMode)
})
})
document.querySelectorAll('[data-theme-toggle="true"]').forEach((button) => {
button.addEventListener('click', () => {
const nextThemeMode = getNextThemeMode(themeMode)
window.localStorage.setItem(THEME_STORAGE_KEY, nextThemeMode)
currentThemeMode = nextThemeMode
renderApp(currentLocale, currentThemeMode)
})
})
}
function bindRevealAnimations() {
const revealTargets = document.querySelectorAll('[data-reveal]')
if (!revealTargets.length) return
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
if (prefersReducedMotion) {
revealTargets.forEach((node) => node.classList.add('is-visible'))
return
}
requestAnimationFrame(() => {
requestAnimationFrame(() => {
revealTargets.forEach((node) => node.classList.add('is-visible'))
})
})
}
function bindMotionEffects() {
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
const prefersFinePointer = window.matchMedia('(pointer: fine)').matches
if (prefersReducedMotion || !prefersFinePointer) return
const clamp = (value, min, max) => Math.min(max, Math.max(min, value))
const bindPointerSurface = (selector, prefix, maxTilt = 6, maxShift = 16) => {
const element = document.querySelector(selector)
if (!element) return
const reset = () => {
element.style.setProperty(`--${prefix}-pointer-x`, '50%')
element.style.setProperty(`--${prefix}-pointer-y`, '50%')
element.style.setProperty(`--${prefix}-tilt-x`, '0deg')
element.style.setProperty(`--${prefix}-tilt-y`, '0deg')
element.style.setProperty(`--${prefix}-shift-x`, '0px')
element.style.setProperty(`--${prefix}-shift-y`, '0px')
}
const update = (event) => {
const rect = element.getBoundingClientRect()
const px = clamp(((event.clientX - rect.left) / rect.width) * 100, 0, 100)
const py = clamp(((event.clientY - rect.top) / rect.height) * 100, 0, 100)
const nx = clamp((px - 50) / 50, -1, 1)
const ny = clamp((py - 50) / 50, -1, 1)
element.style.setProperty(`--${prefix}-pointer-x`, `${px.toFixed(2)}%`)
element.style.setProperty(`--${prefix}-pointer-y`, `${py.toFixed(2)}%`)
element.style.setProperty(`--${prefix}-tilt-x`, `${(-ny * maxTilt).toFixed(2)}deg`)
element.style.setProperty(`--${prefix}-tilt-y`, `${(nx * maxTilt).toFixed(2)}deg`)
element.style.setProperty(`--${prefix}-shift-x`, `${(nx * maxShift).toFixed(2)}px`)
element.style.setProperty(`--${prefix}-shift-y`, `${(ny * maxShift).toFixed(2)}px`)
}
reset()
element.addEventListener('pointermove', update)
element.addEventListener('pointerleave', reset)
}
bindPointerSurface('.hero', 'hero', 5.5, 18)
bindPointerSurface('.proof-strip', 'proof', 3.5, 10)
}
let currentLocale = getInitialLocale()
let currentThemeMode = getInitialThemeMode()
const colorSchemeMedia = window.matchMedia('(prefers-color-scheme: dark)')
if (typeof colorSchemeMedia.addEventListener === 'function') {
colorSchemeMedia.addEventListener('change', () => {
if (currentThemeMode === 'system') {
renderApp(currentLocale, currentThemeMode)
}
})
}
renderApp(currentLocale, currentThemeMode)
+2052
View File
File diff suppressed because it is too large. Load diff
+19
View File
@@ -0,0 +1,19 @@
{
"buildCommand": "pnpm build",
"outputDirectory": "dist",
"redirects": [
{
"source": "/docs",
"destination": "https://docs.always200.com/",
"permanent": true
},
{
"source": "/docs/:path*",
"destination": "https://docs.always200.com/:path*",
"permanent": true
}
],
"github": {
"silent": true
}
}