import { customElement, noShadowDOM } from 'solid-element'; import {createSignal, createResource, For, Show, createEffect} from 'solid-js'; import { parseYarn, compile, YarnRunner } from '../yarn-spinner'; import type { RunnerOptions } from '../yarn-spinner/runtime/runner'; import type { RuntimeResult, TextResult, OptionsResult } from '../yarn-spinner/runtime/results'; import {loadElementSrc, resolvePath} from "./utils/path"; // 缓存已加载的 yarn 文件内容 const yarnCache = new Map(); // 加载 yarn 文件内容 async function loadYarnFile(path: string): Promise { if (yarnCache.has(path)) { return yarnCache.get(path)!; } const response = await fetch(path); const content = await response.text(); yarnCache.set(path, content); return content; } // 加载多个 yarn 文件并拼接 async function loadYarnFiles(paths: string[]): Promise { const contents = await Promise.all(paths.map(path => loadYarnFile(path))); return contents.join('\n'); } customElement('md-yarn-spinner', { startAt: "start", }, (props, { element }) => { noShadowDOM(); const [dialogueHistory, setDialogueHistory] = createSignal([]); const [currentOptions, setCurrentOptions] = createSignal(null); const [isEnded, setIsEnded] = createSignal(false); const [runnerInstance, setRunnerInstance] = createSignal(null); // 获取文件路径 const {articlePath, rawSrc} = loadElementSrc(element); const yarnPaths = (rawSrc || '').split(',') .map((s: string) => resolvePath(articlePath, s.trim())) .filter(Boolean); // 加载 yarn 内容 const [yarnContent] = createResource(() => yarnPaths, loadYarnFiles); // 创建 runner const createRunner = () => { const content = yarnContent(); if (!content) return null; try { const ast = parseYarn(content); const program = compile(ast); return new YarnRunner(program, props); } catch (error) { console.error('Failed to initialize YarnRunner:', error); return null; } }; function advance(index?: number){ const runner = runnerInstance(); if(!runner) return; if(runner.currentResult?.type !== 'options' && runner.currentResult?.isDialogueEnd) return; runner.advance(index); processRunnerOutput(runner); } createEffect(() => { if(!yarnContent()) return; setRunnerInstance(createRunner()); processRunnerOutput(runnerInstance()!); }); // 处理 runner 输出 const processRunnerOutput = (runner: YarnRunner) => { const result = runner.currentResult; if (!result) return; if(result.type === 'options'){ setCurrentOptions(result); }else{ setCurrentOptions(null); } setDialogueHistory([...runner.history]); }; // 重新开始 const restart = () => { setDialogueHistory([]); setCurrentOptions(null); setIsEnded(false); setRunnerInstance(createRunner()); processRunnerOutput(runnerInstance()!); }; // 渲染对话历史项 const renderEntry = (result: RuntimeResult) => { if (result.type === 'text') { const textResult = result as TextResult; return (
{textResult.speaker}: {textResult.text}
); } if (result.type === 'command') { return (
[{result.command}]
); } return null; }; return (
{/* 对话历史 */}
点击重新开始开始对话
加载对话中...
{renderEntry}
{/* 当前选项 */}
{(option, index) => ( )}
{/* 浮动工具栏 */}
); });