Spaces:
Running
Running
Surf Animation Studio commited on
Commit ·
e8a358e
1
Parent(s): aca5ba2
chore: clear repository contents before replacement
Browse files- .gitignore +0 -7
- .gitkeep +0 -0
- README.md +0 -129
- assets/index-BCSP7C4S.js +0 -62
- assets/index-C7-Og2To.css +0 -1
- index.html +0 -15
- index.source.html +0 -14
- package-lock.json +0 -1122
- package.json +0 -15
- scripts/hf-tts.mjs +0 -38
- src/main.js +0 -382
- src/styles.css +0 -105
.gitignore
DELETED
|
@@ -1,7 +0,0 @@
|
|
| 1 |
-
node_modules/
|
| 2 |
-
dist/
|
| 3 |
-
.env
|
| 4 |
-
.env.*
|
| 5 |
-
!.env.example
|
| 6 |
-
*.log
|
| 7 |
-
.DS_Store
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.gitkeep
ADDED
|
File without changes
|
README.md
DELETED
|
@@ -1,129 +0,0 @@
|
|
| 1 |
-
---
|
| 2 |
-
title: 收猴子的故事 · 经济学动画工作台
|
| 3 |
-
emoji: 🍌
|
| 4 |
-
colorFrom: yellow
|
| 5 |
-
colorTo: indigo
|
| 6 |
-
sdk: static
|
| 7 |
-
pinned: false
|
| 8 |
-
---
|
| 9 |
-
|
| 10 |
-
# 《收猴子的故事》经济学动画工作台
|
| 11 |
-
|
| 12 |
-
> 一个原创的、浏览器内实时渲染的横屏 / 竖屏动画编辑与发布工作台。
|
| 13 |
-
>
|
| 14 |
-
> 主题:用“收猴子的故事”讲清楚制度、路径依赖、激励与组织文化。
|
| 15 |
-
>
|
| 16 |
-
> 本项目采用原创的数学可视化讲解语法:用圆、线、坐标、状态变化和镜头推进构成画面;不复制任何频道的片头、角色、口播或素材。
|
| 17 |
-
|
| 18 |
-
## 目标
|
| 19 |
-
|
| 20 |
-
- 在网页中直接预览一支约 3 分钟的中文经济学动画。
|
| 21 |
-
- 横屏 16:9:适合 B 站;竖屏 9:16:适合抖音,并预留安全区处理刘海与底部 UI 遮挡。
|
| 22 |
-
- 画面有连续运动、镜头推进、动态图形、字幕卡、音效与音乐;旁白可由浏览器本地语音朗读。
|
| 23 |
-
- 提供本地渲染导出:优先 MP4,当前浏览器不支持 MP4 编码时自动降级 WebM,并给出清晰提示。
|
| 24 |
-
- 提供可复制的 B 站 / 抖音标题、简介、标签。
|
| 25 |
-
- 提供横屏与竖屏封面图下载。
|
| 26 |
-
- 不依赖远程素材也能运行:动画、音乐、音效均由代码生成;外部 TTS 为可选增强。
|
| 27 |
-
|
| 28 |
-
## 故事与创作边界
|
| 29 |
-
|
| 30 |
-
故事使用经济学课堂里常见的“猴子—梯子—香蕉—喷水”寓言作为**隐喻**,不是对真实动物实验的复述。脚本会明确指出:这个寓言常被用来讨论“没有人知道为什么却继续遵守”的制度惯性,但它不应被当作真实实验史料。
|
| 31 |
-
|
| 32 |
-
讲解采用原创的“财经博主式、快节奏、图形化、带反问和暂停”的表达,不复刻任何在世创作者的独特措辞、声音、头像或品牌资产。
|
| 33 |
-
|
| 34 |
-
## 实现大纲
|
| 35 |
-
|
| 36 |
-
### 1. 研究与脚本
|
| 37 |
-
|
| 38 |
-
1. 固定核心命题:规则如何从激励变成惯例,再变成无人追问的禁区。
|
| 39 |
-
2. 将故事拆成 8 幕:钩子、第一次尝试、惩罚机制、群体复制、新人替换、规则遗忘、现实映射、结尾反问。
|
| 40 |
-
3. 为每幕配置旁白、屏幕文字、视觉动作、音效、音乐变化和时长。
|
| 41 |
-
4. 旁白文本与屏幕字幕分离,方便后续换成真实配音或外部 TTS。
|
| 42 |
-
|
| 43 |
-
### 2. 视觉与动画
|
| 44 |
-
|
| 45 |
-
1. 使用 Canvas 2D 作为确定性渲染层,统一时间轴与镜头状态。
|
| 46 |
-
2. 采用“对象形变、向量关系、坐标系、颜色编码”的数学讲解动画语法;不直接复制 3Blue1Brown 的画面或角色。
|
| 47 |
-
3. 用镜头参数驱动:世界坐标、缩放、平移、淡入淡出、擦除、脉冲和分层深度。
|
| 48 |
-
4. 同一套场景适配横屏与竖屏:竖屏时自动布局为上图下文,并保留 `safe-top` / `safe-bottom` 安全区。
|
| 49 |
-
5. 画面包括自绘猴子、梯子、香蕉、喷水装置、制度循环、激励函数、现实映射卡片和封面。
|
| 50 |
-
|
| 51 |
-
### 3. 音频
|
| 52 |
-
|
| 53 |
-
1. 浏览器内用 Web Audio API 合成低音、鼓点、木琴式动机、滴答、喷水、提示和转场音效。
|
| 54 |
-
2. 浏览器的 SpeechSynthesis 提供本地旁白试听,并在播放时随章节自动切换;导出时保证字幕、音乐和音效可录制。
|
| 55 |
-
3. `scripts/hf-tts.mjs` 提供可选 Hugging Face 中文 TTS 生成入口,密钥只从 `HF_TOKEN` 环境变量读取,不写入前端与 Git。生成服务不可用时,网页仍以浏览器本地旁白完整工作。
|
| 56 |
-
|
| 57 |
-
### 4. 网页工作台
|
| 58 |
-
|
| 59 |
-
1. 左侧播放区:实时画布、横 / 竖屏切换、播放 / 暂停、重播、音频开关。
|
| 60 |
-
2. 右侧控制区:章节时间轴、章节选择、导演说明、导出按钮、文案复制。
|
| 61 |
-
3. 底部信息条:当前画幅、时间码、渲染状态、MP4 能力检测。
|
| 62 |
-
4. 复制按钮将标题、描述、标签写入剪贴板,并在失败时提供可选文本区域。
|
| 63 |
-
|
| 64 |
-
### 5. 导出
|
| 65 |
-
|
| 66 |
-
1. 用 `canvas.captureStream()` 和 Web Audio `MediaStreamDestination` 组合本地媒体流。
|
| 67 |
-
2. 优先探测 `video/mp4`;若浏览器支持则直接导出 MP4。
|
| 68 |
-
3. 若不支持 MP4,导出器会在浏览器内按需加载 ffmpeg.wasm,把 WebM 转为 MP4;若网络或浏览器策略阻止加载,则明确降级为高兼容 WebM,不伪称 MP4。
|
| 69 |
-
4. 导出仅在用户点击后进行,不上传画面、旁白或项目数据;转码发生在本地浏览器。若本地存在 `public/audio/narration.wav`,导出会自动把它与音乐、音效混入;否则仍保留字幕和旁白试听。
|
| 70 |
-
|
| 71 |
-
### 6. 质量验收
|
| 72 |
-
|
| 73 |
-
- [ ] README 计划先于代码提交。
|
| 74 |
-
- [ ] 所有章节可从头播放,时间轴不越界。
|
| 75 |
-
- [ ] 横屏主体不被字幕区遮挡。
|
| 76 |
-
- [ ] 竖屏内容避开刘海与底部手势 / UI 安全区。
|
| 77 |
-
- [ ] 播放、暂停、重播、章节跳转可用。
|
| 78 |
-
- [ ] 音效与音乐开关可用;旁白可读。
|
| 79 |
-
- [ ] 导出按钮给出真实格式与能力提示,不伪称 MP4。
|
| 80 |
-
- [ ] 标题、描述、标签可复制。
|
| 81 |
-
- [ ] 横竖屏封面可下载。
|
| 82 |
-
- [ ] 不把任何 Hugging Face / GitHub 密钥提交到仓库。
|
| 83 |
-
|
| 84 |
-
## 计划中的大动作与提交节奏
|
| 85 |
-
|
| 86 |
-
1. `plan`: 写入本 README,确认范围、脚本和技术���线。
|
| 87 |
-
2. `scaffold`: 建立网页工程、基础样式、时间轴数据和本地运行入口。
|
| 88 |
-
3. `animation`: 完成横屏 / 竖屏 Canvas 动画、章节和字幕。
|
| 89 |
-
4. `audio`: 完成代码音乐、音效、旁白试听和媒体导出管线。
|
| 90 |
-
5. `publishing`: 完成平台文案复制、封面导出和安全区提示。
|
| 91 |
-
6. `qa`: 完成构建检查、交互检查、格式能力提示与最终说明。
|
| 92 |
-
|
| 93 |
-
每个大动作完成后都应提交一次 Git commit,并尝试推送到 `Ethscriptions/pols`。当前执行环境若没有该仓库的 GitHub 写权限,提交会保留在本地并在最终报告中明确标注阻塞点。
|
| 94 |
-
|
| 95 |
-
## 实施状态(2026-10-06)
|
| 96 |
-
|
| 97 |
-
- ✅ `plan`:README 先提交,commit `62afb0a`。
|
| 98 |
-
- ✅ `scaffold`:Vite 单页工程、Canvas 画布、8 幕时间轴、横竖屏布局,commit `bd7ebaf`。
|
| 99 |
-
- ✅ `animation`:猴子 / 梯子 / 香蕉 / 喷水 / 激励函数 / 现实映射 / 结尾提问的程序化动画已接入。
|
| 100 |
-
- ✅ `audio`:Web Audio 音乐与音效、浏览器中文旁白试听、可选 Hugging Face TTS 脚本,commit `74e1af7`。
|
| 101 |
-
- ✅ `publishing`:B 站 / 抖音标题、描述、标签复制;横屏 / 竖屏封面在浏览器生成并可下载。
|
| 102 |
-
- ✅ `export`:原生 MP4 能力探测;不支持时按需使用浏览器内 ffmpeg.wasm 转码,失败才降级 WebM,commit `f021f6e`。
|
| 103 |
-
- ✅ `qa`:`npm run build`、`node --check`、`git diff --check` 已通过。
|
| 104 |
-
- ⚠️ 推送阻塞:执行环境没有可用的 GitHub 登录凭证;`Ethscriptions/pols` 的匿名访问返回 404,且 `git push` 无法读取用户名。因此没有伪造“已推送”状态,所有提交目前保留在本地。
|
| 105 |
-
- ⚠️ 浏览器自动化验收阻塞:Playwright Chromium 已下载,但运行环境缺少 `libglib-2.0.so.0`,所以无法在此环境截取真实浏览器画面;Vite 开发服务器与生产构建均可启动。
|
| 106 |
-
- ⚠️ 本次调用 Hugging Face 推理端点时,运行环境 DNS 无法解析 `api-inference.huggingface.co`;因此未把未经验证的外部音频写进仓库。网页仍可用 SpeechSynthesis 旁白和代码合成音乐 / 音效运行。
|
| 107 |
-
|
| 108 |
-
## 本地运行
|
| 109 |
-
|
| 110 |
-
```bash
|
| 111 |
-
npm install
|
| 112 |
-
npm run dev
|
| 113 |
-
```
|
| 114 |
-
|
| 115 |
-
打开终端给出的本地地址。项目是 Vite 单页应用,所有核心动画在浏览器中渲染。
|
| 116 |
-
|
| 117 |
-
可选:在网络与模型服务可用时,使用 Hugging Face 生成一段中文旁白测试音频(不要把 token 写入源码或提交到 Git):
|
| 118 |
-
|
| 119 |
-
```bash
|
| 120 |
-
HF_TOKEN=hf_xxx npm run tts
|
| 121 |
-
```
|
| 122 |
-
|
| 123 |
-
网页默认不依赖这段外部音频,点击“试听”或播放时间轴即可由浏览器本地语音朗读。
|
| 124 |
-
|
| 125 |
-
## 安全说明
|
| 126 |
-
|
| 127 |
-
- 不在源码、README、构建产物或日志里保存 Hugging Face token。
|
| 128 |
-
- 若需要调用外部模型,请使用本地 `.env`(且 `.gitignore` 已忽略)或本地服务端代理。
|
| 129 |
-
- 本仓库不上传视频、语音、用户剪贴板内容或导出文件。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assets/index-BCSP7C4S.js
DELETED
|
@@ -1,62 +0,0 @@
|
|
| 1 |
-
(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))r(o);new MutationObserver(o=>{for(const a of o)if(a.type==="childList")for(const l of a.addedNodes)l.tagName==="LINK"&&l.rel==="modulepreload"&&r(l)}).observe(document,{childList:!0,subtree:!0});function n(o){const a={};return o.integrity&&(a.integrity=o.integrity),o.referrerPolicy&&(a.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?a.credentials="include":o.crossOrigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function r(o){if(o.ep)return;o.ep=!0;const a=n(o);fetch(o.href,a)}})();const ht="modulepreload",gt=function(t){return"/"+t},ot={},at=function(e,n,r){let o=Promise.resolve();if(n&&n.length>0){let p=function(u){return Promise.all(u.map(c=>Promise.resolve(c).then(d=>({status:"fulfilled",value:d}),d=>({status:"rejected",reason:d}))))};var l=p;document.getElementsByTagName("link");const i=document.querySelector("meta[property=csp-nonce]"),s=i?.nonce||i?.getAttribute("nonce");o=p(n.map(u=>{if(u=gt(u),u in ot)return;ot[u]=!0;const c=u.endsWith(".css"),d=c?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${u}"]${d}`))return;const f=document.createElement("link");if(f.rel=c?"stylesheet":ht,c||(f.as="script"),f.crossOrigin="",f.href=u,s&&f.setAttribute("nonce",s),document.head.appendChild(f),c)return new Promise((b,m)=>{f.addEventListener("load",b),f.addEventListener("error",()=>m(new Error(`Unable to preload CSS for ${u}`)))})}))}function a(i){const s=new Event("vite:preloadError",{cancelable:!0});if(s.payload=i,window.dispatchEvent(s),!s.defaultPrevented)throw i}return o.then(i=>{for(const s of i||[])s.status==="rejected"&&a(s.reason);return e().catch(a)})},xt=30,A=168,j=Math.PI*2,M=[{id:"hook",start:0,end:14,chapter:"01 / 钩子",title:"为什么没人敢拿?",kicker:"一个香蕉,五只猴子,零个解释",color:"#f4b33e",voice:"想象一间笼子,里面有五只猴子。笼子顶上挂着一串香蕉。看起来,它只差一把梯子。可是,为什么没有一只猴子去拿?",note:"用一个极简问题,把观众从“看见规则”带到“追问规则”。"},{id:"first",start:14,end:34,chapter:"02 / 第一次尝试",title:"第一个上梯子的人",kicker:"收益在上面,代价在下面",color:"#80d6c5",voice:"第一只猴子爬上梯子。它看见的,是立刻可得的香蕉;其他猴子看见的,却是即将到来的风险。于是,冷水从上面浇下来,所有猴子都被淋透。",note:"用收益 / 代价的对照解释:理性选择会被群体惩罚改写。"},{id:"punish",start:34,end:56,chapter:"03 / 惩罚机制",title:"群体开始自我管理",kicker:"惩罚一次,规则就记住了一半",color:"#ef6b69",voice:"第二次,另一只猴子试着爬梯子。它还没有碰到香蕉,其他猴子就先把它拽了下来。没有人再等喷水,因为惩罚已经被群体内化成了动作。",note:"红色不是“坏人”,而是一个被复制的反馈回路。"},{id:"replacement",start:56,end:78,chapter:"04 / 新成员",title:"新人不知道发生了什么",kicker:"制度会比记忆活得更久",color:"#a98bf2",voice:"现在,换掉一只老猴子,迎来一只新人。新人当然想爬梯子,可刚伸手,就被四只老猴子打了下来。它不懂冷水,却学会了不许碰梯子。",note:"关键转折:规则脱离原始原因,开始靠“别人都这样”存活。"},{id:"loop",start:78,end:100,chapter:"05 / 规则遗忘",title:"最后,没人见过冷水",kicker:"但所有人都说:不能爬",color:"#70a8ff",voice:"继续换人,直到笼子里已经没有一只猴子经历过冷水。此时,大家仍然在阻止别人爬梯子。问原因?答案通常是:一直都是这样。",note:"“一直都是这样”不是解释,它只是制度的缓存。"},{id:"model",start:100,end:124,chapter:"06 / 经济学模型",title:"这不是猴子的问题",kicker:"激励函数被藏起来了",color:"#ffcc67",voice:"把故事翻译成经济学:一个人做选择,会比较收益和代价。当惩罚足够强,最优策略就从“拿香蕉”变成了“阻止别人拿”。后来,惩罚消失了,策略却留下了。",note:"用坐标轴把“看得见的行为”和“看不见的激励”接上。"},{id:"reality",start:124,end:148,chapter:"07 / 现实映射",title:"我们在哪些笼子里?",kicker:"流程、文化、默认选项",color:"#5dd7ff",voice:"现实里,它可能是一条没人能解释的审批流程,一个“新人别碰”的按钮,或者一句“我们公司一直这么做”。制度不是敌人,但不再适用的制度,会把过去的答案伪装成今天的常识。",note:"不把隐喻简单等同于现实;重点是提出可验证的问题。"},{id:"exit",start:148,end:168,chapter:"08 / 结尾",title:"先问一句:冷水还在吗?",kicker:"好的规则经得起追问",color:"#f16eaa",voice:"所以,下次你听到“不能这样做”,先别急着反抗,也别急着服从。问三件事:原来的风险还在吗?代价是谁承担?有没有更低成本的替代方案?规则最危险的时刻,不是被打破,而是再也没人知道它为何存在。",note:"结尾留下行动问题,而不是把观众带到一个简单的“反制度”结论。"}],N={bilibili:{title:"为什么没人敢拿那串香蕉?|经济学里的“收猴子”故事",description:`一个香蕉、五只猴子、没有人记得的冷水。
|
| 2 |
-
|
| 3 |
-
这是一支原创图形化经济学动画:我们用“收猴子的故事”拆解制度惯性、路径依赖与群体激励。故事是常见寓言隐喻,不是对真实动物实验的复述。
|
| 4 |
-
|
| 5 |
-
看完试着问自己:你所在的流程里,有没有一条“大家都说不能碰”,但没人说得清为什么的规则?
|
| 6 |
-
|
| 7 |
-
如果喜欢这种把抽象机制画出来的讲解,欢迎收藏、转发,评论区留下你的“冷水还在吗”。`,tags:"#经济学 #制度 #组织文化 #路径依赖 #财经科普 #动画 #思维方式"},douyin:{title:"为什么没人敢拿香蕉?收猴子的故事讲透制度惯性",description:`没有人见过冷水,但所有人都说:不能爬。
|
| 8 |
-
|
| 9 |
-
用 3 分钟看懂:激励如何变成规则,规则如何变成习惯,以及我们如何把过去的答案带进今天。
|
| 10 |
-
|
| 11 |
-
寓言是隐喻,不是真实实验史料。你身边有没有“不能碰,但说不清为什么”的流程?`,tags:"#经济学 #收猴子的故事 #职场 #认知 #知识 #科普 #制度惯性"}},St=document.querySelector("#app");St.innerHTML=`
|
| 12 |
-
<main class="shell">
|
| 13 |
-
<header class="topbar">
|
| 14 |
-
<div class="brand"><span class="brand-mark">∿</span><div><strong>MONKEY / RULES</strong><span>经济学动画工作台 · 原创讲解</span></div></div>
|
| 15 |
-
<div class="top-status"><span class="live-dot"></span><span id="renderStatus">实时画布</span><span class="status-divider"></span><span id="formatStatus">16:9 横屏</span></div>
|
| 16 |
-
</header>
|
| 17 |
-
<section class="workspace">
|
| 18 |
-
<section class="stage-panel">
|
| 19 |
-
<div class="stage-shell" id="stageShell">
|
| 20 |
-
<canvas id="stage" width="1280" height="720" aria-label="收猴子的故事动画画布"></canvas>
|
| 21 |
-
<div class="stage-safe safe-top">安全区</div>
|
| 22 |
-
<div class="stage-safe safe-bottom">安全区 · 平台 UI 避让</div>
|
| 23 |
-
<div class="stage-hud"><span id="sceneLabel">01 / 钩子</span><span id="timeLabel">00:00 / 02:48</span></div>
|
| 24 |
-
</div>
|
| 25 |
-
<div class="controls">
|
| 26 |
-
<button class="primary-control" id="playButton" type="button"><span class="play-icon">▶</span><span>播放</span></button>
|
| 27 |
-
<button class="icon-control" id="restartButton" type="button" aria-label="从头播放">↺</button>
|
| 28 |
-
<button class="icon-control" id="muteButton" type="button" aria-label="打开或关闭声音">◖</button>
|
| 29 |
-
<div class="scrub"><input id="scrub" type="range" min="0" max="168" step="0.01" value="0" aria-label="时间轴"/><div class="scrub-progress" id="scrubProgress"></div></div>
|
| 30 |
-
<div class="frame-rate">30 FPS</div>
|
| 31 |
-
</div>
|
| 32 |
-
<div class="orientation-row"><span class="control-label">画幅</span><button class="chip active" data-orientation="landscape" type="button">B站 · 16:9</button><button class="chip" data-orientation="portrait" type="button">抖音 · 9:16</button><span class="safe-note">竖屏自动保留刘海 / 手势安全区</span></div>
|
| 33 |
-
</section>
|
| 34 |
-
<aside class="inspector">
|
| 35 |
-
<div class="inspector-head"><div><span class="eyebrow">EDIT / STORYBOARD</span><h1>收猴子的故事</h1></div><span class="duration-badge">02:48</span></div>
|
| 36 |
-
<p class="lede">用一个寓言,拆开“大家都这样做”背后的激励结构。</p>
|
| 37 |
-
<div class="chapter-list" id="chapterList"></div>
|
| 38 |
-
<div class="inspector-card voice-card"><div class="card-title"><span>本幕旁白</span><button id="speakButton" class="text-button" type="button">试听</button></div><p id="voiceText"></p></div>
|
| 39 |
-
<div class="inspector-card note-card"><div class="card-title"><span>导演提示</span><span class="tiny-dot"></span></div><p id="directorNote"></p></div>
|
| 40 |
-
<button class="export-button" id="exportButton" type="button"><span>↧</span><div><strong>本地导出视频</strong><small>优先 MP4 · 画面与音乐在浏览器内渲染</small></div><span class="export-arrow">→</span></button>
|
| 41 |
-
<div class="export-progress" id="exportProgress" hidden><div class="progress-top"><span id="exportLabel">准备导出…</span><span id="exportPercent">0%</span></div><div class="progress-track"><span id="exportBar"></span></div></div>
|
| 42 |
-
</aside>
|
| 43 |
-
</section>
|
| 44 |
-
<section class="publish-section">
|
| 45 |
-
<div class="section-heading"><div><span class="eyebrow">PUBLISH KIT</span><h2>发布文案 & 封面</h2></div><span class="section-caption">每个平台一键复制 · 原创封面可下载</span></div>
|
| 46 |
-
<div class="publish-grid">
|
| 47 |
-
<article class="publish-card"><div class="platform-head"><span class="platform-icon bilibili-icon">◈</span><div><strong>B站横屏</strong><small>标题 / 简介 / 标签</small></div><button class="copy-button" data-copy="bilibili" type="button">复制整套</button></div><label>标题<textarea readonly id="bilibiliTitle"></textarea></label><label>简介<textarea readonly id="bilibiliDescription"></textarea></label><label>标签<textarea readonly id="bilibiliTags"></textarea></label></article>
|
| 48 |
-
<article class="publish-card"><div class="platform-head"><span class="platform-icon douyin-icon">♪</span><div><strong>抖音竖屏</strong><small>标题 / 简介 / 标签</small></div><button class="copy-button" data-copy="douyin" type="button">复制整套</button></div><label>标题<textarea readonly id="douyinTitle"></textarea></label><label>简介<textarea readonly id="douyinDescription"></textarea></label><label>标签<textarea readonly id="douyinTags"></textarea></label></article>
|
| 49 |
-
<article class="cover-card"><div class="platform-head"><span class="platform-icon cover-icon">✦</span><div><strong>原创封面</strong><small>随画幅生成 · 无外部素材</small></div></div><div class="cover-previews"><div class="cover-preview landscape-cover"><canvas id="landscapeCover" width="640" height="360"></canvas><span>16:9</span></div><div class="cover-preview portrait-cover"><canvas id="portraitCover" width="360" height="640"></canvas><span>9:16</span></div></div><div class="cover-actions"><button id="downloadLandscape" type="button">下载横屏封面</button><button id="downloadPortrait" type="button">下载竖屏封面</button></div></article>
|
| 50 |
-
</div>
|
| 51 |
-
</section>
|
| 52 |
-
<footer class="footer"><span>原创图形化讲解 · 不上传画面与剪贴板内容</span><span>故事为经济学寓言隐喻 · 不是动物实验史料</span></footer>
|
| 53 |
-
</main>
|
| 54 |
-
`;const R=document.querySelector("#stage"),w=R.getContext("2d"),rt=document.querySelector("#stageShell"),X=document.querySelector("#playButton"),it=document.querySelector("#scrub"),wt=document.querySelector("#scrubProgress"),kt=document.querySelector("#timeLabel"),Tt=document.querySelector("#sceneLabel"),Y=document.querySelector("#chapterList"),Ct=document.querySelector("#voiceText"),Lt=document.querySelector("#directorNote"),lt=document.querySelector("#muteButton"),H=document.querySelector("#renderStatus"),At=document.querySelector("#formatStatus"),z=document.querySelector("#exportButton"),Mt=document.querySelector("#exportProgress"),F=document.querySelector("#exportBar"),E=document.querySelector("#exportLabel"),_=document.querySelector("#exportPercent");let h="landscape",g=0,L=!1,P=!1,U=0,st=0,O=null;const T=(t,e=0,n=1)=>Math.max(e,Math.min(n,t)),ct=(t,e,n)=>t+(e-t)*n,qt=t=>t*t*(3-2*t),C=t=>qt(T(t)),V=t=>`${String(Math.floor(t/60)).padStart(2,"0")}:${String(Math.floor(t%60)).padStart(2,"0")}`,K=(t=g)=>M.findIndex(e=>t>=e.start&&t<e.end)>=0?M.findIndex(e=>t>=e.start&&t<e.end):M.length-1;function J(){const t=h==="portrait";R.width=t?720:1280,R.height=t?1280:720,rt.classList.toggle("portrait",t),rt.classList.toggle("landscape",!t),At.textContent=t?"9:16 竖屏":"16:9 横屏",B(g)}function $(){const t=K(),e=M[t];t!==st&&(L&&!P&&O&&(O.sfx("tick"),ut()),st=t),Tt.textContent=e.chapter,kt.textContent=`${V(g)} / ${V(A)}`,it.value=g.toFixed(2),wt.style.width=`${g/A*100}%`,Ct.textContent=e.voice,Lt.textContent=e.note,[...Y.children].forEach((n,r)=>n.classList.toggle("active",r===t))}function Et(){Y.innerHTML=M.map((t,e)=>`<button class="chapter-item" data-index="${e}" type="button"><span class="chapter-no">${String(e+1).padStart(2,"0")}</span><span class="chapter-copy"><strong>${t.title}</strong><small>${t.chapter.replace(/^\d+ \/ /,"")}</small></span><span class="chapter-time">${V(t.start)}</span></button>`).join(""),Y.querySelectorAll(".chapter-item").forEach(t=>t.addEventListener("click",()=>{g=M[Number(t.dataset.index)].start,$(),B(g)}))}function I(t,e,n,r,o,a){t.beginPath(),t.roundRect(e,n,r,o,a),t.closePath()}function y(t,e,n,r,o,a,l=500,i="left"){t.font=`${l} ${o}px Inter, "Noto Sans SC", "PingFang SC", sans-serif`,t.fillStyle=a,t.textAlign=i,t.textBaseline="middle",t.fillText(e,n,r)}function k(t,e,n,r,o,a=null,l=1){t.beginPath(),t.arc(e,n,r,0,j),o&&(t.fillStyle=o,t.fill()),a&&(t.strokeStyle=a,t.lineWidth=l,t.stroke())}function x(t,e,n,r,o,a,l=1,i=[]){t.beginPath(),t.moveTo(e,n),t.lineTo(r,o),t.strokeStyle=a,t.lineWidth=l,t.setLineDash(i),t.stroke(),t.setLineDash([])}function Pt(t,e,n,r,o,a,l,i,s=500){t.font=`${s} ${l}px Inter, "Noto Sans SC", "PingFang SC", sans-serif`,t.fillStyle=i,t.textAlign="left",t.textBaseline="top";let p="",u=r;for(const c of e){const d=p+c;t.measureText(d).width>o&&p?(t.fillText(p,n,u),u+=a,p=c):p=d}return p&&t.fillText(p,n,u),u+a}function q(t){const e=Math.sin(t*17.17+3.2)*43758.5453;return e-Math.floor(e)}function Rt(t,e,n,r,o){const a=t.createLinearGradient(0,0,e,n);a.addColorStop(0,"#080b16"),a.addColorStop(.55,"#0c1021"),a.addColorStop(1,"#10192c"),t.fillStyle=a,t.fillRect(0,0,e,n),t.save(),t.globalAlpha=.2;for(let s=0;s<70;s++){const p=q(s)*e,u=q(s+99)*n;k(t,p,u,1+q(s+31)*2,"#b4c4ff")}t.globalAlpha=.12,x(t,e*.08,n*.78,e*.92,n*.78,r.color,1),x(t,e*.08,n*.78,e*.08,n*.18,r.color,1),t.restore();const l=t.createRadialGradient(e*.72,n*.2,0,e*.72,n*.2,Math.max(e,n)*.7);l.addColorStop(0,`${r.color}20`),l.addColorStop(1,"transparent"),t.fillStyle=l,t.fillRect(0,0,e,n);const i=o*18%80;t.save(),t.globalAlpha=.05;for(let s=-80;s<e+80;s+=80)x(t,s+i,0,s-200+i,n,"#fff",1);t.restore()}function $t(t,e,n,r,o){const a=e*(h==="portrait"?.09:.07);y(t,"经济学寓言 / ORIGINAL MOTION ESSAY",a,n*(h==="portrait"?.1:.11),h==="portrait"?13:15,"#a4abc2",650);const l=h==="portrait"?108:126;I(t,e-a-l,n*.075,l,28,14),t.fillStyle=`${r.color}1f`,t.fill(),y(t,r.chapter,e-a-l/2,n*.075+14,11,r.color,700,"center");const i=C(Math.min(1,o/1.25));t.save(),t.globalAlpha=i,y(t,r.title,a,n*(h==="portrait"?.19:.21),h==="portrait"?34:42,"#f6f7fb",800),y(t,r.kicker,a,n*(h==="portrait"?.24:.285),h==="portrait"?15:18,r.color,650),t.restore()}function D(t,e,n,r,o="neutral",a="#d9a776",l=0){t.save(),t.translate(e,n),t.scale(r,r);const i=Math.sin(l*4+e*.01)*2;t.translate(0,i),k(t,-25,-40,17,"#b77c5a"),k(t,25,-40,17,"#b77c5a"),k(t,0,-42,30,"#d9a776"),k(t,0,-36,22,"#f0c79d"),k(t,-9,-47,3,"#111827"),k(t,9,-47,3,"#111827"),o==="scared"?x(t,-10,-28,10,-28,"#422f2a",3):(t.beginPath(),t.arc(0,-29,9,0,Math.PI),t.strokeStyle="#422f2a",t.lineWidth=3,t.stroke()),t.strokeStyle=a,t.lineWidth=8,t.lineCap="round",x(t,-25,0,-35,38,a,8),x(t,25,0,35,38,a,8),x(t,-19,4,-42,20,a,8),x(t,19,4,42,20,a,8),t.fillStyle=a,t.beginPath(),t.ellipse(-35,42,12,5,0,0,j),t.fill(),t.beginPath(),t.ellipse(35,42,12,5,0,0,j),t.fill(),t.restore()}function Q(t,e,n,r,o,a="#e7b55c",l=0){t.save(),t.translate(e,n),t.rotate(l),t.strokeStyle=a,t.lineWidth=7,t.lineCap="round",x(t,-o/2,0,-o/2,-r,a,7),x(t,o/2,0,o/2,-r,a,7);for(let i=-20;i>-r+5;i-=27)x(t,-o/2,i,o/2,i,a,5);t.restore()}function Z(t,e,n,r,o=!1){t.save(),t.translate(e,n),t.scale(r,r),o&&(t.globalAlpha=.18,k(t,0,0,72,"#f4b33e"),t.globalAlpha=1),t.strokeStyle="#f4c453",t.lineWidth=20,t.lineCap="round",t.beginPath(),t.arc(0,0,50,-.2,Math.PI*.85),t.stroke(),t.strokeStyle="#cf8b2f",t.lineWidth=6,t.beginPath(),t.arc(0,0,50,-.2,Math.PI*.85),t.stroke(),k(t,54,-7,6,"#a96b29"),k(t,-38,38,6,"#a96b29"),t.restore()}function G(t,e,n,r,o,a=.8,l=0){t.save(),t.globalAlpha=a,t.strokeStyle="#66d5f6",t.lineWidth=3,t.lineCap="round";for(let i=0;i<12;i++){const s=e+i/11*r,p=Math.sin(l*8+i)*8;x(t,s,n,s+p,n+o*(.5+q(i)*.5),"#66d5f6",3),k(t,s+p,n+o*(.5+q(i)*.5),3,"#b5f0ff")}t.restore()}function Bt(t,e,n,r,o){const a=h==="portrait",l=a?e*.5:e*.69,i=a?n*.37:n*.53,s=n*.78,p=C(Math.min(1,o/2));Z(t,l,i-150,a?1.1:1.35,!0),x(t,l,i-120,l,i+6,"#9a6d4d",4),Q(t,l,i+190,300,80,"#e8bd71"),x(t,a?e*.1:e*.25,s,a?e*.9:e*.88,s,"#5b6a89",5);const u=a?3:5;for(let c=0;c<u;c++){const d=a?e*(.24+c*.26):e*(.3+c*.14);D(t,d,s-42,a?.82:.72,"neutral",c%2?"#ce9d75":"#c48762",o)}if(r.id==="hook"&&(t.save(),t.globalAlpha=p,y(t,"这是一道经济学题",a?e*.5:e*.13,a?n*.91:n*.86,a?18:22,"#dce3fa",700,a?"center":"left"),t.restore()),r.id==="first"){const c=C(T(o/7));D(t,l,s-35-c*180,.75,"neutral","#d09a73",o),G(t,l-100,i-60,200,170,C(T((o-7)/2)),o),y(t,"收益",l+115,i-164,16,"#f4c453",700),y(t,"代价",l+120,i+20,16,"#66d5f6",700)}if(r.id==="punish"){for(let c=0;c<3;c++){const d=-1.3+c*1.3;x(t,l+Math.cos(d)*110,s-100,l+Math.cos(d)*70,s-55,"#ef6b69",7)}G(t,l-100,i-40,200,170,.5,o),y(t,"惩罚变成了动作",l,n*.86,18,"#ef8b88",700,"center")}if(r.id==="replacement"){const c=a?e*.22:e*.34;D(t,c,s-45,.82,"scared","#a98bf2",o),k(t,c,s-160,38,"#a98bf220","#a98bf2",2),y(t,"?",c,s-160,28,"#a98bf2",800,"center")}if(r.id==="loop"){G(t,l-100,i-40,200,170,.08,o);for(let c=0;c<3;c++){const d=a?e*(.2+c*.3):e*(.34+c*.12);x(t,d,s-125,d+(c===1?-24:24),s-70,"#70a8ff",6)}y(t,"冷水已经消失",l,n*.86,18,"#8eb9ff",700,"center")}}function Ot(t,e,n,r,o){const a=h==="portrait",l=e*(a?.1:.09),i=a?n*.66:n*.68,s=l,p=e-l*2,u=a?n*.22:n*.28,c=C(T(o/2));t.save(),t.globalAlpha=c,x(t,s,i-u,s,i,"#7784a4",2),x(t,s,i,s+p,i,"#7784a4",2),y(t,"行为成本",s-12,i-u-10,12,"#96a1bb",500,"right"),y(t,"行动:拿香蕉 →",s+p,i+25,12,"#96a1bb",500,"right");const d=s+p*.54;t.beginPath(),t.moveTo(s,i-u*.18),t.bezierCurveTo(s+p*.18,i-u*.2,d-40,i-u*.2,d,i-u*.85),t.bezierCurveTo(d+50,i-u*.9,s+p*.8,i-u*.1,s+p,i-u*.08),t.strokeStyle=r.color,t.lineWidth=5,t.stroke();const f=ct(s+p*.24,s+p*.7,C(T(o/8))),b=i-u*.2-C(T(o/8))*u*.55;k(t,f,b,10,"#fff",r.color,5),I(t,f-74,b-45,150,28,14),t.fillStyle="#18213a",t.fill(),y(t,o<8?"收益被看见":"惩罚被内化",f,b-31,12,"#f5f7ff",650,"center"),t.restore();const m=a?e*.5:e*.75,v=a?n*.36:n*.45;Q(t,m,v+90,130,42,"#e3b466",Math.sin(o)*.02),Z(t,m,v-10,.6,!0),y(t,"看得见的行为",m,v+140,14,"#dce3fa",600,"center")}function Ut(t,e,n,r,o){const a=h==="portrait",l=[["流程",`审批三次
|
| 55 |
-
因为一直这样`],["按钮",`新人别碰
|
| 56 |
-
没人解释`],["文化",`默认选项
|
| 57 |
-
变成常识`]],i=a?n*.38:n*.48,s=a?e*.29:e*.25,p=a?e*.19:e*.2;l.forEach((u,c)=>{const d=p+c*s,f=C(T((o-c*.8)/1.5));if(t.save(),t.globalAlpha=f,t.translate(d,i+(1-f)*30),I(t,d-70,i-70,140,140,18),t.fillStyle="#17213a",t.fill(),t.strokeStyle=c===1?r.color:"#334365",t.lineWidth=2,t.stroke(),k(t,d,i-32,18,`${r.color}2c`,r.color,2),y(t,c===0?"≡":c===1?"□":"↻",d,i-32,20,r.color,800,"center"),y(t,u[0],d,i+10,17,"#f5f7ff",700,"center"),u[1].split(`
|
| 58 |
-
`).forEach((b,m)=>y(t,b,d,i+38+m*18,11,"#aeb8d2",500,"center")),t.restore(),c<2){const b=C(T((o-1.2-c*.8)/1.2));x(t,d+82,i,d+s-82,i,`${r.color}88`,3),y(t,"→",d+s-72,i-3,24,r.color,700,"center"),b>0&&k(t,ct(d+82,d+s-82,b),i,5,r.color)}}),y(t,"过去的风险 → 今天的默认",e*.5,a?n*.72:n*.77,a?20:24,r.color,750,"center")}function Vt(t,e,n,r,o){const a=h==="portrait",l=e*.5,i=a?n*.48:n*.5,s=C(T(o/2)),p=(a?e:n)*(.12+C(T(o/9))*.18);t.save(),t.globalAlpha=s,t.strokeStyle=`${r.color}99`,t.lineWidth=2;for(let c=0;c<3;c++)t.beginPath(),t.arc(l,i,p+c*24,0,j),t.stroke();k(t,l,i,p*.56,"#15233a",r.color,3),y(t,"问",l,i-5,a?44:56,"#f7f4ed",800,"center"),y(t,"冷水还在吗?",l,i+p+60,a?24:32,r.color,750,"center"),t.restore(),["风险还在吗?","代价谁承担?","有更低成本的方案吗?"].forEach((c,d)=>{const f=a?n*(.72+d*.06):n*(.76+(d-1)*.08),b=C(T((o-2-d*.7)/1.2));t.save(),t.globalAlpha=b,I(t,l-(a?135:150),f-18,a?270:300,36,18),t.fillStyle="#18213a",t.fill(),y(t,c,l,f,a?14:16,"#dbe6ff",650,"center"),t.restore()})}function B(t=g){const e=R.width,n=R.height,r=K(t),o=M[r],a=t-o.start;w.clearRect(0,0,e,n),Rt(w,e,n,o,t),$t(w,e,n,o,a),["hook","first","punish","replacement","loop"].includes(o.id)?Bt(w,e,n,o,a):o.id==="model"?Ot(w,e,n,o,a):o.id==="reality"?Ut(w,e,n,o,a):Vt(w,e,n,o,a);const l=e*(h==="portrait"?.09:.07),i=o.voice,s=h==="portrait"?n*.87:n*.91;w.save(),w.globalAlpha=.93,I(w,l,s-28,e-l*2,h==="portrait"?76:58,16),w.fillStyle="#090d1ddd",w.fill(),w.strokeStyle=`${o.color}38`,w.lineWidth=1,w.stroke(),Pt(w,i,l+18,s-17,e-l*2-36,h==="portrait"?20:18,h==="portrait"?13:14,"#e6ebfb",500),w.restore()}function dt(t){U||(U=t);const e=Math.min(.05,(t-U)/1e3);U=t,L&&(g+=e,g>=A&&(g=0,L=!1,X.innerHTML='<span class="play-icon">▶</span><span>播放</span>'),$(),B(g)),requestAnimationFrame(dt)}function pt(t){L=t,U=0,X.innerHTML=L?'<span class="play-icon">Ⅱ</span><span>暂停</span>':'<span class="play-icon">▶</span><span>播放</span>',H.textContent=L?"实时播放":"实时画布",L&&!P&&tt().startPreview()}function tt(){if(O)return O;const t=new(window.AudioContext||window.webkitAudioContext),e=t.createGain();e.gain.value=.18,e.connect(t.destination);const n=t.createMediaStreamDestination();e.connect(n);let r=!1;function o(s="tick",p=t.currentTime){const u=t.createOscillator(),c=t.createGain(),d=s==="spray"?760:s==="drop"?180:420;u.type=s==="spray"?"triangle":"sine",u.frequency.setValueAtTime(d,p),u.frequency.exponentialRampToValueAtTime(d*.55,p+(s==="spray"?.25:.12)),c.gain.setValueAtTime(1e-4,p),c.gain.exponentialRampToValueAtTime(s==="spray"?.15:.09,p+.012),c.gain.exponentialRampToValueAtTime(1e-4,p+(s==="spray"?.28:.16)),u.connect(c).connect(e),u.start(p),u.stop(p+.32)}function a(s=t.currentTime+.05,p=0,u=A){const c=[55,65.41,73.42,82.41,73.42,65.41,49,65.41];for(let d=0;d<Math.ceil(u/1.5);d++){const f=s+d*1.5,b=c[(d+p)%c.length],m=t.createOscillator(),v=t.createGain();m.type=d%4===0?"triangle":"sine",m.frequency.setValueAtTime(b,f),v.gain.setValueAtTime(1e-4,f),v.gain.linearRampToValueAtTime(d%4===0?.055:.025,f+.08),v.gain.exponentialRampToValueAtTime(1e-4,f+1.32),m.connect(v).connect(e),m.start(f),m.stop(f+1.35),d%4===0&&o("drop",f)}}function l(){t.state==="suspended"&&t.resume(),r||(a(),r=!0)}async function i(){const s=new(window.AudioContext||window.webkitAudioContext),p=s.createGain();p.gain.value=.2,p.connect(s.destination);const u=s.createMediaStreamDestination();p.connect(u);const c=[55,65.41,73.42,82.41,73.42,65.41,49,65.41],d=s.currentTime+.08;for(let f=0;f<Math.ceil(A/1.5);f++){const b=d+f*1.5,m=s.createOscillator(),v=s.createGain();m.type=f%4===0?"triangle":"sine",m.frequency.setValueAtTime(c[f%c.length],b),v.gain.setValueAtTime(1e-4,b),v.gain.linearRampToValueAtTime(f%4===0?.055:.025,b+.08),v.gain.exponentialRampToValueAtTime(1e-4,b+1.32),m.connect(v).connect(p),m.start(b),m.stop(b+1.35)}M.forEach((f,b)=>{const m=d+f.start,v=s.createOscillator(),S=s.createGain();v.type="sine",v.frequency.setValueAtTime(b%2?640:420,m),S.gain.setValueAtTime(1e-4,m),S.gain.linearRampToValueAtTime(.04,m+.02),S.gain.exponentialRampToValueAtTime(1e-4,m+.15),v.connect(S).connect(p),v.start(m),v.stop(m+.18)});try{const f=await fetch("/audio/narration.wav",{cache:"no-store"});if(f.ok){const b=await f.arrayBuffer(),m=await s.decodeAudioData(b),v=s.createBufferSource();v.buffer=m,v.connect(p),v.start(d)}}catch(f){console.info("Optional narration asset not found; exporting captions, music and SFX.",f)}return{context:s,dest:u,startAt:d}}return O={ac:t,master:e,startPreview:l,sfx:o,prepareExport:i,toggle(){P=!P,e.gain.value=P?0:.18,P&&"speechSynthesis"in window&&window.speechSynthesis.cancel(),lt.textContent=P?"⊘":"◖"}},O}async function ut(){if(!("speechSynthesis"in window))return;window.speechSynthesis.cancel();const t=new SpeechSynthesisUtterance(M[K()].voice);t.lang="zh-CN",t.rate=.96,t.pitch=1.03,window.speechSynthesis.speak(t)}function It(){return['video/mp4;codecs="avc1.42E01E,mp4a.40.2"',"video/mp4","video/webm;codecs=vp9,opus","video/webm"].find(e=>window.MediaRecorder?.isTypeSupported?.(e))||""}async function Wt(t){const e=await at(()=>import("https://cdn.jsdelivr.net/npm/@ffmpeg/ffmpeg@0.12.10/+esm"),[]),n=await at(()=>import("https://cdn.jsdelivr.net/npm/@ffmpeg/util@0.12.1/+esm"),[]),r=new e.FFmpeg;r.on("progress",({progress:l})=>{const i=Math.round(100*T(l));E.textContent=`本地转码 MP4 · ${i}%`,_.textContent=`${i}%`,F.style.width=`${i}%`});const o="https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.10/dist/umd";await r.load({coreURL:await n.toBlobURL(`${o}/ffmpeg-core.js`,"text/javascript"),wasmURL:await n.toBlobURL(`${o}/ffmpeg-core.wasm`,"application/wasm")}),await r.writeFile("input.webm",await n.fetchFile(t)),await r.exec(["-i","input.webm","-c:v","libx264","-c:a","aac","-pix_fmt","yuv420p","-movflags","faststart","output.mp4"]);const a=await r.readFile("output.mp4");return await r.deleteFile("input.webm"),await r.deleteFile("output.mp4"),new Blob([a.buffer],{type:"video/mp4"})}async function Nt(){if(!window.MediaRecorder||!R.captureStream){E.textContent="当前浏览器不支持画布录制";return}z.disabled=!0,Mt.hidden=!1,F.style.width="0%",_.textContent="0%",E.textContent="正在准备本地音轨…";const t=await tt().prepareExport(),e=R.captureStream(xt),n=new MediaStream([...e.getVideoTracks(),...t.dest.stream.getAudioTracks()]),r=It(),o=[],a=new MediaRecorder(n,r?{mimeType:r,videoBitsPerSecond:7e6}:void 0),l=performance.now(),i=g,s=L,p=h;a.ondataavailable=S=>{S.data.size&&o.push(S.data)};const u=new Promise(S=>{a.onstop=S});a.start(250),H.textContent="正在导出",await new Promise(S=>{performance.now();function et(yt){const W=Math.min(A,(yt-l)/1e3);g=W,B(g),$();const nt=Math.round(W/A*100);F.style.width=`${nt}%`,_.textContent=`${nt}%`,E.textContent=`渲染第 ${V(W)} / ${V(A)} 帧`,W>=A?(a.stop(),S()):requestAnimationFrame(et)}requestAnimationFrame(et)}),await u,e.getTracks().forEach(S=>S.stop()),t.dest.stream.getTracks().forEach(S=>S.stop()),await t.context.close();const c=new Blob(o,{type:r||"video/webm"});let d=c,f=(r||"").includes("mp4");if(!f){E.textContent="浏览器原生不支持 MP4,准备本地转码…";try{d=await Wt(c),f=!0}catch(S){console.warn("Local MP4 conversion unavailable; keeping WebM fallback.",S),E.textContent="本地 MP4 转码不可用,保留兼容 WebM"}}const b=f?"mp4":"webm",m=URL.createObjectURL(d),v=document.createElement("a");v.href=m,v.download=`收猴子的故事-${h==="portrait"?"抖音竖屏":"B站横屏"}.${b}`,v.click(),setTimeout(()=>URL.revokeObjectURL(m),2e3),g=i,L=s,h=p,J(),$(),F.style.width="100%",_.textContent="完成",E.textContent=f?"MP4 已保存到下载目录":"当前浏览器不支持 MP4,已保存兼容 WebM",H.textContent="实时画布",z.disabled=!1}function Ft(){for(const t of["bilibili","douyin"])document.querySelector(`#${t}Title`).value=N[t].title,document.querySelector(`#${t}Description`).value=N[t].description,document.querySelector(`#${t}Tags`).value=N[t].tags}function _t(t,e){const n=N[t],r=`标题:${n.title}
|
| 59 |
-
|
| 60 |
-
简介:${n.description}
|
| 61 |
-
|
| 62 |
-
标签:${n.tags}`;navigator.clipboard?.writeText(r).then(()=>{const o=e.textContent;e.textContent="已复制",setTimeout(()=>e.textContent=o,1600)}).catch(()=>{const o=document.createElement("textarea");o.value=r,document.body.appendChild(o),o.select(),document.execCommand("copy"),o.remove(),e.textContent="已复制",setTimeout(()=>e.textContent="复制整套",1600)})}function ft(t,e){const n=t.getContext("2d"),r=t.width,o=t.height,a=n.createLinearGradient(0,0,r,o);a.addColorStop(0,"#080b16"),a.addColorStop(1,"#19213d"),n.fillStyle=a,n.fillRect(0,0,r,o);for(let s=0;s<22;s++)k(n,q(s)*r,q(s+32)*o,1+q(s+12)*3,"#a8b9ff55");const l=e?r*.5:r*.7,i=e?o*.72:o*.78;Z(n,l,e?o*.22:o*.3,e?.72:.9,!0),Q(n,l,i-40,e?230:180,e?54:46,"#e8bd71");for(let s=0;s<(e?3:5);s++)D(n,e?r*(.25+s*.25):r*(.3+s*.14),i,e?.5:.44,"neutral","#c9946d",0);y(n,"收猴子的故事",e?r*.1:r*.08,o*.82,e?25:28,"#f7f4ed",800),y(n,"为什么没人敢拿?",e?r*.1:r*.08,e?o*.88:o*.9,e?15:17,"#f4b33e",650)}function mt(t,e){t.toBlob(n=>{const r=document.createElement("a");r.href=URL.createObjectURL(n),r.download=e,r.click(),setTimeout(()=>URL.revokeObjectURL(r.href),1e3)},"image/png")}X.addEventListener("click",()=>pt(!L));document.querySelector("#restartButton").addEventListener("click",()=>{g=0,pt(!0),$(),B(g)});document.querySelector("#speakButton").addEventListener("click",ut);lt.addEventListener("click",()=>tt().toggle());it.addEventListener("input",t=>{g=Number(t.target.value),$(),B(g)});document.querySelectorAll("[data-orientation]").forEach(t=>t.addEventListener("click",()=>{h=t.dataset.orientation,document.querySelectorAll("[data-orientation]").forEach(e=>e.classList.toggle("active",e===t)),J()}));z.addEventListener("click",Nt);document.querySelectorAll(".copy-button").forEach(t=>t.addEventListener("click",()=>_t(t.dataset.copy,t)));const bt=document.querySelector("#landscapeCover"),vt=document.querySelector("#portraitCover");ft(bt,!1);ft(vt,!0);document.querySelector("#downloadLandscape").addEventListener("click",()=>mt(bt,"收猴子的故事-横屏封面.png"));document.querySelector("#downloadPortrait").addEventListener("click",()=>mt(vt,"收猴子的故事-竖屏封面.png"));Et();Ft();$();J();B(0);requestAnimationFrame(dt);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assets/index-C7-Og2To.css
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
@import"https://fonts.googleapis.com/css2?family=DM+Mono:wght@400;500&family=Inter:wght@400;500;600;700;800&family=Noto+Sans+SC:wght@400;500;600;700;800&display=swap";:root{color-scheme:dark;font-family:Inter,Noto Sans SC,system-ui,sans-serif;background:#080a12;color:#f4f6fb;font-synthesis:none;--bg: #080a12;--panel: #0e1220;--panel-2: #11172a;--stroke: #202941;--muted: #8f9ab6;--muted-2: #65718e;--yellow: #f4b33e;--cyan: #71d6c5}*{box-sizing:border-box}body{margin:0;min-width:320px;background:radial-gradient(circle at 20% 0%,#12182a 0%,var(--bg) 42%)}button,textarea,input{font:inherit}button{cursor:pointer}button:disabled{cursor:wait;opacity:.6}.shell{width:min(1600px,100%);margin:0 auto;padding:0 34px 38px}.topbar{height:76px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #1a2236}.brand{display:flex;align-items:center;gap:12px}.brand-mark{display:grid;place-items:center;width:35px;height:35px;border-radius:12px;background:#f4b33e;color:#111522;font-size:25px;font-weight:800;transform:rotate(-9deg)}.brand strong{display:block;letter-spacing:.12em;font-size:12px}.brand span:not(.brand-mark){display:block;color:var(--muted);font-size:11px;margin-top:3px}.top-status{display:flex;align-items:center;color:#98a4c1;font:500 11px DM Mono,monospace;letter-spacing:.04em}.live-dot{width:7px;height:7px;border-radius:50%;background:#68ddb1;box-shadow:0 0 0 4px #68ddb11c;margin-right:8px}.status-divider{width:1px;height:16px;background:#2a3550;margin:0 13px}.workspace{display:grid;grid-template-columns:minmax(0,1.54fr) minmax(350px,.76fr);gap:28px;padding-top:30px}.stage-panel{min-width:0}.stage-shell{position:relative;width:100%;aspect-ratio:16 / 9;overflow:hidden;border-radius:17px;background:#080b16;border:1px solid #2a3552;box-shadow:0 24px 80px #0008,inset 0 1px #ffffff0e}.stage-shell.portrait{width:min(100%,500px);aspect-ratio:9 / 16;margin:0 auto}#stage{display:block;width:100%;height:100%}.stage-hud{position:absolute;left:18px;right:18px;top:14px;display:flex;justify-content:space-between;color:#d3d9ebcc;font:500 10px DM Mono,monospace;letter-spacing:.04em;pointer-events:none}.stage-safe{position:absolute;left:10%;right:10%;color:#fff2;font:400 8px DM Mono,monospace;pointer-events:none;opacity:0;transition:opacity .2s}.stage-shell.portrait .stage-safe{opacity:1}.safe-top{top:5%;border-top:1px dashed #ffffff26;padding-top:4px}.safe-bottom{bottom:5%;border-bottom:1px dashed #ffffff26;padding-bottom:4px;text-align:right}.controls{display:flex;align-items:center;gap:10px;padding:14px 0 10px}.primary-control,.icon-control,.chip,.copy-button,.cover-actions button{border:1px solid #2b3754;color:#dfe6fb;background:#11182a;border-radius:8px}.primary-control{height:34px;padding:0 13px;display:flex;align-items:center;gap:8px;background:#f4b33e;color:#151a27;border-color:#f4b33e;font-weight:750;font-size:12px}.play-icon{font-size:11px}.icon-control{width:34px;height:34px;font-size:17px;color:#acb8d0}.primary-control:hover,.icon-control:hover,.chip:hover,.copy-button:hover,.cover-actions button:hover{filter:brightness(1.17)}.scrub{position:relative;flex:1;height:24px;display:flex;align-items:center}.scrub input{position:relative;z-index:2;width:100%;appearance:none;background:transparent}.scrub input::-webkit-slider-runnable-track{height:3px;background:#2a3550;border-radius:4px}.scrub input::-webkit-slider-thumb{appearance:none;width:11px;height:11px;margin-top:-4px;border-radius:50%;background:#f4b33e;border:2px solid #f8d98e;box-shadow:0 0 0 3px #f4b33e2a}.scrub-progress{position:absolute;z-index:1;left:0;top:10px;width:0;height:3px;background:#f4b33e;border-radius:4px;pointer-events:none}.frame-rate{color:#687492;font:500 10px DM Mono,monospace}.orientation-row{display:flex;align-items:center;flex-wrap:wrap;gap:8px;color:var(--muted)}.control-label{font-size:11px;margin-right:4px}.chip{height:28px;padding:0 10px;color:#96a2be;font-size:11px}.chip.active{color:#f7d891;border-color:#a2742d;background:#f4b33e17}.safe-note{color:#596783;font-size:10px;margin-left:5px}.inspector{border-left:1px solid #202a43;padding-left:28px;min-width:0}.inspector-head{display:flex;justify-content:space-between;align-items:flex-start}.eyebrow{display:block;color:#697693;font:500 10px DM Mono,monospace;letter-spacing:.12em}h1{font-size:clamp(24px,3vw,36px);line-height:1.1;letter-spacing:-.04em;margin:8px 0 0}.duration-badge{border:1px solid #2c3854;color:#aeb9d4;padding:6px 9px;border-radius:7px;font:500 10px DM Mono,monospace}.lede{color:#99a5bf;line-height:1.65;margin:13px 0 20px;font-size:13px;max-width:390px}.chapter-list{display:flex;flex-direction:column;gap:3px;max-height:380px;overflow:auto;padding-right:4px}.chapter-item{display:grid;grid-template-columns:28px 1fr auto;gap:9px;align-items:center;text-align:left;width:100%;min-height:44px;padding:7px 8px;border:1px solid transparent;color:#99a5c0;background:transparent;border-radius:9px}.chapter-item:hover{background:#151d31}.chapter-item.active{color:#f5f7fd;background:#171f34;border-color:#2d3b5e;box-shadow:inset 3px 0 var(--yellow)}.chapter-no{font:500 10px DM Mono,monospace;color:#63708c}.chapter-item.active .chapter-no{color:var(--yellow)}.chapter-copy strong{display:block;font-size:12px;font-weight:650}.chapter-copy small{display:block;color:#65718b;font-size:10px;margin-top:3px}.chapter-time{color:#697590;font:500 10px DM Mono,monospace}.inspector-card{border-top:1px solid #222d47;margin-top:20px;padding-top:15px}.card-title{display:flex;align-items:center;justify-content:space-between;color:#8190ae;font:500 10px DM Mono,monospace;letter-spacing:.07em;text-transform:uppercase}.text-button{border:0;background:transparent;color:#f4b33e;font-size:11px;padding:0}.voice-card p,.note-card p{color:#c1c9dc;font-size:12px;line-height:1.7;margin:9px 0 0}.note-card p{color:#8491ad}.tiny-dot{width:5px;height:5px;border-radius:50%;background:#68ddb1}.export-button{width:100%;margin-top:22px;min-height:57px;border:1px solid #73531f;border-radius:10px;background:linear-gradient(110deg,#f4b33e,#f2c766);color:#171b28;display:grid;grid-template-columns:24px 1fr 20px;align-items:center;text-align:left;padding:9px 13px}.export-button>span:first-child{font-size:22px;font-weight:800}.export-button strong,.export-button small{display:block}.export-button strong{font-size:12px}.export-button small{font-size:10px;margin-top:3px;opacity:.68}.export-arrow{font-size:18px;text-align:right}.export-progress{margin-top:10px;padding:10px 2px 0}.progress-top{display:flex;justify-content:space-between;color:#9aa6c0;font:500 10px DM Mono,monospace}.progress-track{height:4px;margin-top:8px;background:#222c45;border-radius:4px;overflow:hidden}.progress-track span{display:block;height:100%;width:0;background:var(--yellow);transition:width .1s}.publish-section{margin-top:56px;border-top:1px solid #1d263c;padding-top:25px}.section-heading{display:flex;align-items:flex-end;justify-content:space-between;margin-bottom:18px}h2{font-size:22px;letter-spacing:-.04em;margin:7px 0 0}.section-caption{color:#687593;font-size:11px}.publish-grid{display:grid;grid-template-columns:1fr 1fr 1.05fr;gap:14px}.publish-card,.cover-card{border:1px solid #212c45;border-radius:12px;background:#0e1424;padding:15px}.platform-head{display:flex;align-items:center;gap:9px;margin-bottom:14px}.platform-head>div:not(.cover-previews){flex:1}.platform-head strong,.platform-head small{display:block}.platform-head strong{font-size:12px}.platform-head small{color:#6f7b97;font-size:10px;margin-top:3px}.platform-icon{display:grid;place-items:center;width:28px;height:28px;border-radius:8px;font-size:16px}.bilibili-icon{background:#73d7eb20;color:#73d7eb}.douyin-icon{background:#f06fa820;color:#f59bc5}.cover-icon{background:#f4b33e20;color:#f4b33e}.copy-button{font-size:10px;padding:7px 9px;color:#e4b759;border-color:#684f25;background:#2a2114;white-space:nowrap}.publish-card label{display:block;color:#6f7c98;font:500 9px DM Mono,monospace;margin-top:11px}.publish-card textarea{resize:vertical;width:100%;min-height:34px;border:1px solid #202b45;border-radius:7px;background:#0a0f1d;color:#c4ccde;padding:8px;margin-top:5px;line-height:1.55;font:400 11px Noto Sans SC,sans-serif}.publish-card label:nth-of-type(2) textarea{min-height:110px}.cover-previews{display:flex;gap:10px;align-items:center;height:185px}.cover-preview{position:relative;background:#090d18;border:1px solid #29344f;border-radius:8px;overflow:hidden}.cover-preview canvas{display:block;width:100%;height:100%}.landscape-cover{width:66%;aspect-ratio:16 / 9}.portrait-cover{width:30%;height:100%}.cover-preview>span{position:absolute;left:7px;bottom:6px;font:500 9px DM Mono,monospace;color:#d9e2f7;background:#080b16aa;padding:3px 5px;border-radius:4px}.cover-actions{display:flex;gap:8px;margin-top:12px}.cover-actions button{flex:1;padding:8px 4px;font-size:10px;color:#aab6d0}.footer{display:flex;justify-content:space-between;color:#56627d;font:500 10px DM Mono,monospace;margin-top:26px;padding-top:18px;border-top:1px solid #1a2338}@media(max-width:1080px){.workspace{grid-template-columns:1fr}.inspector{border-left:0;border-top:1px solid #202a43;padding:26px 0 0}.chapter-list{display:grid;grid-template-columns:repeat(2,1fr);max-height:none}.publish-grid{grid-template-columns:1fr 1fr}.cover-card{grid-column:1 / -1}}@media(max-width:670px){.shell{padding:0 15px 28px}.topbar{height:64px}.top-status{display:none}.workspace{padding-top:18px;gap:20px}.stage-shell{border-radius:11px}.controls{gap:6px}.frame-rate{display:none}.safe-note{width:100%;margin:4px 0 0 52px}.chapter-list{grid-template-columns:1fr}.publish-section{margin-top:35px}.section-heading{display:block}.section-caption{display:block;margin-top:8px}.publish-grid{grid-template-columns:1fr}.cover-card{grid-column:auto}.footer{display:block;line-height:1.8}.footer span{display:block}}
|
|
|
|
|
|
index.html
DELETED
|
@@ -1,15 +0,0 @@
|
|
| 1 |
-
<!doctype html>
|
| 2 |
-
<html lang="zh-CN">
|
| 3 |
-
<head>
|
| 4 |
-
<meta charset="UTF-8" />
|
| 5 |
-
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
| 6 |
-
<meta name="theme-color" content="#090b15" />
|
| 7 |
-
<meta name="description" content="收猴子的故事——经济学动画工作台" />
|
| 8 |
-
<title>收猴子的故事 · 经济学动画工作台</title>
|
| 9 |
-
<script type="module" crossorigin src="/assets/index-BCSP7C4S.js"></script>
|
| 10 |
-
<link rel="stylesheet" crossorigin href="/assets/index-C7-Og2To.css">
|
| 11 |
-
</head>
|
| 12 |
-
<body>
|
| 13 |
-
<div id="app"></div>
|
| 14 |
-
</body>
|
| 15 |
-
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
index.source.html
DELETED
|
@@ -1,14 +0,0 @@
|
|
| 1 |
-
<!doctype html>
|
| 2 |
-
<html lang="zh-CN">
|
| 3 |
-
<head>
|
| 4 |
-
<meta charset="UTF-8" />
|
| 5 |
-
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
| 6 |
-
<meta name="theme-color" content="#090b15" />
|
| 7 |
-
<meta name="description" content="收猴子的故事——经济学动画工作台" />
|
| 8 |
-
<title>收猴子的故事 · 经济学动画工作台</title>
|
| 9 |
-
</head>
|
| 10 |
-
<body>
|
| 11 |
-
<div id="app"></div>
|
| 12 |
-
<script type="module" src="/src/main.js"></script>
|
| 13 |
-
</body>
|
| 14 |
-
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
package-lock.json
DELETED
|
@@ -1,1122 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"name": "monkey-story-motion",
|
| 3 |
-
"version": "1.0.0",
|
| 4 |
-
"lockfileVersion": 3,
|
| 5 |
-
"requires": true,
|
| 6 |
-
"packages": {
|
| 7 |
-
"": {
|
| 8 |
-
"name": "monkey-story-motion",
|
| 9 |
-
"version": "1.0.0",
|
| 10 |
-
"devDependencies": {
|
| 11 |
-
"vite": "^7.1.7"
|
| 12 |
-
}
|
| 13 |
-
},
|
| 14 |
-
"node_modules/@esbuild/aix-ppc64": {
|
| 15 |
-
"version": "0.28.2",
|
| 16 |
-
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz",
|
| 17 |
-
"integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==",
|
| 18 |
-
"cpu": [
|
| 19 |
-
"ppc64"
|
| 20 |
-
],
|
| 21 |
-
"dev": true,
|
| 22 |
-
"license": "MIT",
|
| 23 |
-
"optional": true,
|
| 24 |
-
"os": [
|
| 25 |
-
"aix"
|
| 26 |
-
],
|
| 27 |
-
"engines": {
|
| 28 |
-
"node": ">=18"
|
| 29 |
-
}
|
| 30 |
-
},
|
| 31 |
-
"node_modules/@esbuild/android-arm": {
|
| 32 |
-
"version": "0.28.2",
|
| 33 |
-
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz",
|
| 34 |
-
"integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==",
|
| 35 |
-
"cpu": [
|
| 36 |
-
"arm"
|
| 37 |
-
],
|
| 38 |
-
"dev": true,
|
| 39 |
-
"license": "MIT",
|
| 40 |
-
"optional": true,
|
| 41 |
-
"os": [
|
| 42 |
-
"android"
|
| 43 |
-
],
|
| 44 |
-
"engines": {
|
| 45 |
-
"node": ">=18"
|
| 46 |
-
}
|
| 47 |
-
},
|
| 48 |
-
"node_modules/@esbuild/android-arm64": {
|
| 49 |
-
"version": "0.28.2",
|
| 50 |
-
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz",
|
| 51 |
-
"integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==",
|
| 52 |
-
"cpu": [
|
| 53 |
-
"arm64"
|
| 54 |
-
],
|
| 55 |
-
"dev": true,
|
| 56 |
-
"license": "MIT",
|
| 57 |
-
"optional": true,
|
| 58 |
-
"os": [
|
| 59 |
-
"android"
|
| 60 |
-
],
|
| 61 |
-
"engines": {
|
| 62 |
-
"node": ">=18"
|
| 63 |
-
}
|
| 64 |
-
},
|
| 65 |
-
"node_modules/@esbuild/android-x64": {
|
| 66 |
-
"version": "0.28.2",
|
| 67 |
-
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz",
|
| 68 |
-
"integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==",
|
| 69 |
-
"cpu": [
|
| 70 |
-
"x64"
|
| 71 |
-
],
|
| 72 |
-
"dev": true,
|
| 73 |
-
"license": "MIT",
|
| 74 |
-
"optional": true,
|
| 75 |
-
"os": [
|
| 76 |
-
"android"
|
| 77 |
-
],
|
| 78 |
-
"engines": {
|
| 79 |
-
"node": ">=18"
|
| 80 |
-
}
|
| 81 |
-
},
|
| 82 |
-
"node_modules/@esbuild/darwin-arm64": {
|
| 83 |
-
"version": "0.28.2",
|
| 84 |
-
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz",
|
| 85 |
-
"integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==",
|
| 86 |
-
"cpu": [
|
| 87 |
-
"arm64"
|
| 88 |
-
],
|
| 89 |
-
"dev": true,
|
| 90 |
-
"license": "MIT",
|
| 91 |
-
"optional": true,
|
| 92 |
-
"os": [
|
| 93 |
-
"darwin"
|
| 94 |
-
],
|
| 95 |
-
"engines": {
|
| 96 |
-
"node": ">=18"
|
| 97 |
-
}
|
| 98 |
-
},
|
| 99 |
-
"node_modules/@esbuild/darwin-x64": {
|
| 100 |
-
"version": "0.28.2",
|
| 101 |
-
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz",
|
| 102 |
-
"integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==",
|
| 103 |
-
"cpu": [
|
| 104 |
-
"x64"
|
| 105 |
-
],
|
| 106 |
-
"dev": true,
|
| 107 |
-
"license": "MIT",
|
| 108 |
-
"optional": true,
|
| 109 |
-
"os": [
|
| 110 |
-
"darwin"
|
| 111 |
-
],
|
| 112 |
-
"engines": {
|
| 113 |
-
"node": ">=18"
|
| 114 |
-
}
|
| 115 |
-
},
|
| 116 |
-
"node_modules/@esbuild/freebsd-arm64": {
|
| 117 |
-
"version": "0.28.2",
|
| 118 |
-
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz",
|
| 119 |
-
"integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==",
|
| 120 |
-
"cpu": [
|
| 121 |
-
"arm64"
|
| 122 |
-
],
|
| 123 |
-
"dev": true,
|
| 124 |
-
"license": "MIT",
|
| 125 |
-
"optional": true,
|
| 126 |
-
"os": [
|
| 127 |
-
"freebsd"
|
| 128 |
-
],
|
| 129 |
-
"engines": {
|
| 130 |
-
"node": ">=18"
|
| 131 |
-
}
|
| 132 |
-
},
|
| 133 |
-
"node_modules/@esbuild/freebsd-x64": {
|
| 134 |
-
"version": "0.28.2",
|
| 135 |
-
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz",
|
| 136 |
-
"integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==",
|
| 137 |
-
"cpu": [
|
| 138 |
-
"x64"
|
| 139 |
-
],
|
| 140 |
-
"dev": true,
|
| 141 |
-
"license": "MIT",
|
| 142 |
-
"optional": true,
|
| 143 |
-
"os": [
|
| 144 |
-
"freebsd"
|
| 145 |
-
],
|
| 146 |
-
"engines": {
|
| 147 |
-
"node": ">=18"
|
| 148 |
-
}
|
| 149 |
-
},
|
| 150 |
-
"node_modules/@esbuild/linux-arm": {
|
| 151 |
-
"version": "0.28.2",
|
| 152 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz",
|
| 153 |
-
"integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==",
|
| 154 |
-
"cpu": [
|
| 155 |
-
"arm"
|
| 156 |
-
],
|
| 157 |
-
"dev": true,
|
| 158 |
-
"license": "MIT",
|
| 159 |
-
"optional": true,
|
| 160 |
-
"os": [
|
| 161 |
-
"linux"
|
| 162 |
-
],
|
| 163 |
-
"engines": {
|
| 164 |
-
"node": ">=18"
|
| 165 |
-
}
|
| 166 |
-
},
|
| 167 |
-
"node_modules/@esbuild/linux-arm64": {
|
| 168 |
-
"version": "0.28.2",
|
| 169 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz",
|
| 170 |
-
"integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==",
|
| 171 |
-
"cpu": [
|
| 172 |
-
"arm64"
|
| 173 |
-
],
|
| 174 |
-
"dev": true,
|
| 175 |
-
"license": "MIT",
|
| 176 |
-
"optional": true,
|
| 177 |
-
"os": [
|
| 178 |
-
"linux"
|
| 179 |
-
],
|
| 180 |
-
"engines": {
|
| 181 |
-
"node": ">=18"
|
| 182 |
-
}
|
| 183 |
-
},
|
| 184 |
-
"node_modules/@esbuild/linux-ia32": {
|
| 185 |
-
"version": "0.28.2",
|
| 186 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz",
|
| 187 |
-
"integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==",
|
| 188 |
-
"cpu": [
|
| 189 |
-
"ia32"
|
| 190 |
-
],
|
| 191 |
-
"dev": true,
|
| 192 |
-
"license": "MIT",
|
| 193 |
-
"optional": true,
|
| 194 |
-
"os": [
|
| 195 |
-
"linux"
|
| 196 |
-
],
|
| 197 |
-
"engines": {
|
| 198 |
-
"node": ">=18"
|
| 199 |
-
}
|
| 200 |
-
},
|
| 201 |
-
"node_modules/@esbuild/linux-loong64": {
|
| 202 |
-
"version": "0.28.2",
|
| 203 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz",
|
| 204 |
-
"integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==",
|
| 205 |
-
"cpu": [
|
| 206 |
-
"loong64"
|
| 207 |
-
],
|
| 208 |
-
"dev": true,
|
| 209 |
-
"license": "MIT",
|
| 210 |
-
"optional": true,
|
| 211 |
-
"os": [
|
| 212 |
-
"linux"
|
| 213 |
-
],
|
| 214 |
-
"engines": {
|
| 215 |
-
"node": ">=18"
|
| 216 |
-
}
|
| 217 |
-
},
|
| 218 |
-
"node_modules/@esbuild/linux-mips64el": {
|
| 219 |
-
"version": "0.28.2",
|
| 220 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz",
|
| 221 |
-
"integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==",
|
| 222 |
-
"cpu": [
|
| 223 |
-
"mips64el"
|
| 224 |
-
],
|
| 225 |
-
"dev": true,
|
| 226 |
-
"license": "MIT",
|
| 227 |
-
"optional": true,
|
| 228 |
-
"os": [
|
| 229 |
-
"linux"
|
| 230 |
-
],
|
| 231 |
-
"engines": {
|
| 232 |
-
"node": ">=18"
|
| 233 |
-
}
|
| 234 |
-
},
|
| 235 |
-
"node_modules/@esbuild/linux-ppc64": {
|
| 236 |
-
"version": "0.28.2",
|
| 237 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz",
|
| 238 |
-
"integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==",
|
| 239 |
-
"cpu": [
|
| 240 |
-
"ppc64"
|
| 241 |
-
],
|
| 242 |
-
"dev": true,
|
| 243 |
-
"license": "MIT",
|
| 244 |
-
"optional": true,
|
| 245 |
-
"os": [
|
| 246 |
-
"linux"
|
| 247 |
-
],
|
| 248 |
-
"engines": {
|
| 249 |
-
"node": ">=18"
|
| 250 |
-
}
|
| 251 |
-
},
|
| 252 |
-
"node_modules/@esbuild/linux-riscv64": {
|
| 253 |
-
"version": "0.28.2",
|
| 254 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz",
|
| 255 |
-
"integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==",
|
| 256 |
-
"cpu": [
|
| 257 |
-
"riscv64"
|
| 258 |
-
],
|
| 259 |
-
"dev": true,
|
| 260 |
-
"license": "MIT",
|
| 261 |
-
"optional": true,
|
| 262 |
-
"os": [
|
| 263 |
-
"linux"
|
| 264 |
-
],
|
| 265 |
-
"engines": {
|
| 266 |
-
"node": ">=18"
|
| 267 |
-
}
|
| 268 |
-
},
|
| 269 |
-
"node_modules/@esbuild/linux-s390x": {
|
| 270 |
-
"version": "0.28.2",
|
| 271 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz",
|
| 272 |
-
"integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==",
|
| 273 |
-
"cpu": [
|
| 274 |
-
"s390x"
|
| 275 |
-
],
|
| 276 |
-
"dev": true,
|
| 277 |
-
"license": "MIT",
|
| 278 |
-
"optional": true,
|
| 279 |
-
"os": [
|
| 280 |
-
"linux"
|
| 281 |
-
],
|
| 282 |
-
"engines": {
|
| 283 |
-
"node": ">=18"
|
| 284 |
-
}
|
| 285 |
-
},
|
| 286 |
-
"node_modules/@esbuild/linux-x64": {
|
| 287 |
-
"version": "0.28.2",
|
| 288 |
-
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz",
|
| 289 |
-
"integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==",
|
| 290 |
-
"cpu": [
|
| 291 |
-
"x64"
|
| 292 |
-
],
|
| 293 |
-
"dev": true,
|
| 294 |
-
"license": "MIT",
|
| 295 |
-
"optional": true,
|
| 296 |
-
"os": [
|
| 297 |
-
"linux"
|
| 298 |
-
],
|
| 299 |
-
"engines": {
|
| 300 |
-
"node": ">=18"
|
| 301 |
-
}
|
| 302 |
-
},
|
| 303 |
-
"node_modules/@esbuild/netbsd-arm64": {
|
| 304 |
-
"version": "0.28.2",
|
| 305 |
-
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz",
|
| 306 |
-
"integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==",
|
| 307 |
-
"cpu": [
|
| 308 |
-
"arm64"
|
| 309 |
-
],
|
| 310 |
-
"dev": true,
|
| 311 |
-
"license": "MIT",
|
| 312 |
-
"optional": true,
|
| 313 |
-
"os": [
|
| 314 |
-
"netbsd"
|
| 315 |
-
],
|
| 316 |
-
"engines": {
|
| 317 |
-
"node": ">=18"
|
| 318 |
-
}
|
| 319 |
-
},
|
| 320 |
-
"node_modules/@esbuild/netbsd-x64": {
|
| 321 |
-
"version": "0.28.2",
|
| 322 |
-
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz",
|
| 323 |
-
"integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==",
|
| 324 |
-
"cpu": [
|
| 325 |
-
"x64"
|
| 326 |
-
],
|
| 327 |
-
"dev": true,
|
| 328 |
-
"license": "MIT",
|
| 329 |
-
"optional": true,
|
| 330 |
-
"os": [
|
| 331 |
-
"netbsd"
|
| 332 |
-
],
|
| 333 |
-
"engines": {
|
| 334 |
-
"node": ">=18"
|
| 335 |
-
}
|
| 336 |
-
},
|
| 337 |
-
"node_modules/@esbuild/openbsd-arm64": {
|
| 338 |
-
"version": "0.28.2",
|
| 339 |
-
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz",
|
| 340 |
-
"integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==",
|
| 341 |
-
"cpu": [
|
| 342 |
-
"arm64"
|
| 343 |
-
],
|
| 344 |
-
"dev": true,
|
| 345 |
-
"license": "MIT",
|
| 346 |
-
"optional": true,
|
| 347 |
-
"os": [
|
| 348 |
-
"openbsd"
|
| 349 |
-
],
|
| 350 |
-
"engines": {
|
| 351 |
-
"node": ">=18"
|
| 352 |
-
}
|
| 353 |
-
},
|
| 354 |
-
"node_modules/@esbuild/openbsd-x64": {
|
| 355 |
-
"version": "0.28.2",
|
| 356 |
-
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz",
|
| 357 |
-
"integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==",
|
| 358 |
-
"cpu": [
|
| 359 |
-
"x64"
|
| 360 |
-
],
|
| 361 |
-
"dev": true,
|
| 362 |
-
"license": "MIT",
|
| 363 |
-
"optional": true,
|
| 364 |
-
"os": [
|
| 365 |
-
"openbsd"
|
| 366 |
-
],
|
| 367 |
-
"engines": {
|
| 368 |
-
"node": ">=18"
|
| 369 |
-
}
|
| 370 |
-
},
|
| 371 |
-
"node_modules/@esbuild/openharmony-arm64": {
|
| 372 |
-
"version": "0.28.2",
|
| 373 |
-
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz",
|
| 374 |
-
"integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==",
|
| 375 |
-
"cpu": [
|
| 376 |
-
"arm64"
|
| 377 |
-
],
|
| 378 |
-
"dev": true,
|
| 379 |
-
"license": "MIT",
|
| 380 |
-
"optional": true,
|
| 381 |
-
"os": [
|
| 382 |
-
"openharmony"
|
| 383 |
-
],
|
| 384 |
-
"engines": {
|
| 385 |
-
"node": ">=18"
|
| 386 |
-
}
|
| 387 |
-
},
|
| 388 |
-
"node_modules/@esbuild/sunos-x64": {
|
| 389 |
-
"version": "0.28.2",
|
| 390 |
-
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz",
|
| 391 |
-
"integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==",
|
| 392 |
-
"cpu": [
|
| 393 |
-
"x64"
|
| 394 |
-
],
|
| 395 |
-
"dev": true,
|
| 396 |
-
"license": "MIT",
|
| 397 |
-
"optional": true,
|
| 398 |
-
"os": [
|
| 399 |
-
"sunos"
|
| 400 |
-
],
|
| 401 |
-
"engines": {
|
| 402 |
-
"node": ">=18"
|
| 403 |
-
}
|
| 404 |
-
},
|
| 405 |
-
"node_modules/@esbuild/win32-arm64": {
|
| 406 |
-
"version": "0.28.2",
|
| 407 |
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz",
|
| 408 |
-
"integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==",
|
| 409 |
-
"cpu": [
|
| 410 |
-
"arm64"
|
| 411 |
-
],
|
| 412 |
-
"dev": true,
|
| 413 |
-
"license": "MIT",
|
| 414 |
-
"optional": true,
|
| 415 |
-
"os": [
|
| 416 |
-
"win32"
|
| 417 |
-
],
|
| 418 |
-
"engines": {
|
| 419 |
-
"node": ">=18"
|
| 420 |
-
}
|
| 421 |
-
},
|
| 422 |
-
"node_modules/@esbuild/win32-ia32": {
|
| 423 |
-
"version": "0.28.2",
|
| 424 |
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz",
|
| 425 |
-
"integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==",
|
| 426 |
-
"cpu": [
|
| 427 |
-
"ia32"
|
| 428 |
-
],
|
| 429 |
-
"dev": true,
|
| 430 |
-
"license": "MIT",
|
| 431 |
-
"optional": true,
|
| 432 |
-
"os": [
|
| 433 |
-
"win32"
|
| 434 |
-
],
|
| 435 |
-
"engines": {
|
| 436 |
-
"node": ">=18"
|
| 437 |
-
}
|
| 438 |
-
},
|
| 439 |
-
"node_modules/@esbuild/win32-x64": {
|
| 440 |
-
"version": "0.28.2",
|
| 441 |
-
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz",
|
| 442 |
-
"integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==",
|
| 443 |
-
"cpu": [
|
| 444 |
-
"x64"
|
| 445 |
-
],
|
| 446 |
-
"dev": true,
|
| 447 |
-
"license": "MIT",
|
| 448 |
-
"optional": true,
|
| 449 |
-
"os": [
|
| 450 |
-
"win32"
|
| 451 |
-
],
|
| 452 |
-
"engines": {
|
| 453 |
-
"node": ">=18"
|
| 454 |
-
}
|
| 455 |
-
},
|
| 456 |
-
"node_modules/@napi-rs/lzma-linux-x64-gnu": {
|
| 457 |
-
"version": "1.5.1",
|
| 458 |
-
"resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz",
|
| 459 |
-
"integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==",
|
| 460 |
-
"cpu": [
|
| 461 |
-
"x64"
|
| 462 |
-
],
|
| 463 |
-
"dev": true,
|
| 464 |
-
"license": "MIT",
|
| 465 |
-
"optional": true,
|
| 466 |
-
"os": [
|
| 467 |
-
"linux"
|
| 468 |
-
],
|
| 469 |
-
"engines": {
|
| 470 |
-
"node": "^22.20 || ^24.12 || >=25"
|
| 471 |
-
}
|
| 472 |
-
},
|
| 473 |
-
"node_modules/@rollup/rollup-android-arm-eabi": {
|
| 474 |
-
"version": "4.64.0",
|
| 475 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.64.0.tgz",
|
| 476 |
-
"integrity": "sha512-FLLpX941CD/7aBvX3GqLGBM9OvY5PGXTryPJkG5r5/OuBpZpV5k45Uo3RxAvqd35tsyc3hH1bhtz921IKzoIzg==",
|
| 477 |
-
"cpu": [
|
| 478 |
-
"arm"
|
| 479 |
-
],
|
| 480 |
-
"dev": true,
|
| 481 |
-
"license": "MIT",
|
| 482 |
-
"optional": true,
|
| 483 |
-
"os": [
|
| 484 |
-
"android"
|
| 485 |
-
]
|
| 486 |
-
},
|
| 487 |
-
"node_modules/@rollup/rollup-android-arm64": {
|
| 488 |
-
"version": "4.64.0",
|
| 489 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.64.0.tgz",
|
| 490 |
-
"integrity": "sha512-iI32XM/jnDYJsvx5ai/za/7gWO9XoSeFFtU06/ZCaRgyilfJOjaopD3nOpk2WrTCGOyeDt4hleUiGOAnd5tHNg==",
|
| 491 |
-
"cpu": [
|
| 492 |
-
"arm64"
|
| 493 |
-
],
|
| 494 |
-
"dev": true,
|
| 495 |
-
"license": "MIT",
|
| 496 |
-
"optional": true,
|
| 497 |
-
"os": [
|
| 498 |
-
"android"
|
| 499 |
-
]
|
| 500 |
-
},
|
| 501 |
-
"node_modules/@rollup/rollup-darwin-arm64": {
|
| 502 |
-
"version": "4.64.0",
|
| 503 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.64.0.tgz",
|
| 504 |
-
"integrity": "sha512-dRBZlbt2sEtF8Mn4MODThze9fIPdDoOhVSlXMAKy5dQbOYK9pOOoPY4Fat3OTtr0+8zbJo5TTZwlsfxY/kDNJg==",
|
| 505 |
-
"cpu": [
|
| 506 |
-
"arm64"
|
| 507 |
-
],
|
| 508 |
-
"dev": true,
|
| 509 |
-
"license": "MIT",
|
| 510 |
-
"optional": true,
|
| 511 |
-
"os": [
|
| 512 |
-
"darwin"
|
| 513 |
-
]
|
| 514 |
-
},
|
| 515 |
-
"node_modules/@rollup/rollup-darwin-x64": {
|
| 516 |
-
"version": "4.64.0",
|
| 517 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.64.0.tgz",
|
| 518 |
-
"integrity": "sha512-Y3whrJOMucfkhgNkvcM93M6j8C53iEAMmPMYD99C6G1kUy4ELigLWk9ar2/4aCBSRgsql0zdlOemLgW2m5GZgQ==",
|
| 519 |
-
"cpu": [
|
| 520 |
-
"x64"
|
| 521 |
-
],
|
| 522 |
-
"dev": true,
|
| 523 |
-
"license": "MIT",
|
| 524 |
-
"optional": true,
|
| 525 |
-
"os": [
|
| 526 |
-
"darwin"
|
| 527 |
-
]
|
| 528 |
-
},
|
| 529 |
-
"node_modules/@rollup/rollup-freebsd-arm64": {
|
| 530 |
-
"version": "4.64.0",
|
| 531 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.64.0.tgz",
|
| 532 |
-
"integrity": "sha512-S6S1GEzPjFF6IONYJX3AKt/799+8RgBgCcwRnUo+Ep1IvBxUbw5M51iU0TDdK2NWWVnSvCQ8yi6Em0fGASMOng==",
|
| 533 |
-
"cpu": [
|
| 534 |
-
"arm64"
|
| 535 |
-
],
|
| 536 |
-
"dev": true,
|
| 537 |
-
"license": "MIT",
|
| 538 |
-
"optional": true,
|
| 539 |
-
"os": [
|
| 540 |
-
"freebsd"
|
| 541 |
-
]
|
| 542 |
-
},
|
| 543 |
-
"node_modules/@rollup/rollup-freebsd-x64": {
|
| 544 |
-
"version": "4.64.0",
|
| 545 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.64.0.tgz",
|
| 546 |
-
"integrity": "sha512-Ed1DPoFENkXj4IGzSB12SesV40iP6LxCCrbTIkSGIQBpOOxpBM+0kj62lmaeIHfMWTDoddqTOtqc7s8LYfn+Pw==",
|
| 547 |
-
"cpu": [
|
| 548 |
-
"x64"
|
| 549 |
-
],
|
| 550 |
-
"dev": true,
|
| 551 |
-
"license": "MIT",
|
| 552 |
-
"optional": true,
|
| 553 |
-
"os": [
|
| 554 |
-
"freebsd"
|
| 555 |
-
]
|
| 556 |
-
},
|
| 557 |
-
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
| 558 |
-
"version": "4.64.0",
|
| 559 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.64.0.tgz",
|
| 560 |
-
"integrity": "sha512-J/6xhutJPtFyIkmHdf4olOdHHKH5kOJNumojdWn+Elv5ICEYTVOa362Vw27TxsK0BLzb1le3EDlO3UbvFK1lnQ==",
|
| 561 |
-
"cpu": [
|
| 562 |
-
"arm"
|
| 563 |
-
],
|
| 564 |
-
"dev": true,
|
| 565 |
-
"license": "MIT",
|
| 566 |
-
"optional": true,
|
| 567 |
-
"os": [
|
| 568 |
-
"linux"
|
| 569 |
-
]
|
| 570 |
-
},
|
| 571 |
-
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
| 572 |
-
"version": "4.64.0",
|
| 573 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.64.0.tgz",
|
| 574 |
-
"integrity": "sha512-G4L/Nnx59d2LvDoV/P4S5lZ7oVLT6hodOHMacqjiq2LL2gts86KbkEBTYL53+fBt0odhf7XPIbIATGoYiJctxg==",
|
| 575 |
-
"cpu": [
|
| 576 |
-
"arm"
|
| 577 |
-
],
|
| 578 |
-
"dev": true,
|
| 579 |
-
"license": "MIT",
|
| 580 |
-
"optional": true,
|
| 581 |
-
"os": [
|
| 582 |
-
"linux"
|
| 583 |
-
]
|
| 584 |
-
},
|
| 585 |
-
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
| 586 |
-
"version": "4.64.0",
|
| 587 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.64.0.tgz",
|
| 588 |
-
"integrity": "sha512-zF16pvJDEUksGYRbfnXne7pdklAyj2XDmJeYKgP93oq0cUyX9Y8O7lUo1R+IY1GagP/YBvCEDCe7eEcAKpw//g==",
|
| 589 |
-
"cpu": [
|
| 590 |
-
"arm64"
|
| 591 |
-
],
|
| 592 |
-
"dev": true,
|
| 593 |
-
"license": "MIT",
|
| 594 |
-
"optional": true,
|
| 595 |
-
"os": [
|
| 596 |
-
"linux"
|
| 597 |
-
]
|
| 598 |
-
},
|
| 599 |
-
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
| 600 |
-
"version": "4.64.0",
|
| 601 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.64.0.tgz",
|
| 602 |
-
"integrity": "sha512-nIggWn8n+w12+R5lsV+PYYoUY8vuObxPAQqJI7WES+9v0qspYuAHoZMfYM0yLxlipasT5BY9ht5W8t5c/rSCkg==",
|
| 603 |
-
"cpu": [
|
| 604 |
-
"arm64"
|
| 605 |
-
],
|
| 606 |
-
"dev": true,
|
| 607 |
-
"license": "MIT",
|
| 608 |
-
"optional": true,
|
| 609 |
-
"os": [
|
| 610 |
-
"linux"
|
| 611 |
-
]
|
| 612 |
-
},
|
| 613 |
-
"node_modules/@rollup/rollup-linux-loong64-gnu": {
|
| 614 |
-
"version": "4.64.0",
|
| 615 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.64.0.tgz",
|
| 616 |
-
"integrity": "sha512-WLOTiHPzszP0dxIMsHpFeDSOSB5I49Mj8K4+yKE3H1X/D0JRG5XZ5jrctJLMjnUc/LUzaKiwq6EHWNJ8pup8PA==",
|
| 617 |
-
"cpu": [
|
| 618 |
-
"loong64"
|
| 619 |
-
],
|
| 620 |
-
"dev": true,
|
| 621 |
-
"license": "MIT",
|
| 622 |
-
"optional": true,
|
| 623 |
-
"os": [
|
| 624 |
-
"linux"
|
| 625 |
-
]
|
| 626 |
-
},
|
| 627 |
-
"node_modules/@rollup/rollup-linux-loong64-musl": {
|
| 628 |
-
"version": "4.64.0",
|
| 629 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.64.0.tgz",
|
| 630 |
-
"integrity": "sha512-35RK9aRhh7g7Pmeu5gQ01aCvrhXzj9LoBE6xtUcEH1ns+nyTaSzxSpADmBQINu4QiTDGvmQYwo1j5hlg1rRXFw==",
|
| 631 |
-
"cpu": [
|
| 632 |
-
"loong64"
|
| 633 |
-
],
|
| 634 |
-
"dev": true,
|
| 635 |
-
"license": "MIT",
|
| 636 |
-
"optional": true,
|
| 637 |
-
"os": [
|
| 638 |
-
"linux"
|
| 639 |
-
]
|
| 640 |
-
},
|
| 641 |
-
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
| 642 |
-
"version": "4.64.0",
|
| 643 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.64.0.tgz",
|
| 644 |
-
"integrity": "sha512-RqTfnpoTjuWfPAmqK5p2edt2AMCDJWm6oTn7sKfmyx+6tRga0ip05SJ977o6udTcIiDeF/CJMUfkaN4gtWt6Aw==",
|
| 645 |
-
"cpu": [
|
| 646 |
-
"ppc64"
|
| 647 |
-
],
|
| 648 |
-
"dev": true,
|
| 649 |
-
"license": "MIT",
|
| 650 |
-
"optional": true,
|
| 651 |
-
"os": [
|
| 652 |
-
"linux"
|
| 653 |
-
]
|
| 654 |
-
},
|
| 655 |
-
"node_modules/@rollup/rollup-linux-ppc64-musl": {
|
| 656 |
-
"version": "4.64.0",
|
| 657 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.64.0.tgz",
|
| 658 |
-
"integrity": "sha512-RQjZPigjxFvFZDijG6rjlSXxhRaTf9zLYVpGK+lVcga64oMqP1GbgVqCb46eHTTceSIgp89Ja0y5aPe3GFBYHg==",
|
| 659 |
-
"cpu": [
|
| 660 |
-
"ppc64"
|
| 661 |
-
],
|
| 662 |
-
"dev": true,
|
| 663 |
-
"license": "MIT",
|
| 664 |
-
"optional": true,
|
| 665 |
-
"os": [
|
| 666 |
-
"linux"
|
| 667 |
-
]
|
| 668 |
-
},
|
| 669 |
-
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
| 670 |
-
"version": "4.64.0",
|
| 671 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.64.0.tgz",
|
| 672 |
-
"integrity": "sha512-tjL58mgQcTRnDRpeky+Sth1MKQeYi2Ct6r3Luq9E5aTSjnM/i/JbozqZkKqp9thM5+oDSZxo+05f9kdQedfTGA==",
|
| 673 |
-
"cpu": [
|
| 674 |
-
"riscv64"
|
| 675 |
-
],
|
| 676 |
-
"dev": true,
|
| 677 |
-
"license": "MIT",
|
| 678 |
-
"optional": true,
|
| 679 |
-
"os": [
|
| 680 |
-
"linux"
|
| 681 |
-
]
|
| 682 |
-
},
|
| 683 |
-
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
| 684 |
-
"version": "4.64.0",
|
| 685 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.64.0.tgz",
|
| 686 |
-
"integrity": "sha512-ictoRyVaoZ8C67myHef+8hhCa6IplXJJ/xUCkVCxziV4zxMZh+jvhkhGsPBLqj1S72fnqD0QH5zL4lcaa4X7DQ==",
|
| 687 |
-
"cpu": [
|
| 688 |
-
"riscv64"
|
| 689 |
-
],
|
| 690 |
-
"dev": true,
|
| 691 |
-
"license": "MIT",
|
| 692 |
-
"optional": true,
|
| 693 |
-
"os": [
|
| 694 |
-
"linux"
|
| 695 |
-
]
|
| 696 |
-
},
|
| 697 |
-
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
| 698 |
-
"version": "4.64.0",
|
| 699 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.64.0.tgz",
|
| 700 |
-
"integrity": "sha512-8aVvZ2hfr9RLFzBQkQNmYeHLDja8fLeQAmLt757zdKN/B+8L809KlLJKctQjS22wZotptV8G6o7fVPuAs8S1tQ==",
|
| 701 |
-
"cpu": [
|
| 702 |
-
"s390x"
|
| 703 |
-
],
|
| 704 |
-
"dev": true,
|
| 705 |
-
"license": "MIT",
|
| 706 |
-
"optional": true,
|
| 707 |
-
"os": [
|
| 708 |
-
"linux"
|
| 709 |
-
]
|
| 710 |
-
},
|
| 711 |
-
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
| 712 |
-
"version": "4.64.0",
|
| 713 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.64.0.tgz",
|
| 714 |
-
"integrity": "sha512-2dEF8GAcDKwshUfydD+GhosppNyBzhVUHkdFF3CXd3FpOkzj/RZzF5aoEtzklujklT5qFVRU2GK/cFeLaTNKVg==",
|
| 715 |
-
"cpu": [
|
| 716 |
-
"x64"
|
| 717 |
-
],
|
| 718 |
-
"dev": true,
|
| 719 |
-
"license": "MIT",
|
| 720 |
-
"optional": true,
|
| 721 |
-
"os": [
|
| 722 |
-
"linux"
|
| 723 |
-
]
|
| 724 |
-
},
|
| 725 |
-
"node_modules/@rollup/rollup-linux-x64-musl": {
|
| 726 |
-
"version": "4.64.0",
|
| 727 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.64.0.tgz",
|
| 728 |
-
"integrity": "sha512-3XHTvoo2hKkh0MiOfS7SkhHjMaaWF7P4F3drocZZaho/Z8w7h/jpnuBHm91BOQLfvrKHs1yR+zir4E8RI3XzTQ==",
|
| 729 |
-
"cpu": [
|
| 730 |
-
"x64"
|
| 731 |
-
],
|
| 732 |
-
"dev": true,
|
| 733 |
-
"license": "MIT",
|
| 734 |
-
"optional": true,
|
| 735 |
-
"os": [
|
| 736 |
-
"linux"
|
| 737 |
-
]
|
| 738 |
-
},
|
| 739 |
-
"node_modules/@rollup/rollup-openbsd-x64": {
|
| 740 |
-
"version": "4.64.0",
|
| 741 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.64.0.tgz",
|
| 742 |
-
"integrity": "sha512-HKtkzodL4a3KFWDXDoO61cRynAWTR3LnAE0oGtfG9lcNFpNuVYUFOByxDgNqEvic9rvaQbAj+SFwTEWDptRaIw==",
|
| 743 |
-
"cpu": [
|
| 744 |
-
"x64"
|
| 745 |
-
],
|
| 746 |
-
"dev": true,
|
| 747 |
-
"license": "MIT",
|
| 748 |
-
"optional": true,
|
| 749 |
-
"os": [
|
| 750 |
-
"openbsd"
|
| 751 |
-
]
|
| 752 |
-
},
|
| 753 |
-
"node_modules/@rollup/rollup-openharmony-arm64": {
|
| 754 |
-
"version": "4.64.0",
|
| 755 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.64.0.tgz",
|
| 756 |
-
"integrity": "sha512-JBsxqiY16uyyCujtoC3s7zL+SrYyNElptD7K4V+KUoJ2V8/XG3hPWpheK+SuPwNh1cikJvIZmIdsrYXvD6PLJw==",
|
| 757 |
-
"cpu": [
|
| 758 |
-
"arm64"
|
| 759 |
-
],
|
| 760 |
-
"dev": true,
|
| 761 |
-
"license": "MIT",
|
| 762 |
-
"optional": true,
|
| 763 |
-
"os": [
|
| 764 |
-
"openharmony"
|
| 765 |
-
]
|
| 766 |
-
},
|
| 767 |
-
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
| 768 |
-
"version": "4.64.0",
|
| 769 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.64.0.tgz",
|
| 770 |
-
"integrity": "sha512-S5Qrijh37qhnUYQ0ay8WN3LuWeKjow7wTUyCOPMZtnXiBXYXdsZETI6ZLfYnQAKovltckPLBFdNeD61P2rJc7g==",
|
| 771 |
-
"cpu": [
|
| 772 |
-
"arm64"
|
| 773 |
-
],
|
| 774 |
-
"dev": true,
|
| 775 |
-
"license": "MIT",
|
| 776 |
-
"optional": true,
|
| 777 |
-
"os": [
|
| 778 |
-
"win32"
|
| 779 |
-
]
|
| 780 |
-
},
|
| 781 |
-
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
| 782 |
-
"version": "4.64.0",
|
| 783 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.64.0.tgz",
|
| 784 |
-
"integrity": "sha512-zbZ6iMmLFEsCtTdc2/y1F/cDFVNNCPF7Ae8PCT/wdPE//hGwYp6r+MyCLMFX+kOeIe09Pz6ASfnnfpSTN1FJQQ==",
|
| 785 |
-
"cpu": [
|
| 786 |
-
"ia32"
|
| 787 |
-
],
|
| 788 |
-
"dev": true,
|
| 789 |
-
"license": "MIT",
|
| 790 |
-
"optional": true,
|
| 791 |
-
"os": [
|
| 792 |
-
"win32"
|
| 793 |
-
]
|
| 794 |
-
},
|
| 795 |
-
"node_modules/@rollup/rollup-win32-x64-gnu": {
|
| 796 |
-
"version": "4.64.0",
|
| 797 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.64.0.tgz",
|
| 798 |
-
"integrity": "sha512-L6Hw69oNpuYaOr1zCG30v0/DqKYCSRwodPGwIcVZ/6f8SOzq9rxtL/xNVI5MO4wjli+Ko0ErK9uHJL3mNPt8wQ==",
|
| 799 |
-
"cpu": [
|
| 800 |
-
"x64"
|
| 801 |
-
],
|
| 802 |
-
"dev": true,
|
| 803 |
-
"license": "MIT",
|
| 804 |
-
"optional": true,
|
| 805 |
-
"os": [
|
| 806 |
-
"win32"
|
| 807 |
-
]
|
| 808 |
-
},
|
| 809 |
-
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
| 810 |
-
"version": "4.64.0",
|
| 811 |
-
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.64.0.tgz",
|
| 812 |
-
"integrity": "sha512-nl+CTWIgOGszQmUz/kanfgrfFZ4qiwxKIfT/ryl3N3Ewk5tqizmYtNXiqb7Bu/fDtNgu1kyxdFiAUJNur3iysg==",
|
| 813 |
-
"cpu": [
|
| 814 |
-
"x64"
|
| 815 |
-
],
|
| 816 |
-
"dev": true,
|
| 817 |
-
"license": "MIT",
|
| 818 |
-
"optional": true,
|
| 819 |
-
"os": [
|
| 820 |
-
"win32"
|
| 821 |
-
]
|
| 822 |
-
},
|
| 823 |
-
"node_modules/@types/estree": {
|
| 824 |
-
"version": "1.0.9",
|
| 825 |
-
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
|
| 826 |
-
"integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
|
| 827 |
-
"dev": true,
|
| 828 |
-
"license": "MIT"
|
| 829 |
-
},
|
| 830 |
-
"node_modules/esbuild": {
|
| 831 |
-
"version": "0.28.2",
|
| 832 |
-
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz",
|
| 833 |
-
"integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==",
|
| 834 |
-
"dev": true,
|
| 835 |
-
"hasInstallScript": true,
|
| 836 |
-
"license": "MIT",
|
| 837 |
-
"bin": {
|
| 838 |
-
"esbuild": "bin/esbuild"
|
| 839 |
-
},
|
| 840 |
-
"engines": {
|
| 841 |
-
"node": ">=18"
|
| 842 |
-
},
|
| 843 |
-
"optionalDependencies": {
|
| 844 |
-
"@esbuild/aix-ppc64": "0.28.2",
|
| 845 |
-
"@esbuild/android-arm": "0.28.2",
|
| 846 |
-
"@esbuild/android-arm64": "0.28.2",
|
| 847 |
-
"@esbuild/android-x64": "0.28.2",
|
| 848 |
-
"@esbuild/darwin-arm64": "0.28.2",
|
| 849 |
-
"@esbuild/darwin-x64": "0.28.2",
|
| 850 |
-
"@esbuild/freebsd-arm64": "0.28.2",
|
| 851 |
-
"@esbuild/freebsd-x64": "0.28.2",
|
| 852 |
-
"@esbuild/linux-arm": "0.28.2",
|
| 853 |
-
"@esbuild/linux-arm64": "0.28.2",
|
| 854 |
-
"@esbuild/linux-ia32": "0.28.2",
|
| 855 |
-
"@esbuild/linux-loong64": "0.28.2",
|
| 856 |
-
"@esbuild/linux-mips64el": "0.28.2",
|
| 857 |
-
"@esbuild/linux-ppc64": "0.28.2",
|
| 858 |
-
"@esbuild/linux-riscv64": "0.28.2",
|
| 859 |
-
"@esbuild/linux-s390x": "0.28.2",
|
| 860 |
-
"@esbuild/linux-x64": "0.28.2",
|
| 861 |
-
"@esbuild/netbsd-arm64": "0.28.2",
|
| 862 |
-
"@esbuild/netbsd-x64": "0.28.2",
|
| 863 |
-
"@esbuild/openbsd-arm64": "0.28.2",
|
| 864 |
-
"@esbuild/openbsd-x64": "0.28.2",
|
| 865 |
-
"@esbuild/openharmony-arm64": "0.28.2",
|
| 866 |
-
"@esbuild/sunos-x64": "0.28.2",
|
| 867 |
-
"@esbuild/win32-arm64": "0.28.2",
|
| 868 |
-
"@esbuild/win32-ia32": "0.28.2",
|
| 869 |
-
"@esbuild/win32-x64": "0.28.2"
|
| 870 |
-
}
|
| 871 |
-
},
|
| 872 |
-
"node_modules/fdir": {
|
| 873 |
-
"version": "6.5.0",
|
| 874 |
-
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
| 875 |
-
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
|
| 876 |
-
"dev": true,
|
| 877 |
-
"license": "MIT",
|
| 878 |
-
"engines": {
|
| 879 |
-
"node": ">=12.0.0"
|
| 880 |
-
},
|
| 881 |
-
"peerDependencies": {
|
| 882 |
-
"picomatch": "^3 || ^4"
|
| 883 |
-
},
|
| 884 |
-
"peerDependenciesMeta": {
|
| 885 |
-
"picomatch": {
|
| 886 |
-
"optional": true
|
| 887 |
-
}
|
| 888 |
-
}
|
| 889 |
-
},
|
| 890 |
-
"node_modules/fsevents": {
|
| 891 |
-
"version": "2.3.3",
|
| 892 |
-
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
| 893 |
-
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
| 894 |
-
"dev": true,
|
| 895 |
-
"hasInstallScript": true,
|
| 896 |
-
"license": "MIT",
|
| 897 |
-
"optional": true,
|
| 898 |
-
"os": [
|
| 899 |
-
"darwin"
|
| 900 |
-
],
|
| 901 |
-
"engines": {
|
| 902 |
-
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
| 903 |
-
}
|
| 904 |
-
},
|
| 905 |
-
"node_modules/nanoid": {
|
| 906 |
-
"version": "3.3.20",
|
| 907 |
-
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz",
|
| 908 |
-
"integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==",
|
| 909 |
-
"dev": true,
|
| 910 |
-
"funding": [
|
| 911 |
-
{
|
| 912 |
-
"type": "github",
|
| 913 |
-
"url": "https://github.com/sponsors/ai"
|
| 914 |
-
}
|
| 915 |
-
],
|
| 916 |
-
"license": "MIT",
|
| 917 |
-
"bin": {
|
| 918 |
-
"nanoid": "bin/nanoid.cjs"
|
| 919 |
-
},
|
| 920 |
-
"engines": {
|
| 921 |
-
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
| 922 |
-
}
|
| 923 |
-
},
|
| 924 |
-
"node_modules/picocolors": {
|
| 925 |
-
"version": "1.1.1",
|
| 926 |
-
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
| 927 |
-
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
| 928 |
-
"dev": true,
|
| 929 |
-
"license": "ISC"
|
| 930 |
-
},
|
| 931 |
-
"node_modules/picomatch": {
|
| 932 |
-
"version": "4.0.7",
|
| 933 |
-
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz",
|
| 934 |
-
"integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==",
|
| 935 |
-
"dev": true,
|
| 936 |
-
"license": "MIT",
|
| 937 |
-
"engines": {
|
| 938 |
-
"node": ">=12"
|
| 939 |
-
},
|
| 940 |
-
"funding": {
|
| 941 |
-
"url": "https://github.com/sponsors/jonschlinkert"
|
| 942 |
-
}
|
| 943 |
-
},
|
| 944 |
-
"node_modules/postcss": {
|
| 945 |
-
"version": "8.5.29",
|
| 946 |
-
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.29.tgz",
|
| 947 |
-
"integrity": "sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==",
|
| 948 |
-
"dev": true,
|
| 949 |
-
"funding": [
|
| 950 |
-
{
|
| 951 |
-
"type": "opencollective",
|
| 952 |
-
"url": "https://opencollective.com/postcss/"
|
| 953 |
-
},
|
| 954 |
-
{
|
| 955 |
-
"type": "tidelift",
|
| 956 |
-
"url": "https://tidelift.com/funding/github/npm/postcss"
|
| 957 |
-
},
|
| 958 |
-
{
|
| 959 |
-
"type": "github",
|
| 960 |
-
"url": "https://github.com/sponsors/ai"
|
| 961 |
-
}
|
| 962 |
-
],
|
| 963 |
-
"license": "MIT",
|
| 964 |
-
"dependencies": {
|
| 965 |
-
"nanoid": "^3.3.19",
|
| 966 |
-
"picocolors": "^1.1.1",
|
| 967 |
-
"source-map-js": "^1.2.2"
|
| 968 |
-
},
|
| 969 |
-
"engines": {
|
| 970 |
-
"node": "^10 || ^12 || >=14"
|
| 971 |
-
}
|
| 972 |
-
},
|
| 973 |
-
"node_modules/rollup": {
|
| 974 |
-
"version": "4.64.0",
|
| 975 |
-
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.64.0.tgz",
|
| 976 |
-
"integrity": "sha512-gjA/kFeDfffII8COoPsvLPvE16hWDTEc1+0QKQNKUkuV8iJjqsC4xXs8/FzuL0v9EV39kuxTbAgcFoTRyy2TGQ==",
|
| 977 |
-
"dev": true,
|
| 978 |
-
"license": "MIT",
|
| 979 |
-
"dependencies": {
|
| 980 |
-
"@types/estree": "1.0.9"
|
| 981 |
-
},
|
| 982 |
-
"bin": {
|
| 983 |
-
"rollup": "dist/bin/rollup"
|
| 984 |
-
},
|
| 985 |
-
"engines": {
|
| 986 |
-
"node": ">=18.0.0",
|
| 987 |
-
"npm": ">=8.0.0"
|
| 988 |
-
},
|
| 989 |
-
"optionalDependencies": {
|
| 990 |
-
"@napi-rs/lzma-linux-x64-gnu": "1.5.1",
|
| 991 |
-
"@rollup/rollup-android-arm-eabi": "4.64.0",
|
| 992 |
-
"@rollup/rollup-android-arm64": "4.64.0",
|
| 993 |
-
"@rollup/rollup-darwin-arm64": "4.64.0",
|
| 994 |
-
"@rollup/rollup-darwin-x64": "4.64.0",
|
| 995 |
-
"@rollup/rollup-freebsd-arm64": "4.64.0",
|
| 996 |
-
"@rollup/rollup-freebsd-x64": "4.64.0",
|
| 997 |
-
"@rollup/rollup-linux-arm-gnueabihf": "4.64.0",
|
| 998 |
-
"@rollup/rollup-linux-arm-musleabihf": "4.64.0",
|
| 999 |
-
"@rollup/rollup-linux-arm64-gnu": "4.64.0",
|
| 1000 |
-
"@rollup/rollup-linux-arm64-musl": "4.64.0",
|
| 1001 |
-
"@rollup/rollup-linux-loong64-gnu": "4.64.0",
|
| 1002 |
-
"@rollup/rollup-linux-loong64-musl": "4.64.0",
|
| 1003 |
-
"@rollup/rollup-linux-ppc64-gnu": "4.64.0",
|
| 1004 |
-
"@rollup/rollup-linux-ppc64-musl": "4.64.0",
|
| 1005 |
-
"@rollup/rollup-linux-riscv64-gnu": "4.64.0",
|
| 1006 |
-
"@rollup/rollup-linux-riscv64-musl": "4.64.0",
|
| 1007 |
-
"@rollup/rollup-linux-s390x-gnu": "4.64.0",
|
| 1008 |
-
"@rollup/rollup-linux-x64-gnu": "4.64.0",
|
| 1009 |
-
"@rollup/rollup-linux-x64-musl": "4.64.0",
|
| 1010 |
-
"@rollup/rollup-openbsd-x64": "4.64.0",
|
| 1011 |
-
"@rollup/rollup-openharmony-arm64": "4.64.0",
|
| 1012 |
-
"@rollup/rollup-win32-arm64-msvc": "4.64.0",
|
| 1013 |
-
"@rollup/rollup-win32-ia32-msvc": "4.64.0",
|
| 1014 |
-
"@rollup/rollup-win32-x64-gnu": "4.64.0",
|
| 1015 |
-
"@rollup/rollup-win32-x64-msvc": "4.64.0",
|
| 1016 |
-
"fsevents": "~2.3.2"
|
| 1017 |
-
}
|
| 1018 |
-
},
|
| 1019 |
-
"node_modules/source-map-js": {
|
| 1020 |
-
"version": "1.2.2",
|
| 1021 |
-
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz",
|
| 1022 |
-
"integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==",
|
| 1023 |
-
"dev": true,
|
| 1024 |
-
"license": "BSD-3-Clause",
|
| 1025 |
-
"engines": {
|
| 1026 |
-
"node": ">=0.10.0"
|
| 1027 |
-
}
|
| 1028 |
-
},
|
| 1029 |
-
"node_modules/tinyglobby": {
|
| 1030 |
-
"version": "0.2.17",
|
| 1031 |
-
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
|
| 1032 |
-
"integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
|
| 1033 |
-
"dev": true,
|
| 1034 |
-
"license": "MIT",
|
| 1035 |
-
"dependencies": {
|
| 1036 |
-
"fdir": "^6.5.0",
|
| 1037 |
-
"picomatch": "^4.0.4"
|
| 1038 |
-
},
|
| 1039 |
-
"engines": {
|
| 1040 |
-
"node": ">=12.0.0"
|
| 1041 |
-
},
|
| 1042 |
-
"funding": {
|
| 1043 |
-
"url": "https://github.com/sponsors/SuperchupuDev"
|
| 1044 |
-
}
|
| 1045 |
-
},
|
| 1046 |
-
"node_modules/vite": {
|
| 1047 |
-
"version": "7.3.7",
|
| 1048 |
-
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.7.tgz",
|
| 1049 |
-
"integrity": "sha512-AHEzTORMbi7/7aXp7w7KZ5+DCUdSeLsJz0grY8xHpc9DHdYb0vQu6DqJfm2tOw3gDExNgPgnJTXxM6syYFHaCA==",
|
| 1050 |
-
"dev": true,
|
| 1051 |
-
"license": "MIT",
|
| 1052 |
-
"dependencies": {
|
| 1053 |
-
"esbuild": "^0.27.0 || ^0.28.0",
|
| 1054 |
-
"fdir": "^6.5.0",
|
| 1055 |
-
"picomatch": "^4.0.3",
|
| 1056 |
-
"postcss": "^8.5.6",
|
| 1057 |
-
"rollup": "^4.43.0",
|
| 1058 |
-
"tinyglobby": "^0.2.15"
|
| 1059 |
-
},
|
| 1060 |
-
"bin": {
|
| 1061 |
-
"vite": "bin/vite.js"
|
| 1062 |
-
},
|
| 1063 |
-
"engines": {
|
| 1064 |
-
"node": "^20.19.0 || >=22.12.0"
|
| 1065 |
-
},
|
| 1066 |
-
"funding": {
|
| 1067 |
-
"url": "https://github.com/vitejs/vite?sponsor=1"
|
| 1068 |
-
},
|
| 1069 |
-
"optionalDependencies": {
|
| 1070 |
-
"fsevents": "~2.3.3"
|
| 1071 |
-
},
|
| 1072 |
-
"peerDependencies": {
|
| 1073 |
-
"@types/node": "^20.19.0 || >=22.12.0",
|
| 1074 |
-
"jiti": ">=1.21.0",
|
| 1075 |
-
"less": "^4.0.0",
|
| 1076 |
-
"lightningcss": "^1.21.0",
|
| 1077 |
-
"sass": "^1.70.0",
|
| 1078 |
-
"sass-embedded": "^1.70.0",
|
| 1079 |
-
"stylus": ">=0.54.8",
|
| 1080 |
-
"sugarss": "^5.0.0",
|
| 1081 |
-
"terser": "^5.16.0",
|
| 1082 |
-
"tsx": "^4.8.1",
|
| 1083 |
-
"yaml": "^2.4.2"
|
| 1084 |
-
},
|
| 1085 |
-
"peerDependenciesMeta": {
|
| 1086 |
-
"@types/node": {
|
| 1087 |
-
"optional": true
|
| 1088 |
-
},
|
| 1089 |
-
"jiti": {
|
| 1090 |
-
"optional": true
|
| 1091 |
-
},
|
| 1092 |
-
"less": {
|
| 1093 |
-
"optional": true
|
| 1094 |
-
},
|
| 1095 |
-
"lightningcss": {
|
| 1096 |
-
"optional": true
|
| 1097 |
-
},
|
| 1098 |
-
"sass": {
|
| 1099 |
-
"optional": true
|
| 1100 |
-
},
|
| 1101 |
-
"sass-embedded": {
|
| 1102 |
-
"optional": true
|
| 1103 |
-
},
|
| 1104 |
-
"stylus": {
|
| 1105 |
-
"optional": true
|
| 1106 |
-
},
|
| 1107 |
-
"sugarss": {
|
| 1108 |
-
"optional": true
|
| 1109 |
-
},
|
| 1110 |
-
"terser": {
|
| 1111 |
-
"optional": true
|
| 1112 |
-
},
|
| 1113 |
-
"tsx": {
|
| 1114 |
-
"optional": true
|
| 1115 |
-
},
|
| 1116 |
-
"yaml": {
|
| 1117 |
-
"optional": true
|
| 1118 |
-
}
|
| 1119 |
-
}
|
| 1120 |
-
}
|
| 1121 |
-
}
|
| 1122 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
package.json
DELETED
|
@@ -1,15 +0,0 @@
|
|
| 1 |
-
{
|
| 2 |
-
"name": "monkey-story-motion",
|
| 3 |
-
"version": "1.0.0",
|
| 4 |
-
"private": true,
|
| 5 |
-
"type": "module",
|
| 6 |
-
"scripts": {
|
| 7 |
-
"dev": "vite --host 0.0.0.0",
|
| 8 |
-
"build": "vite build",
|
| 9 |
-
"preview": "vite preview --host 0.0.0.0",
|
| 10 |
-
"tts": "node scripts/hf-tts.mjs"
|
| 11 |
-
},
|
| 12 |
-
"devDependencies": {
|
| 13 |
-
"vite": "^7.1.7"
|
| 14 |
-
}
|
| 15 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
scripts/hf-tts.mjs
DELETED
|
@@ -1,38 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env node
|
| 2 |
-
/**
|
| 3 |
-
* Optional local narration generator.
|
| 4 |
-
*
|
| 5 |
-
* Usage:
|
| 6 |
-
* HF_TOKEN=hf_... node scripts/hf-tts.mjs
|
| 7 |
-
*
|
| 8 |
-
* The token is read only from the environment and is never written to the
|
| 9 |
-
* repository. The browser app remains usable without generated audio by using
|
| 10 |
-
* SpeechSynthesis for local narration playback.
|
| 11 |
-
*/
|
| 12 |
-
import fs from 'node:fs/promises';
|
| 13 |
-
import path from 'node:path';
|
| 14 |
-
|
| 15 |
-
const token = process.env.HF_TOKEN;
|
| 16 |
-
if (!token) {
|
| 17 |
-
console.error('Missing HF_TOKEN. Keep it in your shell environment, never in source code.');
|
| 18 |
-
process.exit(1);
|
| 19 |
-
}
|
| 20 |
-
|
| 21 |
-
const endpoint = process.env.HF_TTS_ENDPOINT || 'https://api-inference.huggingface.co/models/facebook/mms-tts-zho';
|
| 22 |
-
const output = process.env.HF_TTS_OUT || 'public/audio/narration.wav';
|
| 23 |
-
const text = process.env.HF_TTS_TEXT || '想象一间笼子,里面有五只猴子。笼子顶上挂着一串香蕉。看起来,它只差一把梯子。可是,为什么没有一只猴子去拿?';
|
| 24 |
-
|
| 25 |
-
const res = await fetch(endpoint, {
|
| 26 |
-
method: 'POST',
|
| 27 |
-
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', Accept: 'audio/wav' },
|
| 28 |
-
body: JSON.stringify({ inputs: text })
|
| 29 |
-
});
|
| 30 |
-
const contentType = res.headers.get('content-type') || '';
|
| 31 |
-
const body = Buffer.from(await res.arrayBuffer());
|
| 32 |
-
if (!res.ok || contentType.includes('application/json')) {
|
| 33 |
-
console.error(`Hugging Face TTS failed (${res.status}): ${body.toString('utf8').slice(0, 800)}`);
|
| 34 |
-
process.exit(1);
|
| 35 |
-
}
|
| 36 |
-
await fs.mkdir(path.dirname(output), { recursive: true });
|
| 37 |
-
await fs.writeFile(output, body);
|
| 38 |
-
console.log(`Wrote ${output} (${body.length} bytes)`);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/main.js
DELETED
|
@@ -1,382 +0,0 @@
|
|
| 1 |
-
import './styles.css';
|
| 2 |
-
|
| 3 |
-
const FPS = 30;
|
| 4 |
-
const DURATION = 168;
|
| 5 |
-
const TAU = Math.PI * 2;
|
| 6 |
-
|
| 7 |
-
const scenes = [
|
| 8 |
-
{
|
| 9 |
-
id: 'hook', start: 0, end: 14, chapter: '01 / 钩子', title: '为什么没人敢拿?', kicker: '一个香蕉,五只猴子,零个解释', color: '#f4b33e',
|
| 10 |
-
voice: '想象一间笼子,里面有五只猴子。笼子顶上挂着一串香蕉。看起来,它只差一把梯子。可是,为什么没有一只猴子去拿?',
|
| 11 |
-
note: '用一个极简问题,把观众从“看见规则”带到“追问规则”。'
|
| 12 |
-
},
|
| 13 |
-
{
|
| 14 |
-
id: 'first', start: 14, end: 34, chapter: '02 / 第一次尝试', title: '第一个上梯子的人', kicker: '收益在上面,代价在下面', color: '#80d6c5',
|
| 15 |
-
voice: '第一只猴子爬上梯子。它看见的,是立刻可得的香蕉;其他猴子看见的,却是即将到来的风险。于是,冷水从上面浇下来,所有猴子都被淋透。',
|
| 16 |
-
note: '用收益 / 代价的对照解释:理性选择会被群体惩罚改写。'
|
| 17 |
-
},
|
| 18 |
-
{
|
| 19 |
-
id: 'punish', start: 34, end: 56, chapter: '03 / 惩罚机制', title: '群体开始自我管理', kicker: '惩罚一次,规则就记住了一半', color: '#ef6b69',
|
| 20 |
-
voice: '第二次,另一只猴子试着爬梯子。它还没有碰到香蕉,其他猴子就先把它拽了下来。没有人再等喷水,因为惩罚已经被群体内化成了动作。',
|
| 21 |
-
note: '红色不是“坏人”,而是一个被复制的反馈回路。'
|
| 22 |
-
},
|
| 23 |
-
{
|
| 24 |
-
id: 'replacement', start: 56, end: 78, chapter: '04 / 新成员', title: '新人不知道发生了什么', kicker: '制度会比记忆活得更久', color: '#a98bf2',
|
| 25 |
-
voice: '现在,换掉一只老猴子,迎来一只新人。新人当然想爬梯子,可刚伸手,就被四只老猴子打了下来。它不懂冷水,却学会了不许碰梯子。',
|
| 26 |
-
note: '关键转折:规则脱离原始原因,开始靠“别人都这样”存活。'
|
| 27 |
-
},
|
| 28 |
-
{
|
| 29 |
-
id: 'loop', start: 78, end: 100, chapter: '05 / 规则遗忘', title: '最后,没人见过冷水', kicker: '但所有人都说:不能爬', color: '#70a8ff',
|
| 30 |
-
voice: '继续换人,直到笼子里已经没有一只猴子经历过冷水。此时,大家仍然在阻止别人爬梯子。问原因?答案通常是:一直都是这样。',
|
| 31 |
-
note: '“一直都是这样”不是解释,它只是制度的缓存。'
|
| 32 |
-
},
|
| 33 |
-
{
|
| 34 |
-
id: 'model', start: 100, end: 124, chapter: '06 / 经济学模型', title: '这不是猴子的问题', kicker: '激励函数被藏起来了', color: '#ffcc67',
|
| 35 |
-
voice: '把故事翻译成经济学:一个人做选择,会比较收益和代价。当惩罚足够强,最优策略就从“拿香蕉”变成了“阻止别人拿”。后来,惩罚消失了,策略却留下了。',
|
| 36 |
-
note: '用坐标轴把“看得见的行为”和“看不见的激励”接上。'
|
| 37 |
-
},
|
| 38 |
-
{
|
| 39 |
-
id: 'reality', start: 124, end: 148, chapter: '07 / 现实映射', title: '我们在哪些笼子里?', kicker: '流程、文化、默认选项', color: '#5dd7ff',
|
| 40 |
-
voice: '现实里,它可能是一条没人能解释的审批流程,一个“新人别碰”的按钮,或者一句“我们公司一直这么做”。制度不是敌人,但不再适用的制度,会把过去的答案伪装成今天的常识。',
|
| 41 |
-
note: '不把隐喻简单等同于现实;重点是提出可验证的问题。'
|
| 42 |
-
},
|
| 43 |
-
{
|
| 44 |
-
id: 'exit', start: 148, end: 168, chapter: '08 / 结尾', title: '先问一句:冷水还在吗?', kicker: '好的规则经得起追问', color: '#f16eaa',
|
| 45 |
-
voice: '所以,下次你听到“不能这样做”,先别急着反抗,也别急着服从。问三件事:原来的风险还在吗?代价是谁承担?有没有更低成本的替代方案?规则最危险的时刻,不是被打破,而是再也没人知道它为何存在。',
|
| 46 |
-
note: '结尾留下行动问题,而不是把观众带到一个简单的“反制度”结论。'
|
| 47 |
-
}
|
| 48 |
-
];
|
| 49 |
-
|
| 50 |
-
const copy = {
|
| 51 |
-
bilibili: {
|
| 52 |
-
title: '为什么没人敢拿那串香蕉?|经济学里的“收猴子”故事',
|
| 53 |
-
description: '一个香蕉、五只猴子、没有人记得的冷水。\n\n这是一支原创图形化经济学动画:我们用“收猴子的故事”拆解制度惯性、路径依赖与群体激励。故事是常见寓言隐喻,不是对真实动物实验的复述。\n\n看完试着问自己:你所在的流程里,有没有一条“大家都说不能碰”,但没人说得清为什么的规则?\n\n如果喜欢这种把抽象机制画出来的讲解,欢迎收藏、转发,评论区留下你的“冷水还在吗”。',
|
| 54 |
-
tags: '#经济学 #制度 #组织文化 #路径依赖 #财经科普 #动画 #思维方式'
|
| 55 |
-
},
|
| 56 |
-
douyin: {
|
| 57 |
-
title: '为什么没人敢拿香蕉?收猴子的故事讲透制度惯性',
|
| 58 |
-
description: '没有人见过冷水,但所有人都说:不能爬。\n\n用 3 分钟看懂:激励如何变成规则,规则如何变成习惯,以及我们如何把过去的答案带进今天。\n\n寓言是隐喻,不是真实实验史料。你身边有没有“不能碰,但说不清为什么”的流程?',
|
| 59 |
-
tags: '#经济学 #收猴子的故事 #职场 #认知 #知识 #科普 #制度惯性'
|
| 60 |
-
}
|
| 61 |
-
};
|
| 62 |
-
|
| 63 |
-
const app = document.querySelector('#app');
|
| 64 |
-
app.innerHTML = `
|
| 65 |
-
<main class="shell">
|
| 66 |
-
<header class="topbar">
|
| 67 |
-
<div class="brand"><span class="brand-mark">∿</span><div><strong>MONKEY / RULES</strong><span>经济学动画工作台 · 原创讲解</span></div></div>
|
| 68 |
-
<div class="top-status"><span class="live-dot"></span><span id="renderStatus">实时画布</span><span class="status-divider"></span><span id="formatStatus">16:9 横屏</span></div>
|
| 69 |
-
</header>
|
| 70 |
-
<section class="workspace">
|
| 71 |
-
<section class="stage-panel">
|
| 72 |
-
<div class="stage-shell" id="stageShell">
|
| 73 |
-
<canvas id="stage" width="1280" height="720" aria-label="收猴子的故事动画画布"></canvas>
|
| 74 |
-
<div class="stage-safe safe-top">安全区</div>
|
| 75 |
-
<div class="stage-safe safe-bottom">安全区 · 平台 UI 避让</div>
|
| 76 |
-
<div class="stage-hud"><span id="sceneLabel">01 / 钩子</span><span id="timeLabel">00:00 / 02:48</span></div>
|
| 77 |
-
</div>
|
| 78 |
-
<div class="controls">
|
| 79 |
-
<button class="primary-control" id="playButton" type="button"><span class="play-icon">▶</span><span>播放</span></button>
|
| 80 |
-
<button class="icon-control" id="restartButton" type="button" aria-label="从头播放">↺</button>
|
| 81 |
-
<button class="icon-control" id="muteButton" type="button" aria-label="打开或关闭声音">◖</button>
|
| 82 |
-
<div class="scrub"><input id="scrub" type="range" min="0" max="168" step="0.01" value="0" aria-label="时间轴"/><div class="scrub-progress" id="scrubProgress"></div></div>
|
| 83 |
-
<div class="frame-rate">30 FPS</div>
|
| 84 |
-
</div>
|
| 85 |
-
<div class="orientation-row"><span class="control-label">画幅</span><button class="chip active" data-orientation="landscape" type="button">B站 · 16:9</button><button class="chip" data-orientation="portrait" type="button">抖音 · 9:16</button><span class="safe-note">竖屏自动保留刘海 / 手势安全区</span></div>
|
| 86 |
-
</section>
|
| 87 |
-
<aside class="inspector">
|
| 88 |
-
<div class="inspector-head"><div><span class="eyebrow">EDIT / STORYBOARD</span><h1>收猴子的故事</h1></div><span class="duration-badge">02:48</span></div>
|
| 89 |
-
<p class="lede">用一个寓言,拆开“大家都这样做”背后的激励结构。</p>
|
| 90 |
-
<div class="chapter-list" id="chapterList"></div>
|
| 91 |
-
<div class="inspector-card voice-card"><div class="card-title"><span>本幕旁白</span><button id="speakButton" class="text-button" type="button">试听</button></div><p id="voiceText"></p></div>
|
| 92 |
-
<div class="inspector-card note-card"><div class="card-title"><span>导演提示</span><span class="tiny-dot"></span></div><p id="directorNote"></p></div>
|
| 93 |
-
<button class="export-button" id="exportButton" type="button"><span>↧</span><div><strong>本地导出视频</strong><small>优先 MP4 · 画面与音乐在浏览器内渲染</small></div><span class="export-arrow">→</span></button>
|
| 94 |
-
<div class="export-progress" id="exportProgress" hidden><div class="progress-top"><span id="exportLabel">准备导出…</span><span id="exportPercent">0%</span></div><div class="progress-track"><span id="exportBar"></span></div></div>
|
| 95 |
-
</aside>
|
| 96 |
-
</section>
|
| 97 |
-
<section class="publish-section">
|
| 98 |
-
<div class="section-heading"><div><span class="eyebrow">PUBLISH KIT</span><h2>发布文案 & 封面</h2></div><span class="section-caption">每个平台一键复制 · 原创封面可下载</span></div>
|
| 99 |
-
<div class="publish-grid">
|
| 100 |
-
<article class="publish-card"><div class="platform-head"><span class="platform-icon bilibili-icon">◈</span><div><strong>B站横屏</strong><small>标题 / 简介 / 标签</small></div><button class="copy-button" data-copy="bilibili" type="button">复制整套</button></div><label>标题<textarea readonly id="bilibiliTitle"></textarea></label><label>简介<textarea readonly id="bilibiliDescription"></textarea></label><label>标签<textarea readonly id="bilibiliTags"></textarea></label></article>
|
| 101 |
-
<article class="publish-card"><div class="platform-head"><span class="platform-icon douyin-icon">♪</span><div><strong>抖音竖屏</strong><small>标题 / 简介 / 标签</small></div><button class="copy-button" data-copy="douyin" type="button">复制整套</button></div><label>标题<textarea readonly id="douyinTitle"></textarea></label><label>简介<textarea readonly id="douyinDescription"></textarea></label><label>标签<textarea readonly id="douyinTags"></textarea></label></article>
|
| 102 |
-
<article class="cover-card"><div class="platform-head"><span class="platform-icon cover-icon">✦</span><div><strong>原创封面</strong><small>随画幅生成 · 无外部素材</small></div></div><div class="cover-previews"><div class="cover-preview landscape-cover"><canvas id="landscapeCover" width="640" height="360"></canvas><span>16:9</span></div><div class="cover-preview portrait-cover"><canvas id="portraitCover" width="360" height="640"></canvas><span>9:16</span></div></div><div class="cover-actions"><button id="downloadLandscape" type="button">下载横屏封面</button><button id="downloadPortrait" type="button">下载竖屏封面</button></div></article>
|
| 103 |
-
</div>
|
| 104 |
-
</section>
|
| 105 |
-
<footer class="footer"><span>原创图形化讲解 · 不上传画面与剪贴板内容</span><span>故事为经济学寓言隐喻 · 不是动物实验史料</span></footer>
|
| 106 |
-
</main>
|
| 107 |
-
`;
|
| 108 |
-
|
| 109 |
-
const canvas = document.querySelector('#stage');
|
| 110 |
-
const ctx = canvas.getContext('2d');
|
| 111 |
-
const stageShell = document.querySelector('#stageShell');
|
| 112 |
-
const playButton = document.querySelector('#playButton');
|
| 113 |
-
const scrub = document.querySelector('#scrub');
|
| 114 |
-
const scrubProgress = document.querySelector('#scrubProgress');
|
| 115 |
-
const timeLabel = document.querySelector('#timeLabel');
|
| 116 |
-
const sceneLabel = document.querySelector('#sceneLabel');
|
| 117 |
-
const chapterList = document.querySelector('#chapterList');
|
| 118 |
-
const voiceText = document.querySelector('#voiceText');
|
| 119 |
-
const directorNote = document.querySelector('#directorNote');
|
| 120 |
-
const muteButton = document.querySelector('#muteButton');
|
| 121 |
-
const renderStatus = document.querySelector('#renderStatus');
|
| 122 |
-
const formatStatus = document.querySelector('#formatStatus');
|
| 123 |
-
const exportButton = document.querySelector('#exportButton');
|
| 124 |
-
const exportProgress = document.querySelector('#exportProgress');
|
| 125 |
-
const exportBar = document.querySelector('#exportBar');
|
| 126 |
-
const exportLabel = document.querySelector('#exportLabel');
|
| 127 |
-
const exportPercent = document.querySelector('#exportPercent');
|
| 128 |
-
|
| 129 |
-
let orientation = 'landscape';
|
| 130 |
-
let currentTime = 0;
|
| 131 |
-
let playing = false;
|
| 132 |
-
let muted = false;
|
| 133 |
-
let rafId = 0;
|
| 134 |
-
let lastStamp = 0;
|
| 135 |
-
let currentSceneIndex = 0;
|
| 136 |
-
let audio = null;
|
| 137 |
-
|
| 138 |
-
const clamp = (n, a = 0, b = 1) => Math.max(a, Math.min(b, n));
|
| 139 |
-
const lerp = (a, b, t) => a + (b - a) * t;
|
| 140 |
-
const ease = (t) => t * t * (3 - 2 * t);
|
| 141 |
-
const smooth = (t) => ease(clamp(t));
|
| 142 |
-
const fmt = (s) => `${String(Math.floor(s / 60)).padStart(2, '0')}:${String(Math.floor(s % 60)).padStart(2, '0')}`;
|
| 143 |
-
const activeScene = (t = currentTime) => scenes.findIndex((s) => t >= s.start && t < s.end) >= 0 ? scenes.findIndex((s) => t >= s.start && t < s.end) : scenes.length - 1;
|
| 144 |
-
|
| 145 |
-
function resizeStage() {
|
| 146 |
-
const portrait = orientation === 'portrait';
|
| 147 |
-
canvas.width = portrait ? 720 : 1280;
|
| 148 |
-
canvas.height = portrait ? 1280 : 720;
|
| 149 |
-
stageShell.classList.toggle('portrait', portrait);
|
| 150 |
-
stageShell.classList.toggle('landscape', !portrait);
|
| 151 |
-
formatStatus.textContent = portrait ? '9:16 竖屏' : '16:9 横屏';
|
| 152 |
-
render(currentTime);
|
| 153 |
-
}
|
| 154 |
-
|
| 155 |
-
function updateUi() {
|
| 156 |
-
const index = activeScene();
|
| 157 |
-
const scene = scenes[index];
|
| 158 |
-
if (index !== currentSceneIndex) {
|
| 159 |
-
if (playing && !muted && audio) {
|
| 160 |
-
audio.sfx('tick');
|
| 161 |
-
speakCurrent();
|
| 162 |
-
}
|
| 163 |
-
currentSceneIndex = index;
|
| 164 |
-
}
|
| 165 |
-
sceneLabel.textContent = scene.chapter;
|
| 166 |
-
timeLabel.textContent = `${fmt(currentTime)} / ${fmt(DURATION)}`;
|
| 167 |
-
scrub.value = currentTime.toFixed(2);
|
| 168 |
-
scrubProgress.style.width = `${(currentTime / DURATION) * 100}%`;
|
| 169 |
-
voiceText.textContent = scene.voice;
|
| 170 |
-
directorNote.textContent = scene.note;
|
| 171 |
-
[...chapterList.children].forEach((el, i) => el.classList.toggle('active', i === index));
|
| 172 |
-
}
|
| 173 |
-
|
| 174 |
-
function buildChapters() {
|
| 175 |
-
chapterList.innerHTML = scenes.map((s, i) => `<button class="chapter-item" data-index="${i}" type="button"><span class="chapter-no">${String(i + 1).padStart(2, '0')}</span><span class="chapter-copy"><strong>${s.title}</strong><small>${s.chapter.replace(/^\d+ \/ /, '')}</small></span><span class="chapter-time">${fmt(s.start)}</span></button>`).join('');
|
| 176 |
-
chapterList.querySelectorAll('.chapter-item').forEach((el) => el.addEventListener('click', () => {
|
| 177 |
-
currentTime = scenes[Number(el.dataset.index)].start;
|
| 178 |
-
updateUi();
|
| 179 |
-
render(currentTime);
|
| 180 |
-
}));
|
| 181 |
-
}
|
| 182 |
-
|
| 183 |
-
function roundedRect(c, x, y, w, h, r) {
|
| 184 |
-
c.beginPath(); c.roundRect(x, y, w, h, r); c.closePath();
|
| 185 |
-
}
|
| 186 |
-
function alpha(c, value) { c.globalAlpha = value; }
|
| 187 |
-
function text(c, str, x, y, size, color, weight = 500, align = 'left') {
|
| 188 |
-
c.font = `${weight} ${size}px Inter, "Noto Sans SC", "PingFang SC", sans-serif`;
|
| 189 |
-
c.fillStyle = color; c.textAlign = align; c.textBaseline = 'middle'; c.fillText(str, x, y);
|
| 190 |
-
}
|
| 191 |
-
function circle(c, x, y, r, fill, stroke = null, sw = 1) {
|
| 192 |
-
c.beginPath(); c.arc(x, y, r, 0, TAU); if (fill) { c.fillStyle = fill; c.fill(); } if (stroke) { c.strokeStyle = stroke; c.lineWidth = sw; c.stroke(); }
|
| 193 |
-
}
|
| 194 |
-
function line(c, x1, y1, x2, y2, color, width = 1, dash = []) { c.beginPath(); c.moveTo(x1, y1); c.lineTo(x2, y2); c.strokeStyle = color; c.lineWidth = width; c.setLineDash(dash); c.stroke(); c.setLineDash([]); }
|
| 195 |
-
function wrapText(c, str, x, y, maxWidth, lineHeight, size, color, weight = 500) {
|
| 196 |
-
c.font = `${weight} ${size}px Inter, "Noto Sans SC", "PingFang SC", sans-serif`;
|
| 197 |
-
c.fillStyle = color; c.textAlign = 'left'; c.textBaseline = 'top';
|
| 198 |
-
let lineStr = ''; let yy = y;
|
| 199 |
-
for (const ch of str) { const test = lineStr + ch; if (c.measureText(test).width > maxWidth && lineStr) { c.fillText(lineStr, x, yy); yy += lineHeight; lineStr = ch; } else lineStr = test; }
|
| 200 |
-
if (lineStr) c.fillText(lineStr, x, yy);
|
| 201 |
-
return yy + lineHeight;
|
| 202 |
-
}
|
| 203 |
-
function hashNoise(i) { const x = Math.sin(i * 17.17 + 3.2) * 43758.5453; return x - Math.floor(x); }
|
| 204 |
-
|
| 205 |
-
function drawBackground(c, w, h, scene, t) {
|
| 206 |
-
const g = c.createLinearGradient(0, 0, w, h); g.addColorStop(0, '#080b16'); g.addColorStop(.55, '#0c1021'); g.addColorStop(1, '#10192c'); c.fillStyle = g; c.fillRect(0, 0, w, h);
|
| 207 |
-
c.save(); c.globalAlpha = .2;
|
| 208 |
-
for (let i = 0; i < 70; i++) { const x = hashNoise(i) * w; const y = hashNoise(i + 99) * h; circle(c, x, y, 1 + hashNoise(i + 31) * 2, '#b4c4ff'); }
|
| 209 |
-
c.globalAlpha = .12; line(c, w * .08, h * .78, w * .92, h * .78, scene.color, 1); line(c, w * .08, h * .78, w * .08, h * .18, scene.color, 1); c.restore();
|
| 210 |
-
const glow = c.createRadialGradient(w * .72, h * .2, 0, w * .72, h * .2, Math.max(w, h) * .7); glow.addColorStop(0, `${scene.color}20`); glow.addColorStop(1, 'transparent'); c.fillStyle = glow; c.fillRect(0, 0, w, h);
|
| 211 |
-
const drift = (t * 18) % 80; c.save(); c.globalAlpha = .05; for (let x = -80; x < w + 80; x += 80) line(c, x + drift, 0, x - 200 + drift, h, '#fff', 1); c.restore();
|
| 212 |
-
}
|
| 213 |
-
|
| 214 |
-
function drawHeader(c, w, h, scene, local) {
|
| 215 |
-
const pad = w * (orientation === 'portrait' ? .09 : .07);
|
| 216 |
-
text(c, '经济学寓言 / ORIGINAL MOTION ESSAY', pad, h * (orientation === 'portrait' ? .1 : .11), orientation === 'portrait' ? 13 : 15, '#a4abc2', 650);
|
| 217 |
-
const badgeW = orientation === 'portrait' ? 108 : 126; roundedRect(c, w - pad - badgeW, h * .075, badgeW, 28, 14); c.fillStyle = `${scene.color}1f`; c.fill(); text(c, scene.chapter, w - pad - badgeW / 2, h * .075 + 14, 11, scene.color, 700, 'center');
|
| 218 |
-
const p = smooth(Math.min(1, local / 1.25));
|
| 219 |
-
c.save(); c.globalAlpha = p; text(c, scene.title, pad, h * (orientation === 'portrait' ? .19 : .21), orientation === 'portrait' ? 34 : 42, '#f6f7fb', 800); text(c, scene.kicker, pad, h * (orientation === 'portrait' ? .24 : .285), orientation === 'portrait' ? 15 : 18, scene.color, 650); c.restore();
|
| 220 |
-
}
|
| 221 |
-
|
| 222 |
-
function drawMonkey(c, x, y, scale, mood = 'neutral', accent = '#d9a776', t = 0) {
|
| 223 |
-
c.save(); c.translate(x, y); c.scale(scale, scale);
|
| 224 |
-
const bob = Math.sin(t * 4 + x * .01) * 2; c.translate(0, bob);
|
| 225 |
-
circle(c, -25, -40, 17, '#b77c5a'); circle(c, 25, -40, 17, '#b77c5a'); circle(c, 0, -42, 30, '#d9a776'); circle(c, 0, -36, 22, '#f0c79d');
|
| 226 |
-
circle(c, -9, -47, 3, '#111827'); circle(c, 9, -47, 3, '#111827');
|
| 227 |
-
if (mood === 'scared') { line(c, -10, -28, 10, -28, '#422f2a', 3); } else { c.beginPath(); c.arc(0, -29, 9, 0, Math.PI); c.strokeStyle = '#422f2a'; c.lineWidth = 3; c.stroke(); }
|
| 228 |
-
c.strokeStyle = accent; c.lineWidth = 8; c.lineCap = 'round'; line(c, -25, 0, -35, 38, accent, 8); line(c, 25, 0, 35, 38, accent, 8); line(c, -19, 4, -42, 20, accent, 8); line(c, 19, 4, 42, 20, accent, 8);
|
| 229 |
-
c.fillStyle = accent; c.beginPath(); c.ellipse(-35, 42, 12, 5, 0, 0, TAU); c.fill(); c.beginPath(); c.ellipse(35, 42, 12, 5, 0, 0, TAU); c.fill();
|
| 230 |
-
c.restore();
|
| 231 |
-
}
|
| 232 |
-
function drawLadder(c, x, y, height, width, color = '#e7b55c', tilt = 0) {
|
| 233 |
-
c.save(); c.translate(x, y); c.rotate(tilt); c.strokeStyle = color; c.lineWidth = 7; c.lineCap = 'round'; line(c, -width / 2, 0, -width / 2, -height, color, 7); line(c, width / 2, 0, width / 2, -height, color, 7); for (let yy = -20; yy > -height + 5; yy -= 27) line(c, -width / 2, yy, width / 2, yy, color, 5); c.restore();
|
| 234 |
-
}
|
| 235 |
-
function drawBanana(c, x, y, scale, glow = false) {
|
| 236 |
-
c.save(); c.translate(x, y); c.scale(scale, scale); if (glow) { c.globalAlpha = .18; circle(c, 0, 0, 72, '#f4b33e'); c.globalAlpha = 1; }
|
| 237 |
-
c.strokeStyle = '#f4c453'; c.lineWidth = 20; c.lineCap = 'round'; c.beginPath(); c.arc(0, 0, 50, -.2, Math.PI * .85); c.stroke(); c.strokeStyle = '#cf8b2f'; c.lineWidth = 6; c.beginPath(); c.arc(0, 0, 50, -.2, Math.PI * .85); c.stroke(); circle(c, 54, -7, 6, '#a96b29'); circle(c, -38, 38, 6, '#a96b29'); c.restore();
|
| 238 |
-
}
|
| 239 |
-
function drawWater(c, x, y, w, h, alphaValue = .8, t = 0) {
|
| 240 |
-
c.save(); c.globalAlpha = alphaValue; c.strokeStyle = '#66d5f6'; c.lineWidth = 3; c.lineCap = 'round'; for (let i = 0; i < 12; i++) { const xx = x + (i / 11) * w; const sway = Math.sin(t * 8 + i) * 8; line(c, xx, y, xx + sway, y + h * (.5 + hashNoise(i) * .5), '#66d5f6', 3); circle(c, xx + sway, y + h * (.5 + hashNoise(i) * .5), 3, '#b5f0ff'); } c.restore();
|
| 241 |
-
}
|
| 242 |
-
function drawBananaScene(c, w, h, scene, local) {
|
| 243 |
-
const portrait = orientation === 'portrait'; const cx = portrait ? w * .5 : w * .69; const top = portrait ? h * .37 : h * .53; const floor = portrait ? h * .78 : h * .78; const p = smooth(Math.min(1, local / 2));
|
| 244 |
-
drawBanana(c, cx, top - 150, portrait ? 1.1 : 1.35, true); line(c, cx, top - 120, cx, top + 6, '#9a6d4d', 4); drawLadder(c, cx, top + 190, 300, 80, '#e8bd71');
|
| 245 |
-
line(c, portrait ? w * .1 : w * .25, floor, portrait ? w * .9 : w * .88, floor, '#5b6a89', 5);
|
| 246 |
-
const count = portrait ? 3 : 5; for (let i = 0; i < count; i++) { const x = portrait ? w * (.24 + i * .26) : w * (.3 + i * .14); drawMonkey(c, x, floor - 42, portrait ? .82 : .72, 'neutral', i % 2 ? '#ce9d75' : '#c48762', local); }
|
| 247 |
-
if (scene.id === 'hook') { const scale = .9 + Math.sin(local * 2) * .03; c.save(); c.globalAlpha = p; text(c, '这是一道经济学题', portrait ? w * .5 : w * .13, portrait ? h * .91 : h * .86, portrait ? 18 : 22, '#dce3fa', 700, portrait ? 'center' : 'left'); c.restore(); }
|
| 248 |
-
if (scene.id === 'first') { const climb = smooth(clamp(local / 7)); drawMonkey(c, cx, floor - 35 - climb * 180, .75, 'neutral', '#d09a73', local); drawWater(c, cx - 100, top - 60, 200, 170, smooth(clamp((local - 7) / 2)), local); text(c, '收益', cx + 115, top - 164, 16, '#f4c453', 700); text(c, '代价', cx + 120, top + 20, 16, '#66d5f6', 700); }
|
| 249 |
-
if (scene.id === 'punish') { for (let i = 0; i < 3; i++) { const angle = -1.3 + i * 1.3; line(c, cx + Math.cos(angle) * 110, floor - 100, cx + Math.cos(angle) * 70, floor - 55, '#ef6b69', 7); } drawWater(c, cx - 100, top - 40, 200, 170, .5, local); text(c, '惩罚变成了动作', cx, h * .86, 18, '#ef8b88', 700, 'center'); }
|
| 250 |
-
if (scene.id === 'replacement') { const newcomerX = portrait ? w * .22 : w * .34; drawMonkey(c, newcomerX, floor - 45, .82, 'scared', '#a98bf2', local); circle(c, newcomerX, floor - 160, 38, '#a98bf220', '#a98bf2', 2); text(c, '?', newcomerX, floor - 160, 28, '#a98bf2', 800, 'center'); }
|
| 251 |
-
if (scene.id === 'loop') { drawWater(c, cx - 100, top - 40, 200, 170, .08, local); for (let i = 0; i < 3; i++) { const x1 = portrait ? w * (.2 + i * .3) : w * (.34 + i * .12); line(c, x1, floor - 125, x1 + (i === 1 ? -24 : 24), floor - 70, '#70a8ff', 6); } text(c, '冷水已经消失', cx, h * .86, 18, '#8eb9ff', 700, 'center'); }
|
| 252 |
-
}
|
| 253 |
-
|
| 254 |
-
function drawModelScene(c, w, h, scene, local) {
|
| 255 |
-
const portrait = orientation === 'portrait'; const pad = w * (portrait ? .1 : .09); const graphY = portrait ? h * .66 : h * .68; const graphX = pad; const graphW = w - pad * 2; const graphH = portrait ? h * .22 : h * .28;
|
| 256 |
-
const p = smooth(clamp(local / 2)); c.save(); c.globalAlpha = p;
|
| 257 |
-
line(c, graphX, graphY - graphH, graphX, graphY, '#7784a4', 2); line(c, graphX, graphY, graphX + graphW, graphY, '#7784a4', 2); text(c, '行为成本', graphX - 12, graphY - graphH - 10, 12, '#96a1bb', 500, 'right'); text(c, '行动:拿香蕉 →', graphX + graphW, graphY + 25, 12, '#96a1bb', 500, 'right');
|
| 258 |
-
const pivot = graphX + graphW * .54; c.beginPath(); c.moveTo(graphX, graphY - graphH * .18); c.bezierCurveTo(graphX + graphW * .18, graphY - graphH * .2, pivot - 40, graphY - graphH * .2, pivot, graphY - graphH * .85); c.bezierCurveTo(pivot + 50, graphY - graphH * .9, graphX + graphW * .8, graphY - graphH * .1, graphX + graphW, graphY - graphH * .08); c.strokeStyle = scene.color; c.lineWidth = 5; c.stroke();
|
| 259 |
-
const dotX = lerp(graphX + graphW * .24, graphX + graphW * .7, smooth(clamp(local / 8))); const dotY = graphY - graphH * .2 - smooth(clamp(local / 8)) * graphH * .55; circle(c, dotX, dotY, 10, '#fff', scene.color, 5); roundedRect(c, dotX - 74, dotY - 45, 150, 28, 14); c.fillStyle = '#18213a'; c.fill(); text(c, local < 8 ? '收益被看见' : '惩罚被内化', dotX, dotY - 31, 12, '#f5f7ff', 650, 'center'); c.restore();
|
| 260 |
-
// tiny ladder as the hidden variable
|
| 261 |
-
const bx = portrait ? w * .5 : w * .75; const by = portrait ? h * .36 : h * .45; drawLadder(c, bx, by + 90, 130, 42, '#e3b466', Math.sin(local) * .02); drawBanana(c, bx, by - 10, .6, true); text(c, '看得见的行为', bx, by + 140, 14, '#dce3fa', 600, 'center');
|
| 262 |
-
}
|
| 263 |
-
|
| 264 |
-
function drawRealityScene(c, w, h, scene, local) {
|
| 265 |
-
const portrait = orientation === 'portrait'; const items = [
|
| 266 |
-
['流程', '审批三次\n因为一直这样'], ['按钮', '新人别碰\n没人解释'], ['文化', '默认选项\n变成常识']
|
| 267 |
-
]; const baseY = portrait ? h * .38 : h * .48; const gap = portrait ? w * .29 : w * .25; const start = portrait ? w * .19 : w * .2;
|
| 268 |
-
items.forEach((item, i) => { const x = start + i * gap; const rise = smooth(clamp((local - i * .8) / 1.5)); c.save(); c.globalAlpha = rise; c.translate(x, baseY + (1 - rise) * 30); roundedRect(c, x - 70, baseY - 70, 140, 140, 18); c.fillStyle = '#17213a'; c.fill(); c.strokeStyle = i === 1 ? scene.color : '#334365'; c.lineWidth = 2; c.stroke(); circle(c, x, baseY - 32, 18, `${scene.color}2c`, scene.color, 2); text(c, i === 0 ? '≡' : i === 1 ? '□' : '↻', x, baseY - 32, 20, scene.color, 800, 'center'); text(c, item[0], x, baseY + 10, 17, '#f5f7ff', 700, 'center'); item[1].split('\n').forEach((lineText, j) => text(c, lineText, x, baseY + 38 + j * 18, 11, '#aeb8d2', 500, 'center')); c.restore(); if (i < 2) { const arrowP = smooth(clamp((local - 1.2 - i * .8) / 1.2)); line(c, x + 82, baseY, x + gap - 82, baseY, `${scene.color}88`, 3); text(c, '→', x + gap - 72, baseY - 3, 24, scene.color, 700, 'center'); if (arrowP > 0) { circle(c, lerp(x + 82, x + gap - 82, arrowP), baseY, 5, scene.color); } } });
|
| 269 |
-
text(c, '过去的风险 → 今天的默认', w * .5, portrait ? h * .72 : h * .77, portrait ? 20 : 24, scene.color, 750, 'center');
|
| 270 |
-
}
|
| 271 |
-
|
| 272 |
-
function drawExitScene(c, w, h, scene, local) {
|
| 273 |
-
const portrait = orientation === 'portrait'; const cx = w * .5; const cy = portrait ? h * .48 : h * .5; const p = smooth(clamp(local / 2)); const r = (portrait ? w : h) * (.12 + smooth(clamp(local / 9)) * .18);
|
| 274 |
-
c.save(); c.globalAlpha = p; c.strokeStyle = `${scene.color}99`; c.lineWidth = 2; for (let i = 0; i < 3; i++) { c.beginPath(); c.arc(cx, cy, r + i * 24, 0, TAU); c.stroke(); } circle(c, cx, cy, r * .56, '#15233a', scene.color, 3); text(c, '问', cx, cy - 5, portrait ? 44 : 56, '#f7f4ed', 800, 'center'); text(c, '冷水还在吗?', cx, cy + r + 60, portrait ? 24 : 32, scene.color, 750, 'center'); c.restore();
|
| 275 |
-
const qs = ['风险还在吗?', '代价谁承担?', '有更低成本的方案吗?']; qs.forEach((q, i) => { const yy = portrait ? h * (.72 + i * .06) : h * (.76 + (i - 1) * .08); const qP = smooth(clamp((local - 2 - i * .7) / 1.2)); c.save(); c.globalAlpha = qP; roundedRect(c, cx - (portrait ? 135 : 150), yy - 18, portrait ? 270 : 300, 36, 18); c.fillStyle = '#18213a'; c.fill(); text(c, q, cx, yy, portrait ? 14 : 16, '#dbe6ff', 650, 'center'); c.restore(); });
|
| 276 |
-
}
|
| 277 |
-
|
| 278 |
-
function render(t = currentTime) {
|
| 279 |
-
const w = canvas.width; const h = canvas.height; const idx = activeScene(t); const scene = scenes[idx]; const local = t - scene.start;
|
| 280 |
-
ctx.clearRect(0, 0, w, h); drawBackground(ctx, w, h, scene, t); drawHeader(ctx, w, h, scene, local);
|
| 281 |
-
if (['hook', 'first', 'punish', 'replacement', 'loop'].includes(scene.id)) drawBananaScene(ctx, w, h, scene, local); else if (scene.id === 'model') drawModelScene(ctx, w, h, scene, local); else if (scene.id === 'reality') drawRealityScene(ctx, w, h, scene, local); else drawExitScene(ctx, w, h, scene, local);
|
| 282 |
-
// bottom caption strip is intentionally within the safe zone
|
| 283 |
-
const pad = w * (orientation === 'portrait' ? .09 : .07); const caption = scene.voice; const captionY = orientation === 'portrait' ? h * .87 : h * .91; ctx.save(); ctx.globalAlpha = .93; roundedRect(ctx, pad, captionY - 28, w - pad * 2, orientation === 'portrait' ? 76 : 58, 16); ctx.fillStyle = '#090d1ddd'; ctx.fill(); ctx.strokeStyle = `${scene.color}38`; ctx.lineWidth = 1; ctx.stroke(); wrapText(ctx, caption, pad + 18, captionY - 17, w - pad * 2 - 36, orientation === 'portrait' ? 20 : 18, orientation === 'portrait' ? 13 : 14, '#e6ebfb', 500); ctx.restore();
|
| 284 |
-
}
|
| 285 |
-
|
| 286 |
-
function tick(stamp) {
|
| 287 |
-
if (!lastStamp) lastStamp = stamp;
|
| 288 |
-
const delta = Math.min(.05, (stamp - lastStamp) / 1000); lastStamp = stamp;
|
| 289 |
-
if (playing) { currentTime += delta; if (currentTime >= DURATION) { currentTime = 0; playing = false; playButton.innerHTML = '<span class="play-icon">▶</span><span>播放</span>'; } updateUi(); render(currentTime); }
|
| 290 |
-
rafId = requestAnimationFrame(tick);
|
| 291 |
-
}
|
| 292 |
-
|
| 293 |
-
function setPlaying(value) {
|
| 294 |
-
playing = value; lastStamp = 0; playButton.innerHTML = playing ? '<span class="play-icon">Ⅱ</span><span>暂停</span>' : '<span class="play-icon">▶</span><span>播放</span>'; renderStatus.textContent = playing ? '实时播放' : '实时画布';
|
| 295 |
-
if (playing && !muted) ensureAudio().startPreview();
|
| 296 |
-
}
|
| 297 |
-
|
| 298 |
-
function ensureAudio() {
|
| 299 |
-
if (audio) return audio;
|
| 300 |
-
const ac = new (window.AudioContext || window.webkitAudioContext)(); const master = ac.createGain(); master.gain.value = .18; master.connect(ac.destination); const mediaDest = ac.createMediaStreamDestination(); master.connect(mediaDest);
|
| 301 |
-
let started = false;
|
| 302 |
-
function blip(type = 'tick', at = ac.currentTime) { const osc = ac.createOscillator(); const g = ac.createGain(); const freq = type === 'spray' ? 760 : type === 'drop' ? 180 : 420; osc.type = type === 'spray' ? 'triangle' : 'sine'; osc.frequency.setValueAtTime(freq, at); osc.frequency.exponentialRampToValueAtTime(freq * .55, at + (type === 'spray' ? .25 : .12)); g.gain.setValueAtTime(.0001, at); g.gain.exponentialRampToValueAtTime(type === 'spray' ? .15 : .09, at + .012); g.gain.exponentialRampToValueAtTime(.0001, at + (type === 'spray' ? .28 : .16)); osc.connect(g).connect(master); osc.start(at); osc.stop(at + .32); }
|
| 303 |
-
function scheduleMusic(startAt = ac.currentTime + .05, offset = 0, duration = DURATION) { const notes = [55, 65.41, 73.42, 82.41, 73.42, 65.41, 49, 65.41]; for (let i = 0; i < Math.ceil(duration / 1.5); i++) { const at = startAt + i * 1.5; const root = notes[(i + offset) % notes.length]; const osc = ac.createOscillator(); const g = ac.createGain(); osc.type = i % 4 === 0 ? 'triangle' : 'sine'; osc.frequency.setValueAtTime(root, at); g.gain.setValueAtTime(.0001, at); g.gain.linearRampToValueAtTime(i % 4 === 0 ? .055 : .025, at + .08); g.gain.exponentialRampToValueAtTime(.0001, at + 1.32); osc.connect(g).connect(master); osc.start(at); osc.stop(at + 1.35); if (i % 4 === 0) blip('drop', at); } }
|
| 304 |
-
function startPreview() { if (ac.state === 'suspended') ac.resume(); if (!started) { scheduleMusic(); started = true; } }
|
| 305 |
-
async function prepareExport() { const exportAc = new (window.AudioContext || window.webkitAudioContext)(); const out = exportAc.createGain(); out.gain.value = .2; out.connect(exportAc.destination); const dest = exportAc.createMediaStreamDestination(); out.connect(dest); const notes = [55, 65.41, 73.42, 82.41, 73.42, 65.41, 49, 65.41]; const start = exportAc.currentTime + .08; for (let i = 0; i < Math.ceil(DURATION / 1.5); i++) { const at = start + i * 1.5; const osc = exportAc.createOscillator(); const g = exportAc.createGain(); osc.type = i % 4 === 0 ? 'triangle' : 'sine'; osc.frequency.setValueAtTime(notes[i % notes.length], at); g.gain.setValueAtTime(.0001, at); g.gain.linearRampToValueAtTime(i % 4 === 0 ? .055 : .025, at + .08); g.gain.exponentialRampToValueAtTime(.0001, at + 1.32); osc.connect(g).connect(out); osc.start(at); osc.stop(at + 1.35); }
|
| 306 |
-
scenes.forEach((s, i) => { const at = start + s.start; const osc = exportAc.createOscillator(); const g = exportAc.createGain(); osc.type = 'sine'; osc.frequency.setValueAtTime(i % 2 ? 640 : 420, at); g.gain.setValueAtTime(.0001, at); g.gain.linearRampToValueAtTime(.04, at + .02); g.gain.exponentialRampToValueAtTime(.0001, at + .15); osc.connect(g).connect(out); osc.start(at); osc.stop(at + .18); });
|
| 307 |
-
try { const response = await fetch('/audio/narration.wav', { cache: 'no-store' }); if (response.ok) { const buffer = await response.arrayBuffer(); const narration = await exportAc.decodeAudioData(buffer); const source = exportAc.createBufferSource(); source.buffer = narration; source.connect(out); source.start(start); } } catch (error) { console.info('Optional narration asset not found; exporting captions, music and SFX.', error); }
|
| 308 |
-
return { context: exportAc, dest, startAt: start };
|
| 309 |
-
}
|
| 310 |
-
audio = { ac, master, startPreview, sfx: blip, prepareExport, toggle() { muted = !muted; master.gain.value = muted ? 0 : .18; if (muted && 'speechSynthesis' in window) window.speechSynthesis.cancel(); muteButton.textContent = muted ? '⊘' : '◖'; } };
|
| 311 |
-
return audio;
|
| 312 |
-
}
|
| 313 |
-
|
| 314 |
-
async function speakCurrent() { if (!('speechSynthesis' in window)) return; window.speechSynthesis.cancel(); const utterance = new SpeechSynthesisUtterance(scenes[activeScene()].voice); utterance.lang = 'zh-CN'; utterance.rate = .96; utterance.pitch = 1.03; window.speechSynthesis.speak(utterance); }
|
| 315 |
-
|
| 316 |
-
function supportedMime() { const types = ['video/mp4;codecs="avc1.42E01E,mp4a.40.2"', 'video/mp4', 'video/webm;codecs=vp9,opus', 'video/webm']; return types.find((t) => window.MediaRecorder?.isTypeSupported?.(t)) || ''; }
|
| 317 |
-
async function transcodeWebmToMp4(webmBlob) {
|
| 318 |
-
// Lazy-load only when the browser cannot encode MP4 natively. Conversion stays in-browser.
|
| 319 |
-
const ffmpegModule = await import(/* @vite-ignore */ 'https://cdn.jsdelivr.net/npm/@ffmpeg/ffmpeg@0.12.10/+esm');
|
| 320 |
-
const util = await import(/* @vite-ignore */ 'https://cdn.jsdelivr.net/npm/@ffmpeg/util@0.12.1/+esm');
|
| 321 |
-
const ffmpeg = new ffmpegModule.FFmpeg();
|
| 322 |
-
ffmpeg.on('progress', ({ progress }) => {
|
| 323 |
-
const pct = Math.round(100 * clamp(progress));
|
| 324 |
-
exportLabel.textContent = `本地转码 MP4 · ${pct}%`;
|
| 325 |
-
exportPercent.textContent = `${pct}%`;
|
| 326 |
-
exportBar.style.width = `${pct}%`;
|
| 327 |
-
});
|
| 328 |
-
const coreBase = 'https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.10/dist/umd';
|
| 329 |
-
await ffmpeg.load({
|
| 330 |
-
coreURL: await util.toBlobURL(`${coreBase}/ffmpeg-core.js`, 'text/javascript'),
|
| 331 |
-
wasmURL: await util.toBlobURL(`${coreBase}/ffmpeg-core.wasm`, 'application/wasm')
|
| 332 |
-
});
|
| 333 |
-
await ffmpeg.writeFile('input.webm', await util.fetchFile(webmBlob));
|
| 334 |
-
await ffmpeg.exec(['-i', 'input.webm', '-c:v', 'libx264', '-c:a', 'aac', '-pix_fmt', 'yuv420p', '-movflags', 'faststart', 'output.mp4']);
|
| 335 |
-
const data = await ffmpeg.readFile('output.mp4');
|
| 336 |
-
await ffmpeg.deleteFile('input.webm');
|
| 337 |
-
await ffmpeg.deleteFile('output.mp4');
|
| 338 |
-
return new Blob([data.buffer], { type: 'video/mp4' });
|
| 339 |
-
}
|
| 340 |
-
async function exportVideo() {
|
| 341 |
-
if (!window.MediaRecorder || !canvas.captureStream) { exportLabel.textContent = '当前浏览器不支持画布录制'; return; }
|
| 342 |
-
exportButton.disabled = true; exportProgress.hidden = false; exportBar.style.width = '0%'; exportPercent.textContent = '0%'; exportLabel.textContent = '正在准备本地音轨…';
|
| 343 |
-
const audioPack = await ensureAudio().prepareExport(); const videoStream = canvas.captureStream(FPS); const combined = new MediaStream([...videoStream.getVideoTracks(), ...audioPack.dest.stream.getAudioTracks()]); const mime = supportedMime(); const chunks = []; const recorder = new MediaRecorder(combined, mime ? { mimeType: mime, videoBitsPerSecond: 7_000_000 } : undefined);
|
| 344 |
-
const startedAt = performance.now(); const previousTime = currentTime; const previousPlaying = playing; const previousOrientation = orientation;
|
| 345 |
-
recorder.ondataavailable = (e) => { if (e.data.size) chunks.push(e.data); };
|
| 346 |
-
const finished = new Promise((resolve) => { recorder.onstop = resolve; });
|
| 347 |
-
recorder.start(250); renderStatus.textContent = '正在导出';
|
| 348 |
-
await new Promise((resolve) => { let last = performance.now(); function frame(now) { const elapsed = Math.min(DURATION, (now - startedAt) / 1000); currentTime = elapsed; render(currentTime); updateUi(); const pct = Math.round(elapsed / DURATION * 100); exportBar.style.width = `${pct}%`; exportPercent.textContent = `${pct}%`; exportLabel.textContent = `渲染第 ${fmt(elapsed)} / ${fmt(DURATION)} 帧`; if (elapsed >= DURATION) { recorder.stop(); resolve(); } else { last = now; requestAnimationFrame(frame); } } requestAnimationFrame(frame); });
|
| 349 |
-
await finished; videoStream.getTracks().forEach((track) => track.stop()); audioPack.dest.stream.getTracks().forEach((track) => track.stop()); await audioPack.context.close();
|
| 350 |
-
const sourceBlob = new Blob(chunks, { type: mime || 'video/webm' });
|
| 351 |
-
let outputBlob = sourceBlob;
|
| 352 |
-
let isMp4 = (mime || '').includes('mp4');
|
| 353 |
-
if (!isMp4) {
|
| 354 |
-
exportLabel.textContent = '浏览器原生不支持 MP4,准备本地转码…';
|
| 355 |
-
try {
|
| 356 |
-
outputBlob = await transcodeWebmToMp4(sourceBlob);
|
| 357 |
-
isMp4 = true;
|
| 358 |
-
} catch (error) {
|
| 359 |
-
console.warn('Local MP4 conversion unavailable; keeping WebM fallback.', error);
|
| 360 |
-
exportLabel.textContent = '本地 MP4 转码不可用,保留兼容 WebM';
|
| 361 |
-
}
|
| 362 |
-
}
|
| 363 |
-
const ext = isMp4 ? 'mp4' : 'webm'; const url = URL.createObjectURL(outputBlob); const a = document.createElement('a'); a.href = url; a.download = `收猴子的故事-${orientation === 'portrait' ? '抖音竖屏' : 'B站横屏'}.${ext}`; a.click(); setTimeout(() => URL.revokeObjectURL(url), 2000);
|
| 364 |
-
currentTime = previousTime; playing = previousPlaying; orientation = previousOrientation; resizeStage(); updateUi(); exportBar.style.width = '100%'; exportPercent.textContent = '完成'; exportLabel.textContent = isMp4 ? 'MP4 已保存到下载目录' : '当前浏览器不支持 MP4,已保存兼容 WebM'; renderStatus.textContent = '实时画布'; exportButton.disabled = false;
|
| 365 |
-
}
|
| 366 |
-
|
| 367 |
-
function fillCopy() { for (const key of ['bilibili', 'douyin']) { document.querySelector(`#${key}Title`).value = copy[key].title; document.querySelector(`#${key}Description`).value = copy[key].description; document.querySelector(`#${key}Tags`).value = copy[key].tags; } }
|
| 368 |
-
function copyPack(key, button) { const pack = copy[key]; const val = `标题:${pack.title}\n\n简介:${pack.description}\n\n标签:${pack.tags}`; navigator.clipboard?.writeText(val).then(() => { const original = button.textContent; button.textContent = '已复制'; setTimeout(() => button.textContent = original, 1600); }).catch(() => { const ta = document.createElement('textarea'); ta.value = val; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); ta.remove(); button.textContent = '已复制'; setTimeout(() => button.textContent = '复制整套', 1600); }); }
|
| 369 |
-
function drawCover(target, isPortrait) { const c = target.getContext('2d'); const w = target.width; const h = target.height; const s = scenes[0]; const grad = c.createLinearGradient(0, 0, w, h); grad.addColorStop(0, '#080b16'); grad.addColorStop(1, '#19213d'); c.fillStyle = grad; c.fillRect(0, 0, w, h); for (let i = 0; i < 22; i++) circle(c, hashNoise(i) * w, hashNoise(i + 32) * h, 1 + hashNoise(i + 12) * 3, '#a8b9ff55'); const x = isPortrait ? w * .5 : w * .7; const floor = isPortrait ? h * .72 : h * .78; drawBanana(c, x, isPortrait ? h * .22 : h * .3, isPortrait ? .72 : .9, true); drawLadder(c, x, floor - 40, isPortrait ? 230 : 180, isPortrait ? 54 : 46, '#e8bd71'); for (let i = 0; i < (isPortrait ? 3 : 5); i++) drawMonkey(c, isPortrait ? w * (.25 + i * .25) : w * (.3 + i * .14), floor, isPortrait ? .5 : .44, 'neutral', '#c9946d', 0); text(c, '收猴子的故事', isPortrait ? w * .1 : w * .08, isPortrait ? h * .82 : h * .82, isPortrait ? 25 : 28, '#f7f4ed', 800); text(c, '为什么没人敢拿?', isPortrait ? w * .1 : w * .08, isPortrait ? h * .88 : h * .9, isPortrait ? 15 : 17, '#f4b33e', 650); }
|
| 370 |
-
function downloadCanvas(target, name) { target.toBlob((blob) => { const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = name; a.click(); setTimeout(() => URL.revokeObjectURL(a.href), 1000); }, 'image/png'); }
|
| 371 |
-
|
| 372 |
-
playButton.addEventListener('click', () => setPlaying(!playing));
|
| 373 |
-
document.querySelector('#restartButton').addEventListener('click', () => { currentTime = 0; setPlaying(true); updateUi(); render(currentTime); });
|
| 374 |
-
document.querySelector('#speakButton').addEventListener('click', speakCurrent);
|
| 375 |
-
muteButton.addEventListener('click', () => ensureAudio().toggle());
|
| 376 |
-
scrub.addEventListener('input', (e) => { currentTime = Number(e.target.value); updateUi(); render(currentTime); });
|
| 377 |
-
document.querySelectorAll('[data-orientation]').forEach((button) => button.addEventListener('click', () => { orientation = button.dataset.orientation; document.querySelectorAll('[data-orientation]').forEach((b) => b.classList.toggle('active', b === button)); resizeStage(); }));
|
| 378 |
-
exportButton.addEventListener('click', exportVideo);
|
| 379 |
-
document.querySelectorAll('.copy-button').forEach((button) => button.addEventListener('click', () => copyPack(button.dataset.copy, button)));
|
| 380 |
-
const landscapeCover = document.querySelector('#landscapeCover'); const portraitCover = document.querySelector('#portraitCover'); drawCover(landscapeCover, false); drawCover(portraitCover, true); document.querySelector('#downloadLandscape').addEventListener('click', () => downloadCanvas(landscapeCover, '收猴子的故事-横屏封面.png')); document.querySelector('#downloadPortrait').addEventListener('click', () => downloadCanvas(portraitCover, '收猴子的故事-竖屏封面.png'));
|
| 381 |
-
|
| 382 |
-
buildChapters(); fillCopy(); updateUi(); resizeStage(); render(0); rafId = requestAnimationFrame(tick);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/styles.css
DELETED
|
@@ -1,105 +0,0 @@
|
|
| 1 |
-
@import url('https://fonts.googleapis.com/css2?family=DM+Mono:wght@400;500&family=Inter:wght@400;500;600;700;800&family=Noto+Sans+SC:wght@400;500;600;700;800&display=swap');
|
| 2 |
-
|
| 3 |
-
:root { color-scheme: dark; font-family: Inter, "Noto Sans SC", system-ui, sans-serif; background: #080a12; color: #f4f6fb; font-synthesis: none; --bg: #080a12; --panel: #0e1220; --panel-2: #11172a; --stroke: #202941; --muted: #8f9ab6; --muted-2: #65718e; --yellow: #f4b33e; --cyan: #71d6c5; }
|
| 4 |
-
* { box-sizing: border-box; }
|
| 5 |
-
body { margin: 0; min-width: 320px; background: radial-gradient(circle at 20% 0%, #12182a 0%, var(--bg) 42%); }
|
| 6 |
-
button, textarea, input { font: inherit; }
|
| 7 |
-
button { cursor: pointer; }
|
| 8 |
-
button:disabled { cursor: wait; opacity: .6; }
|
| 9 |
-
.shell { width: min(1600px, 100%); margin: 0 auto; padding: 0 34px 38px; }
|
| 10 |
-
.topbar { height: 76px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid #1a2236; }
|
| 11 |
-
.brand { display: flex; align-items: center; gap: 12px; }
|
| 12 |
-
.brand-mark { display: grid; place-items: center; width: 35px; height: 35px; border-radius: 12px; background: #f4b33e; color: #111522; font-size: 25px; font-weight: 800; transform: rotate(-9deg); }
|
| 13 |
-
.brand strong { display: block; letter-spacing: .12em; font-size: 12px; }
|
| 14 |
-
.brand span:not(.brand-mark) { display: block; color: var(--muted); font-size: 11px; margin-top: 3px; }
|
| 15 |
-
.top-status { display: flex; align-items: center; color: #98a4c1; font: 500 11px "DM Mono", monospace; letter-spacing: .04em; }
|
| 16 |
-
.live-dot { width: 7px; height: 7px; border-radius: 50%; background: #68ddb1; box-shadow: 0 0 0 4px #68ddb11c; margin-right: 8px; }
|
| 17 |
-
.status-divider { width: 1px; height: 16px; background: #2a3550; margin: 0 13px; }
|
| 18 |
-
.workspace { display: grid; grid-template-columns: minmax(0, 1.54fr) minmax(350px, .76fr); gap: 28px; padding-top: 30px; }
|
| 19 |
-
.stage-panel { min-width: 0; }
|
| 20 |
-
.stage-shell { position: relative; width: 100%; aspect-ratio: 16 / 9; overflow: hidden; border-radius: 17px; background: #080b16; border: 1px solid #2a3552; box-shadow: 0 24px 80px #0008, inset 0 1px #ffffff0e; }
|
| 21 |
-
.stage-shell.portrait { width: min(100%, 500px); aspect-ratio: 9 / 16; margin: 0 auto; }
|
| 22 |
-
#stage { display: block; width: 100%; height: 100%; }
|
| 23 |
-
.stage-hud { position: absolute; left: 18px; right: 18px; top: 14px; display: flex; justify-content: space-between; color: #d3d9ebcc; font: 500 10px "DM Mono", monospace; letter-spacing: .04em; pointer-events: none; }
|
| 24 |
-
.stage-safe { position: absolute; left: 10%; right: 10%; color: #ffffff22; font: 400 8px "DM Mono", monospace; pointer-events: none; opacity: 0; transition: opacity .2s; }
|
| 25 |
-
.stage-shell.portrait .stage-safe { opacity: 1; }
|
| 26 |
-
.safe-top { top: 5%; border-top: 1px dashed #ffffff26; padding-top: 4px; }
|
| 27 |
-
.safe-bottom { bottom: 5%; border-bottom: 1px dashed #ffffff26; padding-bottom: 4px; text-align: right; }
|
| 28 |
-
.controls { display: flex; align-items: center; gap: 10px; padding: 14px 0 10px; }
|
| 29 |
-
.primary-control, .icon-control, .chip, .copy-button, .cover-actions button { border: 1px solid #2b3754; color: #dfe6fb; background: #11182a; border-radius: 8px; }
|
| 30 |
-
.primary-control { height: 34px; padding: 0 13px; display: flex; align-items: center; gap: 8px; background: #f4b33e; color: #151a27; border-color: #f4b33e; font-weight: 750; font-size: 12px; }
|
| 31 |
-
.play-icon { font-size: 11px; }
|
| 32 |
-
.icon-control { width: 34px; height: 34px; font-size: 17px; color: #acb8d0; }
|
| 33 |
-
.primary-control:hover, .icon-control:hover, .chip:hover, .copy-button:hover, .cover-actions button:hover { filter: brightness(1.17); }
|
| 34 |
-
.scrub { position: relative; flex: 1; height: 24px; display: flex; align-items: center; }
|
| 35 |
-
.scrub input { position: relative; z-index: 2; width: 100%; appearance: none; background: transparent; }
|
| 36 |
-
.scrub input::-webkit-slider-runnable-track { height: 3px; background: #2a3550; border-radius: 4px; }
|
| 37 |
-
.scrub input::-webkit-slider-thumb { appearance: none; width: 11px; height: 11px; margin-top: -4px; border-radius: 50%; background: #f4b33e; border: 2px solid #f8d98e; box-shadow: 0 0 0 3px #f4b33e2a; }
|
| 38 |
-
.scrub-progress { position: absolute; z-index: 1; left: 0; top: 10px; width: 0; height: 3px; background: #f4b33e; border-radius: 4px; pointer-events: none; }
|
| 39 |
-
.frame-rate { color: #687492; font: 500 10px "DM Mono", monospace; }
|
| 40 |
-
.orientation-row { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; color: var(--muted); }
|
| 41 |
-
.control-label { font-size: 11px; margin-right: 4px; }
|
| 42 |
-
.chip { height: 28px; padding: 0 10px; color: #96a2be; font-size: 11px; }
|
| 43 |
-
.chip.active { color: #f7d891; border-color: #a2742d; background: #f4b33e17; }
|
| 44 |
-
.safe-note { color: #596783; font-size: 10px; margin-left: 5px; }
|
| 45 |
-
.inspector { border-left: 1px solid #202a43; padding-left: 28px; min-width: 0; }
|
| 46 |
-
.inspector-head { display: flex; justify-content: space-between; align-items: flex-start; }
|
| 47 |
-
.eyebrow { display: block; color: #697693; font: 500 10px "DM Mono", monospace; letter-spacing: .12em; }
|
| 48 |
-
h1 { font-size: clamp(24px, 3vw, 36px); line-height: 1.1; letter-spacing: -.04em; margin: 8px 0 0; }
|
| 49 |
-
.duration-badge { border: 1px solid #2c3854; color: #aeb9d4; padding: 6px 9px; border-radius: 7px; font: 500 10px "DM Mono", monospace; }
|
| 50 |
-
.lede { color: #99a5bf; line-height: 1.65; margin: 13px 0 20px; font-size: 13px; max-width: 390px; }
|
| 51 |
-
.chapter-list { display: flex; flex-direction: column; gap: 3px; max-height: 380px; overflow: auto; padding-right: 4px; }
|
| 52 |
-
.chapter-item { display: grid; grid-template-columns: 28px 1fr auto; gap: 9px; align-items: center; text-align: left; width: 100%; min-height: 44px; padding: 7px 8px; border: 1px solid transparent; color: #99a5c0; background: transparent; border-radius: 9px; }
|
| 53 |
-
.chapter-item:hover { background: #151d31; }
|
| 54 |
-
.chapter-item.active { color: #f5f7fd; background: #171f34; border-color: #2d3b5e; box-shadow: inset 3px 0 var(--yellow); }
|
| 55 |
-
.chapter-no { font: 500 10px "DM Mono", monospace; color: #63708c; }
|
| 56 |
-
.chapter-item.active .chapter-no { color: var(--yellow); }
|
| 57 |
-
.chapter-copy strong { display: block; font-size: 12px; font-weight: 650; }
|
| 58 |
-
.chapter-copy small { display: block; color: #65718b; font-size: 10px; margin-top: 3px; }
|
| 59 |
-
.chapter-time { color: #697590; font: 500 10px "DM Mono", monospace; }
|
| 60 |
-
.inspector-card { border-top: 1px solid #222d47; margin-top: 20px; padding-top: 15px; }
|
| 61 |
-
.card-title { display: flex; align-items: center; justify-content: space-between; color: #8190ae; font: 500 10px "DM Mono", monospace; letter-spacing: .07em; text-transform: uppercase; }
|
| 62 |
-
.text-button { border: 0; background: transparent; color: #f4b33e; font-size: 11px; padding: 0; }
|
| 63 |
-
.voice-card p, .note-card p { color: #c1c9dc; font-size: 12px; line-height: 1.7; margin: 9px 0 0; }
|
| 64 |
-
.note-card p { color: #8491ad; }
|
| 65 |
-
.tiny-dot { width: 5px; height: 5px; border-radius: 50%; background: #68ddb1; }
|
| 66 |
-
.export-button { width: 100%; margin-top: 22px; min-height: 57px; border: 1px solid #73531f; border-radius: 10px; background: linear-gradient(110deg, #f4b33e, #f2c766); color: #171b28; display: grid; grid-template-columns: 24px 1fr 20px; align-items: center; text-align: left; padding: 9px 13px; }
|
| 67 |
-
.export-button > span:first-child { font-size: 22px; font-weight: 800; }
|
| 68 |
-
.export-button strong, .export-button small { display: block; }
|
| 69 |
-
.export-button strong { font-size: 12px; }
|
| 70 |
-
.export-button small { font-size: 10px; margin-top: 3px; opacity: .68; }
|
| 71 |
-
.export-arrow { font-size: 18px; text-align: right; }
|
| 72 |
-
.export-progress { margin-top: 10px; padding: 10px 2px 0; }
|
| 73 |
-
.progress-top { display: flex; justify-content: space-between; color: #9aa6c0; font: 500 10px "DM Mono", monospace; }
|
| 74 |
-
.progress-track { height: 4px; margin-top: 8px; background: #222c45; border-radius: 4px; overflow: hidden; }
|
| 75 |
-
.progress-track span { display: block; height: 100%; width: 0; background: var(--yellow); transition: width .1s; }
|
| 76 |
-
.publish-section { margin-top: 56px; border-top: 1px solid #1d263c; padding-top: 25px; }
|
| 77 |
-
.section-heading { display: flex; align-items: flex-end; justify-content: space-between; margin-bottom: 18px; }
|
| 78 |
-
h2 { font-size: 22px; letter-spacing: -.04em; margin: 7px 0 0; }
|
| 79 |
-
.section-caption { color: #687593; font-size: 11px; }
|
| 80 |
-
.publish-grid { display: grid; grid-template-columns: 1fr 1fr 1.05fr; gap: 14px; }
|
| 81 |
-
.publish-card, .cover-card { border: 1px solid #212c45; border-radius: 12px; background: #0e1424; padding: 15px; }
|
| 82 |
-
.platform-head { display: flex; align-items: center; gap: 9px; margin-bottom: 14px; }
|
| 83 |
-
.platform-head > div:not(.cover-previews) { flex: 1; }
|
| 84 |
-
.platform-head strong, .platform-head small { display: block; }
|
| 85 |
-
.platform-head strong { font-size: 12px; }
|
| 86 |
-
.platform-head small { color: #6f7b97; font-size: 10px; margin-top: 3px; }
|
| 87 |
-
.platform-icon { display: grid; place-items: center; width: 28px; height: 28px; border-radius: 8px; font-size: 16px; }
|
| 88 |
-
.bilibili-icon { background: #73d7eb20; color: #73d7eb; }
|
| 89 |
-
.douyin-icon { background: #f06fa820; color: #f59bc5; }
|
| 90 |
-
.cover-icon { background: #f4b33e20; color: #f4b33e; }
|
| 91 |
-
.copy-button { font-size: 10px; padding: 7px 9px; color: #e4b759; border-color: #684f25; background: #2a2114; white-space: nowrap; }
|
| 92 |
-
.publish-card label { display: block; color: #6f7c98; font: 500 9px "DM Mono", monospace; margin-top: 11px; }
|
| 93 |
-
.publish-card textarea { resize: vertical; width: 100%; min-height: 34px; border: 1px solid #202b45; border-radius: 7px; background: #0a0f1d; color: #c4ccde; padding: 8px; margin-top: 5px; line-height: 1.55; font: 400 11px "Noto Sans SC", sans-serif; }
|
| 94 |
-
.publish-card label:nth-of-type(2) textarea { min-height: 110px; }
|
| 95 |
-
.cover-previews { display: flex; gap: 10px; align-items: center; height: 185px; }
|
| 96 |
-
.cover-preview { position: relative; background: #090d18; border: 1px solid #29344f; border-radius: 8px; overflow: hidden; }
|
| 97 |
-
.cover-preview canvas { display: block; width: 100%; height: 100%; }
|
| 98 |
-
.landscape-cover { width: 66%; aspect-ratio: 16 / 9; }
|
| 99 |
-
.portrait-cover { width: 30%; height: 100%; }
|
| 100 |
-
.cover-preview > span { position: absolute; left: 7px; bottom: 6px; font: 500 9px "DM Mono", monospace; color: #d9e2f7; background: #080b16aa; padding: 3px 5px; border-radius: 4px; }
|
| 101 |
-
.cover-actions { display: flex; gap: 8px; margin-top: 12px; }
|
| 102 |
-
.cover-actions button { flex: 1; padding: 8px 4px; font-size: 10px; color: #aab6d0; }
|
| 103 |
-
.footer { display: flex; justify-content: space-between; color: #56627d; font: 500 10px "DM Mono", monospace; margin-top: 26px; padding-top: 18px; border-top: 1px solid #1a2338; }
|
| 104 |
-
@media (max-width: 1080px) { .workspace { grid-template-columns: 1fr; } .inspector { border-left: 0; border-top: 1px solid #202a43; padding: 26px 0 0; } .chapter-list { display: grid; grid-template-columns: repeat(2, 1fr); max-height: none; } .publish-grid { grid-template-columns: 1fr 1fr; } .cover-card { grid-column: 1 / -1; } }
|
| 105 |
-
@media (max-width: 670px) { .shell { padding: 0 15px 28px; } .topbar { height: 64px; } .top-status { display: none; } .workspace { padding-top: 18px; gap: 20px; } .stage-shell { border-radius: 11px; } .controls { gap: 6px; } .frame-rate { display: none; } .safe-note { width: 100%; margin: 4px 0 0 52px; } .chapter-list { grid-template-columns: 1fr; } .publish-section { margin-top: 35px; } .section-heading { display: block; } .section-caption { display: block; margin-top: 8px; } .publish-grid { grid-template-columns: 1fr; } .cover-card { grid-column: auto; } .footer { display: block; line-height: 1.8; } .footer span { display: block; } }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|