chore: ruler files update

Signed-off-by: Dmytro Stanchiev <git@dmytros.dev>
This commit is contained in:
2026-05-24 21:03:49 -04:00
parent 97b3ddd653
commit abb472c83d
303 changed files with 46670 additions and 25369 deletions

View File

@@ -0,0 +1,201 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>什么是 token · narration demo</title>
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<style>
body { margin: 0; background: #0a0a0a; font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", sans-serif; min-height: 100vh; display: flex; align-items: center; justify-content: center; flex-direction: column; }
#root { box-shadow: 0 20px 60px rgba(0,0,0,0.5); }
.scene-padding { padding: 120px; height: 100%; box-sizing: border-box; display: flex; flex-direction: column; justify-content: center; }
</style>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
// ── timeline.json (inline) ─────────────────────────────────
const TIMELINE = {
"title": "什么是 token",
"voice": null,
"speed": 1,
"gap": 0.4,
"totalDuration": 23.808,
"scenes": [
{"id":"intro","start":0,"end":4.368,"duration":4.368,"audio":"audio/intro.mp3","text":"你有没有想过,当我们和 AI 对话的时候AI 到底是怎么理解我们的话的呢。","cues":[{"id":"question","offset":1.08,"absoluteTime":1.08}]},
{"id":"token-1","start":4.768,"end":7.576,"duration":2.808,"audio":"audio/token-1.mp3","text":"答案是它根本不理解汉字,它只认识 token。","cues":[{"id":"reveal","offset":1.632,"absoluteTime":6.4}]},
{"id":"token-2","start":7.976,"end":16.808,"duration":8.832,"audio":"audio/token-2.mp3","text":"你可以把 token 理解成 AI 的最小信息单位。\n比如「人工智能」这四个字在 AI 眼里可能是两个 token人工智能。","cues":[{"id":"split","offset":5.4,"absoluteTime":13.376}]},
{"id":"ending","start":17.208,"end":23.664,"duration":6.456,"audio":"audio/ending.mp3","text":"所以下次看到「百万 token 上下文」这种说法,你就知道,它说的是 AI 一次能记住多少个这样的小块。","cues":[{"id":"context","offset":2.376,"absoluteTime":19.584}]}
],
"voiceover": "voiceover.mp3"
};
// ── narration_stage.jsx (inline) ───────────────────────────
const NarrationStageLib = (() => {
const NarrationContext = React.createContext({ time: 0, scene: null, sceneTime: 0, isCueTriggered: () => false, cueProgress: () => 0 });
function NarrationStage({ timeline, audioSrc, width = 1920, height = 1080, background = '#0e0e0e', controls = true, children }) {
const audioRef = React.useRef(null);
const [time, setTime] = React.useState(0);
const [playing, setPlaying] = React.useState(false);
const recording = typeof window !== 'undefined' && window.__recording === true;
React.useEffect(() => {
if (typeof window === 'undefined') return;
window.__totalDuration = timeline.totalDuration;
window.__ready = true;
}, [timeline.totalDuration]);
React.useEffect(() => {
let raf;
const tick = () => {
if (recording) {
if (typeof window.__time === 'number') setTime(window.__time);
} else if (audioRef.current && !audioRef.current.paused) {
setTime(audioRef.current.currentTime);
}
raf = requestAnimationFrame(tick);
};
tick();
return () => cancelAnimationFrame(raf);
}, [recording]);
const currentScene = React.useMemo(() => {
if (!timeline.scenes) return null;
for (let i = 0; i < timeline.scenes.length; i++) {
const s = timeline.scenes[i];
const next = timeline.scenes[i + 1];
if (time >= s.start && (!next || time < next.start)) return s;
}
return timeline.scenes[0];
}, [time, timeline.scenes]);
const sceneTime = currentScene ? Math.max(0, time - currentScene.start) : 0;
const allCues = React.useMemo(() => {
const map = {};
for (const s of timeline.scenes || []) for (const c of s.cues || []) map[c.id] = c;
return map;
}, [timeline.scenes]);
const isCueTriggered = React.useCallback((cueId) => { const c = allCues[cueId]; return c ? time >= c.absoluteTime : false; }, [allCues, time]);
const cueProgress = React.useCallback((cueId, ramp = 0.5) => { const c = allCues[cueId]; if (!c) return 0; const dt = time - c.absoluteTime; if (dt <= 0) return 0; if (dt >= ramp) return 1; return dt / ramp; }, [allCues, time]);
const ctx = { time, scene: currentScene, sceneTime, isCueTriggered, cueProgress };
const handlePlayPause = () => { if (!audioRef.current) return; if (audioRef.current.paused) { audioRef.current.play(); setPlaying(true); } else { audioRef.current.pause(); setPlaying(false); } };
const handleSeek = (e) => { if (!audioRef.current) return; const t = parseFloat(e.target.value); audioRef.current.currentTime = t; setTime(t); };
return (
<NarrationContext.Provider value={ctx}>
<div style={{ position: 'relative', width, height, background, overflow: 'hidden', color: '#fff', fontFamily: '-apple-system, BlinkMacSystemFont, "PingFang SC", sans-serif' }}>
{children}
</div>
{!recording && <audio ref={audioRef} src={audioSrc} preload="auto" onEnded={() => setPlaying(false)} />}
{!recording && controls && (
<div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '12px 16px', background: '#1a1a1a', color: '#ddd', fontFamily: 'monospace', fontSize: 13, width, boxSizing: 'border-box' }}>
<button onClick={handlePlayPause} style={{ padding: '6px 14px', background: '#fff', color: '#000', border: 0, borderRadius: 4, cursor: 'pointer', fontWeight: 600 }}>
{playing ? '❚❚ Pause' : '▶ Play'}
</button>
<input type="range" min={0} max={timeline.totalDuration} step={0.01} value={time} onChange={handleSeek} style={{ flex: 1 }} />
<span style={{ minWidth: 110, textAlign: 'right' }}>{time.toFixed(2)} / {timeline.totalDuration.toFixed(2)}s</span>
<span style={{ padding: '4px 10px', background: '#2a2a2a', borderRadius: 4, minWidth: 100, textAlign: 'center' }}>{currentScene ? currentScene.id : '—'}</span>
</div>
)}
</NarrationContext.Provider>
);
}
function Scene({ id, children, keepMounted = false }) {
const { scene, sceneTime } = React.useContext(NarrationContext);
const isActive = scene && scene.id === id;
if (!isActive && !keepMounted) return null;
const content = typeof children === 'function' ? children(sceneTime, scene) : children;
return <div style={{ position: 'absolute', inset: 0, opacity: isActive ? 1 : 0, pointerEvents: isActive ? 'auto' : 'none', transition: keepMounted ? 'opacity 0.2s' : undefined }}>{content}</div>;
}
function Cue({ id, ramp = 0.5, children }) {
const { isCueTriggered, cueProgress } = React.useContext(NarrationContext);
return children(isCueTriggered(id), cueProgress(id, ramp));
}
return { NarrationStage, Scene, Cue };
})();
const { NarrationStage, Scene, Cue } = NarrationStageLib;
// ── 视觉内容 ─────────────────────────────────────────────
const App = () => (
<NarrationStage timeline={TIMELINE} audioSrc="_narration_token/voiceover.mp3" width={1920} height={1080} background="#0a0a0a">
{/* Scene 1: 大问号引入 */}
<Scene id="intro">
<div className="scene-padding" style={{ alignItems: 'center', justifyContent: 'center' }}>
<Cue id="question">{(triggered, p) => (
<div style={{ fontSize: 320, color: triggered ? '#ffd54a' : '#3a3a3a', fontWeight: 200, transition: 'color 0.4s', transform: `scale(${0.8 + p * 0.2})`, lineHeight: 1 }}>?</div>
)}</Cue>
<div style={{ fontSize: 56, color: '#aaa', marginTop: 60, letterSpacing: '0.05em', fontWeight: 300 }}>AI 是怎么理解我们的话的</div>
</div>
</Scene>
{/* Scene 2: reveal 关键词 */}
<Scene id="token-1">
<div className="scene-padding" style={{ alignItems: 'center', justifyContent: 'center' }}>
<div style={{ fontSize: 64, color: '#888', marginBottom: 80, fontWeight: 300 }}>它不认识汉字</div>
<Cue id="reveal">{(triggered, p) => (
<div style={{
fontSize: 280, fontWeight: 700, color: '#ffd54a', letterSpacing: '0.05em',
opacity: p, transform: `translateY(${(1 - p) * 40}px)`,
fontFamily: 'monospace', textShadow: triggered ? '0 0 40px rgba(255, 213, 74, 0.4)' : 'none'
}}>
token
</div>
)}</Cue>
</div>
</Scene>
{/* Scene 3: 拆字演示 */}
<Scene id="token-2">
<div className="scene-padding" style={{ alignItems: 'center', justifyContent: 'center' }}>
<div style={{ fontSize: 48, color: '#aaa', marginBottom: 100, fontWeight: 300 }}>token = AI 的最小信息单位</div>
<Cue id="split">{(triggered, p) => (
<div style={{ display: 'flex', gap: triggered ? 80 : 8, transition: 'gap 0.6s cubic-bezier(0.16, 1, 0.3, 1)' }}>
<div style={{ fontSize: 200, fontWeight: 600, color: triggered ? '#ffd54a' : '#fff', padding: triggered ? '40px 60px' : '40px 20px', border: triggered ? '4px solid #ffd54a' : '4px solid transparent', borderRadius: 24, transition: 'all 0.6s cubic-bezier(0.16, 1, 0.3, 1)', background: triggered ? 'rgba(255, 213, 74, 0.05)' : 'transparent' }}>
人工
</div>
<div style={{ fontSize: 200, fontWeight: 600, color: triggered ? '#ffd54a' : '#fff', padding: triggered ? '40px 60px' : '40px 20px', border: triggered ? '4px solid #ffd54a' : '4px solid transparent', borderRadius: 24, transition: 'all 0.6s cubic-bezier(0.16, 1, 0.3, 1)', background: triggered ? 'rgba(255, 213, 74, 0.05)' : 'transparent' }}>
智能
</div>
</div>
)}</Cue>
<div style={{ fontSize: 36, color: '#666', marginTop: 60, opacity: 0.6 }}>人工智能= 2 token</div>
</div>
</Scene>
{/* Scene 4: 总结 */}
<Scene id="ending">
<div className="scene-padding" style={{ alignItems: 'center', justifyContent: 'center' }}>
<Cue id="context">{(triggered, p) => (
<>
<div style={{ fontSize: 96, fontWeight: 700, letterSpacing: '0.02em', marginBottom: 40, color: '#fff', opacity: triggered ? 1 : 0.3, transition: 'opacity 0.5s' }}>
<span style={{ color: '#ffd54a' }}>1,000,000</span> token
</div>
<div style={{ fontSize: 48, color: '#888', fontWeight: 300, opacity: p }}>
AI 一次能记住的<span style={{ color: '#fff', fontWeight: 500 }}>小块数量</span>
</div>
</>
)}</Cue>
</div>
</Scene>
{/* 全局水印 */}
<div style={{ position: 'absolute', bottom: 24, right: 32, fontSize: 11, color: 'rgba(255,255,255,0.35)', letterSpacing: '0.15em', fontFamily: 'monospace', pointerEvents: 'none', zIndex: 100 }}>
Created by Huashu-Design
</div>
</NarrationStage>
);
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>