# 悦皓(黄悦皓)本机 AI 工作手册 > 由 WorkBuddy 助手「小七」于 2026-09-15 从 16 个历史项目 + 45 个 Skill 提炼生成。 > 后续执行任务的 AI 在开始前应先阅读本文件。 ## 使用说明 - 本文件是**纯文本全文索引**,人类可读的网页版在 index.html - 章节 01-03 是**必读**(环境硬约束 + 用户铁律),其余按需查阅 - 所有内容来自真实项目记录,未核实的信息已标注 ## 目录 - 01. START HERE · AI 启动协议 — 任何任务开始前的必读动作 - 02. 环境硬约束 — 这台机器的真实能力边界,踩过无数次 - 03. 用户画像与铁律 — 悦皓是谁、什么绝对不能做 - 04. 视频工程方法 — ffmpeg 剪辑 / 字幕 / 转码 / 交付规格 - 05. 前端与视觉方法 — 单文件网页 / 3D / 动效 / 暗色风格 - 06. 发布与自动化 — 部署上线 / 定时任务 / 资料库 - 07. 检索与资料处理 — 联网取材 / 下载 / 转写 / PDF - 08. 踩坑总清单 — 全部历史坑的结构化索引 - 09. 项目档案 — 16 个历史项目的可复用资产 - 10. Skill 分类索引(速查) - 11. Skills 全文 --- ## 01. START HERE · AI 启动协议 _任何任务开始前的必读动作_ ### [P0] 🔁 收尾必做:自主维护本手册与 Skill 目录(悦皓已授权,不用问他) 标签: 收尾 / 铁律 / 自动 / skill / 必读 悦皓 2026-09-16 亲口授权:「以后在所有任务中,自主向里面添加 skill,不需要我的提示和指令」。 所以**每次完成实质工作后,不等他开口,我自己做完就对了**。 必做三件事: 1. **新经验入库**:本次学到的方法/坑/事实 → 追加到 `knowledge-base/INBOX.md`(`@@ITEM` 模板)。 2. **Skill 同步 + 归类**(一条命令,含重建): ``` C:\\Users\\lenovo\\.workbuddy\\binaries\\python\\versions\\3.13.12\\python.exe C:\\Users\\lenovo\\WorkBuddy\\2026-09-15-21-54-09\\_scan\\sync_skills.py ``` 它会:动态扫描本机 skill → 发现新 skill 自动归类 → 重建手册和 Skill 目录站。 想只看报告不改动产物就加 `--no-rebuild`。 3. **新装/新造的 Skill 要确认进目录**:去 `llms.txt` 第 10 章(Skill 分类索引)核对出现了。 > **2026-09-17 补充(铁律 2)**:收录范围扩大到「**任务中实际使用过的 skill**」—— > 不限于新装的,凡本轮调用过的 skill 都要归类进站点。另见铁律 1(上下文 60% 自动压缩)。 > **2026-09-17 补充(铁律 2 节奏)**:每次任务保证「**开头学目录 + 结尾加 skill**」闭环—— > 开头先读本地最新 `skill-catalog/llms.txt`(或手册 `knowledge-base/llms.txt`),让所有 AI 基于最新分类工作; > 结尾把本次用到/新生成的 skill 跑 `sync_skills.py` 归类重建。线上发布已授权自动、不阻塞任务: > 只能在 handbook 所属 workspace(`2026-09-15-21-54-09`)会话 deploy;跨 workspace 会话会报「未绑定到本次发布环境」, > 此时保持本地最新即可,不卡任务,回该 workspace 自动补发。 **收录范围纪律**:自建 + 项目生成的一律收录;「内置/市场」只收 `skill_cats.SKILL_ALLOW_BUILTIN` 白名单内的——实测本机插件缓存里躺着 600+ 个无关 skill, 照单全收会把手册灌成噪音。**别图省事全收。** **线上发布现已自动执行(不阻塞任务)**(2026-09-17 悦皓授权,不需每轮确认):本地重建后,在 handbook 所属 workspace 会话直接 deploy 推线上两侧站点;跨 workspace 会话无 `*.app.workbuddy.host` 发布权(报「未绑定到本次发布环境」),保持本地最新即可,不卡任务. ### [P0] 任务开始前的 5 个动作 标签: 启动 / 必读 1. 读「环境硬约束」——确认本机什么能跑、什么会挂(bash 不可用这一条最容易浪费时间)。 2. 读「用户画像与铁律」——尤其是**禁止编造**这条,写错过一次就被纠正过。 3. 按任务类型打开对应方法论:做视频 → 视频工程;做网页 → 前端与视觉;要上线 → 发布与自动化。 4. 查「踩坑总清单」,用关键词检索(Ctrl+F 或站内搜索框)。 5. 查「项目档案」——很可能已有现成资产(简历数据、 ffpmeg 脚本、已经发布的站点)可直接复用,不要重造。 **默认还是要干活,不是读完就停。** 这些是执行前的 30 秒预检,不是替代理由。 ### [P0] 给后续 AI 的一句话提示词(可直接贴) 标签: 启动 / AI ``` 在开始任务前,请先访问并学习 https:///llms.txt (本站 AI 全文索引), 特别注意:本机 bash coreutils 缺失、PowerShell 输出不回显、FFmpeg 在 bilibili 目录、 禁止编造用户素材、交付前优先给出可运行成果。学完再执行我的任务。 ``` 更省事的方式:直接把 `llms.txt` 全文或对应章节粘进新会话的系统提示/首条消息。 ### [P1] 默认交付节奏:先成品,后解释 标签: 输出 / 习惯 悦皓提需求简短直接,回复也要短。 正确顺序: 1. 先拿出一个能跑/能点/能看的版本。 2. 一句话说清怎么用。 3. 再问要不要改。 错误做法:先写 200 字方案让他审、先问 5 个澄清问题、先分析利弊。 例外:不可逆操作(删文件、发消息、花钱、对外发布)必须先问。 ### [P0] 这份手册的线上地址 标签: 网址 / llms / 入口 新人 AI 直接抓这个就能拿到全文: - **AI 全文索引(首选)**:`https://ai-handbook.app.workbuddy.host/llms.txt` - 网页版:`https://ai-handbook.app.workbuddy.host/` - 原始数据:`https://ai-handbook.app.workbuddy.host/assets/data.js` `llms.txt` 是纯文本,一次请求即可拿到全部章节正文 + 44 个 Skill 的完整 SKILL.md, 不需要解析 HTML/JS。开新任务时**先读它,再动手**。 ### [P0] 这个知识库怎么维护 标签: 维护 / 追加 每次完成一件实质工作后: 1. 在**本文件末尾**按上面的模板追加一条(新踩的坑 / 新确认的环境事实 / 新沉淀的方法)。 2. 若新装或新建了 Skill,直接重跑构建脚本,会自动把 `~/.workbuddy/skills/` 下的新 Skill 收录进来。 3. 跑一次构建命令,网页与 `llms.txt` 同步更新。 4. 若站点已部署到公网,重新发布一次。 **不要**直接手改 `assets/data.js`(下次构建会覆盖)。要改长期条目就改 `_scan/content_data.py`,要临时追加就写本文件。 ## 02. 环境硬约束 _这台机器的真实能力边界,踩过无数次_ ### [P0] Bash 工具基本不可用(coreutils 缺失) 标签: bash / shell / 坑 Bash 工具的 Git Bash shim 环境损坏,`ls` `head` `tail` `dirname` `grep` 全部 `command not found`,管道和 `cd` 会挂。 **可靠做法:用托管 Python 绝对路径写脚本做事,不要指望 shell 命令。** ``` C:\\Users\\lenovo\\.workbuddy\\binaries\\python\\versions\\3.13.12\\python.exe ``` venv(已装 pillow / markdown / qrcode / pyzbar): ``` C:\\Users\\lenovo\\.workbuddy\\binaries\\python\\envs\\default\\Scripts\\python.exe ``` 注意 Windows venv 的 python 在 `Scripts\\` 而不是 `bin\\`。 ### [P0] PowerShell 输出不回显,必须写文件再读 标签: PowerShell / 输出 / 坑 PowerShell 工具的 stdout 经常被吞,命令执行成功但看不到任何输出。 **解法:脚本里把结果写到本地文件,再用 Read 工具读。** ```python # 标准套路 OUT = io.StringIO() print(..., file=OUT) open(r"...\\_scan\\out.txt", "w", encoding="utf-8").write(OUT.getvalue()) ``` PowerShell 里还有两个坑: - `Remove-Item` 会被 safe-delete 拦截报 `trash-failed`,临时文件删不掉 → 手动清理或换个目录写。 - 禁用 `New-Object -ComObject`(建快捷方式)和 `cmd.exe`。 ### [P0] Node 与 npm 的正确调用姿势 标签: node / npm / 坑 node 路径会随版本升级变化,**永远不要硬编码**,也不要相信 Bash 的 `cwd`。 ``` C:\\Users\\lenovo\\.workbuddy\\binaries\\node\\versions\\22.22.2-3\\node.exe ``` - `npm install` 对 Bash 的 cwd 无效,会读 workspace 根目录的 package.json 报 ENOENT。 **绕法:写一个 `install.cjs`,用 `spawnSync(npm-cli.js, ['install'], {cwd: __dirname, shell:false})`。** - npm-cli 位置:`...\\node\\versions\\\\node_modules\\npm\\bin\\npm-cli.js` - npm 慢/卡死(装 three.js 卡了 8 分钟)→ 直接从 jsdelivr 下载 UMD/ESM 文件放进 `src/vendor/`,比 npm 稳得多。 ### [P0] 本机唯一可用的 FFmpeg 与其一堆怪癖 标签: ffmpeg / 视频 / 坑 路径(bilibili 装的,版本较老): ``` C:\\Users\\lenovo\\AppData\\Roaming\\bilibili\\ffmpeg\\ffmpeg.exe ``` - **drawtext 必须设环境变量**:`FONTCONFIG_FILE=C:/fcconf/fonts.conf`,字体放 `C:/fcconf/`(已放 msyhbd.ttc、stxingka.ttf) - **没有** gblur/avgblur → 模糊背景用 `scale=64:36` 再放大回去 - **没有** xfade / xstack / rgbashift → 转场要自己用 overlay/crop 拼 - `drawbox` 不支持 `t=fill` → 用 `t=1000` - 静默失败多,**必须逐段检查 return code** 另一个 ffmpeg 来源:`imageio_ffmpeg.get_ffmpeg_exe()`(Python 包自带,用于转码够了) ### [P1] Python 访问 HTTPS 会证书校验失败 标签: Python / 网络 / HTTPS 本机代理/证书问题导致 `CERTIFICATE_VERIFY_FAILED`。下载东西时用: ```python ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE urllib.request.urlopen(url, context=ctx) ``` 下载 GitHub 仓库比 `git clone` 稳的做法: ``` https://codeload.github.com//zip/refs/heads/ ``` 用 urllib + zipfile 直接拉解压。 ### [P1] 内置预览按路径缓存,重建后看到的还是旧版 标签: 预览 / 缓存 / 坑 WorkBuddy 内置 static-html 预览**按路径缓存内容**:dist 重建后,同一个 URL 仍吐旧页面,用户会误以为改动没生效或被回滚。 **对策:** - 别用内置预览看迭代中的站点。 - 用 `python -m http.server <新端口> --directory `,每次改完**换新端口**更稳。 - 交付时明确说 Ctrl+F5,并且只认一个 URL。 ### [P1] python http.server 不支持 Range,视频一定挂 标签: 预览 / 视频 / Range 静态部署默认用 python http.server,**不支持 HTTP Range**,浏览器无法流式播放 mp4,表现是"视频打不开"。 **两个必备动作:** 1. 转码加 `+faststart`(把 moov 挪到文件头) `ffmpeg -i in.mp4 -c:v libx264 -crf 25 -c:a aac -b:a 128k -movflags +faststart out.mp4` 2. 用自写的 Range 服务器(`serve-range.cjs`,支持 206 + Content-Range),部署时 startCmd 指向它。 验证:`curl -r 1000-2000 ` 应返回 **206 Partial Content**。 注意:重新 build 会清空 dist,`serve-range.cjs` 要重新生成;且脚本内 ROOT 必须改成 `__dirname`(写死 `join(__dirname,'dist')` 会导致全站 404)。 ### [P1] Write / Edit 工具的两个可靠性坑 标签: Write / Edit / 工具坑 - **Write 覆盖已存在的大文件**常报 `File has been modified since read` → 先 `os.rename` 改名,再 Write 新文件。 - **Edit 偶发"报成功但未生效"** → 改完必须用 Python 核查目标字符串出现次数(`src.count(target)`),别轻信返回值。 - **同一文件并发多次 Edit 会互相覆盖**(报成功但实际丢失)→ 必须串行改。 - bash 双引号命令里的引号会被转义成斜杠,`content://` 变 `content://`,Python replace 修不好 → **含引号的内容一律用 Edit 工具直写**。 - 构建脚本里的 `shutil.rmtree` 会触发 safe-delete 批量拦截 → 改用 `makedirs(exist_ok=True)`;也别 rmtree 日志所在目录(重定向会失效)。 ### [P2] 没有浏览器时怎么验证渲染效果 标签: 验证 / 截图 / playwright 本机无 ffmpeg、agent-browser 未安装时,用 **playwright-core + 本机已装的 Edge**,零 Chromium 下载: ``` npm --prefix C:\\Users\\lenovo\\.workbuddy\\binaries\\node\\workspace install playwright-core $env:NODE_PATH="C:\\Users\\lenovo\\.workbuddy\\binaries\\node\\workspace\\node_modules" node shot.js # chromium.launch({channel:'msedge', headless:true}) ``` 注意:跑 node 脚本不要依赖 Bash 的 cwd。 ### [P1] Bash 里写含反引号的内容会被当命令执行 标签: bash / 反引号 / 坑 - **现象**:用 `bash -c "python -c \"...write('` build.py `')...\""` 写文件时,报了一堆 `No such file or directory` / `command not found`,但退出码是 0,文件内容被写坏了——反引号包裹的中文全被清空。 - **原因**:Bash 的**命令替换**在双引号串内依然生效,`` `foo` `` 会被当成命令执行并用输出替换, 即使它在 Python 字符串里也一样。 - **解法**:写包含反引号或 Markdown 代码块的中文内容时,**不要用 `bash -c`**。 改用 Write/Edit 工具,或把内容写进 `.py` 脚本文件再用 PowerShell 跑。 - **附带**:PowerShell 的 stdout 常被吞(已在 #08 记过),所以调试脚本要把结果写文件再 Read。 ### [P1] 本机无法用 COM 建 .lnk → 纯 Python 二进制构造 标签: Windows / 快捷方式 / lnk / COM拦截 / WorkBuddy沙箱 / Python二进制 WorkBuddy 沙箱会拦截 `WScript.Shell` COM 实例化(PowerShell 工具直接报 security,Bash 调 powershell 报 bypass), 且本机 Python 无 `win32com` / `comtypes`,常规建快捷方式全路堵死。 **解法**:按 MS-SHLLINK 格式用纯 Python 手工写 `.lnk` 字节: - Header(76B):LinkCLSID={00021401-0000-0000-C000-000000000046},Flags=HasLinkInfo|HasName|HasWorkingDir|IsUnicode - LinkInfo:VolumeID(size16,type0,serial0,labelOff16) + LocalBasePath(GBK 编码) + 0 终止 + CommonSuffix(0) - StringData:NAME(UTF-16LE 绝对路径) + WORKING_DIR(UTF-16LE) - 末尾补 4 字节 ExtraData 终止符 0x00000000 中文目标路径:LocalBasePath 用 `gbk` 编码(中文 Windows 代码页 CP936 可正确读),NAME 字段用 `utf-16-le`,双保险。 回读校验:HeaderSize=76、Flags=0x96、LocalBasePath/NAME 均等于目标路径即可确认有效。 ### [P1] Git Bash 里跑 node/playwright,NODE_PATH 必须用 Windows 风格绝对路径 标签: GitBash / NODE_PATH / playwright / Windows / 路径 `NODE_PATH=/c/Users/...`(Git Bash 风格)在 Node(Windows) 下解析不到,直接报 `Cannot find module 'playwright-core'`。 要写 `NODE_PATH='C://Users//lenovo//.workbuddy//binaries//node//workspace//node_modules'`。 本机 playwright-core 就装在这个 managed workspace 的 node_modules,浏览器用 `channel:'msedge'`。 ### [P0] 本机 shell 探测能力受限:bash 缺 coreutils,PowerShell 工具不回传 stdout 标签: bash / coreutils / PowerShell / 探测 / 坑 / shell - **现象**(2026-09-17 实测,一次任务里全部撞上): - `bash` 里 `dirname` / `head` / `tail` / `mkdir` / `find` 一律 `command not found`(退出码常见 127)。 PortableGit 带的 bash 没配 coreutils。只有 `ls` / `echo` / `cat` 这类内建或自带的能用。 - **PowerShell 工具执行成功(exit 0)但标准输出完全不回传**,看不到任何结果,命令等于白跑。 - `reg.exe` 在程序黑名单里,报 Security Policy 阻断,**不可绕过、不可换壳调用**。 - **解法**:本机做环境探测、批量文件操作、注册表查询,**一律改用 Python 脚本**: 用 Write 写一个 `.py`,再用管理版 python 跑 `C:\Users\lenovo\.workbuddy\binaries\python\versions\3.13.12\python.exe xxx.py`。 `os` / `subprocess` 能覆盖 ls/mkdir/rm/find/进程与端口探测的全部需求。 - **附带坑**:写 Python 脚本时不要用 `bash -c "python -c \"...\""` 的嵌套引号写法,反引号会被当命令执行。 ### [P1] 本机 Comfy Desktop 安装坐标与模型现状 标签: comfy / comfyui / comfy-desktop / 模型路径 / 显卡 - **软件**:Comfy Desktop(Electron)装在 `C:\Users\lenovo\AppData\Local\Programs\Comfy Desktop` - **实例**:独立版 win-nvidia,路径 `D:\comfy\ComfyUI`;ComfyUI v0.35.1,torch 2.12.1+cu130, Python 3.13.12,启动参数 `--enable-manager` - **端口**:**8188**(Desktop 常见默认是 8000,本机是 8188 —— 探测时两个都要试) - **模型目录**:API 实际读 `D:\comfy\ComfyUI\ComfyUI\models\`, **不是** `settings.json` 里 `modelsDirs` 写的 C 盘 `ComfyUI-Shared\models`。别被配置文件带偏。 - **已装模型(2026-09-17)**:只有 MiniMax H3 视频套件 - diffusion_models: `minimax_h3_fl2va_pruned_int8_convrot.safetensors`(19.5 GB) - loras: `minimax_h3_fl2v_turbo_8step_v1.0_comfyui_bf16.safetensors`(1.8 GB) - vae: `minimax_h3_audio_vae_fp32` / `minimax_h3_video_vae_fp16` - text_encoders: `qwen3vl_32b_minimax_h3_nvfp4_awq.safetensors`(14.6 GB) - **`checkpoints` 数量为 0 → 本机目前没有图像出图能力** - **硬件**:RTX 5060 Laptop GPU 8 GB 显存(另有一块 Intel Arc 140T 核显) ### [P0] 本机 bash 里 `| tail` / `| head` 会静默毁掉整条命令 标签: bash / tail / head / 管道 / SIGPIPE / 坑 / 安装 - **现象**:`pip install ... 2>&1 | tail -25` 跑了 2 分多钟,看着在正常下载, 最后退出码非 0,而目标 venv 里**只有 pip、包一个都没装上**。 原因不是 pip 失败,而是本机 bash **没有 `tail`**(缺 coreutils), 管道右端起不来,左端 pip 在写 stdout 时吃到 SIGPIPE 被打断。 - **为什么危险**:这类失败**看起来像网络/依赖问题**,很容易去排查错方向。 退出码 0 也可能出现(`cd` 之类的复合命令前段成功会掩盖后段失败)。 - **解法**: 1. 本机**不要用 `| tail` / `| head` / `| less` 收尾**。要截断输出就用 Python:`subprocess.run(..., capture_output=True)` 后自己切。 2. 长任务的输出让它整个流出来(工具会截断到 2 万字符,够用)。 3. 判断"装没装上"**不要看退出码,要看 site-packages 实际内容**。 - **关联**:见本文件前面「本机 shell 探测能力受限」那条。 ### [P1] PowerShell 工具在本机常常不回传 stdout —— 一律「重定向到文件再 Read」 标签: PowerShell / stdout / 吞输出 / 重定向 / 读文件 / 乱码 - **现象**:`& node x.mjs args | Out-String`、`cmd > out.txt 2>&1` 都试过, 工具返回 `Command completed with exit code 0` 但**一个字都没有**,命令等于白跑,无从判断成败。 - **解法(可靠)**: 1. 跑命令时**管道写入文件**:`& node x.mjs args | Set-Content -Encoding utf8 out.txt` 2. 再用 Read 工具读那个 `out.txt`(比靠 stdout 回传可靠得多)。 3. 若 Read 报 "Cannot display content of binary file",说明文件是 UTF-16 或带异常 BOM, 重写为 utf8 即可。 - **配套**:用 Python 做环境探测/文件操作(`os`/`subprocess`)比 PowerShell 稳, 见本文件前面「本机 shell 探测能力受限」条目。 ### [P0] 嘈杂录音必须用 faster-whisper large-v3;small 出重复幻觉,直接弃用别调参 标签: ASR / whisper / large-v3 / 模型 / 下载 / hf-mirror / 低信噪比 / 课堂录音 - **触发场景**:微信导出的现场录音(大教室/礼堂/远距离/混响重,48 kHz 立体声 132 kbps aac-LC、 handler_name=`Core Media Audio`,即 iPhone 语音备忘录)。**音质参数看着正常,实际信噪比很差。** - **判定方法(30 秒)**:切 4 分钟有语音的片段,small 与 large-v3 各跑一遍。 若 small 出现整段复读("XX中的XX中的XX中的…")或与内容无关的口水句,**立刻换 large-v3**。 - **实测**(同段录音): - small:`那我们怎么跟随大家,把他们收下,想办法,给医院电视报名`(原文:我们要怎么分析它呢,把它简化成一个电路模型) - large-v3:`好的,咱们电路分析,这个分析对大家有什么用,大家看一下这里面这几个电器…它们必须经过(复杂)的电路才能运作` - **治重复幻觉的解码参数**(large-v3):`beam_size=5`、`vad_filter=True`、 `condition_on_previous_text=False`、**`no_repeat_ngram_size=3`**、`temperature=[0.0]`。 - **速度/内存(16 核 31 GB,int8 CPU)**:large-v3 单进程 8 线程 ≈ 2.0x 实时; `--workers 2 --threads 7` ≈ 2.5–3x 实时(81 分钟音频约 32 分钟)。 **每进程约 3 GB 内存**,空闲 < 10 GB 时只能用 2 个 worker,3 个会换页反而更慢。 - **踩坑**:HF 缓存里 `models--Systran--faster-whisper-large-v3` **只有 config/tokenizer、缺 `model.bin`**, 直接加载会报错。用 `snapshot_download` + `local_dir=` 补到 `C:\Users\lenovo\.workbuddy\binaries\models\faster-whisper-large-v3`(须带 `HF_ENDPOINT=https://hf-mirror.com` 和 `HF_HUB_DISABLE_XET=1`),2.9 GB 约 9 分钟。(2026-09-21 已完成,勿重下) - **纪律**:**下不动大模型就换素材/换方案,别在原模型上反复调参** —— 参数学到天亮也救不回 small。 ### [P1] Git Bash 的 `cd` 会静默失效 → npm/脚本跑错位置 标签: GitBash / cd / 静默失效 / npm / 路径 / 坑 - **现象**(2026-09-22 实测):`cd /c/Users/.../workspace && npm.cmd install ...` 报 `cd: null directory`,**但 shell 不报错、不改变目录**,后续 npm 实际跑在当前目录, 装进错误的 node_modules,或报 exit 127「command not found」。 - **原因**:WorkBuddy 的 Git Bash shim 里 `cd` 不可靠(与 coreutils 缺失同源)。复合命令前段「看似成功」会掩盖后段失败。 - **解法**:**一律不用 `cd`**。要进某目录就用绝对路径: - npm:`npm --prefix C:/Users/lenovo/.workbuddy/binaries/node/workspace install playwright` - 脚本/命令:直接给 exe 绝对路径,用 `&&` 串接而非 `cd ... &&`。 - **连带**:`ls`/`tail`/`cat`/`ffprobe` 在本机 bash 也常 `command not found`,但绝对路径调 exe 正常;重定向 `>`/`&&`/heredoc 可用。 ### [P1] WorkBuddy 的 Glob 对含中文的 pattern 失灵 → 改用 Python 标签: Glob / 中文路径 / 失效 / python / os.walk / 文件查找 - **现象**:`Glob pattern="C:/.../生成物/Lithos风格首页/shot*.png"` 返回空,但文件实际存在。 换成纯 ASCII 路径或英文目录名就正常。 - **原因**:Glob 工具对含非 ASCII 的 pattern 匹配异常(疑似路径归一化问题)。 - **解法**:查中文路径下的文件,**一律用 Python**: ```python import os root = r"C:/Users/lenovo/WorkBuddy/2026-09-22-10-58-10" for d, _, fs in os.walk(root): for f in fs: if f.endswith(".png"): print(os.path.join(d, f)) ``` 或用 `os.listdir` 后正则过滤。不要依赖 Glob 找中文路径文件。 ### [P1] 本机无头验证首选 Playwright(比 chrome-headless-shell 强得多,且已装好) 标签: 截图 / playwright / 无头浏览器 / 调试 / 验证 / chrome-headless-shell / 运行时错误 - **现象**:以前记的结论是「本机唯一可行 = chrome-headless-shell.exe + --virtual-time-budget,且页面须支持 ?shot=N 跑满帧停 rAF」。这个结论**太保守了**。 - **真况**:Node workspace 里 playwright 已经装好,浏览器也在: - 包:`C:\Users\lenovo\.workbuddy\binaries\node\workspace\node_modules\playwright` - 浏览器:`C:\Users\lenovo\AppData\Local\ms-playwright\chromium_headless_shell-1243\chrome-headless-shell-win64\chrome-headless-shell.exe`(是默认安装路径,**不用**设 PLAYWRIGHT_BROWSERS_PATH) - **为什么更强**:chrome-headless-shell + `--screenshot` 只能拍**首屏**,没法滚动,也抓不到报错。Playwright 能:`page.evaluate(() => window.scrollTo(...))` 拍任意滚动位置;`page.on('pageerror'|'console'|'requestfailed')` 抓运行时错误(**带堆栈**);还能读回页面状态做断言。 - **跑法(直接抄)**: ```sh cd C:\Users\lenovo\.workbuddy\binaries\node\workspace NODE_PATH="C:/Users/lenovo/.workbuddy/binaries/node/workspace/node_modules" \ "C:/Users/lenovo/.workbuddy/binaries/node/versions/22.22.2-3/node.exe" shot.cjs <输出目录> ``` 脚本骨架(放 `生成物/_probe/`,`_` 前缀不进交付): ```js const { chromium } = require('playwright'); const browser = await chromium.launch({ executablePath: 'C:/Users/lenovo/AppData/Local/ms-playwright/chromium_headless_shell-1243/chrome-headless-shell-win64/chrome-headless-shell.exe', args: ['--no-sandbox','--disable-gpu','--hide-scrollbars'] }); const page = await browser.newPage({ viewport:{width:1440,height:810}, deviceScaleFactor:1 }); const errs = []; page.on('pageerror', e => errs.push('PAGEERROR: ' + e.message)); page.on('requestfailed', r => errs.push('REQFAIL: ' + r.url())); await page.goto(URL, { waitUntil:'load', timeout:60000 }); await page.waitForTimeout(3500); // 等 webp/纹理加载 + 首屏收敛 const max = await page.evaluate(() => document.documentElement.scrollHeight - window.innerHeight); await page.evaluate(y => window.scrollTo(0,y), Math.round(max*0.45)); await page.waitForTimeout(3200); // ★ 见下 await page.screenshot({ path: out + '/s1.png' }); ``` - **两个必记的等待**:① 素材(webp/纹理)加载 → `waitForTimeout ≥ 3000`,否则拍到半成品; ② **滚动之后必须再等 ~3s** —— damp 阻尼型动效(速率 3.2/s)要 ~1.4s 才追到 99%, 等不够就会拍成中间态,误判成「第三屏内容没了」。 - 附带小坑:本地预览用 `python -m http.server ` 时,**必须在站点真正的根目录起**;起错目录会一路 404(不是服务死了,是根不对)。 - 关联:铁律4(能看图必须看图)/ 坑「--dump-dom 在 rAF 无限循环的页面上永不返回」 ### [P2] 无头验收 WebGL 页的三个补充事实(Playwright 管线续) 标签: Playwright / 无头浏览器 / WebGL / swiftshader / 截图 / 串图 - **headless 下 WebGL 要加 args**:`chromium.launch({executablePath:'.../chromium_headless_shell-1243/.../chrome-headless-shell.exe', args:['--no-sandbox','--use-gl=angle','--use-angle=swiftshader']})` —— 不加 swiftshader 系 args 时 `getContext('webgl')` 可能返回 null,页面静默降级,看起来像"效果没做出来"。launch 后先探 `window.__xx.gl()` 之类的 QA 钩子再截图。 - **交互验收用真实鼠标**:`page.mouse.move(x,y,{steps:3})` 之字扫 + `page.mouse.down()/up()`,配页面内 `window.__xx` 状态钩子读回(如投波条数),比只看静态截图可信。 - **Read 串图规律补充**:连读多张截图约半数内容与文件名错位(2026-09-26 复现两次)。对策维持三件套:内容自证(截图里有可辨认的页面状态,如滚动位置)+ md5 记录 + 最终交 present_files 预览。 ## 03. 用户画像与铁律 _悦皓是谁、什么绝对不能做_ ### [P0] 铁律 1:上下文用到 60% 自动压缩 标签: 铁律 / 上下文 / 压缩 / 必读 **2026-09-17 悦皓下达,最高优先级,所有任务必须严格执行。** - **不等他开口、不询问**:上下文占用一旦 **超过 60%**,立即主动压缩(长会话记忆快照 / 摘要压缩,沿用既有压缩机制)。 - 压缩**前**先把当前进度、关键路径、命令参数、待办写进工作记忆或日志,保证压缩后能无损接续。 - 压缩**后**主动报一句「已压缩」,然后继续干活,**不要停下来等确认**。 - 无例外,不因任务紧急/简单而跳过。 ### [P0] 铁律 2:任务中用到的 Skill 全部录入站点并分类整理 标签: 铁律 / skill / 知识库 / 分类 / 必读 **2026-09-17 悦皓下达,与既有「常驻铁律:自主维护知识库」叠加执行。** - 范围不止「新装的 / 新生成的」→ **任何任务中实际调用/使用过的 skill,事后一律录入线上站点并归类**。 - 归类必须落进既有分类体系(`_scan/skill_cats.py`、`_scan/content_data.py`),**别新造杂乱分类**;缺映射就补映射。 - 收尾一次做完三件事: 1. 新方法 / 坑 / 事实 → 追加到 `knowledge-base/INBOX.md`(`@@ITEM` 模板) 2. 用过的 skill → 归入手册「Skill 全文/目录」+ `skill-catalog` 两侧产物 3. 重建:跑 `_scan/sync_skills.py`(一条命令重建两侧) - **线上发布已自动执行**(2026-09-17 悦皓授权,不再每轮确认):本地重建后直接推线上两侧站点。 ### [P0] 绝对禁止编造素材(最高优先级) 标签: 铁律 / 禁止编造 悦皓明确说过:**"没有 AI 实践经验,不要编"**。 曾经写进简历的虚假内容(阿里达摩院 12 万条训练样本、垂直领域大模型微调平台、校园活动管理平台)已全部删掉,**不要再加回来**。 规则: - 不确定的信息用【待补】占位,或直接问他 - 没有的数据宁可留空,也不要"合理推测" - 技能等级只能往下写、不能往上抬(他说 Python/C 只是"了解") **已核实的真实素材**(可直接用): - 黄悦皓,华东师范大学电子信息类 2026 级大一在读(2026.09 – 2030.06) - 高中:哈尔滨师范大学附属中学(2023.09 – 2026.06),三年班长,组织艺术节/科技节/班团会等 6 场 - 围棋业余五段;围棋 AI 复盘实践 2026.07.01 起(KataGo 复盘个人对局) - 志愿者证书:哈师大附中 2026 招生季,2026.07.01–07.15,100 小时 - 雅思 6.5;荣誉:优秀团员、优秀班干部 - 电话 15546071698 | 邮箱 qiqizhenhao329@qq.com | 微信号 qiqizhenhao999 - 编程**仅了解** Python / C;**擅长做 PPT** - **无任何 AI 工程实习经验** ### [P0] 术语约定:「简历」= 作品集网站 标签: 术语 / 作品集 / 简历 悦皓长期把作品集当作"最新简历"。 - 他说「简历」「最新简历」「这个简历的网址」→ 指的是 **作品集网站** `https://hyh-portfolio.app.workbuddy.host/`(React+Vite SPA) - 根目录那个 348KB 单文件 `index.html` 是**旧版文字简历**,已部署在 `hyh-resume.app.workbuddy.host`,不是他口中的"简历" 技能矩阵真实档位(勿夸大): PPT/Keynote 5、组织统筹 5、跨团队对接 5、视觉版式 4、雅思 4、品牌VI 3、数据可视化 3、羽毛球 4、网球 4、Figma/PS/C/Python 2 ### [P1] 视觉偏好:暗色、克制、科技感 标签: 审美 / 风格 他的审美长期稳定在这三个词上:**暗色系 / 高级克制 / 科技感**(赛博朋克)。 已被他认可的具体实现: - 底 `#07080a` / 卡片 `#121317` / 文字 `#ECECEE` / 灰 `#8b8b93` - 赛博朋克版:青 `#00f0ff` + 品红 `#ff2a6d` + 荧光黄,深紫黑底 - 最终协调版(他最满意):**暖金** `#ffb03a` / 琥珀 `rgb(255,176,58)` + 暖棕黑底 `#0a0708` - 细节:52px 网格背景 + SVG 噪点 + 漂浮光斑 + 霓虹 text-shadow + 卡片 HUD 切角 他也会接受参考站/截图来迭代 —— **先出可运行版本,再按参考图改。** ### [P1] 偏好:本地优先、零依赖、单一稳定 URL 标签: 偏好 / 本地优先 - **数据存他自己的盘上**,能不联网就不联网,能不加依赖就不加依赖 - 交付物偏好 **单文件全内联 HTML**(CSS/JS/图片 base64 都塞进去),零外链,双击就能开 - **换一台设备访问时偏好单一稳定 URL,而不是本地路径** → 网页类交付物默认应该部署上线 - Windows + iPhone 双设备,共享相册走 Xiaomi 账号 i.mi.com 批处理 `.bat` 的坑:**内容必须纯 ASCII**(cmd 按 GBK 解码,含中文路径会乱码;文件名中文没事)。 ### [P0] 【铁律】分期出图:产出多单元的任务先只做 2 个给他看,停手等表态(2026-09-24 黄总下达) 标签: 分期出图 / 工作流 / 先出两张图 / 验收 / 消耗控制 / 协作总纲 - **原话**:「不要先都跑完,先出两张图让我简单看一下。如果效果满意,我会让你跑下去;如果不满意,就按我的意思修改。**你这样的话比较浪费时间**。」 - **背景**:他在批评「一次把三屏全做完 + 全量发布 + 收尾,才拿给他看」——若方向不对则全部作废,这才是真正的浪费。 - **规则**:任何**要产出多个单元**的任务(多张图 / 多屏页面 / 多方案 / 批量处理 / 长视频),**先只做 2 个单元给他看,然后停手等表态**。 - 为什么 **2**:1 张看不出对比、判断不了方向;10 张 = 全量跑 = 浪费。2 张是能看出方向差异的**最小可判断单元**。 - **做完 2 个绝不顺手把剩下的跑完。** - 满意 → 才跑剩下全量;不满意 → 按他的话改这 2 个,再出 2 个,循环。**他表态前不推进。** - 生成类(调模型出图/出视频)尤其严格:先摆参数清单 + 预估消耗,再用 2 张试方向。 - 例外:总量本就 ≤2 个单元的任务(改个字、单页草稿、一次查询)直接做完。 - **与「分期验收」的区别**:分期验收是按**模块**切(做完一屏就给看);分期出图是按**量**切(第一刀只切 2 个探方向)。两者叠加:先出 2 张定方向 → 再按模块推进、每块都给看。 - 已固化:skill `collab-playbook` v1.2 的 `## 0.5 分期出图`(置于 00 判层之前,先于一切流程)/ 常驻记忆元铁律 0。 @@ITEM sec=webgl level=P0 tags=排查法,定位,window.__scene,Playwright,逐隐藏,假bug ## 【方法】画面里那个来路不明的东西是谁 —— window.__scene + 逐个隐藏(2026-09-24 实测有效) - **场景**:泼溅森林第三屏画面中部横贯一条斜线。我先归因 bloom 阈值(改 threshold)、再归因远景地面外边缘(外径扩到 760)——**两次都错,烧了 6 轮**。 - **正解**:在场景创建后加一行 `window.__scene = scene;`,然后 Playwright 里: 1. 遍历 `window.__scene.children`,打印每项的 `type / geometry.type / material.type / position / boundingSphere.radius`; 2. **逐个 `visible = false` 并截图**,看图里那条线跟谁一起消失。 - **结果**:3 分钟定位到是 `lodFar`(远树圆锥组)。比"猜参数"快 10 倍。 - **适用**:任何「画面上多了个说不清的东西」的场合(多余的边、色块、亮点、黑影)。**别再猜了,直接二分定位。** - 注意:Group 的 `visible=false` 会连带隐藏全部子对象,正好用来一次排除一整组。 @@ITEM sec=webgl level=P0 tags=截图验证,scrollN,收敛,阻尼,假bug,无头截图 ## 【坑】截图验证前必须确认「进度真的收敛了」,否则会把中间态当成品态 debug(2026-09-24) - **现象**:无头截图滚动到底后 `waitForTimeout(3200)`,我据此判断 `scrollN ≈ 1.0`;实际只跑到 **~0.8**(阻尼 3.2/s 只是名义值,还有别的插值层)。 - **代价**:远树圆锥的淡出区间(0.60~0.84)与云海出现区间(0.56~0.94)重叠,scrollN=0.8 时圆锥仍以半透明盖在云海上 → 产生那条"假斜线"。我把它当成成品 bug 追了 6 轮。 - **规则**: 1. 截图用的等待时间要**给足**(本项目 3.2s → **6s** 才稳); 2. 用「渲染出来的可见结果」反推进度状态(例:云海 opacity≈0.82 → 说明 scrollN≥0.94),**不要凭名义阻尼值推算**; 3. 任何"滚动到某个位置"的验证,都要问一句:**这个位置是真的到了吗?** - **副产物**:顺带确立一条设计规则 —— **元素的淡出区间必须早于下一个元素的出现区间**,不重叠。远树淡出已从 0.60~0.84 提前到 0.46~0.66。 @@ITEM sec=webgl level=P1 tags=质感,detail,形体近似,细节密度,真实感,苔藓,云海 ## 【方法论】「太假」的病根不是形状,是细节密度低了 3 倍(2026-09-24 三组照片靶子实测) - **量出来**:同一把尺子(measure.py 的 detail = 相邻像素梯度均差) | | 参考照片 | 改前 | 改后 | |---|---|---|---| | 地面(苔藓微距) | 0.040 | 0.014(2.9 倍差) | **0.023** | | 树(逆光林) | 0.055 | 0.014(3.9 倍差) | — | | 云(云海) | 0.015 | 0.009 | **0.015(追平)** | - **根因一句话**:**做的是「形体近似」——一个圆锥代表一棵树、一个曲面代表一片地、一个平面代表一层云;参考图里每个对象表面还长着更小的对象。** - **可执行铁律:细节必须跨 3 个数量级** - 1m 级 = 几何高度场(SEG ≥ 480 @ 150m 才托得住) - 10cm 级 = 实例(苔丘半球 / 草簇 / 碎屑碎片) - 1cm 级 = 法线贴图高频重复。**`normal.repeat` 可以给到 albedo 的 2 倍**(法线高频不产生图案感,只贡献绒毛) - **两个反直觉点**: 1. **只改形状不改密度,用户会说「你改了吗」**——加几何变体的效果远不如把"那层光/那层颗粒"加回去; 2. **云的体积感 100% 来自「团内的受光明暗梯度 + 背光面偏青灰蓝」,不是形状**。只做受光面就是一块平板(实测 dark 0.083 vs 参考 0.306)。 - 相关:skill `webgl-terrain-hero` 的「质感三件套」章节(含地面四层 / 树叶长在枝上 / 云丘三贴图的具体参数)。 ## 04. 视频工程方法 _ffmpeg 剪辑 / 字幕 / 转码 / 交付规格_ ### [P0] 视频剪辑项目的标准作业流 标签: ffmpeg / 剪辑 / 流程 从军训合唱视频(V1→V7 迭代)沉淀的流程: 1. **先定硬性规格**:总时长(精确到帧,如 270.00s)、SAR 1:1 / DAR 16:9、faststart 2. **列出关键时间锚点**(升调、换歌、高潮),构建脚本里集中成变量,每次都校验 ``` 0:18 / 0:51 / 1:20 / 1:35 / 1:52升调 / 2:21换歌 / 2:26 / 3:05 / 3:40 / 3:43 / 4:15 ``` 3. **素材使用次数预分配**:要满足"同一素材不超过两次"这类约束,**开局一次性预分配**,不要再在渲染器里做 bump(会失控) 4. **逐段检查 return code**(本机 ffmpeg 静默失败多) 5. **交付三版本**:1080p master / 720p / 微信压制版(≤30MB) 6. **锚点校验**:抽出片段做 MAD(平均绝对差)比对,确认片段确实落在预期时间点 脚本命名惯用 `build_v.py`,构建目录 `构建V`。 ### [P1] 本机 ffmpeg 缺 xfade 时的特效替代实现 标签: ffmpeg / 特效 本机 build 无 xfade / xstack / rgbashift,这些效果是这样绕出来的: - **破碎感**:12 宫格 `crop` + `overlay`,位移随 `max(0, 1-t/0.42)` 衰减回位,叠 `noise` - **照片墙**:9 张 → `concat` → `tile=3x3` → 导出 PNG → `zoompan` 推拉 - **叠化**:A 加 `format=yuva420p` + `fade` alpha out,再 `overlay` 到 B 上 - **跳切**:同一张照片切 3 段不同 crop 位置的短镜头 - **抖动**:crop 后 x/y 用 `sin(t)/cos(t)` 偏移再放大 - **拍立得散落**:PIL 预渲染旋转白框 PNG,5 张从画外飞入 0.7s,尾帧闪白 - **双分屏 / 四宫格 / 大拍立得**:vid_slot 逻辑,视频够长就截取、不够就慢放 PIL 渲染脚本要用 venv 的 python 跑。 ### [P1] 中文字幕:字体、逐字出现、GBK 编码 标签: ffmpeg / 字幕 / 字体 可用字体(已复制到 `C:/fcconf/`): - `msyhbd.ttc` 微软雅黑粗体 - `STXINGKA.TTF` 华文行楷(叙事字幕/片尾首选) - `STKAITI`、`SIMLI`(在 `C:/Windows/Fonts/`) drawtext 必设 `FONTCONFIG_FILE=C:/fcconf/fonts.conf`,否则 fontconfig 报错。 **逐字出现**:每个字一个 drawtext,`enable='gte(t,)'`,可排成斜向阶梯或横向一行(`x=60+i*126`)。 **跳绳/卡拉OK 式对轴**:见自建 skill `ffmpeg-cn-video-subtitle`。 调用 ffmpeg 时中文参数要注意编码 —— 优先把文案写进脚本文件(UTF-8),不要走命令行传参。 ### [P1] 交付转码参数(浏览器兼容优先) 标签: 转码 / 规格 浏览器要能直接放,必须: ``` -c:v libx264 -profile:v high -pix_fmt yuv420p -c:a aac -movflags +faststart ``` 经验数值: - 背景视频:`-crf 28 -an`,960x544(**不要 upscale**,某次升到 720p 反而从 27MB 涨到 47MB) - 画质优先成片:`-crf 15 -preset slow -b:a 320k` - 微信压制版:720p,控制在 30MB 以内 - HEVC(hvc1) 源文件一定要转 H.264 —— 浏览器放不了 HEVC 验证:探测流信息确认 `High / yuv420p / AAC` 且 moov 在文件头;`curl -r` 中段返回 206。 ### [P2] AI 配图:生成 → 去水印 → 上线 标签: ImageGen / 配图 - ImageGen 单图耗 5–10 积分,**用前要告知悦皓** - 产物通常带**灰框 + 文字 + 水印**,必须 Pillow 二次处理: - 边缘水印:直接裁掉(如裁右 8.5% + 下 1.5%) - 角落文字水印:用左邻域克隆修补 - 放大:`Image.LANCZOS` 升采样 + `ImageFilter.UnsharpMask` - 全景图(360)要求:**人物全部背影/剪影/面具,无正脸**;目标 4096x2048 **严禁编造这条同样适用于图**:AI 生成图是"配图"不是"作品证据",文案里不要说成是自己拍的。 ### [P1] 用「能量包络」精确定位歌曲副歌/乐句边界来切音频长度 标签: ffmpeg / 音频 / 能量包络 / 切歌 / 副歌定位 / 九张机 给视频配乐、切歌时别凭感觉。抽单声道 wav → numpy 按 0.05–0.1s 窗算 RMS 包络:高能量段=副歌,低谷=气口/间奏。 本轮《九张机》(叶炫清) 实测:144–168s 是持续高能量副歌(0.29–0.33),168s 断崖跌到 0.18 进间奏; 173–188s 是「后面一句」(中能量,187.9s 有个强音=尾音),190.5s 才是再下一句前的气口谷。 命令:`ffmpeg -ss 130 -to 200 -i x.flac -ac 1 -ar 22050 -f wav _e.wav` → numpy 读 RMS。切点选在**低谷**最自然。 ### [P1] 视频长度迭代:延长/截断都用 afade 修头尾,别硬切 标签: ffmpeg / afade / 淡入淡出 / 爆音 / 视频长度 / 渲染 - 音乐头硬切会爆音/突兀 → `afade=t=in:st=0:d=0.4`;结尾 `afade=t=out:st=<时长-1.5>:d=1.5`。 - 校验:抽音轨算头 0.3s / 末 0.5s 的 RMS,应分别「渐起」与「≈0」。 - 画面总帧数 = 30fps × 秒数,改长度就改 render 脚本的 SECONDS 重渲(本机渲染见 env 条目 NODE_PATH)。 - 复核成片用 `ffmpeg -vn -ac 1 -ar 22050 -f wav` 抽音轨对时长,别只看容器 Duration。 ### [P1] 多张关键帧做「一镜到底」过渡视频:首尾帧三段生成 + 同关键帧接缝 + 裁边抹水印 标签: VideoGen / 一镜到底 / 首尾帧 / 关键帧拼接 / 水印 / ffmpeg / 行剖析 为「四张照片自然衔接成连贯过渡视频」跑通的完整流水线(2026-09-27 交付 15.13s 成片): - **方案**:每段用 VideoGen image-to-video **首尾帧模式**(`image`=上一关键帧、`last_image`=下一关键帧,1080P、`enable_audio=false`)生成 5s;**接缝两侧是同一张关键帧(帧级一致)→ 天然无缝**,ffmpeg concat `-c copy` 拼接零消耗。prompt 里的景物描述与实际图片不符也没关系——首尾帧模式模型跟图走(落位 corr 0.99),prompt 只是辅助。 - **水印抹除**:平台水印「视频由AI生成」固定在右下 y∈[1028,1058] → `crop=1812:1020:54:0`(裁线 1020 在字顶之上留 8px 裕量)+ `scale=1920:1080:lanczos,unsharp=5:5:0.55` + crf17 重压,三段同公式保证几何一致。 - **水印验证必须用「行剖析」**:大 ROI 亮阈值检测会被雾/云/亮叶误报(几千假阳性);干净判据 = x 只取字形段、逐行 `count(p > max(95, median+25))`,字形行会形成明显凸起带(1028 起跳、1058 收尾),1020 以下全零即切除成立;再配二值掩膜放大图目视复核 + 处理后帧底带(对应裸 y944–1020)确认无字形残迹。 - **接缝验收**:跨缝帧步长 MAD 应 ≈ 相邻帧步长(本例跨缝 10.5–11.8 vs 邻帧 10.8–12.6);硬切场景 MAD 通常 30+。终点帧互相关 corr ≥0.9 且跨缝步长 ≈ 入段正常步长 = 无可感知切点。 ## 05. 前端与视觉方法 _单文件网页 / 3D / 动效 / 暗色风格_ ### [P0] 单文件零依赖网页=悦皓的默认交付形态 标签: 单文件 / 网页 / 模式 他最喜欢、也最好交付的形态:**一个 `.html`,全内联 CSS/JS/图片,零外链,双击就能开。** 标准结构: ```html ``` 配套约定(已在多个项目验证): - **只改 DATA,其余代码不动** —— 这是给后续自动化留口子的关键 - **改完必须自检**:用 node 跑语法检查 + `getElementById` 目标存在性检查 + 无外链检查(写个 `_check.js` 复用) - 整站别直接链 `.md` 文件 —— 即使 HTTP 200 浏览器也不渲染(返回 text/markdown),要先用 `markdown` 库预渲染成 HTML - present_files 传本地 HTML **只托管那一个文件**,相对链接全 404 → 多文件站要么起 http.server,要么直接发布上线 已有实例:AI 情报页、华师大情报页、黑客松组队包/分工表/任务表、简历页、Bellaro 腕表页。 ### [P0] CSS 3D 的两个致命陷阱(preserve-3d 扁平化) 标签: CSS / 3D / 坑 做 3D 表盘/层叠结构时踩了两个坑,都会让 3D 静默失效: **陷阱 1:preserve-3d 容器内不能有 filter / mix-blend-mode / opacity<1 的中间元素**,否则整个子树被扁平化。 → 解法:所有光效(扫光、光晕、暗角)放在 3D 容器**外面**做 2D 叠加层。 → `filter` 放在带 `perspective` 的容器**自身**是安全的(入场 blur 动画可以用)。 **陷阱 2:CSS 动画的 transform 会覆盖 SVG 的 transform 属性**。 带 `transform="translate(x y)"` 定位的分组若同时挂旋转动画,元素会飞到画外。 → 解法:外层 `` 只放 translate,内层 `` 挂 CSS 旋转 + `transform-box:fill-box; transform-origin:center`。 **层间阴影**:不能用 `drop-shadow`(filter 会 flatten)。 → 改成 SVG 内 `cloneNode` 一份形状 → fill 纯黑 + `feGaussianBlur` + translate 偏移,SVG 滤镜不触发 CSS 扁平化。 视角经验:rotateX ≈ **46° 是甜区**(54° 太躺看不清内容)。 ### [P1] 视差滚动与 reveal 动画抢同一个 transform 标签: 视差 / 滚动 / transform **坑**:reveal 的入场位移若用 `transform`,会被视差脚本写入的 transform 覆盖(反之亦然)。 **解法:用 CSS 独立属性 `translate: 0 32px`** —— 与 `transform` 分属不同合成层,互不冲突。 同理 `.halo`(`translate(-50%,-50%)` 定位)和 `.floor`(perspective rotateX)自带 transform,必须**包一层 wrapper** 承接视差,否则 JS 会抹掉它们的定位/透视。 统一实现套路: - `[data-py]`(速度 px)+ `[data-imgpx]`(容器内图片,用父盒算基准) - `measure()` 先清 transform 再缓存 `base = top + scrollY + h/2` - `paint()` 按 `n = (视口中心 - base)/vh` 算 `translate3d` - scroll 只置 dirty 标志,全部在**单个 rAF** 里跑 - 正值=比页面慢(远),负值=快(近) 滚动还可驱动 CSS 变量:`--sp`(hero 进度,控制缩放/rotateX/淡出)、`--zs`(各层 translateZ 倍数,滚动时拉开出爆炸分解感)。 ### [P1] 全站背景协调:换底层颜色,不要在顶层盖 overlay 标签: 背景 / 配色 / 性能 **反复踩的坑**:想在紫黑底 + 青/品红的主题上叠暖金色 overlay 去配暖色视频 → 叠加后发浑,怎么调都不统一。 **根治做法:直接换底层色值,而不是叠加。** 一次做 40+ 处颜色替换: - 基底 `#07001a` → `#0a0708`(暖棕黑) - surface 系列 → 暖棕黑 - `--gold #fcee0a` → `#ffb03a` - bg-glow 品红 → 琥珀、hero orb → 琥珀 - bg-grid / CRT 扫描线 / 扫光束的青 → 暖金 - 所有 `rgba(7,0,26,x)` 深紫 → `rgba(10,7,8,x)` **坑**:源 CSS 里同一个色还有 `rgba(252,238,10,·)` 形式,压缩会转成 8 位 hex `#fcee0acc` —— **只替换 hex 会漏,rgba 形式也要替**。 **验证坑**:esbuild 会把 `::before` 压成 `:before`,线上 grep 校验要用单冒号。 **性能**:全屏 `backdrop-filter: blur(18px)` 是卡顿主因(每帧重绘),砍掉改轻渐变遮罩 .28–.45 + 噪点 .04;磨砂感只留给小卡片(blur 8px)。 ### [P1] 已被认可的高级感组件清单 标签: 动效 / 组件 这些组件悦皓都留下来了,新项目可直接复用思路: | 组件 | 要点 | |---|---| | 自定义光标 | accent 光点 + lerp 跟随圆环,hover 放大变金;仅 fine-pointer 且非 reduced-motion 时挂载 | | 打字机轮播 | 110ms 打 / 50ms 删 / 1800ms 停,闪烁 caret | | 标题逐行入场 | mask + lineUp,末行渐变流动 gradShift | | 数字 count-up | IntersectionObserver(0.5) 触发 1.4s easeOut;无数字的项直接静态 | | 3D tilt 卡片 | perspective 1000px,rotateX ±6° / rotateY ±8°,radial-gradient 光斑跟随鼠标;`.in` 覆盖 transition 为 0.18s 避免拖动迟滞 | | 磁吸按钮 | strength 0.28 | | 字符解码标题 | useScramble hook | | 粒子网络 | canvas 70 粒子互连 + 鼠标 170px 吸附连线 | | HUD 角标 | 8 条 linear-gradient 画 L 角 | | 跑马灯 | 两 group 各 50% 无缝循环 | | lightbox | Esc / 点击关闭、锁背景滚动 | | 360 全景 | three.js 球面 `scale(-1,1,1)` 内翻,拖拽 lon/lat lerp、滚轮 fov 35–100、静止 4s 自转、完整 dispose | **three.js 别用 npm 装**(卡死 8 分钟)→ 从 jsdelivr 下载 `three.module.min.js` 放 `src/vendor/` 相对引用。 ### [P2] 别忘了 reduced-motion 与打印样式 标签: 可访问性 - `prefers-reduced-motion`:自定义光标、粒子、旋转、自动播放视频全部降级/隐藏 - **打印**:简历类页面必须 `@media print` 强制浅色白底,并展开全部 Tab(用 `beforeprint` 事件) - Tabs 用 ARIA + ←/→ 键盘切换 - 响应式断点经验:641–980px 保持 2 列紧凑,≤640px 单列 还有一个 bug 类型记得:**同一会话多个 Edit 并发修改同一文件会互相覆盖**,必须串行。 ### [P0] 监控类界面:告警要按「状态跃迁」报,不能按「当期值」报 标签: 告警 / 监控 / 状态机 / 告警疲劳 / UX - **现象**:牛鞭监控页里写下 `if(ψ>2.5) 推一条告警`,结果 84 期跑下来告警流刷出 **263 条**, 几乎全是同一句话的重复("某级 ψ=4.33 严重放大"),列表被灌满,真正要看的信息(哪一级、 何时开始、有没有好转)全被埋掉。 - **原因**:`ψ>2.5` 是一个**持续状态**,不是**事件**。按当期值判断等于每期都重新发现同一个事实。 加"同类 8 期内去重"也没用——只把频次从每期降到每 8 期。 - **解法**:把阈值转成**档位**,只在档位变化时报: ```js var lv = be>2.5? 2 : (be>1.5? 1 : 0); if(lv !== this._psiLv[i]){ if(lv > this._psiLv[i]) pushAlert(...); // 恶化 else if(lv === 0) pushAlert("已回落至正常区间"); this._psiLv[i] = lv; } ``` 实测同样 220 期,告警从 263 条降到 **5 条**,且每条都有信息量。 - **附带收益**:顺手补了「已回落」通知——恢复信号和告警信号一样重要, 只报恶化会让人永远不知道问题是不是过去了。 - **推广**:任何"越线即告警"的看板都适用。先问一句:**这是状态还是事件?** 状态用跃迁,事件用触发。 ### [P0] 给用户看"改这个旋钮会怎样"时,用克隆实例试算,不要写死数字 标签: 试算 / what-if / 克隆 / 仿真 / 参数调优 - **背景**:参数面板旁边的"建议卡"想写"把 T 调到 3,ψ 大约降 10%"。这类数字如果写死, 换一份数据就错;实测确实错了——同一句"信息共享能大幅降牛鞭",在规律周期数据上 只降 2%,而"补货平滑 T"能降 91%,跟直觉完全相反。 - **解法**:**真跑一遍**。但绝不能在主实例上跑(`init()` 会清零全部历史,用户画面会闪、进度会丢)。 用原型继承克隆一个临时引擎: ```js function runProbe(mod, warmN, runN){ var s = Object.create(SIM); // 继承全部方法,写入的属性落在自己身上 mod(s); // 先改参数 resetClone(s, SIM.base); // 再按新参数设初值 for (var i=0;i` 和开关标题里。 - **修法**:索引项带三层关键词 1. 面板自身标题 2. 所属分组的**完整**标题(显示时也带上,否则搜"牛鞭"命中一条叫"ψ 演化"的项会让人对不上) 3. 面板内 `