| 1 | import { useLayoutEffect, useState } from "react"; |
| 2 | import { createRoot } from "react-dom/client"; |
| 3 | import { Markdown } from "../src/components/Markdown"; |
| 4 | import { LocaleProvider } from "../src/lib/i18n"; |
| 5 | import { applyTheme, THEME_STYLES, type Theme, type ThemeStyle } from "../src/lib/theme"; |
| 6 | import { applyThemePack, applyThemeScene, type ThemeScene } from "../src/lib/themePack"; |
| 7 | import "../src/styles.css"; |
| 8 | |
| 9 | const source = '// 主题切换时,代码保持可读\nfunction greet(name: string) {\n const count = 42;\n return `你好,${name}! (${count})`;\n}\n'; |
| 10 | const chunks = [source.slice(0, source.indexOf(" const")), source.slice(0, source.indexOf(" return")), source]; |
| 11 | function Fixture() { |
| 12 | const [mode, setMode] = useState<Theme>("light"); |
| 13 | const [style, setStyle] = useState<ThemeStyle>("graphite"); |
| 14 | const [pack, setPack] = useState("base"); |
| 15 | const [scene, setScene] = useState<ThemeScene>("task"); |
| 16 | const [step, setStep] = useState(0); |
| 17 | useLayoutEffect(() => { |
| 18 | applyThemePack(null); |
| 19 | applyTheme(mode, style, { persist: false }); |
| 20 | applyThemeScene(scene); |
| 21 | if (pack === "base") return; |
| 22 | const tokens = pack === "inverted" ? { bg: "#ffffff", bgSoft: "#fafafa", fg: "#ffffff" } |
| 23 | : pack === "midtone" ? { bg: "#3fcd1c", bgSoft: "#cd1ce4", fg: "#3fcd1c" } |
| 24 | : { bg: "#203040", bgSoft: "#ffffff26", fg: "#ffffff55" }; |
| 25 | applyThemePack({ id: "code-preview", name: "Code preview", baseStyle: style, builtin: false, active: true, |
| 26 | hasBackground: pack === "wallpaper", tokens: { light: tokens, dark: tokens }, recipes: {}, |
| 27 | backgroundUrl: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAusB9Wl6X9sAAAAASUVORK5CYII=", |
| 28 | background: { focusX: .5, focusY: .5, homeOpacity: 1, taskOpacity: 1, overlayStrength: 0, paneOpacity: .1 }, |
| 29 | }, { preview: true }); |
| 30 | }, [mode, style, pack, scene]); |
| 31 | const growing = '```typescript\n' + chunks[Math.min(step, 2)] + (step >= 3 ? '```' : ''); |
| 32 | return <div className="app" style={{ display: "block" }}><main className="chat-transcript" style={{ height: "100vh", overflow: "auto", background: "var(--bg)", color: "var(--fg)", padding: "20px clamp(16px, 5vw, 64px)" }}> |
| 33 | <p>代码块主题验证 · 使用实际 Markdown 组件与示例代码</p> |
| 34 | <div style={{ display: "flex", gap: 12, flexWrap: "wrap" }}> |
| 35 | <label>明暗 <select aria-label="Mode" value={mode} onChange={event => setMode(event.target.value as Theme)}>{["light", "dark", "auto"].map(value => <option key={value}>{value}</option>)}</select></label> |
| 36 | <label>主题 <select aria-label="Style" value={style} onChange={event => setStyle(event.target.value as ThemeStyle)}>{THEME_STYLES.map(value => <option key={value}>{value}</option>)}</select></label> |
| 37 | <label>自定义背景 <select aria-label="Palette" value={pack} onChange={event => setPack(event.target.value)}>{["base", "inverted", "midtone", "translucent", "wallpaper"].map(value => <option key={value}>{value}</option>)}</select></label> |
| 38 | <label>场景 <select aria-label="Scene" value={scene} onChange={event => setScene(event.target.value as ThemeScene)}>{["home", "task"].map(value => <option key={value}>{value}</option>)}</select></label> |
| 39 | </div> |
| 40 | <section aria-label="Streaming code"> |
| 41 | <h3>流式生成</h3> |
| 42 | <button type="button" onClick={() => setStep(value => Math.min(3, value + 1))} disabled={step >= 3}>追加代码</button>{" "} |
| 43 | <button type="button" onClick={() => setStep(0)}>重新生成</button> |
| 44 | <Markdown text={growing} streaming={step < 3} /> |
| 45 | </section> |
| 46 | <section aria-label="Completed code"> |
| 47 | <Markdown text={'### 完整代码\n\n```typescript\n' + source + '```\n\n```python\n# 多行字符串与注释\nmessage = """first\nsecond"""\nprint(message, 42)\n```\n\n```\n没有指定语言,保留原文。\n```'} /> |
| 48 | </section> |
| 49 | </main></div>; |
| 50 | } |
| 51 | createRoot(document.getElementById("root")!).render(<LocaleProvider><Fixture /></LocaleProvider>); |
| 52 |