@slidejs/runner-splide 0.1.2 → 0.1.4

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.
@@ -1,25 +1,21 @@
1
1
 
2
- > @slidejs/runner-splide@0.1.2 build /Volumes/ORICO/ws/prj/slidejs/slidejs/packages/@slidejs/runner-splide
2
+ > @slidejs/runner-splide@0.1.4 build /Volumes/ORICO/ws/prj/slidejs/slidejs/packages/@slidejs/runner-splide
3
3
  > vite build
4
4
 
5
- vite v5.4.21 building for production...
6
- src/adapter.ts:320:10 - error TS2352: Conversion of type 'HTMLElement' to type 'Record<string, unknown>' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
7
- Index signature for type 'string' is missing in type 'HTMLElement'.
8
-
9
- 320 (element as Record<string, unknown>)[key] = value;
10
-    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11
-
5
+ vite v7.3.1 building client environment for production...
12
6
  transforming...
13
- 3 modules transformed.
7
+ 6 modules transformed.
14
8
  rendering chunks...
15
9
 
16
10
  [vite:dts] Start generate declaration files...
17
11
  computing gzip size...
18
- dist/index.mjs 7.27 kB │ gzip: 2.40 kB │ map: 19.28 kB
12
+ dist/style.css 1.89 kB │ gzip: 0.55 kB
13
+ dist/index.mjs 15.24 kB │ gzip: 4.20 kB │ map: 22.76 kB
19
14
  [vite:dts] Start rollup declaration files...
20
- Analysis will use the bundled TypeScript version 5.4.2
15
+ Analysis will use the bundled TypeScript version 5.8.2
21
16
  *** The target project appears to use TypeScript 5.9.3 which is newer than the bundled compiler engine; consider upgrading API Extractor.
22
- [vite:dts] Declaration files built in 2035ms.
17
+ [vite:dts] Declaration files built in 6394ms.
23
18
 
24
- dist/index.cjs 4.65 kB │ gzip: 1.62 kB │ map: 18.34 kB
25
- built in 2.15s
19
+ dist/style.css 1.89 kB │ gzip: 0.55 kB
20
+ dist/index.cjs 12.07 kB gzip: 3.37 kB │ map: 21.79 kB
21
+ ✓ built in 7.43s
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const p=require("@splidejs/splide"),a=require("@slidejs/dsl"),h=require("@slidejs/runner");class c{constructor(){this.name="splide",this.eventHandlers=new Map}async initialize(i,t){try{if(this.createSplideStructure(i),!this.splideContainer)throw new Error("Splide container not created");const e={type:"slide",perPage:1,perMove:1,gap:"1rem",pagination:!0,arrows:!0,keyboard:"global",...t==null?void 0:t.splideConfig};this.splide=new p.Splide(this.splideContainer,e),this.splide.mount(),await new Promise(n=>{requestAnimationFrame(()=>{n()})}),this.setupEventListeners(),this.emit("ready")}catch(e){const n=e instanceof Error?e.message:String(e);throw this.emit("error",{message:n}),new Error(`Failed to initialize SplideAdapter: ${n}`)}}async render(i){if(!this.splideList||!this.splide)throw new Error("SplideAdapter not initialized");try{this.splideList.innerHTML="";for(const t of i){const e=await this.renderSlide(t);this.splideList.appendChild(e)}this.splide.refresh(),this.emit("slideRendered",{totalSlides:i.length})}catch(t){const e=t instanceof Error?t.message:String(t);throw this.emit("error",{message:e}),new Error(`Failed to render slides: ${e}`)}}async destroy(){this.splide&&(this.splide.destroy(),this.splide=void 0),this.splideContainer&&(this.splideContainer.innerHTML="",this.splideContainer=void 0),this.splideTrack=void 0,this.splideList=void 0,this.eventHandlers.clear()}navigateTo(i){if(!this.splide)throw new Error("SplideAdapter not initialized");this.splide.go(i)}getCurrentIndex(){return this.splide?this.splide.index:0}getTotalSlides(){return this.splide?this.splide.length:0}async updateSlide(i,t){if(!this.splideList||!this.splide)throw new Error("SplideAdapter not initialized");try{const n=this.splideList.querySelectorAll(".splide__slide")[i];if(!n)throw new Error(`Slide at index ${i} not found`);const s=await this.renderSlide(t);n.replaceWith(s),this.splide.refresh()}catch(e){const n=e instanceof Error?e.message:String(e);throw this.emit("error",{message:n}),new Error(`Failed to update slide: ${n}`)}}on(i,t){this.eventHandlers.has(i)||this.eventHandlers.set(i,new Set),this.eventHandlers.get(i).add(t)}off(i,t){const e=this.eventHandlers.get(i);e&&e.delete(t)}createSplideStructure(i){const t=document.createElement("div");t.className="splide";const e=document.createElement("div");e.className="splide__track";const n=document.createElement("ul");n.className="splide__list",e.appendChild(n),t.appendChild(e),i.appendChild(t),this.splideContainer=t,this.splideTrack=e,this.splideList=n}setupEventListeners(){this.splide&&this.splide.on("moved",(i,t)=>{this.emit("slideChanged",{index:i,previousIndex:t,from:t,to:i})})}async renderSlide(i){const t=document.createElement("li");if(t.className="splide__slide",i.content.type==="dynamic"){const e=await this.renderDynamicContent(i.content.component,i.content.props);t.appendChild(e)}else{const e=this.renderTextContent(i.content.lines);t.appendChild(e)}return t}async renderDynamicContent(i,t){const e=document.createElement(i);for(const[n,s]of Object.entries(t))typeof s=="string"||typeof s=="number"?e.setAttribute(n,String(s)):typeof s=="boolean"?s&&e.setAttribute(n,""):e[n]=s;return e}renderTextContent(i){const t=document.createElement("div");t.className="slide-content";let e=null;for(const n of i){const s=n.trim();if(!s){e=null;continue}if(s.startsWith("# ")){e=null;const r=document.createElement("h1");r.textContent=s.substring(2),t.appendChild(r);continue}if(s.startsWith("## ")){e=null;const r=document.createElement("h2");r.textContent=s.substring(3),t.appendChild(r);continue}if(s.startsWith("### ")){e=null;const r=document.createElement("h3");r.textContent=s.substring(4),t.appendChild(r);continue}const l=s.match(/^!\[(.*?)\]\((.*?)\)$/);if(l){e=null;const r=document.createElement("img");r.alt=l[1],r.src=l[2],r.style.maxWidth="80%",r.style.maxHeight="500px",t.appendChild(r);continue}if(s.startsWith("- ")){e||(e=document.createElement("ul"),t.appendChild(e));const r=document.createElement("li");r.textContent=s.substring(2),e.appendChild(r);continue}e=null;const o=document.createElement("p");o.textContent=s,t.appendChild(o)}return t}emit(i,t){const e=this.eventHandlers.get(i);if(e)for(const n of e)try{n(t)}catch(s){console.error(`Error in ${i} handler:`,s)}}}async function u(d,i,t){const e=await a.parseSlideDSL(d),n=a.compile(e),s=new c,l=new h.SlideRunner({container:t.container,adapter:s,adapterOptions:{splideConfig:t.splideOptions}});return await l.run(n,i),l}exports.SplideAdapter=c;exports.createSlideRunner=u;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const g=require("@splidejs/splide"),_=require("@slidejs/dsl"),m=require("@slidejs/runner");class f{constructor(){this.name="splide",this.eventHandlers=new Map}async initialize(t,i){try{if(this.createSplideStructure(t),!this.splideContainer)throw new Error("Splide container not created");const e={type:"slide",perPage:1,perMove:1,gap:"1rem",pagination:!0,arrows:!0,keyboard:"global",...i?.splideConfig};this.splide=new g.Splide(this.splideContainer,e),this.splide.mount(),await new Promise(n=>{requestAnimationFrame(()=>{n()})}),this.setupEventListeners(),this.emit("ready")}catch(e){const n=e instanceof Error?e.message:String(e);throw this.emit("error",{message:n}),new Error(`Failed to initialize SplideAdapter: ${n}`)}}async render(t){if(!this.splideList||!this.splide)throw new Error("SplideAdapter not initialized");try{this.splideList.innerHTML="";for(const i of t){const e=await this.renderSlide(i);this.splideList.appendChild(e)}this.splide.refresh(),this.emit("slideRendered",{totalSlides:t.length})}catch(i){const e=i instanceof Error?i.message:String(i);throw this.emit("error",{message:e}),new Error(`Failed to render slides: ${e}`)}}async destroy(){this.splide&&(this.splide.destroy(),this.splide=void 0),this.splideContainer&&(this.splideContainer.innerHTML="",this.splideContainer=void 0),this.splideTrack=void 0,this.splideList=void 0,this.eventHandlers.clear()}navigateTo(t){if(!this.splide)throw new Error("SplideAdapter not initialized");this.splide.go(t)}getCurrentIndex(){return this.splide?this.splide.index:0}getTotalSlides(){return this.splide?this.splide.length:0}async updateSlide(t,i){if(!this.splideList||!this.splide)throw new Error("SplideAdapter not initialized");try{const n=this.splideList.querySelectorAll(".splide__slide")[t];if(!n)throw new Error(`Slide at index ${t} not found`);const s=await this.renderSlide(i);n.replaceWith(s),this.splide.refresh()}catch(e){const n=e instanceof Error?e.message:String(e);throw this.emit("error",{message:n}),new Error(`Failed to update slide: ${n}`)}}on(t,i){this.eventHandlers.has(t)||this.eventHandlers.set(t,new Set),this.eventHandlers.get(t).add(i)}off(t,i){const e=this.eventHandlers.get(t);e&&e.delete(i)}createSplideStructure(t){const i=document.createElement("div");i.className="splide";const e=document.createElement("div");e.className="splide__track";const n=document.createElement("ul");n.className="splide__list",e.appendChild(n),i.appendChild(e),t.appendChild(i),this.splideContainer=i,this.splideTrack=e,this.splideList=n}setupEventListeners(){this.splide&&this.splide.on("moved",(t,i)=>{this.emit("slideChanged",{index:t,previousIndex:i,from:i,to:t})})}async renderSlide(t){const i=document.createElement("li");if(i.className="splide__slide",t.content.type==="dynamic"){const e=await this.renderDynamicContent(t.content.component,t.content.props);i.appendChild(e)}else{const e=this.renderTextContent(t.content.lines);i.appendChild(e)}return i}async renderDynamicContent(t,i){const e=document.createElement(t);for(const[n,s]of Object.entries(i))typeof s=="string"||typeof s=="number"?e.setAttribute(n,String(s)):typeof s=="boolean"?s&&e.setAttribute(n,""):e[n]=s;return e}renderTextContent(t){const i=document.createElement("div");i.className="slide-content";let e=null;for(const n of t){const s=n.trim();if(!s){e=null;continue}if(s.startsWith("# ")){e=null;const r=document.createElement("h1");r.textContent=s.substring(2),i.appendChild(r);continue}if(s.startsWith("## ")){e=null;const r=document.createElement("h2");r.textContent=s.substring(3),i.appendChild(r);continue}if(s.startsWith("### ")){e=null;const r=document.createElement("h3");r.textContent=s.substring(4),i.appendChild(r);continue}const d=s.match(/^!\[(.*?)\]\((.*?)\)$/);if(d){e=null;const r=document.createElement("img");r.alt=d[1],r.src=d[2],r.style.maxWidth="80%",r.style.maxHeight="500px",i.appendChild(r);continue}if(s.startsWith("- ")){e||(e=document.createElement("ul"),i.appendChild(e));const r=document.createElement("li");r.textContent=s.substring(2),e.appendChild(r);continue}e=null;const l=document.createElement("p");l.textContent=s,i.appendChild(l)}return i}emit(t,i){const e=this.eventHandlers.get(t);if(e)for(const n of e)try{n(i)}catch(s){console.error(`Error in ${t} handler:`,s)}}}const u=".splide__container{box-sizing:border-box;position:relative}.splide__list{backface-visibility:hidden;display:-ms-flexbox;display:flex;height:100%;margin:0!important;padding:0!important}.splide.is-initialized:not(.is-active) .splide__list{display:block}.splide__pagination{-ms-flex-align:center;align-items:center;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:center;justify-content:center;margin:0;pointer-events:none}.splide__pagination li{display:inline-block;line-height:1;list-style-type:none;margin:0;pointer-events:auto}.splide:not(.is-overflow) .splide__pagination{display:none}.splide__progress__bar{width:0}.splide{position:relative;visibility:hidden}.splide.is-initialized,.splide.is-rendered{visibility:visible}.splide__slide{backface-visibility:hidden;box-sizing:border-box;-ms-flex-negative:0;flex-shrink:0;list-style-type:none!important;margin:0;position:relative}.splide__slide img{vertical-align:bottom}.splide__spinner{animation:splide-loading 1s linear infinite;border:2px solid #999;border-left-color:transparent;border-radius:50%;contain:strict;display:inline-block;height:20px;inset:0;margin:auto;position:absolute;width:20px}.splide__sr{clip:rect(0 0 0 0);border:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.splide__toggle.is-active .splide__toggle__play,.splide__toggle__pause{display:none}.splide__toggle.is-active .splide__toggle__pause{display:inline}.splide__track{overflow:hidden;position:relative;z-index:0}@keyframes splide-loading{0%{transform:rotate(0)}to{transform:rotate(1turn)}}.splide__track--draggable{-webkit-touch-callout:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}.splide__track--fade>.splide__list>.splide__slide{margin:0!important;opacity:0;z-index:0}.splide__track--fade>.splide__list>.splide__slide.is-active{opacity:1;z-index:1}.splide--rtl{direction:rtl}.splide__track--ttb>.splide__list{display:block}.splide__arrow{-ms-flex-align:center;align-items:center;background:#ccc;border:0;border-radius:50%;cursor:pointer;display:-ms-flexbox;display:flex;height:2em;-ms-flex-pack:center;justify-content:center;opacity:.7;padding:0;position:absolute;top:50%;transform:translateY(-50%);width:2em;z-index:1}.splide__arrow svg{fill:#000;height:1.2em;width:1.2em}.splide__arrow:hover:not(:disabled){opacity:.9}.splide__arrow:disabled{opacity:.3}.splide__arrow:focus-visible{outline:3px solid #0bf;outline-offset:3px}.splide__arrow--prev{left:1em}.splide__arrow--prev svg{transform:scaleX(-1)}.splide__arrow--next{right:1em}.splide.is-focus-in .splide__arrow:focus{outline:3px solid #0bf;outline-offset:3px}.splide__pagination{bottom:.5em;left:0;padding:0 1em;position:absolute;right:0;z-index:1}.splide__pagination__page{background:#ccc;border:0;border-radius:50%;display:inline-block;height:8px;margin:3px;opacity:.7;padding:0;position:relative;transition:transform .2s linear;width:8px}.splide__pagination__page.is-active{background:#fff;transform:scale(1.4);z-index:1}.splide__pagination__page:hover{cursor:pointer;opacity:.9}.splide__pagination__page:focus-visible{outline:3px solid #0bf;outline-offset:3px}.splide.is-focus-in .splide__pagination__page:focus{outline:3px solid #0bf;outline-offset:3px}.splide__progress__bar{background:#ccc;height:3px}.splide__slide{-webkit-tap-highlight-color:rgba(0,0,0,0)}.splide__slide:focus{outline:0}@supports (outline-offset:-3px){.splide__slide:focus-visible{outline:3px solid #0bf;outline-offset:-3px}}@media screen and (-ms-high-contrast:none){.splide__slide:focus-visible{border:3px solid #0bf}}@supports (outline-offset:-3px){.splide.is-focus-in .splide__slide:focus{outline:3px solid #0bf;outline-offset:-3px}}@media screen and (-ms-high-contrast:none){.splide.is-focus-in .splide__slide:focus{border:3px solid #0bf}.splide.is-focus-in .splide__track>.splide__list>.splide__slide:focus{border-color:#0bf}}.splide__toggle{cursor:pointer}.splide__toggle:focus-visible{outline:3px solid #0bf;outline-offset:3px}.splide.is-focus-in .splide__toggle:focus{outline:3px solid #0bf;outline-offset:3px}.splide__track--nav>.splide__list>.splide__slide{border:3px solid transparent;cursor:pointer}.splide__track--nav>.splide__list>.splide__slide.is-active{border:3px solid #000}.splide__arrows--rtl .splide__arrow--prev{left:auto;right:1em}.splide__arrows--rtl .splide__arrow--prev svg{transform:scaleX(1)}.splide__arrows--rtl .splide__arrow--next{left:1em;right:auto}.splide__arrows--rtl .splide__arrow--next svg{transform:scaleX(-1)}.splide__arrows--ttb .splide__arrow{left:50%;transform:translate(-50%)}.splide__arrows--ttb .splide__arrow--prev{top:1em}.splide__arrows--ttb .splide__arrow--prev svg{transform:rotate(-90deg)}.splide__arrows--ttb .splide__arrow--next{bottom:1em;top:auto}.splide__arrows--ttb .splide__arrow--next svg{transform:rotate(90deg)}.splide__pagination--ttb{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;inset:0 .5em 0 auto;padding:1em 0}",b=".splide{width:100%;height:100%;position:relative;--slidejs-splide-arrow-color: var(--slidejs-arrow-color, #007aff);--slidejs-splide-pagination-color: var(--slidejs-pagination-color, #007aff);--slidejs-splide-pagination-active-color: var(--slidejs-pagination-active-color, #007aff);--slidejs-splide-progress-bar-color: var(--slidejs-progress-bar-color, #007aff)}.splide__track{width:100%;height:100%}.splide__list{width:100%;height:100%;display:flex;flex-direction:row}.splide__slide{display:flex;align-items:center;justify-content:center;width:100%;height:100%;min-height:100%;flex-shrink:0;background:var(--slidejs-background-color, #fff);padding:2rem;box-sizing:border-box}.slide-content{width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center}.slide-content h1{font-size:3rem;margin-bottom:1rem;color:var(--slidejs-heading-color, #333)}.slide-content h2{font-size:2rem;margin-bottom:1rem;color:var(--slidejs-heading-color, #666)}.slide-content h3{font-size:1.5rem;margin-bottom:.5rem;color:var(--slidejs-heading-color, #666)}.slide-content p{font-size:1.2rem;line-height:1.6;color:var(--slidejs-text-color, #555);margin-bottom:1rem}.slide-content ul{text-align:left;display:inline-block;font-size:1.2rem;line-height:1.8;color:var(--slidejs-text-color, #555)}.slide-content li{margin-bottom:.5rem}.slide-content img{border-radius:8px;box-shadow:0 4px 12px #0000001a}.splide__arrow{background-color:var(--slidejs-splide-arrow-color, #007aff)}.splide__pagination{position:absolute;bottom:1rem;left:50%;transform:translate(-50%);z-index:10}.splide__pagination__page{background-color:var(--slidejs-splide-pagination-color, #007aff)}.splide__pagination__page.is-active{background-color:var(--slidejs-splide-pagination-active-color, #007aff)}.splide__progress__bar{background-color:var(--slidejs-splide-progress-bar-color, #007aff)}";async function x(p,t,i){const e=await _.parseSlideDSL(p),n=_.compile(e),s="splide-styles";if(!document.head.querySelector(`#${s}`)){const o=document.createElement("style");o.id=s,o.textContent=u,document.head.appendChild(o)}let l;if(typeof i.container=="string"){const o=document.querySelector(i.container);if(!o)throw new Error(`Container not found: ${i.container}`);l=o}else l=i.container;const r="slidejs-runner-splide-styles";if(!l.querySelector(`#${r}`)){const o=document.createElement("style");o.id=r,o.textContent=b,l.appendChild(o)}const a=document.createElement("div");a.style.width="100%",a.style.height="100%",l.appendChild(a);const h=new f,c=new m.SlideRunner({container:a,adapter:h,adapterOptions:{splideConfig:i.splideOptions}});return await c.run(n,t),c}exports.SplideAdapter=f;exports.createSlideRunner=x;
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/adapter.ts","../src/runner.ts"],"sourcesContent":["/**\n * @slidejs/runner-splide - SplideAdapter 适配器实现\n *\n * 将 Slide DSL 渲染为 Splide 幻灯片\n */\n\nimport { Splide } from '@splidejs/splide';\nimport type { Options } from '@splidejs/splide';\nimport type { SlideDefinition } from '@slidejs/core';\nimport type { SlideAdapter, AdapterEvent, EventHandler } from '@slidejs/runner';\nimport type { SplideAdapterOptions } from './types';\n\n/**\n * Splide 适配器\n *\n * 实现 SlideAdapter 接口,将 SlideDefinition 渲染为 Splide 幻灯片\n */\nexport class SplideAdapter implements SlideAdapter {\n readonly name = 'splide';\n\n private splide?: Splide;\n private splideContainer?: HTMLElement;\n private splideTrack?: HTMLElement;\n private splideList?: HTMLElement;\n private eventHandlers: Map<AdapterEvent, Set<EventHandler>> = new Map();\n\n /**\n * 初始化 Splide 适配器\n *\n * @param container - 容器元素\n * @param options - Splide 选项\n */\n async initialize(container: HTMLElement, options?: SplideAdapterOptions): Promise<void> {\n try {\n // 创建 Splide DOM 结构\n this.createSplideStructure(container);\n\n // 初始化 Splide\n if (!this.splideContainer) {\n throw new Error('Splide container not created');\n }\n\n const splideConfig: Options = {\n // 默认配置\n type: 'slide',\n perPage: 1,\n perMove: 1,\n gap: '1rem',\n pagination: true,\n arrows: true,\n keyboard: 'global',\n ...options?.splideConfig,\n };\n\n this.splide = new Splide(this.splideContainer, splideConfig);\n this.splide.mount();\n\n // 等待初始化完成\n await new Promise<void>(resolve => {\n requestAnimationFrame(() => {\n resolve();\n });\n });\n\n // 设置事件监听\n this.setupEventListeners();\n\n // 触发 ready 事件\n this.emit('ready');\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.emit('error', { message: errorMessage });\n throw new Error(`Failed to initialize SplideAdapter: ${errorMessage}`);\n }\n }\n\n /**\n * 渲染幻灯片\n *\n * @param slides - 幻灯片定义数组\n */\n async render(slides: SlideDefinition[]): Promise<void> {\n if (!this.splideList || !this.splide) {\n throw new Error('SplideAdapter not initialized');\n }\n\n try {\n // 清空现有幻灯片\n this.splideList.innerHTML = '';\n\n // 渲染每张幻灯片\n for (const slide of slides) {\n const slideElement = await this.renderSlide(slide);\n this.splideList.appendChild(slideElement);\n }\n\n // 刷新 Splide(重新计算和更新)\n this.splide.refresh();\n\n // 触发 slideRendered 事件\n this.emit('slideRendered', { totalSlides: slides.length });\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.emit('error', { message: errorMessage });\n throw new Error(`Failed to render slides: ${errorMessage}`);\n }\n }\n\n /**\n * 销毁适配器\n */\n async destroy(): Promise<void> {\n if (this.splide) {\n this.splide.destroy();\n this.splide = undefined;\n }\n\n if (this.splideContainer) {\n this.splideContainer.innerHTML = '';\n this.splideContainer = undefined;\n }\n\n this.splideTrack = undefined;\n this.splideList = undefined;\n this.eventHandlers.clear();\n }\n\n /**\n * 导航到指定幻灯片\n *\n * @param index - 幻灯片索引\n */\n navigateTo(index: number): void {\n if (!this.splide) {\n throw new Error('SplideAdapter not initialized');\n }\n\n this.splide.go(index);\n }\n\n /**\n * 获取当前幻灯片索引\n */\n getCurrentIndex(): number {\n if (!this.splide) {\n return 0;\n }\n\n return this.splide.index;\n }\n\n /**\n * 获取幻灯片总数\n */\n getTotalSlides(): number {\n if (!this.splide) {\n return 0;\n }\n\n return this.splide.length;\n }\n\n /**\n * 更新指定幻灯片\n *\n * @param index - 幻灯片索引\n * @param slide - 新的幻灯片定义\n */\n async updateSlide(index: number, slide: SlideDefinition): Promise<void> {\n if (!this.splideList || !this.splide) {\n throw new Error('SplideAdapter not initialized');\n }\n\n try {\n // 获取指定索引的 slide 元素\n const slides = this.splideList.querySelectorAll('.splide__slide');\n const targetSlide = slides[index] as HTMLElement;\n\n if (!targetSlide) {\n throw new Error(`Slide at index ${index} not found`);\n }\n\n // 渲染新的幻灯片内容\n const newSlide = await this.renderSlide(slide);\n\n // 替换旧的 slide\n targetSlide.replaceWith(newSlide);\n\n // 刷新 Splide\n this.splide.refresh();\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.emit('error', { message: errorMessage });\n throw new Error(`Failed to update slide: ${errorMessage}`);\n }\n }\n\n /**\n * 注册事件监听器\n *\n * @param event - 事件类型\n * @param handler - 事件处理器\n */\n on(event: AdapterEvent, handler: EventHandler): void {\n if (!this.eventHandlers.has(event)) {\n this.eventHandlers.set(event, new Set());\n }\n this.eventHandlers.get(event)!.add(handler);\n }\n\n /**\n * 移除事件监听器\n *\n * @param event - 事件类型\n * @param handler - 事件处理器\n */\n off(event: AdapterEvent, handler: EventHandler): void {\n const handlers = this.eventHandlers.get(event);\n if (handlers) {\n handlers.delete(handler);\n }\n }\n\n /**\n * 创建 Splide DOM 结构\n *\n * @param container - 容器元素\n */\n private createSplideStructure(container: HTMLElement): void {\n // 创建 .splide 容器\n const splideDiv = document.createElement('div');\n splideDiv.className = 'splide';\n\n // 创建 .splide__track 容器\n const trackDiv = document.createElement('div');\n trackDiv.className = 'splide__track';\n\n // 创建 .splide__list 容器\n const listUl = document.createElement('ul');\n listUl.className = 'splide__list';\n\n trackDiv.appendChild(listUl);\n splideDiv.appendChild(trackDiv);\n container.appendChild(splideDiv);\n\n this.splideContainer = splideDiv;\n this.splideTrack = trackDiv;\n this.splideList = listUl;\n }\n\n /**\n * 设置 Splide 事件监听\n */\n private setupEventListeners(): void {\n if (!this.splide) {\n return;\n }\n\n // 监听幻灯片切换事件\n this.splide.on('moved', (newIndex, prevIndex) => {\n this.emit('slideChanged', {\n index: newIndex,\n previousIndex: prevIndex,\n from: prevIndex,\n to: newIndex,\n });\n });\n }\n\n /**\n * 渲染单张幻灯片\n *\n * @param slide - 幻灯片定义\n * @returns slide 元素\n */\n private async renderSlide(slide: SlideDefinition): Promise<HTMLElement> {\n const slideElement = document.createElement('li');\n slideElement.className = 'splide__slide';\n\n // 渲染内容\n if (slide.content.type === 'dynamic') {\n // 动态内容(Web Components)\n const content = await this.renderDynamicContent(slide.content.component, slide.content.props);\n slideElement.appendChild(content);\n } else {\n // 静态文本内容\n const content = this.renderTextContent(slide.content.lines);\n slideElement.appendChild(content);\n }\n\n return slideElement;\n }\n\n /**\n * 渲染动态内容(Web Component)\n *\n * @param component - 组件名称\n * @param props - 组件属性\n * @returns 组件元素\n */\n private async renderDynamicContent(\n component: string,\n props: Record<string, unknown>\n ): Promise<HTMLElement> {\n // 创建 Web Component 元素\n const element = document.createElement(component);\n\n // 设置属性\n for (const [key, value] of Object.entries(props)) {\n if (typeof value === 'string' || typeof value === 'number') {\n // 字符串和数字 → HTML attributes\n element.setAttribute(key, String(value));\n } else if (typeof value === 'boolean') {\n // 布尔值 → HTML attributes(true 时设置空属性)\n if (value) {\n element.setAttribute(key, '');\n }\n } else {\n // 对象和数组 → JavaScript properties\n (element as Record<string, unknown>)[key] = value;\n }\n }\n\n return element;\n }\n\n /**\n * 渲染文本内容\n *\n * 支持以下格式:\n * - # 标题 -> h1\n * - ## 标题 -> h2\n * - ### 标题 -> h3\n * - ![alt](url) -> img\n * - - 列表项 -> ul/li\n * - 普通文本 -> p\n *\n * @param lines - 文本行数组\n * @returns 内容容器元素\n */\n private renderTextContent(lines: string[]): HTMLElement {\n const container = document.createElement('div');\n container.className = 'slide-content';\n\n let listContainer: HTMLUListElement | null = null;\n\n for (const line of lines) {\n const trimmedLine = line.trim();\n\n // 空行,结束列表\n if (!trimmedLine) {\n listContainer = null;\n continue;\n }\n\n // 标题 - # H1\n if (trimmedLine.startsWith('# ')) {\n listContainer = null;\n const h1 = document.createElement('h1');\n h1.textContent = trimmedLine.substring(2);\n container.appendChild(h1);\n continue;\n }\n\n // 标题 - ## H2\n if (trimmedLine.startsWith('## ')) {\n listContainer = null;\n const h2 = document.createElement('h2');\n h2.textContent = trimmedLine.substring(3);\n container.appendChild(h2);\n continue;\n }\n\n // 标题 - ### H3\n if (trimmedLine.startsWith('### ')) {\n listContainer = null;\n const h3 = document.createElement('h3');\n h3.textContent = trimmedLine.substring(4);\n container.appendChild(h3);\n continue;\n }\n\n // 图片 - ![alt](url)\n const imageMatch = trimmedLine.match(/^!\\[(.*?)\\]\\((.*?)\\)$/);\n if (imageMatch) {\n listContainer = null;\n const img = document.createElement('img');\n img.alt = imageMatch[1];\n img.src = imageMatch[2];\n img.style.maxWidth = '80%';\n img.style.maxHeight = '500px';\n container.appendChild(img);\n continue;\n }\n\n // 列表项 - - 项目\n if (trimmedLine.startsWith('- ')) {\n if (!listContainer) {\n listContainer = document.createElement('ul');\n container.appendChild(listContainer);\n }\n const li = document.createElement('li');\n li.textContent = trimmedLine.substring(2);\n listContainer.appendChild(li);\n continue;\n }\n\n // 普通文本\n listContainer = null;\n const p = document.createElement('p');\n p.textContent = trimmedLine;\n container.appendChild(p);\n }\n\n return container;\n }\n\n /**\n * 触发事件\n *\n * @param event - 事件类型\n * @param data - 事件数据\n */\n private emit(event: AdapterEvent, data?: unknown): void {\n const handlers = this.eventHandlers.get(event);\n if (handlers) {\n for (const handler of handlers) {\n try {\n handler(data);\n } catch (error) {\n console.error(`Error in ${event} handler:`, error);\n }\n }\n }\n }\n}\n","/**\n * @slidejs/runner-splide - SlideRunner 工厂函数\n *\n * 提供创建配置好的 SlideRunner 实例的便捷方法\n */\n\nimport { parseSlideDSL, compile } from '@slidejs/dsl';\nimport { SlideRunner } from '@slidejs/runner';\nimport type { SlideContext } from '@slidejs/context';\nimport { SplideAdapter } from './adapter';\nimport type { SplideAdapterOptions } from './types';\n\n/**\n * SlideRunner 配置选项\n */\nexport interface SlideRunnerConfig {\n /**\n * 容器选择器或 HTMLElement\n */\n container: string | HTMLElement;\n\n /**\n * Splide 配置选项\n */\n splideOptions?: SplideAdapterOptions['splideConfig'];\n}\n\n/**\n * 从 DSL 源代码创建并运行 SlideRunner\n *\n * @example\n * ```typescript\n * import { createSlideRunner } from '@slidejs/runner-splide';\n *\n * const dslSource = `\n * present quiz \"demo\" {\n * rules {\n * rule start \"intro\" {\n * slide {\n * content text { \"Hello World!\" }\n * }\n * }\n * }\n * }\n * `;\n *\n * const context = { sourceType: 'quiz', sourceId: 'demo', items: [] };\n * const runner = await createSlideRunner(dslSource, context, {\n * container: '#app',\n * splideOptions: {\n * type: 'slide',\n * perPage: 1,\n * pagination: true,\n * arrows: true,\n * },\n * });\n * ```\n */\nexport async function createSlideRunner<TContext extends SlideContext = SlideContext>(\n dslSource: string,\n context: TContext,\n config: SlideRunnerConfig\n): Promise<SlideRunner<TContext>> {\n // 1. 解析 DSL\n const ast = await parseSlideDSL(dslSource);\n\n // 2. 编译为 SlideDSL\n const slideDSL = compile<TContext>(ast);\n\n // 3. 创建适配器和 Runner\n const adapter = new SplideAdapter();\n const runner = new SlideRunner<TContext>({\n container: config.container,\n adapter,\n adapterOptions: {\n splideConfig: config.splideOptions,\n },\n });\n\n // 4. 运行演示(这会初始化适配器并渲染幻灯片)\n await runner.run(slideDSL, context);\n\n // 注意:需要手动调用 runner.play() 来启动演示(导航到第一张幻灯片)\n // 返回 runner 以便用户可以控制演示\n return runner;\n}\n"],"names":["SplideAdapter","container","options","splideConfig","Splide","resolve","error","errorMessage","slides","slide","slideElement","index","targetSlide","newSlide","event","handler","handlers","splideDiv","trackDiv","listUl","newIndex","prevIndex","content","component","props","element","key","value","lines","listContainer","line","trimmedLine","h1","h2","h3","imageMatch","img","li","p","data","createSlideRunner","dslSource","context","config","ast","parseSlideDSL","slideDSL","compile","adapter","runner","SlideRunner"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../src/adapter.ts","../src/runner.ts"],"sourcesContent":["/**\n * @slidejs/runner-splide - SplideAdapter 适配器实现\n *\n * 将 Slide DSL 渲染为 Splide 幻灯片\n */\n\nimport { Splide } from '@splidejs/splide';\nimport type { Options } from '@splidejs/splide';\nimport type { SlideDefinition } from '@slidejs/core';\nimport type { SlideAdapter, AdapterEvent, EventHandler } from '@slidejs/runner';\nimport type { SplideAdapterOptions } from './types';\n\n/**\n * Splide 适配器\n *\n * 实现 SlideAdapter 接口,将 SlideDefinition 渲染为 Splide 幻灯片\n */\nexport class SplideAdapter implements SlideAdapter {\n readonly name = 'splide';\n\n private splide?: Splide;\n private splideContainer?: HTMLElement;\n private splideTrack?: HTMLElement;\n private splideList?: HTMLElement;\n private eventHandlers: Map<AdapterEvent, Set<EventHandler>> = new Map();\n\n /**\n * 初始化 Splide 适配器\n *\n * @param container - 容器元素\n * @param options - Splide 选项\n */\n async initialize(container: HTMLElement, options?: SplideAdapterOptions): Promise<void> {\n try {\n // 创建 Splide DOM 结构\n this.createSplideStructure(container);\n\n // 初始化 Splide\n if (!this.splideContainer) {\n throw new Error('Splide container not created');\n }\n\n const splideConfig: Options = {\n // 默认配置\n type: 'slide',\n perPage: 1,\n perMove: 1,\n gap: '1rem',\n pagination: true,\n arrows: true,\n keyboard: 'global',\n ...options?.splideConfig,\n };\n\n this.splide = new Splide(this.splideContainer, splideConfig);\n this.splide.mount();\n\n // 等待初始化完成\n await new Promise<void>(resolve => {\n requestAnimationFrame(() => {\n resolve();\n });\n });\n\n // 设置事件监听\n this.setupEventListeners();\n\n // 触发 ready 事件\n this.emit('ready');\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.emit('error', { message: errorMessage });\n throw new Error(`Failed to initialize SplideAdapter: ${errorMessage}`);\n }\n }\n\n /**\n * 渲染幻灯片\n *\n * @param slides - 幻灯片定义数组\n */\n async render(slides: SlideDefinition[]): Promise<void> {\n if (!this.splideList || !this.splide) {\n throw new Error('SplideAdapter not initialized');\n }\n\n try {\n // 清空现有幻灯片\n this.splideList.innerHTML = '';\n\n // 渲染每张幻灯片\n for (const slide of slides) {\n const slideElement = await this.renderSlide(slide);\n this.splideList.appendChild(slideElement);\n }\n\n // 刷新 Splide(重新计算和更新)\n this.splide.refresh();\n\n // 触发 slideRendered 事件\n this.emit('slideRendered', { totalSlides: slides.length });\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.emit('error', { message: errorMessage });\n throw new Error(`Failed to render slides: ${errorMessage}`);\n }\n }\n\n /**\n * 销毁适配器\n */\n async destroy(): Promise<void> {\n if (this.splide) {\n this.splide.destroy();\n this.splide = undefined;\n }\n\n if (this.splideContainer) {\n this.splideContainer.innerHTML = '';\n this.splideContainer = undefined;\n }\n\n this.splideTrack = undefined;\n this.splideList = undefined;\n this.eventHandlers.clear();\n }\n\n /**\n * 导航到指定幻灯片\n *\n * @param index - 幻灯片索引\n */\n navigateTo(index: number): void {\n if (!this.splide) {\n throw new Error('SplideAdapter not initialized');\n }\n\n this.splide.go(index);\n }\n\n /**\n * 获取当前幻灯片索引\n */\n getCurrentIndex(): number {\n if (!this.splide) {\n return 0;\n }\n\n return this.splide.index;\n }\n\n /**\n * 获取幻灯片总数\n */\n getTotalSlides(): number {\n if (!this.splide) {\n return 0;\n }\n\n return this.splide.length;\n }\n\n /**\n * 更新指定幻灯片\n *\n * @param index - 幻灯片索引\n * @param slide - 新的幻灯片定义\n */\n async updateSlide(index: number, slide: SlideDefinition): Promise<void> {\n if (!this.splideList || !this.splide) {\n throw new Error('SplideAdapter not initialized');\n }\n\n try {\n // 获取指定索引的 slide 元素\n const slides = this.splideList.querySelectorAll('.splide__slide');\n const targetSlide = slides[index] as HTMLElement;\n\n if (!targetSlide) {\n throw new Error(`Slide at index ${index} not found`);\n }\n\n // 渲染新的幻灯片内容\n const newSlide = await this.renderSlide(slide);\n\n // 替换旧的 slide\n targetSlide.replaceWith(newSlide);\n\n // 刷新 Splide\n this.splide.refresh();\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.emit('error', { message: errorMessage });\n throw new Error(`Failed to update slide: ${errorMessage}`);\n }\n }\n\n /**\n * 注册事件监听器\n *\n * @param event - 事件类型\n * @param handler - 事件处理器\n */\n on(event: AdapterEvent, handler: EventHandler): void {\n if (!this.eventHandlers.has(event)) {\n this.eventHandlers.set(event, new Set());\n }\n this.eventHandlers.get(event)!.add(handler);\n }\n\n /**\n * 移除事件监听器\n *\n * @param event - 事件类型\n * @param handler - 事件处理器\n */\n off(event: AdapterEvent, handler: EventHandler): void {\n const handlers = this.eventHandlers.get(event);\n if (handlers) {\n handlers.delete(handler);\n }\n }\n\n /**\n * 创建 Splide DOM 结构\n *\n * @param container - 容器元素\n */\n private createSplideStructure(container: HTMLElement): void {\n // 创建 .splide 容器\n const splideDiv = document.createElement('div');\n splideDiv.className = 'splide';\n\n // 创建 .splide__track 容器\n const trackDiv = document.createElement('div');\n trackDiv.className = 'splide__track';\n\n // 创建 .splide__list 容器\n const listUl = document.createElement('ul');\n listUl.className = 'splide__list';\n\n trackDiv.appendChild(listUl);\n splideDiv.appendChild(trackDiv);\n container.appendChild(splideDiv);\n\n this.splideContainer = splideDiv;\n this.splideTrack = trackDiv;\n this.splideList = listUl;\n }\n\n /**\n * 设置 Splide 事件监听\n */\n private setupEventListeners(): void {\n if (!this.splide) {\n return;\n }\n\n // 监听幻灯片切换事件\n this.splide.on('moved', (newIndex, prevIndex) => {\n this.emit('slideChanged', {\n index: newIndex,\n previousIndex: prevIndex,\n from: prevIndex,\n to: newIndex,\n });\n });\n }\n\n /**\n * 渲染单张幻灯片\n *\n * @param slide - 幻灯片定义\n * @returns slide 元素\n */\n private async renderSlide(slide: SlideDefinition): Promise<HTMLElement> {\n const slideElement = document.createElement('li');\n slideElement.className = 'splide__slide';\n\n // 渲染内容\n if (slide.content.type === 'dynamic') {\n // 动态内容(Web Components)\n const content = await this.renderDynamicContent(slide.content.component, slide.content.props);\n slideElement.appendChild(content);\n } else {\n // 静态文本内容\n const content = this.renderTextContent(slide.content.lines);\n slideElement.appendChild(content);\n }\n\n return slideElement;\n }\n\n /**\n * 渲染动态内容(Web Component)\n *\n * @param component - 组件名称\n * @param props - 组件属性\n * @returns 组件元素\n */\n private async renderDynamicContent(\n component: string,\n props: Record<string, unknown>\n ): Promise<HTMLElement> {\n // 创建 Web Component 元素\n const element = document.createElement(component);\n\n // 设置属性\n for (const [key, value] of Object.entries(props)) {\n if (typeof value === 'string' || typeof value === 'number') {\n // 字符串和数字 → HTML attributes\n element.setAttribute(key, String(value));\n } else if (typeof value === 'boolean') {\n // 布尔值 → HTML attributes(true 时设置空属性)\n if (value) {\n element.setAttribute(key, '');\n }\n } else {\n // 对象和数组 → JavaScript properties\n (element as unknown as Record<string, unknown>)[key] = value;\n }\n }\n\n return element;\n }\n\n /**\n * 渲染文本内容\n *\n * 支持以下格式:\n * - # 标题 -> h1\n * - ## 标题 -> h2\n * - ### 标题 -> h3\n * - ![alt](url) -> img\n * - - 列表项 -> ul/li\n * - 普通文本 -> p\n *\n * @param lines - 文本行数组\n * @returns 内容容器元素\n */\n private renderTextContent(lines: string[]): HTMLElement {\n const container = document.createElement('div');\n container.className = 'slide-content';\n\n let listContainer: HTMLUListElement | null = null;\n\n for (const line of lines) {\n const trimmedLine = line.trim();\n\n // 空行,结束列表\n if (!trimmedLine) {\n listContainer = null;\n continue;\n }\n\n // 标题 - # H1\n if (trimmedLine.startsWith('# ')) {\n listContainer = null;\n const h1 = document.createElement('h1');\n h1.textContent = trimmedLine.substring(2);\n container.appendChild(h1);\n continue;\n }\n\n // 标题 - ## H2\n if (trimmedLine.startsWith('## ')) {\n listContainer = null;\n const h2 = document.createElement('h2');\n h2.textContent = trimmedLine.substring(3);\n container.appendChild(h2);\n continue;\n }\n\n // 标题 - ### H3\n if (trimmedLine.startsWith('### ')) {\n listContainer = null;\n const h3 = document.createElement('h3');\n h3.textContent = trimmedLine.substring(4);\n container.appendChild(h3);\n continue;\n }\n\n // 图片 - ![alt](url)\n const imageMatch = trimmedLine.match(/^!\\[(.*?)\\]\\((.*?)\\)$/);\n if (imageMatch) {\n listContainer = null;\n const img = document.createElement('img');\n img.alt = imageMatch[1];\n img.src = imageMatch[2];\n img.style.maxWidth = '80%';\n img.style.maxHeight = '500px';\n container.appendChild(img);\n continue;\n }\n\n // 列表项 - - 项目\n if (trimmedLine.startsWith('- ')) {\n if (!listContainer) {\n listContainer = document.createElement('ul');\n container.appendChild(listContainer);\n }\n const li = document.createElement('li');\n li.textContent = trimmedLine.substring(2);\n listContainer.appendChild(li);\n continue;\n }\n\n // 普通文本\n listContainer = null;\n const p = document.createElement('p');\n p.textContent = trimmedLine;\n container.appendChild(p);\n }\n\n return container;\n }\n\n /**\n * 触发事件\n *\n * @param event - 事件类型\n * @param data - 事件数据\n */\n private emit(event: AdapterEvent, data?: unknown): void {\n const handlers = this.eventHandlers.get(event);\n if (handlers) {\n for (const handler of handlers) {\n try {\n handler(data);\n } catch (error) {\n console.error(`Error in ${event} handler:`, error);\n }\n }\n }\n }\n}\n","/**\n * @slidejs/runner-splide - SlideRunner 工厂函数\n *\n * 提供创建配置好的 SlideRunner 实例的便捷方法\n */\n\nimport { parseSlideDSL, compile } from '@slidejs/dsl';\nimport { SlideRunner } from '@slidejs/runner';\nimport type { SlideContext } from '@slidejs/context';\nimport { SplideAdapter } from './adapter';\nimport type { SplideAdapterOptions } from './types';\n// 导入 CSS 内容用于注入\n// 注意:根据 @splidejs/splide 的 package.json exports,使用 '@splidejs/splide/css'\nimport splideCSS from '@splidejs/splide/css?inline';\nimport customCSS from './style.css?inline';\n\n/**\n * SlideRunner 配置选项\n */\nexport interface SlideRunnerConfig {\n /**\n * 容器选择器或 HTMLElement\n */\n container: string | HTMLElement;\n\n /**\n * Splide 配置选项\n */\n splideOptions?: SplideAdapterOptions['splideConfig'];\n}\n\n/**\n * 从 DSL 源代码创建并运行 SlideRunner\n *\n * @example\n * ```typescript\n * import { createSlideRunner } from '@slidejs/runner-splide';\n *\n * const dslSource = `\n * present quiz \"demo\" {\n * rules {\n * rule start \"intro\" {\n * slide {\n * content text { \"Hello World!\" }\n * }\n * }\n * }\n * }\n * `;\n *\n * const context = { sourceType: 'quiz', sourceId: 'demo', items: [] };\n * const runner = await createSlideRunner(dslSource, context, {\n * container: '#app',\n * splideOptions: {\n * type: 'slide',\n * perPage: 1,\n * pagination: true,\n * arrows: true,\n * },\n * });\n * ```\n */\nexport async function createSlideRunner<TContext extends SlideContext = SlideContext>(\n dslSource: string,\n context: TContext,\n config: SlideRunnerConfig\n): Promise<SlideRunner<TContext>> {\n // 1. 解析 DSL\n const ast = await parseSlideDSL(dslSource);\n\n // 2. 编译为 SlideDSL\n const slideDSL = compile<TContext>(ast);\n\n // 2.1 注入 Splide CSS 到 document.head(全局,如果尚未注入)\n const globalStyleId = 'splide-styles';\n const globalStyles = document.head.querySelector(`#${globalStyleId}`);\n if (!globalStyles) {\n const style = document.createElement('style');\n style.id = globalStyleId;\n style.textContent = splideCSS;\n document.head.appendChild(style);\n }\n\n // 2.2 获取用户提供的容器元素\n let userContainer: HTMLElement;\n if (typeof config.container === 'string') {\n const element = document.querySelector(config.container);\n if (!element) {\n throw new Error(`Container not found: ${config.container}`);\n }\n userContainer = element as HTMLElement;\n } else {\n userContainer = config.container;\n }\n\n // 2.3 注入自定义 CSS 样式到容器\n const styleId = 'slidejs-runner-splide-styles';\n if (!userContainer.querySelector(`#${styleId}`)) {\n const style = document.createElement('style');\n style.id = styleId;\n style.textContent = customCSS;\n userContainer.appendChild(style);\n }\n\n // 2.4 创建一个新的 div 节点用于 Splide(Splide 会接管这个 div)\n const splideContainer = document.createElement('div');\n // 确保容器占满父元素的高度和宽度\n splideContainer.style.width = '100%';\n splideContainer.style.height = '100%';\n userContainer.appendChild(splideContainer);\n\n // 3. 创建适配器和 Runner(将 splideContainer 传给 Runner,而不是 userContainer)\n const adapter = new SplideAdapter();\n const runner = new SlideRunner<TContext>({\n container: splideContainer,\n adapter,\n adapterOptions: {\n splideConfig: config.splideOptions,\n },\n });\n\n // 4. 运行演示(这会初始化适配器并渲染幻灯片)\n await runner.run(slideDSL, context);\n\n // 注意:需要手动调用 runner.play() 来启动演示(导航到第一张幻灯片)\n // 返回 runner 以便用户可以控制演示\n return runner;\n}\n"],"names":["SplideAdapter","container","options","splideConfig","Splide","resolve","error","errorMessage","slides","slide","slideElement","index","targetSlide","newSlide","event","handler","handlers","splideDiv","trackDiv","listUl","newIndex","prevIndex","content","component","props","element","key","value","lines","listContainer","line","trimmedLine","h1","h2","h3","imageMatch","img","li","p","data","createSlideRunner","dslSource","context","config","ast","parseSlideDSL","slideDSL","compile","globalStyleId","style","splideCSS","userContainer","styleId","customCSS","splideContainer","adapter","runner","SlideRunner"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
- import { Splide as a } from "@splidejs/splide";
2
- import { parseSlideDSL as c, compile as p } from "@slidejs/dsl";
3
- import { SlideRunner as h } from "@slidejs/runner";
4
- class m {
1
+ import { Splide as f } from "@splidejs/splide";
2
+ import { parseSlideDSL as h, compile as m } from "@slidejs/dsl";
3
+ import { SlideRunner as g } from "@slidejs/runner";
4
+ class u {
5
5
  constructor() {
6
6
  this.name = "splide", this.eventHandlers = /* @__PURE__ */ new Map();
7
7
  }
@@ -11,9 +11,9 @@ class m {
11
11
  * @param container - 容器元素
12
12
  * @param options - Splide 选项
13
13
  */
14
- async initialize(i, t) {
14
+ async initialize(t, i) {
15
15
  try {
16
- if (this.createSplideStructure(i), !this.splideContainer)
16
+ if (this.createSplideStructure(t), !this.splideContainer)
17
17
  throw new Error("Splide container not created");
18
18
  const e = {
19
19
  // 默认配置
@@ -24,9 +24,9 @@ class m {
24
24
  pagination: !0,
25
25
  arrows: !0,
26
26
  keyboard: "global",
27
- ...t == null ? void 0 : t.splideConfig
27
+ ...i?.splideConfig
28
28
  };
29
- this.splide = new a(this.splideContainer, e), this.splide.mount(), await new Promise((n) => {
29
+ this.splide = new f(this.splideContainer, e), this.splide.mount(), await new Promise((n) => {
30
30
  requestAnimationFrame(() => {
31
31
  n();
32
32
  });
@@ -41,18 +41,18 @@ class m {
41
41
  *
42
42
  * @param slides - 幻灯片定义数组
43
43
  */
44
- async render(i) {
44
+ async render(t) {
45
45
  if (!this.splideList || !this.splide)
46
46
  throw new Error("SplideAdapter not initialized");
47
47
  try {
48
48
  this.splideList.innerHTML = "";
49
- for (const t of i) {
50
- const e = await this.renderSlide(t);
49
+ for (const i of t) {
50
+ const e = await this.renderSlide(i);
51
51
  this.splideList.appendChild(e);
52
52
  }
53
- this.splide.refresh(), this.emit("slideRendered", { totalSlides: i.length });
54
- } catch (t) {
55
- const e = t instanceof Error ? t.message : String(t);
53
+ this.splide.refresh(), this.emit("slideRendered", { totalSlides: t.length });
54
+ } catch (i) {
55
+ const e = i instanceof Error ? i.message : String(i);
56
56
  throw this.emit("error", { message: e }), new Error(`Failed to render slides: ${e}`);
57
57
  }
58
58
  }
@@ -67,10 +67,10 @@ class m {
67
67
  *
68
68
  * @param index - 幻灯片索引
69
69
  */
70
- navigateTo(i) {
70
+ navigateTo(t) {
71
71
  if (!this.splide)
72
72
  throw new Error("SplideAdapter not initialized");
73
- this.splide.go(i);
73
+ this.splide.go(t);
74
74
  }
75
75
  /**
76
76
  * 获取当前幻灯片索引
@@ -90,14 +90,14 @@ class m {
90
90
  * @param index - 幻灯片索引
91
91
  * @param slide - 新的幻灯片定义
92
92
  */
93
- async updateSlide(i, t) {
93
+ async updateSlide(t, i) {
94
94
  if (!this.splideList || !this.splide)
95
95
  throw new Error("SplideAdapter not initialized");
96
96
  try {
97
- const n = this.splideList.querySelectorAll(".splide__slide")[i];
97
+ const n = this.splideList.querySelectorAll(".splide__slide")[t];
98
98
  if (!n)
99
- throw new Error(`Slide at index ${i} not found`);
100
- const s = await this.renderSlide(t);
99
+ throw new Error(`Slide at index ${t} not found`);
100
+ const s = await this.renderSlide(i);
101
101
  n.replaceWith(s), this.splide.refresh();
102
102
  } catch (e) {
103
103
  const n = e instanceof Error ? e.message : String(e);
@@ -110,8 +110,8 @@ class m {
110
110
  * @param event - 事件类型
111
111
  * @param handler - 事件处理器
112
112
  */
113
- on(i, t) {
114
- this.eventHandlers.has(i) || this.eventHandlers.set(i, /* @__PURE__ */ new Set()), this.eventHandlers.get(i).add(t);
113
+ on(t, i) {
114
+ this.eventHandlers.has(t) || this.eventHandlers.set(t, /* @__PURE__ */ new Set()), this.eventHandlers.get(t).add(i);
115
115
  }
116
116
  /**
117
117
  * 移除事件监听器
@@ -119,33 +119,33 @@ class m {
119
119
  * @param event - 事件类型
120
120
  * @param handler - 事件处理器
121
121
  */
122
- off(i, t) {
123
- const e = this.eventHandlers.get(i);
124
- e && e.delete(t);
122
+ off(t, i) {
123
+ const e = this.eventHandlers.get(t);
124
+ e && e.delete(i);
125
125
  }
126
126
  /**
127
127
  * 创建 Splide DOM 结构
128
128
  *
129
129
  * @param container - 容器元素
130
130
  */
131
- createSplideStructure(i) {
132
- const t = document.createElement("div");
133
- t.className = "splide";
131
+ createSplideStructure(t) {
132
+ const i = document.createElement("div");
133
+ i.className = "splide";
134
134
  const e = document.createElement("div");
135
135
  e.className = "splide__track";
136
136
  const n = document.createElement("ul");
137
- n.className = "splide__list", e.appendChild(n), t.appendChild(e), i.appendChild(t), this.splideContainer = t, this.splideTrack = e, this.splideList = n;
137
+ n.className = "splide__list", e.appendChild(n), i.appendChild(e), t.appendChild(i), this.splideContainer = i, this.splideTrack = e, this.splideList = n;
138
138
  }
139
139
  /**
140
140
  * 设置 Splide 事件监听
141
141
  */
142
142
  setupEventListeners() {
143
- this.splide && this.splide.on("moved", (i, t) => {
143
+ this.splide && this.splide.on("moved", (t, i) => {
144
144
  this.emit("slideChanged", {
145
- index: i,
146
- previousIndex: t,
147
- from: t,
148
- to: i
145
+ index: t,
146
+ previousIndex: i,
147
+ from: i,
148
+ to: t
149
149
  });
150
150
  });
151
151
  }
@@ -155,16 +155,16 @@ class m {
155
155
  * @param slide - 幻灯片定义
156
156
  * @returns slide 元素
157
157
  */
158
- async renderSlide(i) {
159
- const t = document.createElement("li");
160
- if (t.className = "splide__slide", i.content.type === "dynamic") {
161
- const e = await this.renderDynamicContent(i.content.component, i.content.props);
162
- t.appendChild(e);
158
+ async renderSlide(t) {
159
+ const i = document.createElement("li");
160
+ if (i.className = "splide__slide", t.content.type === "dynamic") {
161
+ const e = await this.renderDynamicContent(t.content.component, t.content.props);
162
+ i.appendChild(e);
163
163
  } else {
164
- const e = this.renderTextContent(i.content.lines);
165
- t.appendChild(e);
164
+ const e = this.renderTextContent(t.content.lines);
165
+ i.appendChild(e);
166
166
  }
167
- return t;
167
+ return i;
168
168
  }
169
169
  /**
170
170
  * 渲染动态内容(Web Component)
@@ -173,9 +173,9 @@ class m {
173
173
  * @param props - 组件属性
174
174
  * @returns 组件元素
175
175
  */
176
- async renderDynamicContent(i, t) {
177
- const e = document.createElement(i);
178
- for (const [n, s] of Object.entries(t))
176
+ async renderDynamicContent(t, i) {
177
+ const e = document.createElement(t);
178
+ for (const [n, s] of Object.entries(i))
179
179
  typeof s == "string" || typeof s == "number" ? e.setAttribute(n, String(s)) : typeof s == "boolean" ? s && e.setAttribute(n, "") : e[n] = s;
180
180
  return e;
181
181
  }
@@ -193,11 +193,11 @@ class m {
193
193
  * @param lines - 文本行数组
194
194
  * @returns 内容容器元素
195
195
  */
196
- renderTextContent(i) {
197
- const t = document.createElement("div");
198
- t.className = "slide-content";
196
+ renderTextContent(t) {
197
+ const i = document.createElement("div");
198
+ i.className = "slide-content";
199
199
  let e = null;
200
- for (const n of i) {
200
+ for (const n of t) {
201
201
  const s = n.trim();
202
202
  if (!s) {
203
203
  e = null;
@@ -206,39 +206,39 @@ class m {
206
206
  if (s.startsWith("# ")) {
207
207
  e = null;
208
208
  const r = document.createElement("h1");
209
- r.textContent = s.substring(2), t.appendChild(r);
209
+ r.textContent = s.substring(2), i.appendChild(r);
210
210
  continue;
211
211
  }
212
212
  if (s.startsWith("## ")) {
213
213
  e = null;
214
214
  const r = document.createElement("h2");
215
- r.textContent = s.substring(3), t.appendChild(r);
215
+ r.textContent = s.substring(3), i.appendChild(r);
216
216
  continue;
217
217
  }
218
218
  if (s.startsWith("### ")) {
219
219
  e = null;
220
220
  const r = document.createElement("h3");
221
- r.textContent = s.substring(4), t.appendChild(r);
221
+ r.textContent = s.substring(4), i.appendChild(r);
222
222
  continue;
223
223
  }
224
- const l = s.match(/^!\[(.*?)\]\((.*?)\)$/);
225
- if (l) {
224
+ const d = s.match(/^!\[(.*?)\]\((.*?)\)$/);
225
+ if (d) {
226
226
  e = null;
227
227
  const r = document.createElement("img");
228
- r.alt = l[1], r.src = l[2], r.style.maxWidth = "80%", r.style.maxHeight = "500px", t.appendChild(r);
228
+ r.alt = d[1], r.src = d[2], r.style.maxWidth = "80%", r.style.maxHeight = "500px", i.appendChild(r);
229
229
  continue;
230
230
  }
231
231
  if (s.startsWith("- ")) {
232
- e || (e = document.createElement("ul"), t.appendChild(e));
232
+ e || (e = document.createElement("ul"), i.appendChild(e));
233
233
  const r = document.createElement("li");
234
234
  r.textContent = s.substring(2), e.appendChild(r);
235
235
  continue;
236
236
  }
237
237
  e = null;
238
- const d = document.createElement("p");
239
- d.textContent = s, t.appendChild(d);
238
+ const l = document.createElement("p");
239
+ l.textContent = s, i.appendChild(l);
240
240
  }
241
- return t;
241
+ return i;
242
242
  }
243
243
  /**
244
244
  * 触发事件
@@ -246,29 +246,50 @@ class m {
246
246
  * @param event - 事件类型
247
247
  * @param data - 事件数据
248
248
  */
249
- emit(i, t) {
250
- const e = this.eventHandlers.get(i);
249
+ emit(t, i) {
250
+ const e = this.eventHandlers.get(t);
251
251
  if (e)
252
252
  for (const n of e)
253
253
  try {
254
- n(t);
254
+ n(i);
255
255
  } catch (s) {
256
- console.error(`Error in ${i} handler:`, s);
256
+ console.error(`Error in ${t} handler:`, s);
257
257
  }
258
258
  }
259
259
  }
260
- async function C(o, i, t) {
261
- const e = await c(o), n = p(e), s = new m(), l = new h({
262
- container: t.container,
263
- adapter: s,
260
+ const b = ".splide__container{box-sizing:border-box;position:relative}.splide__list{backface-visibility:hidden;display:-ms-flexbox;display:flex;height:100%;margin:0!important;padding:0!important}.splide.is-initialized:not(.is-active) .splide__list{display:block}.splide__pagination{-ms-flex-align:center;align-items:center;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:center;justify-content:center;margin:0;pointer-events:none}.splide__pagination li{display:inline-block;line-height:1;list-style-type:none;margin:0;pointer-events:auto}.splide:not(.is-overflow) .splide__pagination{display:none}.splide__progress__bar{width:0}.splide{position:relative;visibility:hidden}.splide.is-initialized,.splide.is-rendered{visibility:visible}.splide__slide{backface-visibility:hidden;box-sizing:border-box;-ms-flex-negative:0;flex-shrink:0;list-style-type:none!important;margin:0;position:relative}.splide__slide img{vertical-align:bottom}.splide__spinner{animation:splide-loading 1s linear infinite;border:2px solid #999;border-left-color:transparent;border-radius:50%;contain:strict;display:inline-block;height:20px;inset:0;margin:auto;position:absolute;width:20px}.splide__sr{clip:rect(0 0 0 0);border:0;height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.splide__toggle.is-active .splide__toggle__play,.splide__toggle__pause{display:none}.splide__toggle.is-active .splide__toggle__pause{display:inline}.splide__track{overflow:hidden;position:relative;z-index:0}@keyframes splide-loading{0%{transform:rotate(0)}to{transform:rotate(1turn)}}.splide__track--draggable{-webkit-touch-callout:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}.splide__track--fade>.splide__list>.splide__slide{margin:0!important;opacity:0;z-index:0}.splide__track--fade>.splide__list>.splide__slide.is-active{opacity:1;z-index:1}.splide--rtl{direction:rtl}.splide__track--ttb>.splide__list{display:block}.splide__arrow{-ms-flex-align:center;align-items:center;background:#ccc;border:0;border-radius:50%;cursor:pointer;display:-ms-flexbox;display:flex;height:2em;-ms-flex-pack:center;justify-content:center;opacity:.7;padding:0;position:absolute;top:50%;transform:translateY(-50%);width:2em;z-index:1}.splide__arrow svg{fill:#000;height:1.2em;width:1.2em}.splide__arrow:hover:not(:disabled){opacity:.9}.splide__arrow:disabled{opacity:.3}.splide__arrow:focus-visible{outline:3px solid #0bf;outline-offset:3px}.splide__arrow--prev{left:1em}.splide__arrow--prev svg{transform:scaleX(-1)}.splide__arrow--next{right:1em}.splide.is-focus-in .splide__arrow:focus{outline:3px solid #0bf;outline-offset:3px}.splide__pagination{bottom:.5em;left:0;padding:0 1em;position:absolute;right:0;z-index:1}.splide__pagination__page{background:#ccc;border:0;border-radius:50%;display:inline-block;height:8px;margin:3px;opacity:.7;padding:0;position:relative;transition:transform .2s linear;width:8px}.splide__pagination__page.is-active{background:#fff;transform:scale(1.4);z-index:1}.splide__pagination__page:hover{cursor:pointer;opacity:.9}.splide__pagination__page:focus-visible{outline:3px solid #0bf;outline-offset:3px}.splide.is-focus-in .splide__pagination__page:focus{outline:3px solid #0bf;outline-offset:3px}.splide__progress__bar{background:#ccc;height:3px}.splide__slide{-webkit-tap-highlight-color:rgba(0,0,0,0)}.splide__slide:focus{outline:0}@supports (outline-offset:-3px){.splide__slide:focus-visible{outline:3px solid #0bf;outline-offset:-3px}}@media screen and (-ms-high-contrast:none){.splide__slide:focus-visible{border:3px solid #0bf}}@supports (outline-offset:-3px){.splide.is-focus-in .splide__slide:focus{outline:3px solid #0bf;outline-offset:-3px}}@media screen and (-ms-high-contrast:none){.splide.is-focus-in .splide__slide:focus{border:3px solid #0bf}.splide.is-focus-in .splide__track>.splide__list>.splide__slide:focus{border-color:#0bf}}.splide__toggle{cursor:pointer}.splide__toggle:focus-visible{outline:3px solid #0bf;outline-offset:3px}.splide.is-focus-in .splide__toggle:focus{outline:3px solid #0bf;outline-offset:3px}.splide__track--nav>.splide__list>.splide__slide{border:3px solid transparent;cursor:pointer}.splide__track--nav>.splide__list>.splide__slide.is-active{border:3px solid #000}.splide__arrows--rtl .splide__arrow--prev{left:auto;right:1em}.splide__arrows--rtl .splide__arrow--prev svg{transform:scaleX(1)}.splide__arrows--rtl .splide__arrow--next{left:1em;right:auto}.splide__arrows--rtl .splide__arrow--next svg{transform:scaleX(-1)}.splide__arrows--ttb .splide__arrow{left:50%;transform:translate(-50%)}.splide__arrows--ttb .splide__arrow--prev{top:1em}.splide__arrows--ttb .splide__arrow--prev svg{transform:rotate(-90deg)}.splide__arrows--ttb .splide__arrow--next{bottom:1em;top:auto}.splide__arrows--ttb .splide__arrow--next svg{transform:rotate(90deg)}.splide__pagination--ttb{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;inset:0 .5em 0 auto;padding:1em 0}", x = ".splide{width:100%;height:100%;position:relative;--slidejs-splide-arrow-color: var(--slidejs-arrow-color, #007aff);--slidejs-splide-pagination-color: var(--slidejs-pagination-color, #007aff);--slidejs-splide-pagination-active-color: var(--slidejs-pagination-active-color, #007aff);--slidejs-splide-progress-bar-color: var(--slidejs-progress-bar-color, #007aff)}.splide__track{width:100%;height:100%}.splide__list{width:100%;height:100%;display:flex;flex-direction:row}.splide__slide{display:flex;align-items:center;justify-content:center;width:100%;height:100%;min-height:100%;flex-shrink:0;background:var(--slidejs-background-color, #fff);padding:2rem;box-sizing:border-box}.slide-content{width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center}.slide-content h1{font-size:3rem;margin-bottom:1rem;color:var(--slidejs-heading-color, #333)}.slide-content h2{font-size:2rem;margin-bottom:1rem;color:var(--slidejs-heading-color, #666)}.slide-content h3{font-size:1.5rem;margin-bottom:.5rem;color:var(--slidejs-heading-color, #666)}.slide-content p{font-size:1.2rem;line-height:1.6;color:var(--slidejs-text-color, #555);margin-bottom:1rem}.slide-content ul{text-align:left;display:inline-block;font-size:1.2rem;line-height:1.8;color:var(--slidejs-text-color, #555)}.slide-content li{margin-bottom:.5rem}.slide-content img{border-radius:8px;box-shadow:0 4px 12px #0000001a}.splide__arrow{background-color:var(--slidejs-splide-arrow-color, #007aff)}.splide__pagination{position:absolute;bottom:1rem;left:50%;transform:translate(-50%);z-index:10}.splide__pagination__page{background-color:var(--slidejs-splide-pagination-color, #007aff)}.splide__pagination__page.is-active{background-color:var(--slidejs-splide-pagination-active-color, #007aff)}.splide__progress__bar{background-color:var(--slidejs-splide-progress-bar-color, #007aff)}";
261
+ async function C(p, t, i) {
262
+ const e = await h(p), n = m(e), s = "splide-styles";
263
+ if (!document.head.querySelector(`#${s}`)) {
264
+ const o = document.createElement("style");
265
+ o.id = s, o.textContent = b, document.head.appendChild(o);
266
+ }
267
+ let l;
268
+ if (typeof i.container == "string") {
269
+ const o = document.querySelector(i.container);
270
+ if (!o)
271
+ throw new Error(`Container not found: ${i.container}`);
272
+ l = o;
273
+ } else
274
+ l = i.container;
275
+ const r = "slidejs-runner-splide-styles";
276
+ if (!l.querySelector(`#${r}`)) {
277
+ const o = document.createElement("style");
278
+ o.id = r, o.textContent = x, l.appendChild(o);
279
+ }
280
+ const a = document.createElement("div");
281
+ a.style.width = "100%", a.style.height = "100%", l.appendChild(a);
282
+ const _ = new u(), c = new g({
283
+ container: a,
284
+ adapter: _,
264
285
  adapterOptions: {
265
- splideConfig: t.splideOptions
286
+ splideConfig: i.splideOptions
266
287
  }
267
288
  });
268
- return await l.run(n, i), l;
289
+ return await c.run(n, t), c;
269
290
  }
270
291
  export {
271
- m as SplideAdapter,
292
+ u as SplideAdapter,
272
293
  C as createSlideRunner
273
294
  };
274
295
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":["../src/adapter.ts","../src/runner.ts"],"sourcesContent":["/**\n * @slidejs/runner-splide - SplideAdapter 适配器实现\n *\n * 将 Slide DSL 渲染为 Splide 幻灯片\n */\n\nimport { Splide } from '@splidejs/splide';\nimport type { Options } from '@splidejs/splide';\nimport type { SlideDefinition } from '@slidejs/core';\nimport type { SlideAdapter, AdapterEvent, EventHandler } from '@slidejs/runner';\nimport type { SplideAdapterOptions } from './types';\n\n/**\n * Splide 适配器\n *\n * 实现 SlideAdapter 接口,将 SlideDefinition 渲染为 Splide 幻灯片\n */\nexport class SplideAdapter implements SlideAdapter {\n readonly name = 'splide';\n\n private splide?: Splide;\n private splideContainer?: HTMLElement;\n private splideTrack?: HTMLElement;\n private splideList?: HTMLElement;\n private eventHandlers: Map<AdapterEvent, Set<EventHandler>> = new Map();\n\n /**\n * 初始化 Splide 适配器\n *\n * @param container - 容器元素\n * @param options - Splide 选项\n */\n async initialize(container: HTMLElement, options?: SplideAdapterOptions): Promise<void> {\n try {\n // 创建 Splide DOM 结构\n this.createSplideStructure(container);\n\n // 初始化 Splide\n if (!this.splideContainer) {\n throw new Error('Splide container not created');\n }\n\n const splideConfig: Options = {\n // 默认配置\n type: 'slide',\n perPage: 1,\n perMove: 1,\n gap: '1rem',\n pagination: true,\n arrows: true,\n keyboard: 'global',\n ...options?.splideConfig,\n };\n\n this.splide = new Splide(this.splideContainer, splideConfig);\n this.splide.mount();\n\n // 等待初始化完成\n await new Promise<void>(resolve => {\n requestAnimationFrame(() => {\n resolve();\n });\n });\n\n // 设置事件监听\n this.setupEventListeners();\n\n // 触发 ready 事件\n this.emit('ready');\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.emit('error', { message: errorMessage });\n throw new Error(`Failed to initialize SplideAdapter: ${errorMessage}`);\n }\n }\n\n /**\n * 渲染幻灯片\n *\n * @param slides - 幻灯片定义数组\n */\n async render(slides: SlideDefinition[]): Promise<void> {\n if (!this.splideList || !this.splide) {\n throw new Error('SplideAdapter not initialized');\n }\n\n try {\n // 清空现有幻灯片\n this.splideList.innerHTML = '';\n\n // 渲染每张幻灯片\n for (const slide of slides) {\n const slideElement = await this.renderSlide(slide);\n this.splideList.appendChild(slideElement);\n }\n\n // 刷新 Splide(重新计算和更新)\n this.splide.refresh();\n\n // 触发 slideRendered 事件\n this.emit('slideRendered', { totalSlides: slides.length });\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.emit('error', { message: errorMessage });\n throw new Error(`Failed to render slides: ${errorMessage}`);\n }\n }\n\n /**\n * 销毁适配器\n */\n async destroy(): Promise<void> {\n if (this.splide) {\n this.splide.destroy();\n this.splide = undefined;\n }\n\n if (this.splideContainer) {\n this.splideContainer.innerHTML = '';\n this.splideContainer = undefined;\n }\n\n this.splideTrack = undefined;\n this.splideList = undefined;\n this.eventHandlers.clear();\n }\n\n /**\n * 导航到指定幻灯片\n *\n * @param index - 幻灯片索引\n */\n navigateTo(index: number): void {\n if (!this.splide) {\n throw new Error('SplideAdapter not initialized');\n }\n\n this.splide.go(index);\n }\n\n /**\n * 获取当前幻灯片索引\n */\n getCurrentIndex(): number {\n if (!this.splide) {\n return 0;\n }\n\n return this.splide.index;\n }\n\n /**\n * 获取幻灯片总数\n */\n getTotalSlides(): number {\n if (!this.splide) {\n return 0;\n }\n\n return this.splide.length;\n }\n\n /**\n * 更新指定幻灯片\n *\n * @param index - 幻灯片索引\n * @param slide - 新的幻灯片定义\n */\n async updateSlide(index: number, slide: SlideDefinition): Promise<void> {\n if (!this.splideList || !this.splide) {\n throw new Error('SplideAdapter not initialized');\n }\n\n try {\n // 获取指定索引的 slide 元素\n const slides = this.splideList.querySelectorAll('.splide__slide');\n const targetSlide = slides[index] as HTMLElement;\n\n if (!targetSlide) {\n throw new Error(`Slide at index ${index} not found`);\n }\n\n // 渲染新的幻灯片内容\n const newSlide = await this.renderSlide(slide);\n\n // 替换旧的 slide\n targetSlide.replaceWith(newSlide);\n\n // 刷新 Splide\n this.splide.refresh();\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.emit('error', { message: errorMessage });\n throw new Error(`Failed to update slide: ${errorMessage}`);\n }\n }\n\n /**\n * 注册事件监听器\n *\n * @param event - 事件类型\n * @param handler - 事件处理器\n */\n on(event: AdapterEvent, handler: EventHandler): void {\n if (!this.eventHandlers.has(event)) {\n this.eventHandlers.set(event, new Set());\n }\n this.eventHandlers.get(event)!.add(handler);\n }\n\n /**\n * 移除事件监听器\n *\n * @param event - 事件类型\n * @param handler - 事件处理器\n */\n off(event: AdapterEvent, handler: EventHandler): void {\n const handlers = this.eventHandlers.get(event);\n if (handlers) {\n handlers.delete(handler);\n }\n }\n\n /**\n * 创建 Splide DOM 结构\n *\n * @param container - 容器元素\n */\n private createSplideStructure(container: HTMLElement): void {\n // 创建 .splide 容器\n const splideDiv = document.createElement('div');\n splideDiv.className = 'splide';\n\n // 创建 .splide__track 容器\n const trackDiv = document.createElement('div');\n trackDiv.className = 'splide__track';\n\n // 创建 .splide__list 容器\n const listUl = document.createElement('ul');\n listUl.className = 'splide__list';\n\n trackDiv.appendChild(listUl);\n splideDiv.appendChild(trackDiv);\n container.appendChild(splideDiv);\n\n this.splideContainer = splideDiv;\n this.splideTrack = trackDiv;\n this.splideList = listUl;\n }\n\n /**\n * 设置 Splide 事件监听\n */\n private setupEventListeners(): void {\n if (!this.splide) {\n return;\n }\n\n // 监听幻灯片切换事件\n this.splide.on('moved', (newIndex, prevIndex) => {\n this.emit('slideChanged', {\n index: newIndex,\n previousIndex: prevIndex,\n from: prevIndex,\n to: newIndex,\n });\n });\n }\n\n /**\n * 渲染单张幻灯片\n *\n * @param slide - 幻灯片定义\n * @returns slide 元素\n */\n private async renderSlide(slide: SlideDefinition): Promise<HTMLElement> {\n const slideElement = document.createElement('li');\n slideElement.className = 'splide__slide';\n\n // 渲染内容\n if (slide.content.type === 'dynamic') {\n // 动态内容(Web Components)\n const content = await this.renderDynamicContent(slide.content.component, slide.content.props);\n slideElement.appendChild(content);\n } else {\n // 静态文本内容\n const content = this.renderTextContent(slide.content.lines);\n slideElement.appendChild(content);\n }\n\n return slideElement;\n }\n\n /**\n * 渲染动态内容(Web Component)\n *\n * @param component - 组件名称\n * @param props - 组件属性\n * @returns 组件元素\n */\n private async renderDynamicContent(\n component: string,\n props: Record<string, unknown>\n ): Promise<HTMLElement> {\n // 创建 Web Component 元素\n const element = document.createElement(component);\n\n // 设置属性\n for (const [key, value] of Object.entries(props)) {\n if (typeof value === 'string' || typeof value === 'number') {\n // 字符串和数字 → HTML attributes\n element.setAttribute(key, String(value));\n } else if (typeof value === 'boolean') {\n // 布尔值 → HTML attributes(true 时设置空属性)\n if (value) {\n element.setAttribute(key, '');\n }\n } else {\n // 对象和数组 → JavaScript properties\n (element as Record<string, unknown>)[key] = value;\n }\n }\n\n return element;\n }\n\n /**\n * 渲染文本内容\n *\n * 支持以下格式:\n * - # 标题 -> h1\n * - ## 标题 -> h2\n * - ### 标题 -> h3\n * - ![alt](url) -> img\n * - - 列表项 -> ul/li\n * - 普通文本 -> p\n *\n * @param lines - 文本行数组\n * @returns 内容容器元素\n */\n private renderTextContent(lines: string[]): HTMLElement {\n const container = document.createElement('div');\n container.className = 'slide-content';\n\n let listContainer: HTMLUListElement | null = null;\n\n for (const line of lines) {\n const trimmedLine = line.trim();\n\n // 空行,结束列表\n if (!trimmedLine) {\n listContainer = null;\n continue;\n }\n\n // 标题 - # H1\n if (trimmedLine.startsWith('# ')) {\n listContainer = null;\n const h1 = document.createElement('h1');\n h1.textContent = trimmedLine.substring(2);\n container.appendChild(h1);\n continue;\n }\n\n // 标题 - ## H2\n if (trimmedLine.startsWith('## ')) {\n listContainer = null;\n const h2 = document.createElement('h2');\n h2.textContent = trimmedLine.substring(3);\n container.appendChild(h2);\n continue;\n }\n\n // 标题 - ### H3\n if (trimmedLine.startsWith('### ')) {\n listContainer = null;\n const h3 = document.createElement('h3');\n h3.textContent = trimmedLine.substring(4);\n container.appendChild(h3);\n continue;\n }\n\n // 图片 - ![alt](url)\n const imageMatch = trimmedLine.match(/^!\\[(.*?)\\]\\((.*?)\\)$/);\n if (imageMatch) {\n listContainer = null;\n const img = document.createElement('img');\n img.alt = imageMatch[1];\n img.src = imageMatch[2];\n img.style.maxWidth = '80%';\n img.style.maxHeight = '500px';\n container.appendChild(img);\n continue;\n }\n\n // 列表项 - - 项目\n if (trimmedLine.startsWith('- ')) {\n if (!listContainer) {\n listContainer = document.createElement('ul');\n container.appendChild(listContainer);\n }\n const li = document.createElement('li');\n li.textContent = trimmedLine.substring(2);\n listContainer.appendChild(li);\n continue;\n }\n\n // 普通文本\n listContainer = null;\n const p = document.createElement('p');\n p.textContent = trimmedLine;\n container.appendChild(p);\n }\n\n return container;\n }\n\n /**\n * 触发事件\n *\n * @param event - 事件类型\n * @param data - 事件数据\n */\n private emit(event: AdapterEvent, data?: unknown): void {\n const handlers = this.eventHandlers.get(event);\n if (handlers) {\n for (const handler of handlers) {\n try {\n handler(data);\n } catch (error) {\n console.error(`Error in ${event} handler:`, error);\n }\n }\n }\n }\n}\n","/**\n * @slidejs/runner-splide - SlideRunner 工厂函数\n *\n * 提供创建配置好的 SlideRunner 实例的便捷方法\n */\n\nimport { parseSlideDSL, compile } from '@slidejs/dsl';\nimport { SlideRunner } from '@slidejs/runner';\nimport type { SlideContext } from '@slidejs/context';\nimport { SplideAdapter } from './adapter';\nimport type { SplideAdapterOptions } from './types';\n\n/**\n * SlideRunner 配置选项\n */\nexport interface SlideRunnerConfig {\n /**\n * 容器选择器或 HTMLElement\n */\n container: string | HTMLElement;\n\n /**\n * Splide 配置选项\n */\n splideOptions?: SplideAdapterOptions['splideConfig'];\n}\n\n/**\n * 从 DSL 源代码创建并运行 SlideRunner\n *\n * @example\n * ```typescript\n * import { createSlideRunner } from '@slidejs/runner-splide';\n *\n * const dslSource = `\n * present quiz \"demo\" {\n * rules {\n * rule start \"intro\" {\n * slide {\n * content text { \"Hello World!\" }\n * }\n * }\n * }\n * }\n * `;\n *\n * const context = { sourceType: 'quiz', sourceId: 'demo', items: [] };\n * const runner = await createSlideRunner(dslSource, context, {\n * container: '#app',\n * splideOptions: {\n * type: 'slide',\n * perPage: 1,\n * pagination: true,\n * arrows: true,\n * },\n * });\n * ```\n */\nexport async function createSlideRunner<TContext extends SlideContext = SlideContext>(\n dslSource: string,\n context: TContext,\n config: SlideRunnerConfig\n): Promise<SlideRunner<TContext>> {\n // 1. 解析 DSL\n const ast = await parseSlideDSL(dslSource);\n\n // 2. 编译为 SlideDSL\n const slideDSL = compile<TContext>(ast);\n\n // 3. 创建适配器和 Runner\n const adapter = new SplideAdapter();\n const runner = new SlideRunner<TContext>({\n container: config.container,\n adapter,\n adapterOptions: {\n splideConfig: config.splideOptions,\n },\n });\n\n // 4. 运行演示(这会初始化适配器并渲染幻灯片)\n await runner.run(slideDSL, context);\n\n // 注意:需要手动调用 runner.play() 来启动演示(导航到第一张幻灯片)\n // 返回 runner 以便用户可以控制演示\n return runner;\n}\n"],"names":["SplideAdapter","container","options","splideConfig","Splide","resolve","error","errorMessage","slides","slide","slideElement","index","targetSlide","newSlide","event","handler","handlers","splideDiv","trackDiv","listUl","newIndex","prevIndex","content","component","props","element","key","value","lines","listContainer","line","trimmedLine","h1","h2","h3","imageMatch","img","li","p","data","createSlideRunner","dslSource","context","config","ast","parseSlideDSL","slideDSL","compile","adapter","runner","SlideRunner"],"mappings":";;;AAiBO,MAAMA,EAAsC;AAAA,EAA5C,cAAA;AACL,SAAS,OAAO,UAMhB,KAAQ,oCAA0D,IAAA;AAAA,EAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQtE,MAAM,WAAWC,GAAwBC,GAA+C;AACtF,QAAI;AAKF,UAHA,KAAK,sBAAsBD,CAAS,GAGhC,CAAC,KAAK;AACR,cAAM,IAAI,MAAM,8BAA8B;AAGhD,YAAME,IAAwB;AAAA;AAAA,QAE5B,MAAM;AAAA,QACN,SAAS;AAAA,QACT,SAAS;AAAA,QACT,KAAK;AAAA,QACL,YAAY;AAAA,QACZ,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,GAAGD,KAAA,gBAAAA,EAAS;AAAA,MAAA;AAGd,WAAK,SAAS,IAAIE,EAAO,KAAK,iBAAiBD,CAAY,GAC3D,KAAK,OAAO,MAAA,GAGZ,MAAM,IAAI,QAAc,CAAAE,MAAW;AACjC,8BAAsB,MAAM;AAC1B,UAAAA,EAAA;AAAA,QACF,CAAC;AAAA,MACH,CAAC,GAGD,KAAK,oBAAA,GAGL,KAAK,KAAK,OAAO;AAAA,IACnB,SAASC,GAAO;AACd,YAAMC,IAAeD,aAAiB,QAAQA,EAAM,UAAU,OAAOA,CAAK;AAC1E,iBAAK,KAAK,SAAS,EAAE,SAASC,GAAc,GACtC,IAAI,MAAM,uCAAuCA,CAAY,EAAE;AAAA,IACvE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,OAAOC,GAA0C;AACrD,QAAI,CAAC,KAAK,cAAc,CAAC,KAAK;AAC5B,YAAM,IAAI,MAAM,+BAA+B;AAGjD,QAAI;AAEF,WAAK,WAAW,YAAY;AAG5B,iBAAWC,KAASD,GAAQ;AAC1B,cAAME,IAAe,MAAM,KAAK,YAAYD,CAAK;AACjD,aAAK,WAAW,YAAYC,CAAY;AAAA,MAC1C;AAGA,WAAK,OAAO,QAAA,GAGZ,KAAK,KAAK,iBAAiB,EAAE,aAAaF,EAAO,QAAQ;AAAA,IAC3D,SAASF,GAAO;AACd,YAAMC,IAAeD,aAAiB,QAAQA,EAAM,UAAU,OAAOA,CAAK;AAC1E,iBAAK,KAAK,SAAS,EAAE,SAASC,GAAc,GACtC,IAAI,MAAM,4BAA4BA,CAAY,EAAE;AAAA,IAC5D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAyB;AAC7B,IAAI,KAAK,WACP,KAAK,OAAO,QAAA,GACZ,KAAK,SAAS,SAGZ,KAAK,oBACP,KAAK,gBAAgB,YAAY,IACjC,KAAK,kBAAkB,SAGzB,KAAK,cAAc,QACnB,KAAK,aAAa,QAClB,KAAK,cAAc,MAAA;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,WAAWI,GAAqB;AAC9B,QAAI,CAAC,KAAK;AACR,YAAM,IAAI,MAAM,+BAA+B;AAGjD,SAAK,OAAO,GAAGA,CAAK;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKA,kBAA0B;AACxB,WAAK,KAAK,SAIH,KAAK,OAAO,QAHV;AAAA,EAIX;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAyB;AACvB,WAAK,KAAK,SAIH,KAAK,OAAO,SAHV;AAAA,EAIX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,YAAYA,GAAeF,GAAuC;AACtE,QAAI,CAAC,KAAK,cAAc,CAAC,KAAK;AAC5B,YAAM,IAAI,MAAM,+BAA+B;AAGjD,QAAI;AAGF,YAAMG,IADS,KAAK,WAAW,iBAAiB,gBAAgB,EACrCD,CAAK;AAEhC,UAAI,CAACC;AACH,cAAM,IAAI,MAAM,kBAAkBD,CAAK,YAAY;AAIrD,YAAME,IAAW,MAAM,KAAK,YAAYJ,CAAK;AAG7C,MAAAG,EAAY,YAAYC,CAAQ,GAGhC,KAAK,OAAO,QAAA;AAAA,IACd,SAASP,GAAO;AACd,YAAMC,IAAeD,aAAiB,QAAQA,EAAM,UAAU,OAAOA,CAAK;AAC1E,iBAAK,KAAK,SAAS,EAAE,SAASC,GAAc,GACtC,IAAI,MAAM,2BAA2BA,CAAY,EAAE;AAAA,IAC3D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,GAAGO,GAAqBC,GAA6B;AACnD,IAAK,KAAK,cAAc,IAAID,CAAK,KAC/B,KAAK,cAAc,IAAIA,GAAO,oBAAI,KAAK,GAEzC,KAAK,cAAc,IAAIA,CAAK,EAAG,IAAIC,CAAO;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,IAAID,GAAqBC,GAA6B;AACpD,UAAMC,IAAW,KAAK,cAAc,IAAIF,CAAK;AAC7C,IAAIE,KACFA,EAAS,OAAOD,CAAO;AAAA,EAE3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,sBAAsBd,GAA8B;AAE1D,UAAMgB,IAAY,SAAS,cAAc,KAAK;AAC9C,IAAAA,EAAU,YAAY;AAGtB,UAAMC,IAAW,SAAS,cAAc,KAAK;AAC7C,IAAAA,EAAS,YAAY;AAGrB,UAAMC,IAAS,SAAS,cAAc,IAAI;AAC1C,IAAAA,EAAO,YAAY,gBAEnBD,EAAS,YAAYC,CAAM,GAC3BF,EAAU,YAAYC,CAAQ,GAC9BjB,EAAU,YAAYgB,CAAS,GAE/B,KAAK,kBAAkBA,GACvB,KAAK,cAAcC,GACnB,KAAK,aAAaC;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKQ,sBAA4B;AAClC,IAAK,KAAK,UAKV,KAAK,OAAO,GAAG,SAAS,CAACC,GAAUC,MAAc;AAC/C,WAAK,KAAK,gBAAgB;AAAA,QACxB,OAAOD;AAAA,QACP,eAAeC;AAAA,QACf,MAAMA;AAAA,QACN,IAAID;AAAA,MAAA,CACL;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,YAAYX,GAA8C;AACtE,UAAMC,IAAe,SAAS,cAAc,IAAI;AAIhD,QAHAA,EAAa,YAAY,iBAGrBD,EAAM,QAAQ,SAAS,WAAW;AAEpC,YAAMa,IAAU,MAAM,KAAK,qBAAqBb,EAAM,QAAQ,WAAWA,EAAM,QAAQ,KAAK;AAC5F,MAAAC,EAAa,YAAYY,CAAO;AAAA,IAClC,OAAO;AAEL,YAAMA,IAAU,KAAK,kBAAkBb,EAAM,QAAQ,KAAK;AAC1D,MAAAC,EAAa,YAAYY,CAAO;AAAA,IAClC;AAEA,WAAOZ;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,qBACZa,GACAC,GACsB;AAEtB,UAAMC,IAAU,SAAS,cAAcF,CAAS;AAGhD,eAAW,CAACG,GAAKC,CAAK,KAAK,OAAO,QAAQH,CAAK;AAC7C,MAAI,OAAOG,KAAU,YAAY,OAAOA,KAAU,WAEhDF,EAAQ,aAAaC,GAAK,OAAOC,CAAK,CAAC,IAC9B,OAAOA,KAAU,YAEtBA,KACFF,EAAQ,aAAaC,GAAK,EAAE,IAI7BD,EAAoCC,CAAG,IAAIC;AAIhD,WAAOF;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBQ,kBAAkBG,GAA8B;AACtD,UAAM3B,IAAY,SAAS,cAAc,KAAK;AAC9C,IAAAA,EAAU,YAAY;AAEtB,QAAI4B,IAAyC;AAE7C,eAAWC,KAAQF,GAAO;AACxB,YAAMG,IAAcD,EAAK,KAAA;AAGzB,UAAI,CAACC,GAAa;AAChB,QAAAF,IAAgB;AAChB;AAAA,MACF;AAGA,UAAIE,EAAY,WAAW,IAAI,GAAG;AAChC,QAAAF,IAAgB;AAChB,cAAMG,IAAK,SAAS,cAAc,IAAI;AACtC,QAAAA,EAAG,cAAcD,EAAY,UAAU,CAAC,GACxC9B,EAAU,YAAY+B,CAAE;AACxB;AAAA,MACF;AAGA,UAAID,EAAY,WAAW,KAAK,GAAG;AACjC,QAAAF,IAAgB;AAChB,cAAMI,IAAK,SAAS,cAAc,IAAI;AACtC,QAAAA,EAAG,cAAcF,EAAY,UAAU,CAAC,GACxC9B,EAAU,YAAYgC,CAAE;AACxB;AAAA,MACF;AAGA,UAAIF,EAAY,WAAW,MAAM,GAAG;AAClC,QAAAF,IAAgB;AAChB,cAAMK,IAAK,SAAS,cAAc,IAAI;AACtC,QAAAA,EAAG,cAAcH,EAAY,UAAU,CAAC,GACxC9B,EAAU,YAAYiC,CAAE;AACxB;AAAA,MACF;AAGA,YAAMC,IAAaJ,EAAY,MAAM,uBAAuB;AAC5D,UAAII,GAAY;AACd,QAAAN,IAAgB;AAChB,cAAMO,IAAM,SAAS,cAAc,KAAK;AACxC,QAAAA,EAAI,MAAMD,EAAW,CAAC,GACtBC,EAAI,MAAMD,EAAW,CAAC,GACtBC,EAAI,MAAM,WAAW,OACrBA,EAAI,MAAM,YAAY,SACtBnC,EAAU,YAAYmC,CAAG;AACzB;AAAA,MACF;AAGA,UAAIL,EAAY,WAAW,IAAI,GAAG;AAChC,QAAKF,MACHA,IAAgB,SAAS,cAAc,IAAI,GAC3C5B,EAAU,YAAY4B,CAAa;AAErC,cAAMQ,IAAK,SAAS,cAAc,IAAI;AACtC,QAAAA,EAAG,cAAcN,EAAY,UAAU,CAAC,GACxCF,EAAc,YAAYQ,CAAE;AAC5B;AAAA,MACF;AAGA,MAAAR,IAAgB;AAChB,YAAMS,IAAI,SAAS,cAAc,GAAG;AACpC,MAAAA,EAAE,cAAcP,GAChB9B,EAAU,YAAYqC,CAAC;AAAA,IACzB;AAEA,WAAOrC;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,KAAKa,GAAqByB,GAAsB;AACtD,UAAMvB,IAAW,KAAK,cAAc,IAAIF,CAAK;AAC7C,QAAIE;AACF,iBAAWD,KAAWC;AACpB,YAAI;AACF,UAAAD,EAAQwB,CAAI;AAAA,QACd,SAASjC,GAAO;AACd,kBAAQ,MAAM,YAAYQ,CAAK,aAAaR,CAAK;AAAA,QACnD;AAAA,EAGN;AACF;ACzXA,eAAsBkC,EACpBC,GACAC,GACAC,GACgC;AAEhC,QAAMC,IAAM,MAAMC,EAAcJ,CAAS,GAGnCK,IAAWC,EAAkBH,CAAG,GAGhCI,IAAU,IAAIhD,EAAA,GACdiD,IAAS,IAAIC,EAAsB;AAAA,IACvC,WAAWP,EAAO;AAAA,IAClB,SAAAK;AAAA,IACA,gBAAgB;AAAA,MACd,cAAcL,EAAO;AAAA,IAAA;AAAA,EACvB,CACD;AAGD,eAAMM,EAAO,IAAIH,GAAUJ,CAAO,GAI3BO;AACT;"}
1
+ {"version":3,"file":"index.mjs","sources":["../src/adapter.ts","../src/runner.ts"],"sourcesContent":["/**\n * @slidejs/runner-splide - SplideAdapter 适配器实现\n *\n * 将 Slide DSL 渲染为 Splide 幻灯片\n */\n\nimport { Splide } from '@splidejs/splide';\nimport type { Options } from '@splidejs/splide';\nimport type { SlideDefinition } from '@slidejs/core';\nimport type { SlideAdapter, AdapterEvent, EventHandler } from '@slidejs/runner';\nimport type { SplideAdapterOptions } from './types';\n\n/**\n * Splide 适配器\n *\n * 实现 SlideAdapter 接口,将 SlideDefinition 渲染为 Splide 幻灯片\n */\nexport class SplideAdapter implements SlideAdapter {\n readonly name = 'splide';\n\n private splide?: Splide;\n private splideContainer?: HTMLElement;\n private splideTrack?: HTMLElement;\n private splideList?: HTMLElement;\n private eventHandlers: Map<AdapterEvent, Set<EventHandler>> = new Map();\n\n /**\n * 初始化 Splide 适配器\n *\n * @param container - 容器元素\n * @param options - Splide 选项\n */\n async initialize(container: HTMLElement, options?: SplideAdapterOptions): Promise<void> {\n try {\n // 创建 Splide DOM 结构\n this.createSplideStructure(container);\n\n // 初始化 Splide\n if (!this.splideContainer) {\n throw new Error('Splide container not created');\n }\n\n const splideConfig: Options = {\n // 默认配置\n type: 'slide',\n perPage: 1,\n perMove: 1,\n gap: '1rem',\n pagination: true,\n arrows: true,\n keyboard: 'global',\n ...options?.splideConfig,\n };\n\n this.splide = new Splide(this.splideContainer, splideConfig);\n this.splide.mount();\n\n // 等待初始化完成\n await new Promise<void>(resolve => {\n requestAnimationFrame(() => {\n resolve();\n });\n });\n\n // 设置事件监听\n this.setupEventListeners();\n\n // 触发 ready 事件\n this.emit('ready');\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.emit('error', { message: errorMessage });\n throw new Error(`Failed to initialize SplideAdapter: ${errorMessage}`);\n }\n }\n\n /**\n * 渲染幻灯片\n *\n * @param slides - 幻灯片定义数组\n */\n async render(slides: SlideDefinition[]): Promise<void> {\n if (!this.splideList || !this.splide) {\n throw new Error('SplideAdapter not initialized');\n }\n\n try {\n // 清空现有幻灯片\n this.splideList.innerHTML = '';\n\n // 渲染每张幻灯片\n for (const slide of slides) {\n const slideElement = await this.renderSlide(slide);\n this.splideList.appendChild(slideElement);\n }\n\n // 刷新 Splide(重新计算和更新)\n this.splide.refresh();\n\n // 触发 slideRendered 事件\n this.emit('slideRendered', { totalSlides: slides.length });\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.emit('error', { message: errorMessage });\n throw new Error(`Failed to render slides: ${errorMessage}`);\n }\n }\n\n /**\n * 销毁适配器\n */\n async destroy(): Promise<void> {\n if (this.splide) {\n this.splide.destroy();\n this.splide = undefined;\n }\n\n if (this.splideContainer) {\n this.splideContainer.innerHTML = '';\n this.splideContainer = undefined;\n }\n\n this.splideTrack = undefined;\n this.splideList = undefined;\n this.eventHandlers.clear();\n }\n\n /**\n * 导航到指定幻灯片\n *\n * @param index - 幻灯片索引\n */\n navigateTo(index: number): void {\n if (!this.splide) {\n throw new Error('SplideAdapter not initialized');\n }\n\n this.splide.go(index);\n }\n\n /**\n * 获取当前幻灯片索引\n */\n getCurrentIndex(): number {\n if (!this.splide) {\n return 0;\n }\n\n return this.splide.index;\n }\n\n /**\n * 获取幻灯片总数\n */\n getTotalSlides(): number {\n if (!this.splide) {\n return 0;\n }\n\n return this.splide.length;\n }\n\n /**\n * 更新指定幻灯片\n *\n * @param index - 幻灯片索引\n * @param slide - 新的幻灯片定义\n */\n async updateSlide(index: number, slide: SlideDefinition): Promise<void> {\n if (!this.splideList || !this.splide) {\n throw new Error('SplideAdapter not initialized');\n }\n\n try {\n // 获取指定索引的 slide 元素\n const slides = this.splideList.querySelectorAll('.splide__slide');\n const targetSlide = slides[index] as HTMLElement;\n\n if (!targetSlide) {\n throw new Error(`Slide at index ${index} not found`);\n }\n\n // 渲染新的幻灯片内容\n const newSlide = await this.renderSlide(slide);\n\n // 替换旧的 slide\n targetSlide.replaceWith(newSlide);\n\n // 刷新 Splide\n this.splide.refresh();\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.emit('error', { message: errorMessage });\n throw new Error(`Failed to update slide: ${errorMessage}`);\n }\n }\n\n /**\n * 注册事件监听器\n *\n * @param event - 事件类型\n * @param handler - 事件处理器\n */\n on(event: AdapterEvent, handler: EventHandler): void {\n if (!this.eventHandlers.has(event)) {\n this.eventHandlers.set(event, new Set());\n }\n this.eventHandlers.get(event)!.add(handler);\n }\n\n /**\n * 移除事件监听器\n *\n * @param event - 事件类型\n * @param handler - 事件处理器\n */\n off(event: AdapterEvent, handler: EventHandler): void {\n const handlers = this.eventHandlers.get(event);\n if (handlers) {\n handlers.delete(handler);\n }\n }\n\n /**\n * 创建 Splide DOM 结构\n *\n * @param container - 容器元素\n */\n private createSplideStructure(container: HTMLElement): void {\n // 创建 .splide 容器\n const splideDiv = document.createElement('div');\n splideDiv.className = 'splide';\n\n // 创建 .splide__track 容器\n const trackDiv = document.createElement('div');\n trackDiv.className = 'splide__track';\n\n // 创建 .splide__list 容器\n const listUl = document.createElement('ul');\n listUl.className = 'splide__list';\n\n trackDiv.appendChild(listUl);\n splideDiv.appendChild(trackDiv);\n container.appendChild(splideDiv);\n\n this.splideContainer = splideDiv;\n this.splideTrack = trackDiv;\n this.splideList = listUl;\n }\n\n /**\n * 设置 Splide 事件监听\n */\n private setupEventListeners(): void {\n if (!this.splide) {\n return;\n }\n\n // 监听幻灯片切换事件\n this.splide.on('moved', (newIndex, prevIndex) => {\n this.emit('slideChanged', {\n index: newIndex,\n previousIndex: prevIndex,\n from: prevIndex,\n to: newIndex,\n });\n });\n }\n\n /**\n * 渲染单张幻灯片\n *\n * @param slide - 幻灯片定义\n * @returns slide 元素\n */\n private async renderSlide(slide: SlideDefinition): Promise<HTMLElement> {\n const slideElement = document.createElement('li');\n slideElement.className = 'splide__slide';\n\n // 渲染内容\n if (slide.content.type === 'dynamic') {\n // 动态内容(Web Components)\n const content = await this.renderDynamicContent(slide.content.component, slide.content.props);\n slideElement.appendChild(content);\n } else {\n // 静态文本内容\n const content = this.renderTextContent(slide.content.lines);\n slideElement.appendChild(content);\n }\n\n return slideElement;\n }\n\n /**\n * 渲染动态内容(Web Component)\n *\n * @param component - 组件名称\n * @param props - 组件属性\n * @returns 组件元素\n */\n private async renderDynamicContent(\n component: string,\n props: Record<string, unknown>\n ): Promise<HTMLElement> {\n // 创建 Web Component 元素\n const element = document.createElement(component);\n\n // 设置属性\n for (const [key, value] of Object.entries(props)) {\n if (typeof value === 'string' || typeof value === 'number') {\n // 字符串和数字 → HTML attributes\n element.setAttribute(key, String(value));\n } else if (typeof value === 'boolean') {\n // 布尔值 → HTML attributes(true 时设置空属性)\n if (value) {\n element.setAttribute(key, '');\n }\n } else {\n // 对象和数组 → JavaScript properties\n (element as unknown as Record<string, unknown>)[key] = value;\n }\n }\n\n return element;\n }\n\n /**\n * 渲染文本内容\n *\n * 支持以下格式:\n * - # 标题 -> h1\n * - ## 标题 -> h2\n * - ### 标题 -> h3\n * - ![alt](url) -> img\n * - - 列表项 -> ul/li\n * - 普通文本 -> p\n *\n * @param lines - 文本行数组\n * @returns 内容容器元素\n */\n private renderTextContent(lines: string[]): HTMLElement {\n const container = document.createElement('div');\n container.className = 'slide-content';\n\n let listContainer: HTMLUListElement | null = null;\n\n for (const line of lines) {\n const trimmedLine = line.trim();\n\n // 空行,结束列表\n if (!trimmedLine) {\n listContainer = null;\n continue;\n }\n\n // 标题 - # H1\n if (trimmedLine.startsWith('# ')) {\n listContainer = null;\n const h1 = document.createElement('h1');\n h1.textContent = trimmedLine.substring(2);\n container.appendChild(h1);\n continue;\n }\n\n // 标题 - ## H2\n if (trimmedLine.startsWith('## ')) {\n listContainer = null;\n const h2 = document.createElement('h2');\n h2.textContent = trimmedLine.substring(3);\n container.appendChild(h2);\n continue;\n }\n\n // 标题 - ### H3\n if (trimmedLine.startsWith('### ')) {\n listContainer = null;\n const h3 = document.createElement('h3');\n h3.textContent = trimmedLine.substring(4);\n container.appendChild(h3);\n continue;\n }\n\n // 图片 - ![alt](url)\n const imageMatch = trimmedLine.match(/^!\\[(.*?)\\]\\((.*?)\\)$/);\n if (imageMatch) {\n listContainer = null;\n const img = document.createElement('img');\n img.alt = imageMatch[1];\n img.src = imageMatch[2];\n img.style.maxWidth = '80%';\n img.style.maxHeight = '500px';\n container.appendChild(img);\n continue;\n }\n\n // 列表项 - - 项目\n if (trimmedLine.startsWith('- ')) {\n if (!listContainer) {\n listContainer = document.createElement('ul');\n container.appendChild(listContainer);\n }\n const li = document.createElement('li');\n li.textContent = trimmedLine.substring(2);\n listContainer.appendChild(li);\n continue;\n }\n\n // 普通文本\n listContainer = null;\n const p = document.createElement('p');\n p.textContent = trimmedLine;\n container.appendChild(p);\n }\n\n return container;\n }\n\n /**\n * 触发事件\n *\n * @param event - 事件类型\n * @param data - 事件数据\n */\n private emit(event: AdapterEvent, data?: unknown): void {\n const handlers = this.eventHandlers.get(event);\n if (handlers) {\n for (const handler of handlers) {\n try {\n handler(data);\n } catch (error) {\n console.error(`Error in ${event} handler:`, error);\n }\n }\n }\n }\n}\n","/**\n * @slidejs/runner-splide - SlideRunner 工厂函数\n *\n * 提供创建配置好的 SlideRunner 实例的便捷方法\n */\n\nimport { parseSlideDSL, compile } from '@slidejs/dsl';\nimport { SlideRunner } from '@slidejs/runner';\nimport type { SlideContext } from '@slidejs/context';\nimport { SplideAdapter } from './adapter';\nimport type { SplideAdapterOptions } from './types';\n// 导入 CSS 内容用于注入\n// 注意:根据 @splidejs/splide 的 package.json exports,使用 '@splidejs/splide/css'\nimport splideCSS from '@splidejs/splide/css?inline';\nimport customCSS from './style.css?inline';\n\n/**\n * SlideRunner 配置选项\n */\nexport interface SlideRunnerConfig {\n /**\n * 容器选择器或 HTMLElement\n */\n container: string | HTMLElement;\n\n /**\n * Splide 配置选项\n */\n splideOptions?: SplideAdapterOptions['splideConfig'];\n}\n\n/**\n * 从 DSL 源代码创建并运行 SlideRunner\n *\n * @example\n * ```typescript\n * import { createSlideRunner } from '@slidejs/runner-splide';\n *\n * const dslSource = `\n * present quiz \"demo\" {\n * rules {\n * rule start \"intro\" {\n * slide {\n * content text { \"Hello World!\" }\n * }\n * }\n * }\n * }\n * `;\n *\n * const context = { sourceType: 'quiz', sourceId: 'demo', items: [] };\n * const runner = await createSlideRunner(dslSource, context, {\n * container: '#app',\n * splideOptions: {\n * type: 'slide',\n * perPage: 1,\n * pagination: true,\n * arrows: true,\n * },\n * });\n * ```\n */\nexport async function createSlideRunner<TContext extends SlideContext = SlideContext>(\n dslSource: string,\n context: TContext,\n config: SlideRunnerConfig\n): Promise<SlideRunner<TContext>> {\n // 1. 解析 DSL\n const ast = await parseSlideDSL(dslSource);\n\n // 2. 编译为 SlideDSL\n const slideDSL = compile<TContext>(ast);\n\n // 2.1 注入 Splide CSS 到 document.head(全局,如果尚未注入)\n const globalStyleId = 'splide-styles';\n const globalStyles = document.head.querySelector(`#${globalStyleId}`);\n if (!globalStyles) {\n const style = document.createElement('style');\n style.id = globalStyleId;\n style.textContent = splideCSS;\n document.head.appendChild(style);\n }\n\n // 2.2 获取用户提供的容器元素\n let userContainer: HTMLElement;\n if (typeof config.container === 'string') {\n const element = document.querySelector(config.container);\n if (!element) {\n throw new Error(`Container not found: ${config.container}`);\n }\n userContainer = element as HTMLElement;\n } else {\n userContainer = config.container;\n }\n\n // 2.3 注入自定义 CSS 样式到容器\n const styleId = 'slidejs-runner-splide-styles';\n if (!userContainer.querySelector(`#${styleId}`)) {\n const style = document.createElement('style');\n style.id = styleId;\n style.textContent = customCSS;\n userContainer.appendChild(style);\n }\n\n // 2.4 创建一个新的 div 节点用于 Splide(Splide 会接管这个 div)\n const splideContainer = document.createElement('div');\n // 确保容器占满父元素的高度和宽度\n splideContainer.style.width = '100%';\n splideContainer.style.height = '100%';\n userContainer.appendChild(splideContainer);\n\n // 3. 创建适配器和 Runner(将 splideContainer 传给 Runner,而不是 userContainer)\n const adapter = new SplideAdapter();\n const runner = new SlideRunner<TContext>({\n container: splideContainer,\n adapter,\n adapterOptions: {\n splideConfig: config.splideOptions,\n },\n });\n\n // 4. 运行演示(这会初始化适配器并渲染幻灯片)\n await runner.run(slideDSL, context);\n\n // 注意:需要手动调用 runner.play() 来启动演示(导航到第一张幻灯片)\n // 返回 runner 以便用户可以控制演示\n return runner;\n}\n"],"names":["SplideAdapter","container","options","splideConfig","Splide","resolve","error","errorMessage","slides","slide","slideElement","index","targetSlide","newSlide","event","handler","handlers","splideDiv","trackDiv","listUl","newIndex","prevIndex","content","component","props","element","key","value","lines","listContainer","line","trimmedLine","h1","h2","h3","imageMatch","img","li","p","data","createSlideRunner","dslSource","context","config","ast","parseSlideDSL","slideDSL","compile","globalStyleId","style","splideCSS","userContainer","styleId","customCSS","splideContainer","adapter","runner","SlideRunner"],"mappings":";;;AAiBO,MAAMA,EAAsC;AAAA,EAA5C,cAAA;AACL,SAAS,OAAO,UAMhB,KAAQ,oCAA0D,IAAA;AAAA,EAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQtE,MAAM,WAAWC,GAAwBC,GAA+C;AACtF,QAAI;AAKF,UAHA,KAAK,sBAAsBD,CAAS,GAGhC,CAAC,KAAK;AACR,cAAM,IAAI,MAAM,8BAA8B;AAGhD,YAAME,IAAwB;AAAA;AAAA,QAE5B,MAAM;AAAA,QACN,SAAS;AAAA,QACT,SAAS;AAAA,QACT,KAAK;AAAA,QACL,YAAY;AAAA,QACZ,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,GAAGD,GAAS;AAAA,MAAA;AAGd,WAAK,SAAS,IAAIE,EAAO,KAAK,iBAAiBD,CAAY,GAC3D,KAAK,OAAO,MAAA,GAGZ,MAAM,IAAI,QAAc,CAAAE,MAAW;AACjC,8BAAsB,MAAM;AAC1B,UAAAA,EAAA;AAAA,QACF,CAAC;AAAA,MACH,CAAC,GAGD,KAAK,oBAAA,GAGL,KAAK,KAAK,OAAO;AAAA,IACnB,SAASC,GAAO;AACd,YAAMC,IAAeD,aAAiB,QAAQA,EAAM,UAAU,OAAOA,CAAK;AAC1E,iBAAK,KAAK,SAAS,EAAE,SAASC,GAAc,GACtC,IAAI,MAAM,uCAAuCA,CAAY,EAAE;AAAA,IACvE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,OAAOC,GAA0C;AACrD,QAAI,CAAC,KAAK,cAAc,CAAC,KAAK;AAC5B,YAAM,IAAI,MAAM,+BAA+B;AAGjD,QAAI;AAEF,WAAK,WAAW,YAAY;AAG5B,iBAAWC,KAASD,GAAQ;AAC1B,cAAME,IAAe,MAAM,KAAK,YAAYD,CAAK;AACjD,aAAK,WAAW,YAAYC,CAAY;AAAA,MAC1C;AAGA,WAAK,OAAO,QAAA,GAGZ,KAAK,KAAK,iBAAiB,EAAE,aAAaF,EAAO,QAAQ;AAAA,IAC3D,SAASF,GAAO;AACd,YAAMC,IAAeD,aAAiB,QAAQA,EAAM,UAAU,OAAOA,CAAK;AAC1E,iBAAK,KAAK,SAAS,EAAE,SAASC,GAAc,GACtC,IAAI,MAAM,4BAA4BA,CAAY,EAAE;AAAA,IAC5D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAyB;AAC7B,IAAI,KAAK,WACP,KAAK,OAAO,QAAA,GACZ,KAAK,SAAS,SAGZ,KAAK,oBACP,KAAK,gBAAgB,YAAY,IACjC,KAAK,kBAAkB,SAGzB,KAAK,cAAc,QACnB,KAAK,aAAa,QAClB,KAAK,cAAc,MAAA;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,WAAWI,GAAqB;AAC9B,QAAI,CAAC,KAAK;AACR,YAAM,IAAI,MAAM,+BAA+B;AAGjD,SAAK,OAAO,GAAGA,CAAK;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKA,kBAA0B;AACxB,WAAK,KAAK,SAIH,KAAK,OAAO,QAHV;AAAA,EAIX;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAyB;AACvB,WAAK,KAAK,SAIH,KAAK,OAAO,SAHV;AAAA,EAIX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,YAAYA,GAAeF,GAAuC;AACtE,QAAI,CAAC,KAAK,cAAc,CAAC,KAAK;AAC5B,YAAM,IAAI,MAAM,+BAA+B;AAGjD,QAAI;AAGF,YAAMG,IADS,KAAK,WAAW,iBAAiB,gBAAgB,EACrCD,CAAK;AAEhC,UAAI,CAACC;AACH,cAAM,IAAI,MAAM,kBAAkBD,CAAK,YAAY;AAIrD,YAAME,IAAW,MAAM,KAAK,YAAYJ,CAAK;AAG7C,MAAAG,EAAY,YAAYC,CAAQ,GAGhC,KAAK,OAAO,QAAA;AAAA,IACd,SAASP,GAAO;AACd,YAAMC,IAAeD,aAAiB,QAAQA,EAAM,UAAU,OAAOA,CAAK;AAC1E,iBAAK,KAAK,SAAS,EAAE,SAASC,GAAc,GACtC,IAAI,MAAM,2BAA2BA,CAAY,EAAE;AAAA,IAC3D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,GAAGO,GAAqBC,GAA6B;AACnD,IAAK,KAAK,cAAc,IAAID,CAAK,KAC/B,KAAK,cAAc,IAAIA,GAAO,oBAAI,KAAK,GAEzC,KAAK,cAAc,IAAIA,CAAK,EAAG,IAAIC,CAAO;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,IAAID,GAAqBC,GAA6B;AACpD,UAAMC,IAAW,KAAK,cAAc,IAAIF,CAAK;AAC7C,IAAIE,KACFA,EAAS,OAAOD,CAAO;AAAA,EAE3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,sBAAsBd,GAA8B;AAE1D,UAAMgB,IAAY,SAAS,cAAc,KAAK;AAC9C,IAAAA,EAAU,YAAY;AAGtB,UAAMC,IAAW,SAAS,cAAc,KAAK;AAC7C,IAAAA,EAAS,YAAY;AAGrB,UAAMC,IAAS,SAAS,cAAc,IAAI;AAC1C,IAAAA,EAAO,YAAY,gBAEnBD,EAAS,YAAYC,CAAM,GAC3BF,EAAU,YAAYC,CAAQ,GAC9BjB,EAAU,YAAYgB,CAAS,GAE/B,KAAK,kBAAkBA,GACvB,KAAK,cAAcC,GACnB,KAAK,aAAaC;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKQ,sBAA4B;AAClC,IAAK,KAAK,UAKV,KAAK,OAAO,GAAG,SAAS,CAACC,GAAUC,MAAc;AAC/C,WAAK,KAAK,gBAAgB;AAAA,QACxB,OAAOD;AAAA,QACP,eAAeC;AAAA,QACf,MAAMA;AAAA,QACN,IAAID;AAAA,MAAA,CACL;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,YAAYX,GAA8C;AACtE,UAAMC,IAAe,SAAS,cAAc,IAAI;AAIhD,QAHAA,EAAa,YAAY,iBAGrBD,EAAM,QAAQ,SAAS,WAAW;AAEpC,YAAMa,IAAU,MAAM,KAAK,qBAAqBb,EAAM,QAAQ,WAAWA,EAAM,QAAQ,KAAK;AAC5F,MAAAC,EAAa,YAAYY,CAAO;AAAA,IAClC,OAAO;AAEL,YAAMA,IAAU,KAAK,kBAAkBb,EAAM,QAAQ,KAAK;AAC1D,MAAAC,EAAa,YAAYY,CAAO;AAAA,IAClC;AAEA,WAAOZ;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,qBACZa,GACAC,GACsB;AAEtB,UAAMC,IAAU,SAAS,cAAcF,CAAS;AAGhD,eAAW,CAACG,GAAKC,CAAK,KAAK,OAAO,QAAQH,CAAK;AAC7C,MAAI,OAAOG,KAAU,YAAY,OAAOA,KAAU,WAEhDF,EAAQ,aAAaC,GAAK,OAAOC,CAAK,CAAC,IAC9B,OAAOA,KAAU,YAEtBA,KACFF,EAAQ,aAAaC,GAAK,EAAE,IAI7BD,EAA+CC,CAAG,IAAIC;AAI3D,WAAOF;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBQ,kBAAkBG,GAA8B;AACtD,UAAM3B,IAAY,SAAS,cAAc,KAAK;AAC9C,IAAAA,EAAU,YAAY;AAEtB,QAAI4B,IAAyC;AAE7C,eAAWC,KAAQF,GAAO;AACxB,YAAMG,IAAcD,EAAK,KAAA;AAGzB,UAAI,CAACC,GAAa;AAChB,QAAAF,IAAgB;AAChB;AAAA,MACF;AAGA,UAAIE,EAAY,WAAW,IAAI,GAAG;AAChC,QAAAF,IAAgB;AAChB,cAAMG,IAAK,SAAS,cAAc,IAAI;AACtC,QAAAA,EAAG,cAAcD,EAAY,UAAU,CAAC,GACxC9B,EAAU,YAAY+B,CAAE;AACxB;AAAA,MACF;AAGA,UAAID,EAAY,WAAW,KAAK,GAAG;AACjC,QAAAF,IAAgB;AAChB,cAAMI,IAAK,SAAS,cAAc,IAAI;AACtC,QAAAA,EAAG,cAAcF,EAAY,UAAU,CAAC,GACxC9B,EAAU,YAAYgC,CAAE;AACxB;AAAA,MACF;AAGA,UAAIF,EAAY,WAAW,MAAM,GAAG;AAClC,QAAAF,IAAgB;AAChB,cAAMK,IAAK,SAAS,cAAc,IAAI;AACtC,QAAAA,EAAG,cAAcH,EAAY,UAAU,CAAC,GACxC9B,EAAU,YAAYiC,CAAE;AACxB;AAAA,MACF;AAGA,YAAMC,IAAaJ,EAAY,MAAM,uBAAuB;AAC5D,UAAII,GAAY;AACd,QAAAN,IAAgB;AAChB,cAAMO,IAAM,SAAS,cAAc,KAAK;AACxC,QAAAA,EAAI,MAAMD,EAAW,CAAC,GACtBC,EAAI,MAAMD,EAAW,CAAC,GACtBC,EAAI,MAAM,WAAW,OACrBA,EAAI,MAAM,YAAY,SACtBnC,EAAU,YAAYmC,CAAG;AACzB;AAAA,MACF;AAGA,UAAIL,EAAY,WAAW,IAAI,GAAG;AAChC,QAAKF,MACHA,IAAgB,SAAS,cAAc,IAAI,GAC3C5B,EAAU,YAAY4B,CAAa;AAErC,cAAMQ,IAAK,SAAS,cAAc,IAAI;AACtC,QAAAA,EAAG,cAAcN,EAAY,UAAU,CAAC,GACxCF,EAAc,YAAYQ,CAAE;AAC5B;AAAA,MACF;AAGA,MAAAR,IAAgB;AAChB,YAAMS,IAAI,SAAS,cAAc,GAAG;AACpC,MAAAA,EAAE,cAAcP,GAChB9B,EAAU,YAAYqC,CAAC;AAAA,IACzB;AAEA,WAAOrC;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,KAAKa,GAAqByB,GAAsB;AACtD,UAAMvB,IAAW,KAAK,cAAc,IAAIF,CAAK;AAC7C,QAAIE;AACF,iBAAWD,KAAWC;AACpB,YAAI;AACF,UAAAD,EAAQwB,CAAI;AAAA,QACd,SAASjC,GAAO;AACd,kBAAQ,MAAM,YAAYQ,CAAK,aAAaR,CAAK;AAAA,QACnD;AAAA,EAGN;AACF;;ACrXA,eAAsBkC,EACpBC,GACAC,GACAC,GACgC;AAEhC,QAAMC,IAAM,MAAMC,EAAcJ,CAAS,GAGnCK,IAAWC,EAAkBH,CAAG,GAGhCI,IAAgB;AAEtB,MAAI,CADiB,SAAS,KAAK,cAAc,IAAIA,CAAa,EAAE,GACjD;AACjB,UAAMC,IAAQ,SAAS,cAAc,OAAO;AAC5C,IAAAA,EAAM,KAAKD,GACXC,EAAM,cAAcC,GACpB,SAAS,KAAK,YAAYD,CAAK;AAAA,EACjC;AAGA,MAAIE;AACJ,MAAI,OAAOR,EAAO,aAAc,UAAU;AACxC,UAAMlB,IAAU,SAAS,cAAckB,EAAO,SAAS;AACvD,QAAI,CAAClB;AACH,YAAM,IAAI,MAAM,wBAAwBkB,EAAO,SAAS,EAAE;AAE5D,IAAAQ,IAAgB1B;AAAA,EAClB;AACE,IAAA0B,IAAgBR,EAAO;AAIzB,QAAMS,IAAU;AAChB,MAAI,CAACD,EAAc,cAAc,IAAIC,CAAO,EAAE,GAAG;AAC/C,UAAMH,IAAQ,SAAS,cAAc,OAAO;AAC5C,IAAAA,EAAM,KAAKG,GACXH,EAAM,cAAcI,GACpBF,EAAc,YAAYF,CAAK;AAAA,EACjC;AAGA,QAAMK,IAAkB,SAAS,cAAc,KAAK;AAEpD,EAAAA,EAAgB,MAAM,QAAQ,QAC9BA,EAAgB,MAAM,SAAS,QAC/BH,EAAc,YAAYG,CAAe;AAGzC,QAAMC,IAAU,IAAIvD,EAAA,GACdwD,IAAS,IAAIC,EAAsB;AAAA,IACvC,WAAWH;AAAA,IACX,SAAAC;AAAA,IACA,gBAAgB;AAAA,MACd,cAAcZ,EAAO;AAAA,IAAA;AAAA,EACvB,CACD;AAGD,eAAMa,EAAO,IAAIV,GAAUJ,CAAO,GAI3Bc;AACT;"}
package/dist/style.css ADDED
@@ -0,0 +1 @@
1
+ .splide{width:100%;height:100%;position:relative;--slidejs-splide-arrow-color: var(--slidejs-arrow-color, #007aff);--slidejs-splide-pagination-color: var(--slidejs-pagination-color, #007aff);--slidejs-splide-pagination-active-color: var(--slidejs-pagination-active-color, #007aff);--slidejs-splide-progress-bar-color: var(--slidejs-progress-bar-color, #007aff)}.splide__track{width:100%;height:100%}.splide__list{width:100%;height:100%;display:flex;flex-direction:row}.splide__slide{display:flex;align-items:center;justify-content:center;width:100%;height:100%;min-height:100%;flex-shrink:0;background:var(--slidejs-background-color, #fff);padding:2rem;box-sizing:border-box}.slide-content{width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center}.slide-content h1{font-size:3rem;margin-bottom:1rem;color:var(--slidejs-heading-color, #333)}.slide-content h2{font-size:2rem;margin-bottom:1rem;color:var(--slidejs-heading-color, #666)}.slide-content h3{font-size:1.5rem;margin-bottom:.5rem;color:var(--slidejs-heading-color, #666)}.slide-content p{font-size:1.2rem;line-height:1.6;color:var(--slidejs-text-color, #555);margin-bottom:1rem}.slide-content ul{text-align:left;display:inline-block;font-size:1.2rem;line-height:1.8;color:var(--slidejs-text-color, #555)}.slide-content li{margin-bottom:.5rem}.slide-content img{border-radius:8px;box-shadow:0 4px 12px #0000001a}.splide__arrow{background-color:var(--slidejs-splide-arrow-color, #007aff)}.splide__pagination{position:absolute;bottom:1rem;left:50%;transform:translate(-50%);z-index:10}.splide__pagination__page{background-color:var(--slidejs-splide-pagination-color, #007aff)}.splide__pagination__page.is-active{background-color:var(--slidejs-splide-pagination-active-color, #007aff)}.splide__progress__bar{background-color:var(--slidejs-splide-progress-bar-color, #007aff)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slidejs/runner-splide",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
@@ -15,16 +15,16 @@
15
15
  },
16
16
  "dependencies": {
17
17
  "@splidejs/splide": "^4.1.3",
18
- "@slidejs/context": "0.1.2",
19
- "@slidejs/core": "0.1.2",
20
- "@slidejs/dsl": "0.1.2",
21
- "@slidejs/runner": "0.1.2"
18
+ "@slidejs/dsl": "0.1.4",
19
+ "@slidejs/core": "0.1.4",
20
+ "@slidejs/runner": "0.1.4",
21
+ "@slidejs/context": "0.1.4"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@types/node": "^20.10.0",
25
25
  "typescript": "^5.3.3",
26
- "vite": "^5.0.0",
27
- "vite-plugin-dts": "^3.7.0",
26
+ "vite": "^7.3.1",
27
+ "vite-plugin-dts": "^4.5.4",
28
28
  "vitest": "^1.0.0"
29
29
  },
30
30
  "publishConfig": {
package/src/adapter.ts CHANGED
@@ -317,7 +317,7 @@ export class SplideAdapter implements SlideAdapter {
317
317
  }
318
318
  } else {
319
319
  // 对象和数组 → JavaScript properties
320
- (element as Record<string, unknown>)[key] = value;
320
+ (element as unknown as Record<string, unknown>)[key] = value;
321
321
  }
322
322
  }
323
323
 
package/src/env.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @slidejs/runner-splide - 类型声明
3
+ *
4
+ * 支持 CSS ?inline 和 ?raw 导入
5
+ */
6
+
7
+ declare module '*.css?inline' {
8
+ const content: string;
9
+ export default content;
10
+ }
11
+
12
+ declare module '*.css?raw' {
13
+ const content: string;
14
+ export default content;
15
+ }
16
+
17
+ // 支持 @splidejs/splide/css 的 ?inline 导入
18
+ declare module '@splidejs/splide/css?inline' {
19
+ const content: string;
20
+ export default content;
21
+ }
package/src/index.ts CHANGED
@@ -2,8 +2,18 @@
2
2
  * @slidejs/runner-splide - Splide 适配器
3
3
  *
4
4
  * 将 Slide DSL 渲染为 Splide 幻灯片
5
+ *
6
+ * 样式会自动加载(包含 Splide 核心 CSS)。
7
+ * 主题样式需要单独导入(可选):
8
+ * ```typescript
9
+ * import '@splidejs/splide/css/theme/default';
10
+ * // 或其他主题
11
+ * ```
5
12
  */
6
13
 
14
+ // 导入核心样式(自动加载)
15
+ import './style.css';
16
+
7
17
  export { SplideAdapter } from './adapter';
8
18
  export { createSlideRunner, type SlideRunnerConfig } from './runner';
9
19
  export type { SplideAdapterOptions } from './types';
package/src/runner.ts CHANGED
@@ -9,6 +9,10 @@ import { SlideRunner } from '@slidejs/runner';
9
9
  import type { SlideContext } from '@slidejs/context';
10
10
  import { SplideAdapter } from './adapter';
11
11
  import type { SplideAdapterOptions } from './types';
12
+ // 导入 CSS 内容用于注入
13
+ // 注意:根据 @splidejs/splide 的 package.json exports,使用 '@splidejs/splide/css'
14
+ import splideCSS from '@splidejs/splide/css?inline';
15
+ import customCSS from './style.css?inline';
12
16
 
13
17
  /**
14
18
  * SlideRunner 配置选项
@@ -67,10 +71,48 @@ export async function createSlideRunner<TContext extends SlideContext = SlideCon
67
71
  // 2. 编译为 SlideDSL
68
72
  const slideDSL = compile<TContext>(ast);
69
73
 
70
- // 3. 创建适配器和 Runner
74
+ // 2.1 注入 Splide CSS 到 document.head(全局,如果尚未注入)
75
+ const globalStyleId = 'splide-styles';
76
+ const globalStyles = document.head.querySelector(`#${globalStyleId}`);
77
+ if (!globalStyles) {
78
+ const style = document.createElement('style');
79
+ style.id = globalStyleId;
80
+ style.textContent = splideCSS;
81
+ document.head.appendChild(style);
82
+ }
83
+
84
+ // 2.2 获取用户提供的容器元素
85
+ let userContainer: HTMLElement;
86
+ if (typeof config.container === 'string') {
87
+ const element = document.querySelector(config.container);
88
+ if (!element) {
89
+ throw new Error(`Container not found: ${config.container}`);
90
+ }
91
+ userContainer = element as HTMLElement;
92
+ } else {
93
+ userContainer = config.container;
94
+ }
95
+
96
+ // 2.3 注入自定义 CSS 样式到容器
97
+ const styleId = 'slidejs-runner-splide-styles';
98
+ if (!userContainer.querySelector(`#${styleId}`)) {
99
+ const style = document.createElement('style');
100
+ style.id = styleId;
101
+ style.textContent = customCSS;
102
+ userContainer.appendChild(style);
103
+ }
104
+
105
+ // 2.4 创建一个新的 div 节点用于 Splide(Splide 会接管这个 div)
106
+ const splideContainer = document.createElement('div');
107
+ // 确保容器占满父元素的高度和宽度
108
+ splideContainer.style.width = '100%';
109
+ splideContainer.style.height = '100%';
110
+ userContainer.appendChild(splideContainer);
111
+
112
+ // 3. 创建适配器和 Runner(将 splideContainer 传给 Runner,而不是 userContainer)
71
113
  const adapter = new SplideAdapter();
72
114
  const runner = new SlideRunner<TContext>({
73
- container: config.container,
115
+ container: splideContainer,
74
116
  adapter,
75
117
  adapterOptions: {
76
118
  splideConfig: config.splideOptions,
package/src/style.css ADDED
@@ -0,0 +1,149 @@
1
+ /**
2
+ * @slidejs/runner-splide - Splide 核心样式
3
+ *
4
+ * 注意:Splide 的核心 CSS 通过 runner.ts 注入到 document.head
5
+ * 此文件只包含自定义样式和 CSS 变量映射
6
+ *
7
+ * 使用示例:
8
+ * ```typescript
9
+ * import '@slidejs/runner-splide';
10
+ * // 或显式导入样式
11
+ * import '@slidejs/runner-splide/style.css';
12
+ * ```
13
+ *
14
+ * 自定义样式:
15
+ * 可以通过 CSS 变量自定义样式,例如:
16
+ * ```css
17
+ * :root {
18
+ * --slidejs-arrow-color: #007aff;
19
+ * --slidejs-pagination-color: #007aff;
20
+ * }
21
+ * ```
22
+ */
23
+
24
+ /* Splide 容器基础样式 */
25
+ .splide {
26
+ width: 100%;
27
+ height: 100%;
28
+ position: relative;
29
+
30
+ /* Runner 特定变量映射 - 将标准变量映射到 Splide 特定变量 */
31
+ /* 注意:标准变量的默认值由 @slidejs/theme 包定义,runner 只消费这些变量 */
32
+
33
+ /* Splide 箭头颜色 - 从标准变量映射(fallback 仅作为临时默认值) */
34
+ --slidejs-splide-arrow-color: var(--slidejs-arrow-color, #007aff);
35
+ /* Splide 分页器颜色 - 从标准变量映射(fallback 仅作为临时默认值) */
36
+ --slidejs-splide-pagination-color: var(--slidejs-pagination-color, #007aff);
37
+ /* Splide 分页器激活颜色 - 从标准变量映射(fallback 仅作为临时默认值) */
38
+ --slidejs-splide-pagination-active-color: var(--slidejs-pagination-active-color, #007aff);
39
+ /* Splide 进度条颜色 - 从标准变量映射(fallback 仅作为临时默认值) */
40
+ --slidejs-splide-progress-bar-color: var(--slidejs-progress-bar-color, #007aff);
41
+ }
42
+
43
+ /* Splide Track 和 List 样式 - 确保占满高度 */
44
+ .splide__track {
45
+ width: 100%;
46
+ height: 100%;
47
+ }
48
+
49
+ .splide__list {
50
+ width: 100%;
51
+ height: 100%;
52
+ display: flex;
53
+ flex-direction: row;
54
+ }
55
+
56
+ /* Splide Slide 样式 - 确保内容垂直和水平居中,占满高度 */
57
+ /* 注意:标准变量的默认值由 @slidejs/theme 包定义,fallback 仅作为临时默认值 */
58
+ .splide__slide {
59
+ display: flex;
60
+ align-items: center;
61
+ justify-content: center;
62
+ width: 100%;
63
+ height: 100%;
64
+ min-height: 100%;
65
+ flex-shrink: 0;
66
+ background: var(--slidejs-background-color, #fff);
67
+ padding: 2rem;
68
+ box-sizing: border-box;
69
+ }
70
+
71
+ /* Slide Content 容器样式 - 允许通过 CSS 变量自定义,占满高度 */
72
+ /* 注意:标准变量的默认值由 @slidejs/theme 包定义,fallback 仅作为临时默认值 */
73
+ .slide-content {
74
+ width: 100%;
75
+ height: 100%;
76
+ display: flex;
77
+ flex-direction: column;
78
+ align-items: center;
79
+ justify-content: center;
80
+ text-align: center;
81
+ }
82
+
83
+ .slide-content h1 {
84
+ font-size: 3rem;
85
+ margin-bottom: 1rem;
86
+ color: var(--slidejs-heading-color, #333);
87
+ }
88
+
89
+ .slide-content h2 {
90
+ font-size: 2rem;
91
+ margin-bottom: 1rem;
92
+ color: var(--slidejs-heading-color, #666);
93
+ }
94
+
95
+ .slide-content h3 {
96
+ font-size: 1.5rem;
97
+ margin-bottom: 0.5rem;
98
+ color: var(--slidejs-heading-color, #666);
99
+ }
100
+
101
+ .slide-content p {
102
+ font-size: 1.2rem;
103
+ line-height: 1.6;
104
+ color: var(--slidejs-text-color, #555);
105
+ margin-bottom: 1rem;
106
+ }
107
+
108
+ .slide-content ul {
109
+ text-align: left;
110
+ display: inline-block;
111
+ font-size: 1.2rem;
112
+ line-height: 1.8;
113
+ color: var(--slidejs-text-color, #555);
114
+ }
115
+
116
+ .slide-content li {
117
+ margin-bottom: 0.5rem;
118
+ }
119
+
120
+ .slide-content img {
121
+ border-radius: 8px;
122
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
123
+ }
124
+
125
+ /* 应用 CSS 变量到 Splide 组件 */
126
+ .splide__arrow {
127
+ background-color: var(--slidejs-splide-arrow-color, #007aff);
128
+ }
129
+
130
+ /* 分页器样式 - 覆盖在 slide 上方 */
131
+ .splide__pagination {
132
+ position: absolute;
133
+ bottom: 1rem;
134
+ left: 50%;
135
+ transform: translateX(-50%);
136
+ z-index: 10;
137
+ }
138
+
139
+ .splide__pagination__page {
140
+ background-color: var(--slidejs-splide-pagination-color, #007aff);
141
+ }
142
+
143
+ .splide__pagination__page.is-active {
144
+ background-color: var(--slidejs-splide-pagination-active-color, #007aff);
145
+ }
146
+
147
+ .splide__progress__bar {
148
+ background-color: var(--slidejs-splide-progress-bar-color, #007aff);
149
+ }
package/tsconfig.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "composite": true,
7
7
  "declarationMap": true
8
8
  },
9
- "include": ["src/**/*"],
9
+ "include": ["src/**/*", "src/env.d.ts"],
10
10
  "exclude": ["**/*.test.ts", "dist", "node_modules"],
11
11
  "references": [{ "path": "../core" }, { "path": "../runner" }]
12
12
  }
package/vite.config.ts CHANGED
@@ -20,8 +20,22 @@ export default defineConfig({
20
20
  // Only externalize JS modules from splide, NOT CSS
21
21
  /^@splidejs\/splide\/.*\.js$/,
22
22
  ],
23
+ // 确保 ?inline 和 ?raw 资源被正确处理
24
+ plugins: [],
25
+ output: {
26
+ // 确保 CSS 文件被正确提取为 style.css
27
+ assetFileNames: assetInfo => {
28
+ // 将所有 CSS 文件重命名为 style.css(与 package.json exports 一致)
29
+ if (assetInfo.name && assetInfo.name.endsWith('.css')) {
30
+ return 'style.css';
31
+ }
32
+ return assetInfo.name || 'assets/[name].[ext]';
33
+ },
34
+ },
23
35
  },
24
36
  sourcemap: true,
37
+ // 确保 CSS 被提取到单独的文件
38
+ cssCodeSplit: false,
25
39
  },
26
40
  plugins: [
27
41
  dts({