@slidejs/runner 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +19 -0
- package/.turbo/turbo-test.log +14 -0
- package/.turbo/turbo-typecheck.log +4 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +283 -0
- package/dist/index.js +256 -0
- package/dist/index.js.map +1 -0
- package/package.json +34 -0
- package/src/__tests__/runner.test.ts +623 -0
- package/src/errors.ts +21 -0
- package/src/index.ts +16 -0
- package/src/runner.ts +377 -0
- package/src/types.ts +164 -0
- package/tsconfig.json +15 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vite.config.ts +25 -0
- package/vitest.config.ts +10 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
|
|
2
|
+
> @slidejs/runner@0.1.0 build /Volumes/ORICO/ws/prj/slidejs/slidejs/packages/@slidejs/runner
|
|
3
|
+
> vite build
|
|
4
|
+
|
|
5
|
+
vite v5.4.21 building for production...
|
|
6
|
+
transforming...
|
|
7
|
+
✓ 3 modules transformed.
|
|
8
|
+
rendering chunks...
|
|
9
|
+
|
|
10
|
+
[vite:dts] Start generate declaration files...
|
|
11
|
+
computing gzip size...
|
|
12
|
+
dist/index.js 6.44 kB │ gzip: 2.33 kB │ map: 14.50 kB
|
|
13
|
+
[vite:dts] Start rollup declaration files...
|
|
14
|
+
Analysis will use the bundled TypeScript version 5.4.2
|
|
15
|
+
*** The target project appears to use TypeScript 5.9.3 which is newer than the bundled compiler engine; consider upgrading API Extractor.
|
|
16
|
+
[vite:dts] Declaration files built in 2123ms.
|
|
17
|
+
|
|
18
|
+
dist/index.cjs 3.85 kB │ gzip: 1.32 kB │ map: 13.56 kB
|
|
19
|
+
✓ built in 2.24s
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
|
|
2
|
+
> @slidejs/runner@0.1.0 test /Volumes/ORICO/ws/prj/slidejs/slidejs/packages/@slidejs/runner
|
|
3
|
+
> vitest run
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
RUN v1.6.1 /Volumes/ORICO/ws/prj/slidejs/slidejs/packages/@slidejs/runner
|
|
7
|
+
|
|
8
|
+
✓ src/__tests__/runner.test.ts (28 tests) 73ms
|
|
9
|
+
|
|
10
|
+
Test Files 1 passed (1)
|
|
11
|
+
Tests 28 passed (28)
|
|
12
|
+
Start at 20:49:56
|
|
13
|
+
Duration 2.42s (transform 100ms, setup 0ms, collect 134ms, tests 73ms, environment 685ms, prepare 175ms)
|
|
14
|
+
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a=require("@slidejs/core");class r extends Error{constructor(e,t){super(e),this.code=t,this.name="SlideRunnerError",Object.setPrototypeOf(this,r.prototype)}}class o{constructor(e){if(this.slides=[],this.currentIndex=0,this.isInitialized=!1,!e.adapter)throw new r("Adapter is required","MISSING_ADAPTER");this.adapter=e.adapter,this.plugins=e.plugins??[],this.container=this.resolveContainer(e.container),this.adapterOptions=e.adapterOptions,this.setupAdapterEventListeners()}async run(e,t){try{const i=new a.SlideEngine(e);this.slides=i.generate(t),await this.executePluginHook("beforeRender",this.slides),this.isInitialized||(await this.adapter.initialize(this.container,this.adapterOptions),this.isInitialized=!0),await this.adapter.render(this.slides),await this.executePluginHook("afterRender",this.slides),this.currentIndex=0}catch(i){const s=i instanceof Error?i.message:String(i);throw new r(`Failed to run slides: ${s}`,"RUN_FAILED")}}async renderSlides(e){try{this.slides=e,await this.executePluginHook("beforeRender",this.slides),this.isInitialized||(await this.adapter.initialize(this.container,this.adapterOptions),this.isInitialized=!0),await this.adapter.render(this.slides),await this.executePluginHook("afterRender",this.slides),this.currentIndex=0}catch(t){const i=t instanceof Error?t.message:String(t);throw new r(`Failed to render slides: ${i}`,"RENDER_FAILED")}}navigateTo(e){if(e<0||e>=this.slides.length)throw new r(`Invalid slide index: ${e}. Valid range: 0-${this.slides.length-1}`,"INVALID_INDEX");const t=this.currentIndex,i=e;this.executePluginHook("beforeSlideChange",t,i).then(()=>{this.adapter.navigateTo(e),this.currentIndex=e,this.executePluginHook("afterSlideChange",t,i).catch(s=>{console.error("Plugin afterSlideChange hook failed:",s)})})}play(){if(this.slides.length===0)throw new r("No slides to play. Call run() or renderSlides() first.","NO_SLIDES");if(!this.isInitialized)throw new r("Adapter not initialized. Call run() or renderSlides() first.","NOT_INITIALIZED");this.navigateTo(0)}getCurrentIndex(){return this.adapter.getCurrentIndex()}getTotalSlides(){return this.adapter.getTotalSlides()}async updateSlide(e,t){if(!this.adapter.updateSlide)throw new r(`Adapter "${this.adapter.name}" does not support updateSlide`,"UNSUPPORTED_OPERATION");if(e<0||e>=this.slides.length)throw new r(`Invalid slide index: ${e}. Valid range: 0-${this.slides.length-1}`,"INVALID_INDEX");try{this.slides[e]=t,await this.adapter.updateSlide(e,t)}catch(i){const s=i instanceof Error?i.message:String(i);throw new r(`Failed to update slide: ${s}`,"UPDATE_FAILED")}}async refresh(){try{await this.adapter.render(this.slides)}catch(e){const t=e instanceof Error?e.message:String(e);throw new r(`Failed to refresh slides: ${t}`,"REFRESH_FAILED")}}async destroy(){try{await this.adapter.destroy(),this.slides=[],this.currentIndex=0,this.isInitialized=!1}catch(e){const t=e instanceof Error?e.message:String(e);throw new r(`Failed to destroy runner: ${t}`,"DESTROY_FAILED")}}on(e,t){this.adapter.on(e,t)}off(e,t){this.adapter.off(e,t)}resolveContainer(e){if(typeof e=="string"){const t=document.querySelector(e);if(!t)throw new r(`Container element not found: ${e}`,"CONTAINER_NOT_FOUND");return t}if(!(e instanceof HTMLElement))throw new r("Container must be an HTMLElement or a valid selector","INVALID_CONTAINER");return e}async executePluginHook(e,...t){for(const i of this.plugins){const s=i[e];if(typeof s=="function")try{await s.apply(i,t)}catch(n){console.error(`Plugin "${i.name}" ${String(e)} hook failed:`,n)}}}setupAdapterEventListeners(){this.adapter.on("slideChanged",e=>{typeof e=="object"&&e!==null&&"index"in e&&(this.currentIndex=e.index)})}}exports.SlideRunner=o;exports.SlideRunnerError=r;
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/errors.ts","../src/runner.ts"],"sourcesContent":["/**\n * @slidejs/runner - 错误处理\n *\n * 定义 SlideRunner 相关的错误类\n */\n\n/**\n * SlideRunner 错误类\n */\nexport class SlideRunnerError extends Error {\n /**\n * @param message - 错误消息\n * @param code - 错误代码(可选)\n */\n constructor(message: string, public code?: string) {\n super(message);\n this.name = 'SlideRunnerError';\n // 保持正确的原型链\n Object.setPrototypeOf(this, SlideRunnerError.prototype);\n }\n}\n","/**\n * @slidejs/runner - SlideRunner 核心类\n *\n * 提供幻灯片运行时的核心功能,包括适配器管理、插件系统和生命周期控制\n */\n\nimport type { SlideDSL, SlideDefinition } from '@slidejs/core';\nimport { SlideEngine } from '@slidejs/core';\nimport type { SlideContext } from '@slidejs/context';\nimport type {\n SlideAdapter,\n SlideRunnerConfig,\n SlideRunnerPlugin,\n AdapterEvent,\n EventHandler,\n AdapterOptions,\n} from './types';\nimport { SlideRunnerError } from './errors';\n\n/**\n * SlideRunner 核心类\n *\n * 负责协调 SlideEngine、SlideAdapter 和插件系统,提供完整的幻灯片运行时环境\n */\nexport class SlideRunner<TContext extends SlideContext = SlideContext> {\n private adapter: SlideAdapter;\n private plugins: SlideRunnerPlugin[];\n private container: HTMLElement;\n private adapterOptions?: AdapterOptions;\n private slides: SlideDefinition[] = [];\n private currentIndex = 0;\n private isInitialized = false;\n\n /**\n * 创建 SlideRunner 实例\n *\n * @param config - SlideRunner 配置\n * @throws {SlideRunnerError} 如果配置无效\n */\n constructor(config: SlideRunnerConfig) {\n // 验证配置\n if (!config.adapter) {\n throw new SlideRunnerError('Adapter is required', 'MISSING_ADAPTER');\n }\n\n this.adapter = config.adapter;\n this.plugins = config.plugins ?? [];\n this.container = this.resolveContainer(config.container);\n this.adapterOptions = config.adapterOptions;\n\n // 设置适配器事件监听\n this.setupAdapterEventListeners();\n }\n\n /**\n * 运行幻灯片演示\n *\n * 完整流程:\n * 1. 使用 SlideEngine 生成幻灯片\n * 2. 执行 beforeRender 插件钩子\n * 3. 初始化适配器\n * 4. 渲染幻灯片\n * 5. 执行 afterRender 插件钩子\n *\n * @param dsl - Slide DSL 对象\n * @param context - 幻灯片上下文数据\n */\n async run(dsl: SlideDSL<TContext>, context: TContext): Promise<void> {\n try {\n // 1. 使用 SlideEngine 生成幻灯片\n const engine = new SlideEngine(dsl);\n this.slides = engine.generate(context);\n\n // 2. 执行 beforeRender 插件钩子\n await this.executePluginHook('beforeRender', this.slides);\n\n // 3. 初始化适配器(如果尚未初始化)\n if (!this.isInitialized) {\n await this.adapter.initialize(this.container, this.adapterOptions);\n this.isInitialized = true;\n }\n\n // 4. 渲染幻灯片\n await this.adapter.render(this.slides);\n\n // 5. 执行 afterRender 插件钩子\n await this.executePluginHook('afterRender', this.slides);\n\n // 重置当前索引\n this.currentIndex = 0;\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n throw new SlideRunnerError(\n `Failed to run slides: ${errorMessage}`,\n 'RUN_FAILED'\n );\n }\n }\n\n /**\n * 直接渲染幻灯片数组\n *\n * 跳过 SlideEngine 生成步骤,直接渲染提供的幻灯片\n *\n * @param slides - 幻灯片定义数组\n */\n async renderSlides(slides: SlideDefinition[]): Promise<void> {\n try {\n this.slides = slides;\n\n // 执行 beforeRender 插件钩子\n await this.executePluginHook('beforeRender', this.slides);\n\n // 初始化适配器(如果尚未初始化)\n if (!this.isInitialized) {\n await this.adapter.initialize(this.container, this.adapterOptions);\n this.isInitialized = true;\n }\n\n // 渲染幻灯片\n await this.adapter.render(this.slides);\n\n // 执行 afterRender 插件钩子\n await this.executePluginHook('afterRender', this.slides);\n\n // 重置当前索引\n this.currentIndex = 0;\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n throw new SlideRunnerError(\n `Failed to render slides: ${errorMessage}`,\n 'RENDER_FAILED'\n );\n }\n }\n\n /**\n * 导航到指定幻灯片\n *\n * 执行 beforeSlideChange 和 afterSlideChange 插件钩子\n *\n * @param index - 目标幻灯片索引\n * @throws {SlideRunnerError} 如果索引超出范围\n */\n navigateTo(index: number): void {\n if (index < 0 || index >= this.slides.length) {\n throw new SlideRunnerError(\n `Invalid slide index: ${index}. Valid range: 0-${this.slides.length - 1}`,\n 'INVALID_INDEX'\n );\n }\n\n const fromIndex = this.currentIndex;\n const toIndex = index;\n\n // 执行 beforeSlideChange 插件钩子(同步)\n this.executePluginHook('beforeSlideChange', fromIndex, toIndex).then(\n () => {\n // 调用适配器导航\n this.adapter.navigateTo(index);\n this.currentIndex = index;\n\n // 执行 afterSlideChange 插件钩子(异步)\n this.executePluginHook('afterSlideChange', fromIndex, toIndex).catch(\n (error) => {\n console.error('Plugin afterSlideChange hook failed:', error);\n }\n );\n }\n );\n }\n\n /**\n * 播放/启动演示文稿\n *\n * 导航到第一张幻灯片(索引 0),开始演示\n */\n play(): void {\n if (this.slides.length === 0) {\n throw new SlideRunnerError(\n 'No slides to play. Call run() or renderSlides() first.',\n 'NO_SLIDES'\n );\n }\n\n if (!this.isInitialized) {\n throw new SlideRunnerError(\n 'Adapter not initialized. Call run() or renderSlides() first.',\n 'NOT_INITIALIZED'\n );\n }\n\n // 导航到第一张幻灯片\n this.navigateTo(0);\n }\n\n /**\n * 获取当前幻灯片索引\n */\n getCurrentIndex(): number {\n return this.adapter.getCurrentIndex();\n }\n\n /**\n * 获取幻灯片总数\n */\n getTotalSlides(): number {\n return this.adapter.getTotalSlides();\n }\n\n /**\n * 更新指定幻灯片\n *\n * 仅当适配器支持 updateSlide 方法时可用\n *\n * @param index - 幻灯片索引\n * @param slide - 新的幻灯片定义\n * @throws {SlideRunnerError} 如果适配器不支持更新\n */\n async updateSlide(index: number, slide: SlideDefinition): Promise<void> {\n if (!this.adapter.updateSlide) {\n throw new SlideRunnerError(\n `Adapter \"${this.adapter.name}\" does not support updateSlide`,\n 'UNSUPPORTED_OPERATION'\n );\n }\n\n if (index < 0 || index >= this.slides.length) {\n throw new SlideRunnerError(\n `Invalid slide index: ${index}. Valid range: 0-${this.slides.length - 1}`,\n 'INVALID_INDEX'\n );\n }\n\n try {\n // 更新内部状态\n this.slides[index] = slide;\n\n // 调用适配器更新\n await this.adapter.updateSlide(index, slide);\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n throw new SlideRunnerError(\n `Failed to update slide: ${errorMessage}`,\n 'UPDATE_FAILED'\n );\n }\n }\n\n /**\n * 刷新当前幻灯片\n *\n * 重新渲染所有幻灯片\n */\n async refresh(): Promise<void> {\n try {\n await this.adapter.render(this.slides);\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n throw new SlideRunnerError(\n `Failed to refresh slides: ${errorMessage}`,\n 'REFRESH_FAILED'\n );\n }\n }\n\n /**\n * 销毁 SlideRunner,清理资源\n */\n async destroy(): Promise<void> {\n try {\n await this.adapter.destroy();\n this.slides = [];\n this.currentIndex = 0;\n this.isInitialized = false;\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n throw new SlideRunnerError(\n `Failed to destroy runner: ${errorMessage}`,\n 'DESTROY_FAILED'\n );\n }\n }\n\n /**\n * 注册事件监听器\n *\n * @param event - 事件类型\n * @param handler - 事件处理器\n */\n on(event: AdapterEvent, handler: EventHandler): void {\n this.adapter.on(event, handler);\n }\n\n /**\n * 移除事件监听器\n *\n * @param event - 事件类型\n * @param handler - 事件处理器\n */\n off(event: AdapterEvent, handler: EventHandler): void {\n this.adapter.off(event, handler);\n }\n\n /**\n * 解析容器元素\n *\n * @param container - 容器元素或选择器\n * @returns 解析后的 HTMLElement\n * @throws {SlideRunnerError} 如果容器无效或找不到\n */\n private resolveContainer(container: HTMLElement | string): HTMLElement {\n if (typeof container === 'string') {\n const element = document.querySelector<HTMLElement>(container);\n if (!element) {\n throw new SlideRunnerError(\n `Container element not found: ${container}`,\n 'CONTAINER_NOT_FOUND'\n );\n }\n return element;\n }\n\n if (!(container instanceof HTMLElement)) {\n throw new SlideRunnerError(\n 'Container must be an HTMLElement or a valid selector',\n 'INVALID_CONTAINER'\n );\n }\n\n return container;\n }\n\n /**\n * 执行插件钩子\n *\n * @param hookName - 钩子名称\n * @param args - 钩子参数\n */\n private async executePluginHook(\n hookName: keyof SlideRunnerPlugin,\n ...args: unknown[]\n ): Promise<void> {\n for (const plugin of this.plugins) {\n const hook = plugin[hookName];\n if (typeof hook === 'function') {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n await (hook as any).apply(plugin, args);\n } catch (error) {\n console.error(\n `Plugin \"${plugin.name}\" ${String(hookName)} hook failed:`,\n error\n );\n // 插件错误不应阻止流程继续\n }\n }\n }\n }\n\n /**\n * 设置适配器事件监听\n */\n private setupAdapterEventListeners(): void {\n // 监听 slideChanged 事件,更新内部状态\n this.adapter.on('slideChanged', (data) => {\n if (typeof data === 'object' && data !== null && 'index' in data) {\n this.currentIndex = data.index as number;\n }\n });\n }\n}\n"],"names":["SlideRunnerError","message","code","SlideRunner","config","dsl","context","engine","SlideEngine","error","errorMessage","slides","index","fromIndex","toIndex","slide","event","handler","container","element","hookName","args","plugin","hook","data"],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { SlideContext } from '@slidejs/context';
|
|
2
|
+
import { SlideDefinition } from '@slidejs/core';
|
|
3
|
+
import { SlideDSL } from '@slidejs/core';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 适配器事件类型
|
|
7
|
+
*/
|
|
8
|
+
export declare type AdapterEvent = 'slideChanged' | 'slideRendered' | 'ready' | 'error';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 适配器选项基础接口
|
|
12
|
+
*/
|
|
13
|
+
export declare interface AdapterOptions {
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 事件处理器
|
|
19
|
+
*/
|
|
20
|
+
export declare type EventHandler = (data: unknown) => void;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* SlideAdapter 接口
|
|
24
|
+
*
|
|
25
|
+
* 所有渲染引擎适配器必须实现此接口
|
|
26
|
+
*/
|
|
27
|
+
export declare interface SlideAdapter {
|
|
28
|
+
/**
|
|
29
|
+
* 适配器名称
|
|
30
|
+
*/
|
|
31
|
+
readonly name: string;
|
|
32
|
+
/**
|
|
33
|
+
* 初始化适配器
|
|
34
|
+
*
|
|
35
|
+
* @param container - 容器元素
|
|
36
|
+
* @param options - 适配器选项
|
|
37
|
+
*/
|
|
38
|
+
initialize(container: HTMLElement, options?: AdapterOptions): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* 渲染幻灯片
|
|
41
|
+
*
|
|
42
|
+
* @param slides - 幻灯片定义数组
|
|
43
|
+
*/
|
|
44
|
+
render(slides: SlideDefinition[]): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* 销毁适配器,清理资源
|
|
47
|
+
*/
|
|
48
|
+
destroy(): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* 导航到指定幻灯片
|
|
51
|
+
*
|
|
52
|
+
* @param index - 幻灯片索引
|
|
53
|
+
*/
|
|
54
|
+
navigateTo(index: number): void;
|
|
55
|
+
/**
|
|
56
|
+
* 获取当前幻灯片索引
|
|
57
|
+
*/
|
|
58
|
+
getCurrentIndex(): number;
|
|
59
|
+
/**
|
|
60
|
+
* 获取幻灯片总数
|
|
61
|
+
*/
|
|
62
|
+
getTotalSlides(): number;
|
|
63
|
+
/**
|
|
64
|
+
* 更新指定幻灯片(可选)
|
|
65
|
+
*
|
|
66
|
+
* @param index - 幻灯片索引
|
|
67
|
+
* @param slide - 新的幻灯片定义
|
|
68
|
+
*/
|
|
69
|
+
updateSlide?(index: number, slide: SlideDefinition): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* 注册事件监听器
|
|
72
|
+
*
|
|
73
|
+
* @param event - 事件类型
|
|
74
|
+
* @param handler - 事件处理器
|
|
75
|
+
*/
|
|
76
|
+
on(event: AdapterEvent, handler: EventHandler): void;
|
|
77
|
+
/**
|
|
78
|
+
* 移除事件监听器
|
|
79
|
+
*
|
|
80
|
+
* @param event - 事件类型
|
|
81
|
+
* @param handler - 事件处理器
|
|
82
|
+
*/
|
|
83
|
+
off(event: AdapterEvent, handler: EventHandler): void;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* SlideRunner 核心类
|
|
88
|
+
*
|
|
89
|
+
* 负责协调 SlideEngine、SlideAdapter 和插件系统,提供完整的幻灯片运行时环境
|
|
90
|
+
*/
|
|
91
|
+
export declare class SlideRunner<TContext extends SlideContext = SlideContext> {
|
|
92
|
+
private adapter;
|
|
93
|
+
private plugins;
|
|
94
|
+
private container;
|
|
95
|
+
private adapterOptions?;
|
|
96
|
+
private slides;
|
|
97
|
+
private currentIndex;
|
|
98
|
+
private isInitialized;
|
|
99
|
+
/**
|
|
100
|
+
* 创建 SlideRunner 实例
|
|
101
|
+
*
|
|
102
|
+
* @param config - SlideRunner 配置
|
|
103
|
+
* @throws {SlideRunnerError} 如果配置无效
|
|
104
|
+
*/
|
|
105
|
+
constructor(config: SlideRunnerConfig);
|
|
106
|
+
/**
|
|
107
|
+
* 运行幻灯片演示
|
|
108
|
+
*
|
|
109
|
+
* 完整流程:
|
|
110
|
+
* 1. 使用 SlideEngine 生成幻灯片
|
|
111
|
+
* 2. 执行 beforeRender 插件钩子
|
|
112
|
+
* 3. 初始化适配器
|
|
113
|
+
* 4. 渲染幻灯片
|
|
114
|
+
* 5. 执行 afterRender 插件钩子
|
|
115
|
+
*
|
|
116
|
+
* @param dsl - Slide DSL 对象
|
|
117
|
+
* @param context - 幻灯片上下文数据
|
|
118
|
+
*/
|
|
119
|
+
run(dsl: SlideDSL<TContext>, context: TContext): Promise<void>;
|
|
120
|
+
/**
|
|
121
|
+
* 直接渲染幻灯片数组
|
|
122
|
+
*
|
|
123
|
+
* 跳过 SlideEngine 生成步骤,直接渲染提供的幻灯片
|
|
124
|
+
*
|
|
125
|
+
* @param slides - 幻灯片定义数组
|
|
126
|
+
*/
|
|
127
|
+
renderSlides(slides: SlideDefinition[]): Promise<void>;
|
|
128
|
+
/**
|
|
129
|
+
* 导航到指定幻灯片
|
|
130
|
+
*
|
|
131
|
+
* 执行 beforeSlideChange 和 afterSlideChange 插件钩子
|
|
132
|
+
*
|
|
133
|
+
* @param index - 目标幻灯片索引
|
|
134
|
+
* @throws {SlideRunnerError} 如果索引超出范围
|
|
135
|
+
*/
|
|
136
|
+
navigateTo(index: number): void;
|
|
137
|
+
/**
|
|
138
|
+
* 播放/启动演示文稿
|
|
139
|
+
*
|
|
140
|
+
* 导航到第一张幻灯片(索引 0),开始演示
|
|
141
|
+
*/
|
|
142
|
+
play(): void;
|
|
143
|
+
/**
|
|
144
|
+
* 获取当前幻灯片索引
|
|
145
|
+
*/
|
|
146
|
+
getCurrentIndex(): number;
|
|
147
|
+
/**
|
|
148
|
+
* 获取幻灯片总数
|
|
149
|
+
*/
|
|
150
|
+
getTotalSlides(): number;
|
|
151
|
+
/**
|
|
152
|
+
* 更新指定幻灯片
|
|
153
|
+
*
|
|
154
|
+
* 仅当适配器支持 updateSlide 方法时可用
|
|
155
|
+
*
|
|
156
|
+
* @param index - 幻灯片索引
|
|
157
|
+
* @param slide - 新的幻灯片定义
|
|
158
|
+
* @throws {SlideRunnerError} 如果适配器不支持更新
|
|
159
|
+
*/
|
|
160
|
+
updateSlide(index: number, slide: SlideDefinition): Promise<void>;
|
|
161
|
+
/**
|
|
162
|
+
* 刷新当前幻灯片
|
|
163
|
+
*
|
|
164
|
+
* 重新渲染所有幻灯片
|
|
165
|
+
*/
|
|
166
|
+
refresh(): Promise<void>;
|
|
167
|
+
/**
|
|
168
|
+
* 销毁 SlideRunner,清理资源
|
|
169
|
+
*/
|
|
170
|
+
destroy(): Promise<void>;
|
|
171
|
+
/**
|
|
172
|
+
* 注册事件监听器
|
|
173
|
+
*
|
|
174
|
+
* @param event - 事件类型
|
|
175
|
+
* @param handler - 事件处理器
|
|
176
|
+
*/
|
|
177
|
+
on(event: AdapterEvent, handler: EventHandler): void;
|
|
178
|
+
/**
|
|
179
|
+
* 移除事件监听器
|
|
180
|
+
*
|
|
181
|
+
* @param event - 事件类型
|
|
182
|
+
* @param handler - 事件处理器
|
|
183
|
+
*/
|
|
184
|
+
off(event: AdapterEvent, handler: EventHandler): void;
|
|
185
|
+
/**
|
|
186
|
+
* 解析容器元素
|
|
187
|
+
*
|
|
188
|
+
* @param container - 容器元素或选择器
|
|
189
|
+
* @returns 解析后的 HTMLElement
|
|
190
|
+
* @throws {SlideRunnerError} 如果容器无效或找不到
|
|
191
|
+
*/
|
|
192
|
+
private resolveContainer;
|
|
193
|
+
/**
|
|
194
|
+
* 执行插件钩子
|
|
195
|
+
*
|
|
196
|
+
* @param hookName - 钩子名称
|
|
197
|
+
* @param args - 钩子参数
|
|
198
|
+
*/
|
|
199
|
+
private executePluginHook;
|
|
200
|
+
/**
|
|
201
|
+
* 设置适配器事件监听
|
|
202
|
+
*/
|
|
203
|
+
private setupAdapterEventListeners;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* SlideRunner 配置
|
|
208
|
+
*/
|
|
209
|
+
export declare interface SlideRunnerConfig {
|
|
210
|
+
/**
|
|
211
|
+
* 容器元素或选择器
|
|
212
|
+
*/
|
|
213
|
+
container: HTMLElement | string;
|
|
214
|
+
/**
|
|
215
|
+
* 渲染引擎适配器
|
|
216
|
+
*/
|
|
217
|
+
adapter: SlideAdapter;
|
|
218
|
+
/**
|
|
219
|
+
* 适配器选项
|
|
220
|
+
*/
|
|
221
|
+
adapterOptions?: AdapterOptions;
|
|
222
|
+
/**
|
|
223
|
+
* 插件数组
|
|
224
|
+
*/
|
|
225
|
+
plugins?: SlideRunnerPlugin[];
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* @slidejs/runner - 错误处理
|
|
230
|
+
*
|
|
231
|
+
* 定义 SlideRunner 相关的错误类
|
|
232
|
+
*/
|
|
233
|
+
/**
|
|
234
|
+
* SlideRunner 错误类
|
|
235
|
+
*/
|
|
236
|
+
export declare class SlideRunnerError extends Error {
|
|
237
|
+
code?: string | undefined;
|
|
238
|
+
/**
|
|
239
|
+
* @param message - 错误消息
|
|
240
|
+
* @param code - 错误代码(可选)
|
|
241
|
+
*/
|
|
242
|
+
constructor(message: string, code?: string | undefined);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* SlideRunner 插件接口
|
|
247
|
+
*
|
|
248
|
+
* 插件可以在 SlideRunner 的生命周期钩子中执行自定义逻辑
|
|
249
|
+
*/
|
|
250
|
+
export declare interface SlideRunnerPlugin {
|
|
251
|
+
/**
|
|
252
|
+
* 插件名称
|
|
253
|
+
*/
|
|
254
|
+
name: string;
|
|
255
|
+
/**
|
|
256
|
+
* 渲染前钩子
|
|
257
|
+
*
|
|
258
|
+
* @param slides - 即将渲染的幻灯片数组
|
|
259
|
+
*/
|
|
260
|
+
beforeRender?(slides: SlideDefinition[]): Promise<void> | void;
|
|
261
|
+
/**
|
|
262
|
+
* 渲染后钩子
|
|
263
|
+
*
|
|
264
|
+
* @param slides - 已渲染的幻灯片数组
|
|
265
|
+
*/
|
|
266
|
+
afterRender?(slides: SlideDefinition[]): Promise<void> | void;
|
|
267
|
+
/**
|
|
268
|
+
* 幻灯片切换前钩子
|
|
269
|
+
*
|
|
270
|
+
* @param fromIndex - 当前幻灯片索引
|
|
271
|
+
* @param toIndex - 目标幻灯片索引
|
|
272
|
+
*/
|
|
273
|
+
beforeSlideChange?(fromIndex: number, toIndex: number): Promise<void> | void;
|
|
274
|
+
/**
|
|
275
|
+
* 幻灯片切换后钩子
|
|
276
|
+
*
|
|
277
|
+
* @param fromIndex - 上一张幻灯片索引
|
|
278
|
+
* @param toIndex - 当前幻灯片索引
|
|
279
|
+
*/
|
|
280
|
+
afterSlideChange?(fromIndex: number, toIndex: number): Promise<void> | void;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export { }
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { SlideEngine as a } from "@slidejs/core";
|
|
2
|
+
class r extends Error {
|
|
3
|
+
/**
|
|
4
|
+
* @param message - 错误消息
|
|
5
|
+
* @param code - 错误代码(可选)
|
|
6
|
+
*/
|
|
7
|
+
constructor(e, t) {
|
|
8
|
+
super(e), this.code = t, this.name = "SlideRunnerError", Object.setPrototypeOf(this, r.prototype);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
class l {
|
|
12
|
+
/**
|
|
13
|
+
* 创建 SlideRunner 实例
|
|
14
|
+
*
|
|
15
|
+
* @param config - SlideRunner 配置
|
|
16
|
+
* @throws {SlideRunnerError} 如果配置无效
|
|
17
|
+
*/
|
|
18
|
+
constructor(e) {
|
|
19
|
+
if (this.slides = [], this.currentIndex = 0, this.isInitialized = !1, !e.adapter)
|
|
20
|
+
throw new r("Adapter is required", "MISSING_ADAPTER");
|
|
21
|
+
this.adapter = e.adapter, this.plugins = e.plugins ?? [], this.container = this.resolveContainer(e.container), this.adapterOptions = e.adapterOptions, this.setupAdapterEventListeners();
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 运行幻灯片演示
|
|
25
|
+
*
|
|
26
|
+
* 完整流程:
|
|
27
|
+
* 1. 使用 SlideEngine 生成幻灯片
|
|
28
|
+
* 2. 执行 beforeRender 插件钩子
|
|
29
|
+
* 3. 初始化适配器
|
|
30
|
+
* 4. 渲染幻灯片
|
|
31
|
+
* 5. 执行 afterRender 插件钩子
|
|
32
|
+
*
|
|
33
|
+
* @param dsl - Slide DSL 对象
|
|
34
|
+
* @param context - 幻灯片上下文数据
|
|
35
|
+
*/
|
|
36
|
+
async run(e, t) {
|
|
37
|
+
try {
|
|
38
|
+
const i = new a(e);
|
|
39
|
+
this.slides = i.generate(t), await this.executePluginHook("beforeRender", this.slides), this.isInitialized || (await this.adapter.initialize(this.container, this.adapterOptions), this.isInitialized = !0), await this.adapter.render(this.slides), await this.executePluginHook("afterRender", this.slides), this.currentIndex = 0;
|
|
40
|
+
} catch (i) {
|
|
41
|
+
const s = i instanceof Error ? i.message : String(i);
|
|
42
|
+
throw new r(
|
|
43
|
+
`Failed to run slides: ${s}`,
|
|
44
|
+
"RUN_FAILED"
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 直接渲染幻灯片数组
|
|
50
|
+
*
|
|
51
|
+
* 跳过 SlideEngine 生成步骤,直接渲染提供的幻灯片
|
|
52
|
+
*
|
|
53
|
+
* @param slides - 幻灯片定义数组
|
|
54
|
+
*/
|
|
55
|
+
async renderSlides(e) {
|
|
56
|
+
try {
|
|
57
|
+
this.slides = e, await this.executePluginHook("beforeRender", this.slides), this.isInitialized || (await this.adapter.initialize(this.container, this.adapterOptions), this.isInitialized = !0), await this.adapter.render(this.slides), await this.executePluginHook("afterRender", this.slides), this.currentIndex = 0;
|
|
58
|
+
} catch (t) {
|
|
59
|
+
const i = t instanceof Error ? t.message : String(t);
|
|
60
|
+
throw new r(
|
|
61
|
+
`Failed to render slides: ${i}`,
|
|
62
|
+
"RENDER_FAILED"
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* 导航到指定幻灯片
|
|
68
|
+
*
|
|
69
|
+
* 执行 beforeSlideChange 和 afterSlideChange 插件钩子
|
|
70
|
+
*
|
|
71
|
+
* @param index - 目标幻灯片索引
|
|
72
|
+
* @throws {SlideRunnerError} 如果索引超出范围
|
|
73
|
+
*/
|
|
74
|
+
navigateTo(e) {
|
|
75
|
+
if (e < 0 || e >= this.slides.length)
|
|
76
|
+
throw new r(
|
|
77
|
+
`Invalid slide index: ${e}. Valid range: 0-${this.slides.length - 1}`,
|
|
78
|
+
"INVALID_INDEX"
|
|
79
|
+
);
|
|
80
|
+
const t = this.currentIndex, i = e;
|
|
81
|
+
this.executePluginHook("beforeSlideChange", t, i).then(
|
|
82
|
+
() => {
|
|
83
|
+
this.adapter.navigateTo(e), this.currentIndex = e, this.executePluginHook("afterSlideChange", t, i).catch(
|
|
84
|
+
(s) => {
|
|
85
|
+
console.error("Plugin afterSlideChange hook failed:", s);
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* 播放/启动演示文稿
|
|
93
|
+
*
|
|
94
|
+
* 导航到第一张幻灯片(索引 0),开始演示
|
|
95
|
+
*/
|
|
96
|
+
play() {
|
|
97
|
+
if (this.slides.length === 0)
|
|
98
|
+
throw new r(
|
|
99
|
+
"No slides to play. Call run() or renderSlides() first.",
|
|
100
|
+
"NO_SLIDES"
|
|
101
|
+
);
|
|
102
|
+
if (!this.isInitialized)
|
|
103
|
+
throw new r(
|
|
104
|
+
"Adapter not initialized. Call run() or renderSlides() first.",
|
|
105
|
+
"NOT_INITIALIZED"
|
|
106
|
+
);
|
|
107
|
+
this.navigateTo(0);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* 获取当前幻灯片索引
|
|
111
|
+
*/
|
|
112
|
+
getCurrentIndex() {
|
|
113
|
+
return this.adapter.getCurrentIndex();
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* 获取幻灯片总数
|
|
117
|
+
*/
|
|
118
|
+
getTotalSlides() {
|
|
119
|
+
return this.adapter.getTotalSlides();
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* 更新指定幻灯片
|
|
123
|
+
*
|
|
124
|
+
* 仅当适配器支持 updateSlide 方法时可用
|
|
125
|
+
*
|
|
126
|
+
* @param index - 幻灯片索引
|
|
127
|
+
* @param slide - 新的幻灯片定义
|
|
128
|
+
* @throws {SlideRunnerError} 如果适配器不支持更新
|
|
129
|
+
*/
|
|
130
|
+
async updateSlide(e, t) {
|
|
131
|
+
if (!this.adapter.updateSlide)
|
|
132
|
+
throw new r(
|
|
133
|
+
`Adapter "${this.adapter.name}" does not support updateSlide`,
|
|
134
|
+
"UNSUPPORTED_OPERATION"
|
|
135
|
+
);
|
|
136
|
+
if (e < 0 || e >= this.slides.length)
|
|
137
|
+
throw new r(
|
|
138
|
+
`Invalid slide index: ${e}. Valid range: 0-${this.slides.length - 1}`,
|
|
139
|
+
"INVALID_INDEX"
|
|
140
|
+
);
|
|
141
|
+
try {
|
|
142
|
+
this.slides[e] = t, await this.adapter.updateSlide(e, t);
|
|
143
|
+
} catch (i) {
|
|
144
|
+
const s = i instanceof Error ? i.message : String(i);
|
|
145
|
+
throw new r(
|
|
146
|
+
`Failed to update slide: ${s}`,
|
|
147
|
+
"UPDATE_FAILED"
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* 刷新当前幻灯片
|
|
153
|
+
*
|
|
154
|
+
* 重新渲染所有幻灯片
|
|
155
|
+
*/
|
|
156
|
+
async refresh() {
|
|
157
|
+
try {
|
|
158
|
+
await this.adapter.render(this.slides);
|
|
159
|
+
} catch (e) {
|
|
160
|
+
const t = e instanceof Error ? e.message : String(e);
|
|
161
|
+
throw new r(
|
|
162
|
+
`Failed to refresh slides: ${t}`,
|
|
163
|
+
"REFRESH_FAILED"
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* 销毁 SlideRunner,清理资源
|
|
169
|
+
*/
|
|
170
|
+
async destroy() {
|
|
171
|
+
try {
|
|
172
|
+
await this.adapter.destroy(), this.slides = [], this.currentIndex = 0, this.isInitialized = !1;
|
|
173
|
+
} catch (e) {
|
|
174
|
+
const t = e instanceof Error ? e.message : String(e);
|
|
175
|
+
throw new r(
|
|
176
|
+
`Failed to destroy runner: ${t}`,
|
|
177
|
+
"DESTROY_FAILED"
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* 注册事件监听器
|
|
183
|
+
*
|
|
184
|
+
* @param event - 事件类型
|
|
185
|
+
* @param handler - 事件处理器
|
|
186
|
+
*/
|
|
187
|
+
on(e, t) {
|
|
188
|
+
this.adapter.on(e, t);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* 移除事件监听器
|
|
192
|
+
*
|
|
193
|
+
* @param event - 事件类型
|
|
194
|
+
* @param handler - 事件处理器
|
|
195
|
+
*/
|
|
196
|
+
off(e, t) {
|
|
197
|
+
this.adapter.off(e, t);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* 解析容器元素
|
|
201
|
+
*
|
|
202
|
+
* @param container - 容器元素或选择器
|
|
203
|
+
* @returns 解析后的 HTMLElement
|
|
204
|
+
* @throws {SlideRunnerError} 如果容器无效或找不到
|
|
205
|
+
*/
|
|
206
|
+
resolveContainer(e) {
|
|
207
|
+
if (typeof e == "string") {
|
|
208
|
+
const t = document.querySelector(e);
|
|
209
|
+
if (!t)
|
|
210
|
+
throw new r(
|
|
211
|
+
`Container element not found: ${e}`,
|
|
212
|
+
"CONTAINER_NOT_FOUND"
|
|
213
|
+
);
|
|
214
|
+
return t;
|
|
215
|
+
}
|
|
216
|
+
if (!(e instanceof HTMLElement))
|
|
217
|
+
throw new r(
|
|
218
|
+
"Container must be an HTMLElement or a valid selector",
|
|
219
|
+
"INVALID_CONTAINER"
|
|
220
|
+
);
|
|
221
|
+
return e;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* 执行插件钩子
|
|
225
|
+
*
|
|
226
|
+
* @param hookName - 钩子名称
|
|
227
|
+
* @param args - 钩子参数
|
|
228
|
+
*/
|
|
229
|
+
async executePluginHook(e, ...t) {
|
|
230
|
+
for (const i of this.plugins) {
|
|
231
|
+
const s = i[e];
|
|
232
|
+
if (typeof s == "function")
|
|
233
|
+
try {
|
|
234
|
+
await s.apply(i, t);
|
|
235
|
+
} catch (n) {
|
|
236
|
+
console.error(
|
|
237
|
+
`Plugin "${i.name}" ${String(e)} hook failed:`,
|
|
238
|
+
n
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* 设置适配器事件监听
|
|
245
|
+
*/
|
|
246
|
+
setupAdapterEventListeners() {
|
|
247
|
+
this.adapter.on("slideChanged", (e) => {
|
|
248
|
+
typeof e == "object" && e !== null && "index" in e && (this.currentIndex = e.index);
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
export {
|
|
253
|
+
l as SlideRunner,
|
|
254
|
+
r as SlideRunnerError
|
|
255
|
+
};
|
|
256
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/errors.ts","../src/runner.ts"],"sourcesContent":["/**\n * @slidejs/runner - 错误处理\n *\n * 定义 SlideRunner 相关的错误类\n */\n\n/**\n * SlideRunner 错误类\n */\nexport class SlideRunnerError extends Error {\n /**\n * @param message - 错误消息\n * @param code - 错误代码(可选)\n */\n constructor(message: string, public code?: string) {\n super(message);\n this.name = 'SlideRunnerError';\n // 保持正确的原型链\n Object.setPrototypeOf(this, SlideRunnerError.prototype);\n }\n}\n","/**\n * @slidejs/runner - SlideRunner 核心类\n *\n * 提供幻灯片运行时的核心功能,包括适配器管理、插件系统和生命周期控制\n */\n\nimport type { SlideDSL, SlideDefinition } from '@slidejs/core';\nimport { SlideEngine } from '@slidejs/core';\nimport type { SlideContext } from '@slidejs/context';\nimport type {\n SlideAdapter,\n SlideRunnerConfig,\n SlideRunnerPlugin,\n AdapterEvent,\n EventHandler,\n AdapterOptions,\n} from './types';\nimport { SlideRunnerError } from './errors';\n\n/**\n * SlideRunner 核心类\n *\n * 负责协调 SlideEngine、SlideAdapter 和插件系统,提供完整的幻灯片运行时环境\n */\nexport class SlideRunner<TContext extends SlideContext = SlideContext> {\n private adapter: SlideAdapter;\n private plugins: SlideRunnerPlugin[];\n private container: HTMLElement;\n private adapterOptions?: AdapterOptions;\n private slides: SlideDefinition[] = [];\n private currentIndex = 0;\n private isInitialized = false;\n\n /**\n * 创建 SlideRunner 实例\n *\n * @param config - SlideRunner 配置\n * @throws {SlideRunnerError} 如果配置无效\n */\n constructor(config: SlideRunnerConfig) {\n // 验证配置\n if (!config.adapter) {\n throw new SlideRunnerError('Adapter is required', 'MISSING_ADAPTER');\n }\n\n this.adapter = config.adapter;\n this.plugins = config.plugins ?? [];\n this.container = this.resolveContainer(config.container);\n this.adapterOptions = config.adapterOptions;\n\n // 设置适配器事件监听\n this.setupAdapterEventListeners();\n }\n\n /**\n * 运行幻灯片演示\n *\n * 完整流程:\n * 1. 使用 SlideEngine 生成幻灯片\n * 2. 执行 beforeRender 插件钩子\n * 3. 初始化适配器\n * 4. 渲染幻灯片\n * 5. 执行 afterRender 插件钩子\n *\n * @param dsl - Slide DSL 对象\n * @param context - 幻灯片上下文数据\n */\n async run(dsl: SlideDSL<TContext>, context: TContext): Promise<void> {\n try {\n // 1. 使用 SlideEngine 生成幻灯片\n const engine = new SlideEngine(dsl);\n this.slides = engine.generate(context);\n\n // 2. 执行 beforeRender 插件钩子\n await this.executePluginHook('beforeRender', this.slides);\n\n // 3. 初始化适配器(如果尚未初始化)\n if (!this.isInitialized) {\n await this.adapter.initialize(this.container, this.adapterOptions);\n this.isInitialized = true;\n }\n\n // 4. 渲染幻灯片\n await this.adapter.render(this.slides);\n\n // 5. 执行 afterRender 插件钩子\n await this.executePluginHook('afterRender', this.slides);\n\n // 重置当前索引\n this.currentIndex = 0;\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n throw new SlideRunnerError(\n `Failed to run slides: ${errorMessage}`,\n 'RUN_FAILED'\n );\n }\n }\n\n /**\n * 直接渲染幻灯片数组\n *\n * 跳过 SlideEngine 生成步骤,直接渲染提供的幻灯片\n *\n * @param slides - 幻灯片定义数组\n */\n async renderSlides(slides: SlideDefinition[]): Promise<void> {\n try {\n this.slides = slides;\n\n // 执行 beforeRender 插件钩子\n await this.executePluginHook('beforeRender', this.slides);\n\n // 初始化适配器(如果尚未初始化)\n if (!this.isInitialized) {\n await this.adapter.initialize(this.container, this.adapterOptions);\n this.isInitialized = true;\n }\n\n // 渲染幻灯片\n await this.adapter.render(this.slides);\n\n // 执行 afterRender 插件钩子\n await this.executePluginHook('afterRender', this.slides);\n\n // 重置当前索引\n this.currentIndex = 0;\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n throw new SlideRunnerError(\n `Failed to render slides: ${errorMessage}`,\n 'RENDER_FAILED'\n );\n }\n }\n\n /**\n * 导航到指定幻灯片\n *\n * 执行 beforeSlideChange 和 afterSlideChange 插件钩子\n *\n * @param index - 目标幻灯片索引\n * @throws {SlideRunnerError} 如果索引超出范围\n */\n navigateTo(index: number): void {\n if (index < 0 || index >= this.slides.length) {\n throw new SlideRunnerError(\n `Invalid slide index: ${index}. Valid range: 0-${this.slides.length - 1}`,\n 'INVALID_INDEX'\n );\n }\n\n const fromIndex = this.currentIndex;\n const toIndex = index;\n\n // 执行 beforeSlideChange 插件钩子(同步)\n this.executePluginHook('beforeSlideChange', fromIndex, toIndex).then(\n () => {\n // 调用适配器导航\n this.adapter.navigateTo(index);\n this.currentIndex = index;\n\n // 执行 afterSlideChange 插件钩子(异步)\n this.executePluginHook('afterSlideChange', fromIndex, toIndex).catch(\n (error) => {\n console.error('Plugin afterSlideChange hook failed:', error);\n }\n );\n }\n );\n }\n\n /**\n * 播放/启动演示文稿\n *\n * 导航到第一张幻灯片(索引 0),开始演示\n */\n play(): void {\n if (this.slides.length === 0) {\n throw new SlideRunnerError(\n 'No slides to play. Call run() or renderSlides() first.',\n 'NO_SLIDES'\n );\n }\n\n if (!this.isInitialized) {\n throw new SlideRunnerError(\n 'Adapter not initialized. Call run() or renderSlides() first.',\n 'NOT_INITIALIZED'\n );\n }\n\n // 导航到第一张幻灯片\n this.navigateTo(0);\n }\n\n /**\n * 获取当前幻灯片索引\n */\n getCurrentIndex(): number {\n return this.adapter.getCurrentIndex();\n }\n\n /**\n * 获取幻灯片总数\n */\n getTotalSlides(): number {\n return this.adapter.getTotalSlides();\n }\n\n /**\n * 更新指定幻灯片\n *\n * 仅当适配器支持 updateSlide 方法时可用\n *\n * @param index - 幻灯片索引\n * @param slide - 新的幻灯片定义\n * @throws {SlideRunnerError} 如果适配器不支持更新\n */\n async updateSlide(index: number, slide: SlideDefinition): Promise<void> {\n if (!this.adapter.updateSlide) {\n throw new SlideRunnerError(\n `Adapter \"${this.adapter.name}\" does not support updateSlide`,\n 'UNSUPPORTED_OPERATION'\n );\n }\n\n if (index < 0 || index >= this.slides.length) {\n throw new SlideRunnerError(\n `Invalid slide index: ${index}. Valid range: 0-${this.slides.length - 1}`,\n 'INVALID_INDEX'\n );\n }\n\n try {\n // 更新内部状态\n this.slides[index] = slide;\n\n // 调用适配器更新\n await this.adapter.updateSlide(index, slide);\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n throw new SlideRunnerError(\n `Failed to update slide: ${errorMessage}`,\n 'UPDATE_FAILED'\n );\n }\n }\n\n /**\n * 刷新当前幻灯片\n *\n * 重新渲染所有幻灯片\n */\n async refresh(): Promise<void> {\n try {\n await this.adapter.render(this.slides);\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n throw new SlideRunnerError(\n `Failed to refresh slides: ${errorMessage}`,\n 'REFRESH_FAILED'\n );\n }\n }\n\n /**\n * 销毁 SlideRunner,清理资源\n */\n async destroy(): Promise<void> {\n try {\n await this.adapter.destroy();\n this.slides = [];\n this.currentIndex = 0;\n this.isInitialized = false;\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n throw new SlideRunnerError(\n `Failed to destroy runner: ${errorMessage}`,\n 'DESTROY_FAILED'\n );\n }\n }\n\n /**\n * 注册事件监听器\n *\n * @param event - 事件类型\n * @param handler - 事件处理器\n */\n on(event: AdapterEvent, handler: EventHandler): void {\n this.adapter.on(event, handler);\n }\n\n /**\n * 移除事件监听器\n *\n * @param event - 事件类型\n * @param handler - 事件处理器\n */\n off(event: AdapterEvent, handler: EventHandler): void {\n this.adapter.off(event, handler);\n }\n\n /**\n * 解析容器元素\n *\n * @param container - 容器元素或选择器\n * @returns 解析后的 HTMLElement\n * @throws {SlideRunnerError} 如果容器无效或找不到\n */\n private resolveContainer(container: HTMLElement | string): HTMLElement {\n if (typeof container === 'string') {\n const element = document.querySelector<HTMLElement>(container);\n if (!element) {\n throw new SlideRunnerError(\n `Container element not found: ${container}`,\n 'CONTAINER_NOT_FOUND'\n );\n }\n return element;\n }\n\n if (!(container instanceof HTMLElement)) {\n throw new SlideRunnerError(\n 'Container must be an HTMLElement or a valid selector',\n 'INVALID_CONTAINER'\n );\n }\n\n return container;\n }\n\n /**\n * 执行插件钩子\n *\n * @param hookName - 钩子名称\n * @param args - 钩子参数\n */\n private async executePluginHook(\n hookName: keyof SlideRunnerPlugin,\n ...args: unknown[]\n ): Promise<void> {\n for (const plugin of this.plugins) {\n const hook = plugin[hookName];\n if (typeof hook === 'function') {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n await (hook as any).apply(plugin, args);\n } catch (error) {\n console.error(\n `Plugin \"${plugin.name}\" ${String(hookName)} hook failed:`,\n error\n );\n // 插件错误不应阻止流程继续\n }\n }\n }\n }\n\n /**\n * 设置适配器事件监听\n */\n private setupAdapterEventListeners(): void {\n // 监听 slideChanged 事件,更新内部状态\n this.adapter.on('slideChanged', (data) => {\n if (typeof data === 'object' && data !== null && 'index' in data) {\n this.currentIndex = data.index as number;\n }\n });\n }\n}\n"],"names":["SlideRunnerError","message","code","SlideRunner","config","dsl","context","engine","SlideEngine","error","errorMessage","slides","index","fromIndex","toIndex","slide","event","handler","container","element","hookName","args","plugin","hook","data"],"mappings":"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"}
|