@miraiclip/player 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 comaniacs
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 comaniacs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,15 +1,15 @@
1
- # @miraiclip/player
2
-
3
- `<miraiclip-player>`: a framework-free web component that plays a rendered video file or a live [Miraiclip](https://docs.miraiclip.com/) project, with a scrubber, chapters and keyboard control.
4
-
5
- ```html
6
- <script type="module">import "@miraiclip/player/define";</script>
7
-
8
- <!-- a rendered file, chapters from a WebVTT (or JSON) file -->
9
- <miraiclip-player src="launch.mp4" chapters="launch.chapters.vtt" controls></miraiclip-player>
10
-
11
- <!-- a live document: chapters come from its markers -->
12
- <miraiclip-player document="video.miraiclip.json" controls></miraiclip-player>
13
- ```
14
-
15
- Live documents need `@miraiclip/core` and `@miraiclip/renderer` installed (loaded on demand; file playback needs neither). Docs: https://docs.miraiclip.com/
1
+ # @miraiclip/player
2
+
3
+ `<miraiclip-player>`: a framework-free web component that plays a rendered video file or a live [Miraiclip](https://docs.miraiclip.com/) project, with a scrubber, chapters and keyboard control.
4
+
5
+ ```html
6
+ <script type="module">import "@miraiclip/player/define";</script>
7
+
8
+ <!-- a rendered file, chapters from a WebVTT (or JSON) file -->
9
+ <miraiclip-player src="launch.mp4" chapters="launch.chapters.vtt" controls></miraiclip-player>
10
+
11
+ <!-- a live document: chapters come from its markers -->
12
+ <miraiclip-player document="video.miraiclip.json" controls></miraiclip-player>
13
+ ```
14
+
15
+ Live documents need `@miraiclip/core` and `@miraiclip/renderer` installed (loaded on demand; file playback needs neither). Docs: https://docs.miraiclip.com/
@@ -569,4 +569,4 @@ export {
569
569
  MiraiclipPlayerElement,
570
570
  defineMiraiclipPlayer
571
571
  };
572
- //# sourceMappingURL=chunk-TMHYBF54.js.map
572
+ //# sourceMappingURL=chunk-XKPHDDUA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/chapters.ts","../src/engines.ts","../src/element.ts"],"sourcesContent":["/**\r\n * Chapters for the player: from a document's markers, from JSON, or from a\r\n * WebVTT chapters track. Pure functions — no DOM.\r\n */\r\nexport interface PlayerChapter {\r\n id: string;\r\n label: string;\r\n /** Seconds. */\r\n start: number;\r\n end: number;\r\n}\r\n\r\ninterface MarkerLike {\r\n id: string;\r\n timeUs: number;\r\n label: string;\r\n durationUs?: number;\r\n}\r\n\r\n/**\r\n * A document's markers as chapters (same rule as core's `markerChapters`):\r\n * each runs for its own `durationUs`, or until the next marker; the last one\r\n * until `durationS`.\r\n */\r\nexport function chaptersFromMarkers(markers: Record<string, MarkerLike> | undefined, durationS: number): PlayerChapter[] {\r\n const list = Object.values(markers ?? {}).sort((a, b) => a.timeUs - b.timeUs || a.id.localeCompare(b.id));\r\n return list.map((m, i) => {\r\n const start = m.timeUs / 1e6;\r\n const next = list[i + 1];\r\n const end = m.durationUs !== undefined ? (m.timeUs + m.durationUs) / 1e6 : next ? next.timeUs / 1e6 : Math.max(durationS, start);\r\n return { id: m.id, label: m.label, start, end };\r\n });\r\n}\r\n\r\n/** Parse a WebVTT chapters track (\"00:01.000 --> 00:05.000\" cues with a title line). */\r\nexport function parseVttChapters(vtt: string): PlayerChapter[] {\r\n const chapters: PlayerChapter[] = [];\r\n const blocks = vtt.replace(/\\r/g, \"\").split(/\\n{2,}/);\r\n const time = (t: string): number => {\r\n const parts = t.trim().split(\":\").map(Number);\r\n let s = 0;\r\n for (const p of parts) s = s * 60 + p;\r\n return s;\r\n };\r\n for (const block of blocks) {\r\n const lines = block.split(\"\\n\").filter((l) => l.trim() !== \"\");\r\n const at = lines.findIndex((l) => l.includes(\"-->\"));\r\n if (at < 0) continue;\r\n const [from, to] = lines[at]!.split(\"-->\").map((s) => s.trim().split(/\\s+/)[0]!);\r\n const label = lines.slice(at + 1).join(\" \").trim();\r\n if (!label) continue;\r\n chapters.push({ id: at > 0 ? lines[0]!.trim() : `c${chapters.length + 1}`, label, start: time(from!), end: time(to!) });\r\n }\r\n return chapters.sort((a, b) => a.start - b.start);\r\n}\r\n\r\n/** Accept `[{ label, start, end? }]` (seconds) or `[{ label, timeUs, durationUs? }]` and normalize. */\r\nexport function normalizeChapters(input: unknown, durationS: number): PlayerChapter[] {\r\n if (!Array.isArray(input)) return [];\r\n const raw = input\r\n .map((c, i) => {\r\n const o = c as Record<string, unknown>;\r\n const label = String(o[\"label\"] ?? o[\"title\"] ?? \"\");\r\n const start = typeof o[\"start\"] === \"number\" ? o[\"start\"] : typeof o[\"timeUs\"] === \"number\" ? o[\"timeUs\"] / 1e6 : NaN;\r\n const end =\r\n typeof o[\"end\"] === \"number\"\r\n ? o[\"end\"]\r\n : typeof o[\"durationUs\"] === \"number\" && typeof o[\"timeUs\"] === \"number\"\r\n ? (o[\"timeUs\"] + o[\"durationUs\"]) / 1e6\r\n : undefined;\r\n return { id: String(o[\"id\"] ?? `c${i + 1}`), label, start, end };\r\n })\r\n .filter((c) => c.label && Number.isFinite(c.start))\r\n .sort((a, b) => a.start - b.start);\r\n return raw.map((c, i) => ({ id: c.id, label: c.label, start: c.start, end: c.end ?? raw[i + 1]?.start ?? Math.max(durationS, c.start) }));\r\n}\r\n\r\n/**\r\n * The chapter playing at `timeS` (the latest one that has started and not\r\n * ended). Media elements land a seek on a frame boundary, often a few ms\r\n * before the requested time, so a chapter counts as started `toleranceS`\r\n * early (default 50 ms).\r\n */\r\nexport function chapterAt(chapters: readonly PlayerChapter[], timeS: number, toleranceS = 0.05): PlayerChapter | undefined {\r\n let found: PlayerChapter | undefined;\r\n for (const c of chapters) if (c.start <= timeS + toleranceS && timeS < c.end) found = c;\r\n return found;\r\n}\r\n\r\n/** \"1:05\" / \"1:02:03\". */\r\nexport function formatTime(seconds: number): string {\r\n const s = Math.max(0, Math.floor(seconds + 1e-6));\r\n const h = Math.floor(s / 3600);\r\n const m = Math.floor((s % 3600) / 60);\r\n const ss = String(s % 60).padStart(2, \"0\");\r\n return h > 0 ? `${h}:${String(m).padStart(2, \"0\")}:${ss}` : `${m}:${ss}`;\r\n}\r\n","/**\r\n * Playback engines behind the element: a native <video> for rendered files,\r\n * and the Miraiclip renderer for live documents (loaded on demand, so a\r\n * video-only page never downloads the renderer).\r\n */\r\nexport interface EngineState {\r\n playing: boolean;\r\n timeS: number;\r\n durationS: number;\r\n}\r\n\r\nexport interface PlaybackEngine {\r\n readonly element: HTMLElement;\r\n play(): void;\r\n pause(): void;\r\n seek(seconds: number): void;\r\n state(): EngineState;\r\n setMuted(muted: boolean): void;\r\n /** Called on any time/state change. */\r\n onChange(listener: () => void): void;\r\n onEnded(listener: () => void): void;\r\n onError(listener: (message: string) => void): void;\r\n resize(width: number, height: number): void;\r\n destroy(): void;\r\n}\r\n\r\nexport function createVideoEngine(src: string, options: { loop: boolean; muted: boolean; poster?: string }): PlaybackEngine {\r\n const video = document.createElement(\"video\");\r\n video.src = src;\r\n video.preload = \"metadata\";\r\n video.playsInline = true;\r\n video.loop = options.loop;\r\n video.muted = options.muted;\r\n if (options.poster) video.poster = options.poster;\r\n video.setAttribute(\"part\", \"media\");\r\n const change: (() => void)[] = [];\r\n const ended: (() => void)[] = [];\r\n const errors: ((m: string) => void)[] = [];\r\n const emit = () => change.forEach((l) => l());\r\n for (const type of [\"timeupdate\", \"play\", \"pause\", \"durationchange\", \"loadedmetadata\", \"seeked\"]) video.addEventListener(type, emit);\r\n video.addEventListener(\"ended\", () => ended.forEach((l) => l()));\r\n video.addEventListener(\"error\", () => errors.forEach((l) => l(video.error?.message || \"the video could not be played\")));\r\n return {\r\n element: video,\r\n play: () => void video.play()?.catch?.((e: Error) => errors.forEach((l) => l(e.message))),\r\n pause: () => video.pause(),\r\n seek: (s) => {\r\n video.currentTime = Math.max(0, s);\r\n emit();\r\n },\r\n state: () => ({ playing: !video.paused && !video.ended, timeS: video.currentTime || 0, durationS: Number.isFinite(video.duration) ? video.duration : 0 }),\r\n setMuted: (m) => {\r\n video.muted = m;\r\n },\r\n onChange: (l) => change.push(l),\r\n onEnded: (l) => ended.push(l),\r\n onError: (l) => errors.push(l),\r\n resize: () => undefined,\r\n destroy: () => {\r\n video.pause();\r\n video.removeAttribute(\"src\");\r\n video.load();\r\n },\r\n };\r\n}\r\n\r\n/** Rewrite relative asset `src` paths so they resolve against `base` (the document's own URL by default). */\r\nexport function resolveAssetSources<T extends { assets: Record<string, { src: string }> }>(doc: T, base: string | undefined): T {\r\n if (!base) return doc;\r\n const copy = JSON.parse(JSON.stringify(doc)) as T;\r\n for (const asset of Object.values(copy.assets)) {\r\n if (/^(?:[a-z][a-z0-9+.-]*:|\\/\\/)/i.test(asset.src)) continue; // absolute, data:, blob:\r\n try {\r\n asset.src = new URL(asset.src, base).href;\r\n } catch {\r\n // leave as-is\r\n }\r\n }\r\n return copy;\r\n}\r\n\r\ninterface LiveModules {\r\n core: typeof import(\"@miraiclip/core\");\r\n renderer: typeof import(\"@miraiclip/renderer\");\r\n}\r\n\r\nlet modules: Promise<LiveModules> | undefined;\r\n\r\n/** Load core + renderer once (they are optional peers: only documents need them). */\r\nexport function loadLiveModules(): Promise<LiveModules> {\r\n modules ??= Promise.all([import(\"@miraiclip/core\"), import(\"@miraiclip/renderer\")]).then(([core, renderer]) => ({ core, renderer }));\r\n return modules;\r\n}\r\n\r\nexport async function createDocumentEngine(\r\n doc: import(\"@miraiclip/core\").ProjectDocument,\r\n options: { loop: boolean; muted: boolean; loadPlugins: boolean },\r\n): Promise<PlaybackEngine> {\r\n const { core, renderer } = await loadLiveModules();\r\n if (options.loadPlugins && doc.plugins?.length) await renderer.loadDocumentPlugins(doc);\r\n const project = core.createProject(doc);\r\n const controller = renderer.createPreviewController(project, { player: { loop: options.loop } });\r\n if (options.muted) controller.toggleMute();\r\n const canvas = document.createElement(\"canvas\");\r\n canvas.setAttribute(\"part\", \"media\");\r\n const change: (() => void)[] = [];\r\n const ended: (() => void)[] = [];\r\n const errors: ((m: string) => void)[] = [];\r\n let durationUs = 0;\r\n for (const clip of Object.values(doc.clips)) durationUs = Math.max(durationUs, clip.startUs + clip.durationUs);\r\n let wasPlaying = false;\r\n const emit = () => {\r\n const playing = controller.getState().playing;\r\n if (wasPlaying && !playing && project.getState().playheadUs >= durationUs - 1) ended.forEach((l) => l());\r\n wasPlaying = playing;\r\n change.forEach((l) => l());\r\n };\r\n controller.subscribe(() => {\r\n const error = controller.getState().lastError;\r\n if (error) errors.forEach((l) => l(error));\r\n emit();\r\n });\r\n const offPlayhead = project.events.on(\"playhead\", emit);\r\n let attached = false;\r\n return {\r\n element: canvas,\r\n play: () => controller.play(),\r\n pause: () => controller.pause(),\r\n seek: (s) => controller.seek(Math.round(Math.max(0, s) * 1e6)),\r\n state: () => ({ playing: controller.getState().playing, timeS: project.getState().playheadUs / 1e6, durationS: durationUs / 1e6 }),\r\n setMuted: (m) => {\r\n if (controller.getState().muted !== m) controller.toggleMute();\r\n },\r\n onChange: (l) => change.push(l),\r\n onEnded: (l) => ended.push(l),\r\n onError: (l) => errors.push(l),\r\n resize: (width, height) => {\r\n if (width <= 0 || height <= 0) return;\r\n if (!attached) {\r\n controller.attach(canvas, { width, height });\r\n attached = true;\r\n } else controller.resize(width, height);\r\n },\r\n destroy: () => {\r\n offPlayhead();\r\n controller.destroy();\r\n },\r\n };\r\n}\r\n","/**\r\n * <miraiclip-player>: plays a rendered video file or a live Miraiclip\r\n * document, with a scrubber, chapters (from the document's markers, a JSON\r\n * list or a WebVTT chapters file) and keyboard control. No framework.\r\n *\r\n * <miraiclip-player src=\"launch.mp4\" chapters=\"launch.chapters.vtt\" controls></miraiclip-player>\r\n * <miraiclip-player document=\"video.miraiclip.json\" controls></miraiclip-player>\r\n *\r\n * Events: ready, play, pause, timeupdate, chapterchange ({ chapter }), ended, error ({ message }).\r\n */\r\nimport { chapterAt, chaptersFromMarkers, formatTime, normalizeChapters, parseVttChapters, type PlayerChapter } from \"./chapters.js\";\r\nimport { createDocumentEngine, createVideoEngine, resolveAssetSources, type PlaybackEngine } from \"./engines.js\";\r\n\r\ntype Doc = import(\"@miraiclip/core\").ProjectDocument;\r\n\r\nconst STYLE = `\r\n:host{display:block;position:relative;--mc-accent:#22d3ee;--mc-bg:#000;--mc-fg:#fff;--mc-radius:12px;--mc-font:Inter,system-ui,sans-serif;color:var(--mc-fg);font-family:var(--mc-font)}\r\n:host([hidden]){display:none}\r\n.frame{position:relative;width:100%;aspect-ratio:var(--mc-aspect,16/9);background:var(--mc-bg);border-radius:var(--mc-radius);overflow:hidden;outline:none}\r\n.stage,.stage>*{position:absolute;inset:0;width:100%;height:100%;display:block;object-fit:contain}\r\n.big{position:absolute;inset:0;margin:auto;width:76px;height:76px;border-radius:50%;border:0;background:#000a;color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center}\r\n.big svg{width:34px;height:34px;margin-left:4px}\r\n:host([data-playing]) .big,:host(:not([data-ready])) .big{display:none}\r\n.bar{position:absolute;left:0;right:0;bottom:0;display:flex;align-items:center;gap:10px;padding:28px 12px 10px;background:linear-gradient(transparent,#000c);opacity:0;transition:opacity .2s}\r\n:host([controls]) .frame:hover .bar,:host([controls]) .frame:focus-within .bar,:host([controls]:not([data-playing])) .bar{opacity:1}\r\n:host(:not([controls])) .bar{display:none}\r\nbutton.icon{background:none;border:0;color:inherit;width:34px;height:34px;padding:5px;cursor:pointer;border-radius:8px;flex:none}\r\nbutton.icon:hover,button.icon:focus-visible{background:#ffffff22;outline:none}\r\nbutton svg{width:100%;height:100%;fill:currentColor}\r\n.time{font-size:13px;font-variant-numeric:tabular-nums;white-space:nowrap;opacity:.9}\r\n.scrub{position:relative;flex:1;height:22px;cursor:pointer;outline:none;touch-action:none}\r\n.rail{position:absolute;left:0;right:0;top:9px;height:4px;border-radius:2px;background:#ffffff40}\r\n.fill{position:absolute;left:0;top:9px;height:4px;border-radius:2px;background:var(--mc-accent);width:0}\r\n.tick{position:absolute;top:7px;width:2px;height:8px;background:#fff;opacity:.85;border-radius:1px}\r\n.thumb{position:absolute;top:4px;width:14px;height:14px;margin-left:-7px;border-radius:50%;background:#fff;box-shadow:0 1px 4px #0008;left:0}\r\n.scrub:focus-visible .thumb{outline:2px solid var(--mc-accent);outline-offset:2px}\r\n.label{font-size:13px;max-width:30%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;opacity:.9}\r\n.menu{position:absolute;right:10px;bottom:56px;max-height:60%;overflow:auto;margin:0;padding:6px;list-style:none;border-radius:10px;background:#111827f2;box-shadow:0 10px 30px #0009;min-width:200px}\r\n.menu[hidden]{display:none}\r\n.menu button{display:flex;gap:10px;width:100%;padding:8px 10px;border:0;border-radius:6px;background:none;color:inherit;font:inherit;font-size:14px;text-align:left;cursor:pointer}\r\n.menu button:hover,.menu button:focus-visible{background:#ffffff1f;outline:none}\r\n.menu button[aria-current=\"true\"]{color:var(--mc-accent)}\r\n.menu .t{opacity:.7;font-variant-numeric:tabular-nums}\r\n.error{position:absolute;left:12px;right:12px;top:12px;padding:8px 12px;border-radius:8px;background:#7f1d1dee;font-size:13px}\r\n.error[hidden]{display:none}\r\n`;\r\n\r\nconst ICON = {\r\n play: '<svg viewBox=\"0 0 24 24\"><path d=\"M8 5v14l11-7z\"/></svg>',\r\n pause: '<svg viewBox=\"0 0 24 24\"><path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/></svg>',\r\n chapters: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 6h2v2H4zm4 0h12v2H8zM4 11h2v2H4zm4 0h12v2H8zm-4 5h2v2H4zm4 0h12v2H8z\"/></svg>',\r\n sound: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 9v6h4l5 4V5L8 9zm12.5 3a4.5 4.5 0 0 0-2.5-4v8a4.5 4.5 0 0 0 2.5-4z\"/></svg>',\r\n muted: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 9v6h4l5 4V5L8 9zm16.6 0L19 7.4 16.4 10 13.8 7.4 12.4 9l2.6 2.6-2.6 2.6 1.4 1.4 2.6-2.6 2.6 2.6 1.6-1.6-2.6-2.6z\"/></svg>',\r\n};\r\n\r\nconst BaseElement: typeof HTMLElement = typeof HTMLElement === \"undefined\" ? (class {} as unknown as typeof HTMLElement) : HTMLElement;\r\n\r\nexport class MiraiclipPlayerElement extends BaseElement {\r\n static get observedAttributes(): string[] {\r\n return [\"src\", \"document\", \"chapters\", \"poster\", \"loop\", \"muted\", \"assets-base\"];\r\n }\r\n\r\n private readonly root: ShadowRoot;\r\n private engine: PlaybackEngine | undefined;\r\n private loadToken = 0;\r\n private docValue: Doc | undefined;\r\n private chapterList: PlayerChapter[] = [];\r\n private explicitChapters: PlayerChapter[] | undefined;\r\n private currentChapter: PlayerChapter | undefined;\r\n private resizeObserver: ResizeObserver | undefined;\r\n private dragging = false;\r\n private readonly els: Record<\"frame\" | \"stage\" | \"big\" | \"play\" | \"time\" | \"scrub\" | \"fill\" | \"thumb\" | \"ticks\" | \"label\" | \"chaptersBtn\" | \"menu\" | \"mute\" | \"error\", HTMLElement>;\r\n\r\n constructor() {\r\n super();\r\n this.root = this.attachShadow({ mode: \"open\" });\r\n this.root.innerHTML = `<style>${STYLE}</style>\r\n<div class=\"frame\" part=\"frame\" tabindex=\"0\">\r\n <div class=\"stage\" part=\"stage\"></div>\r\n <button class=\"big\" part=\"big-play\" aria-label=\"Play\">${ICON.play}</button>\r\n <div class=\"bar\" part=\"controls\">\r\n <button class=\"icon play\" aria-label=\"Play\">${ICON.play}</button>\r\n <span class=\"time\" part=\"time\">0:00 / 0:00</span>\r\n <div class=\"scrub\" part=\"scrubber\" role=\"slider\" tabindex=\"0\" aria-label=\"Seek\" aria-valuemin=\"0\" aria-valuemax=\"0\" aria-valuenow=\"0\">\r\n <div class=\"rail\"></div><div class=\"fill\"></div><div class=\"ticks\"></div><div class=\"thumb\"></div>\r\n </div>\r\n <span class=\"label\" part=\"chapter-label\"></span>\r\n <button class=\"icon chapters\" aria-label=\"Chapters\" aria-haspopup=\"true\" aria-expanded=\"false\" hidden>${ICON.chapters}</button>\r\n <button class=\"icon mute\" aria-label=\"Mute\">${ICON.sound}</button>\r\n </div>\r\n <ol class=\"menu\" part=\"chapters\" hidden></ol>\r\n <div class=\"error\" part=\"error\" role=\"alert\" hidden></div>\r\n</div>`;\r\n const $ = (sel: string) => this.root.querySelector(sel) as HTMLElement;\r\n this.els = {\r\n frame: $(\".frame\"),\r\n stage: $(\".stage\"),\r\n big: $(\".big\"),\r\n play: $(\".play\"),\r\n time: $(\".time\"),\r\n scrub: $(\".scrub\"),\r\n fill: $(\".fill\"),\r\n thumb: $(\".thumb\"),\r\n ticks: $(\".ticks\"),\r\n label: $(\".label\"),\r\n chaptersBtn: $(\".chapters\"),\r\n menu: $(\".menu\"),\r\n mute: $(\".mute\"),\r\n error: $(\".error\"),\r\n };\r\n this.wire();\r\n }\r\n\r\n // ------------------------------------------------------------------ API\r\n\r\n /** A project document to play live (takes precedence over `document`/`src` attributes). */\r\n get document(): Doc | undefined {\r\n return this.docValue;\r\n }\r\n set document(doc: Doc | undefined) {\r\n this.docValue = doc;\r\n void this.load();\r\n }\r\n\r\n /** Chapters in seconds. Set to override markers / the `chapters` attribute. */\r\n get chapters(): PlayerChapter[] {\r\n return [...this.chapterList];\r\n }\r\n set chapters(value: PlayerChapter[] | undefined) {\r\n this.explicitChapters = value ? normalizeChapters(value, this.duration) : undefined;\r\n this.refreshChapters();\r\n }\r\n\r\n get currentTime(): number {\r\n return this.engine?.state().timeS ?? 0;\r\n }\r\n set currentTime(seconds: number) {\r\n this.seek(seconds);\r\n }\r\n get duration(): number {\r\n return this.engine?.state().durationS ?? 0;\r\n }\r\n get paused(): boolean {\r\n return !(this.engine?.state().playing ?? false);\r\n }\r\n get currentChapterInfo(): PlayerChapter | undefined {\r\n return this.currentChapter;\r\n }\r\n\r\n play(): void {\r\n this.engine?.play();\r\n }\r\n pause(): void {\r\n this.engine?.pause();\r\n }\r\n toggle(): void {\r\n if (this.paused) this.play();\r\n else this.pause();\r\n }\r\n seek(seconds: number): void {\r\n this.engine?.seek(Math.min(Math.max(0, seconds), this.duration || seconds));\r\n this.render();\r\n }\r\n /** Jump to a chapter by id or index. */\r\n goToChapter(which: string | number): void {\r\n const chapter = typeof which === \"number\" ? this.chapterList[which] : this.chapterList.find((c) => c.id === which);\r\n if (chapter) this.seek(chapter.start);\r\n }\r\n\r\n // ------------------------------------------------------------------ lifecycle\r\n\r\n connectedCallback(): void {\r\n if (typeof ResizeObserver !== \"undefined\") {\r\n this.resizeObserver = new ResizeObserver(() => this.sizeEngine());\r\n this.resizeObserver.observe(this.els.frame);\r\n }\r\n if (!this.engine) void this.load();\r\n }\r\n\r\n disconnectedCallback(): void {\r\n this.resizeObserver?.disconnect();\r\n this.resizeObserver = undefined;\r\n this.engine?.destroy();\r\n this.engine = undefined;\r\n }\r\n\r\n attributeChangedCallback(name: string, previous: string | null, next: string | null): void {\r\n if (previous === next || !this.isConnected) return;\r\n if (name === \"src\" || name === \"document\" || name === \"assets-base\") void this.load();\r\n else if (name === \"chapters\") void this.loadChapterAttribute();\r\n else if (name === \"muted\") this.engine?.setMuted(next !== null);\r\n }\r\n\r\n // ------------------------------------------------------------------ loading\r\n\r\n private async load(): Promise<void> {\r\n const token = ++this.loadToken;\r\n this.engine?.destroy();\r\n this.engine = undefined;\r\n this.removeAttribute(\"data-ready\");\r\n this.showError(null);\r\n this.els.stage.replaceChildren();\r\n const loop = this.hasAttribute(\"loop\");\r\n const muted = this.hasAttribute(\"muted\");\r\n try {\r\n let engine: PlaybackEngine | undefined;\r\n let doc = this.docValue;\r\n const docUrl = this.getAttribute(\"document\");\r\n if (!doc && docUrl) {\r\n const response = await fetch(docUrl);\r\n if (!response.ok) throw new Error(`could not load ${docUrl} (HTTP ${response.status})`);\r\n doc = (await response.json()) as Doc;\r\n if (token !== this.loadToken) return;\r\n }\r\n if (doc) {\r\n const base = this.getAttribute(\"assets-base\") ?? (docUrl ? new URL(docUrl, location.href).href : undefined);\r\n const resolved = resolveAssetSources(doc, base);\r\n this.style.setProperty(\"--mc-aspect\", `${resolved.settings.width}/${resolved.settings.height}`);\r\n engine = await createDocumentEngine(resolved, { loop, muted, loadPlugins: this.hasAttribute(\"plugins\") });\r\n this.docChapters = (durationS) => chaptersFromMarkers(resolved.markers, durationS);\r\n } else {\r\n const src = this.getAttribute(\"src\");\r\n if (!src) return;\r\n const poster = this.getAttribute(\"poster\");\r\n engine = createVideoEngine(src, { loop, muted, ...(poster ? { poster } : {}) });\r\n this.docChapters = undefined;\r\n const video = engine.element as HTMLVideoElement;\r\n video.addEventListener(\"loadedmetadata\", () => {\r\n if (video.videoWidth && video.videoHeight) this.style.setProperty(\"--mc-aspect\", `${video.videoWidth}/${video.videoHeight}`);\r\n this.refreshChapters();\r\n });\r\n }\r\n if (token !== this.loadToken) {\r\n engine.destroy();\r\n return;\r\n }\r\n this.engine = engine;\r\n this.els.stage.append(engine.element);\r\n let wasPlaying = false;\r\n engine.onChange(() => {\r\n const playing = engine.state().playing;\r\n if (playing !== wasPlaying) {\r\n wasPlaying = playing;\r\n this.toggleAttribute(\"data-playing\", playing);\r\n this.dispatch(playing ? \"play\" : \"pause\");\r\n }\r\n this.render();\r\n this.dispatch(\"timeupdate\");\r\n });\r\n engine.onEnded(() => this.dispatch(\"ended\"));\r\n engine.onError((message) => {\r\n this.showError(message);\r\n this.dispatch(\"error\", { message });\r\n });\r\n this.sizeEngine();\r\n await this.loadChapterAttribute();\r\n this.refreshChapters();\r\n this.setAttribute(\"data-ready\", \"\");\r\n this.render();\r\n this.dispatch(\"ready\");\r\n if (this.hasAttribute(\"autoplay\")) engine.play();\r\n } catch (error) {\r\n if (token !== this.loadToken) return;\r\n const message = error instanceof Error ? error.message : String(error);\r\n this.showError(message);\r\n this.dispatch(\"error\", { message });\r\n }\r\n }\r\n\r\n private docChapters: ((durationS: number) => PlayerChapter[]) | undefined;\r\n private attributeChapters: PlayerChapter[] | undefined;\r\n\r\n private async loadChapterAttribute(): Promise<void> {\r\n const url = this.getAttribute(\"chapters\");\r\n if (!url) {\r\n this.attributeChapters = undefined;\r\n return;\r\n }\r\n try {\r\n const response = await fetch(url);\r\n const text = await response.text();\r\n this.attributeChapters = text.trimStart().startsWith(\"WEBVTT\") ? parseVttChapters(text) : normalizeChapters(JSON.parse(text), this.duration);\r\n } catch {\r\n this.attributeChapters = undefined;\r\n }\r\n this.refreshChapters();\r\n }\r\n\r\n private refreshChapters(): void {\r\n const duration = this.duration;\r\n this.chapterList = this.explicitChapters ?? this.attributeChapters ?? this.docChapters?.(duration) ?? [];\r\n const { ticks, menu, chaptersBtn } = this.els;\r\n ticks.replaceChildren();\r\n menu.replaceChildren();\r\n chaptersBtn.hidden = this.chapterList.length === 0;\r\n for (const [i, chapter] of this.chapterList.entries()) {\r\n if (duration > 0 && chapter.start > 0) {\r\n const tick = document.createElement(\"div\");\r\n tick.className = \"tick\";\r\n tick.style.left = `${(chapter.start / duration) * 100}%`;\r\n tick.title = chapter.label;\r\n ticks.append(tick);\r\n }\r\n const li = document.createElement(\"li\");\r\n const button = document.createElement(\"button\");\r\n button.type = \"button\";\r\n button.dataset[\"index\"] = String(i);\r\n button.innerHTML = `<span class=\"t\"></span><span class=\"n\"></span>`;\r\n (button.querySelector(\".t\") as HTMLElement).textContent = formatTime(chapter.start);\r\n (button.querySelector(\".n\") as HTMLElement).textContent = chapter.label;\r\n li.append(button);\r\n menu.append(li);\r\n }\r\n this.currentChapter = undefined;\r\n this.render();\r\n }\r\n\r\n // ------------------------------------------------------------------ UI\r\n\r\n private wire(): void {\r\n const { big, play, scrub, chaptersBtn, menu, mute, frame } = this.els;\r\n big.addEventListener(\"click\", () => this.play());\r\n play.addEventListener(\"click\", () => this.toggle());\r\n this.els.stage.addEventListener(\"click\", () => this.toggle());\r\n mute.addEventListener(\"click\", () => {\r\n const muted = !this.hasAttribute(\"muted\");\r\n this.toggleAttribute(\"muted\", muted);\r\n this.engine?.setMuted(muted);\r\n mute.innerHTML = muted ? ICON.muted : ICON.sound;\r\n mute.setAttribute(\"aria-label\", muted ? \"Unmute\" : \"Mute\");\r\n });\r\n chaptersBtn.addEventListener(\"click\", () => {\r\n const open = menu.hidden;\r\n menu.hidden = !open;\r\n chaptersBtn.setAttribute(\"aria-expanded\", String(open));\r\n if (open) (menu.querySelector<HTMLButtonElement>('button[aria-current=\"true\"]') ?? menu.querySelector<HTMLButtonElement>(\"button\"))?.focus();\r\n });\r\n menu.addEventListener(\"click\", (event) => {\r\n const button = (event.target as HTMLElement).closest(\"button\");\r\n if (!button) return;\r\n this.goToChapter(Number(button.dataset[\"index\"]));\r\n menu.hidden = true;\r\n chaptersBtn.setAttribute(\"aria-expanded\", \"false\");\r\n });\r\n const seekFromPointer = (event: PointerEvent): void => {\r\n const rect = scrub.getBoundingClientRect();\r\n if (rect.width <= 0) return;\r\n this.seek(((event.clientX - rect.left) / rect.width) * this.duration);\r\n };\r\n scrub.addEventListener(\"pointerdown\", (event) => {\r\n this.dragging = true;\r\n scrub.setPointerCapture?.(event.pointerId);\r\n seekFromPointer(event);\r\n });\r\n scrub.addEventListener(\"pointermove\", (event) => {\r\n if (this.dragging) seekFromPointer(event);\r\n });\r\n scrub.addEventListener(\"pointerup\", () => {\r\n this.dragging = false;\r\n });\r\n const keys = (event: KeyboardEvent): void => {\r\n const step = event.shiftKey ? 10 : 5;\r\n if (event.key === \" \" || event.key === \"k\") this.toggle();\r\n else if (event.key === \"ArrowRight\" || event.key === \"l\") this.seek(this.currentTime + step);\r\n else if (event.key === \"ArrowLeft\" || event.key === \"j\") this.seek(this.currentTime - step);\r\n else if (event.key === \"Home\") this.seek(0);\r\n else if (event.key === \"End\") this.seek(this.duration);\r\n else if (event.key === \"]\" ) this.stepChapter(1);\r\n else if (event.key === \"[\") this.stepChapter(-1);\r\n else if (event.key === \"Escape\") menu.hidden = true;\r\n else return;\r\n event.preventDefault();\r\n };\r\n frame.addEventListener(\"keydown\", keys);\r\n }\r\n\r\n private stepChapter(direction: 1 | -1): void {\r\n if (this.chapterList.length === 0) return;\r\n const t = this.currentTime;\r\n if (direction > 0) {\r\n const next = this.chapterList.find((c) => c.start > t + 0.25);\r\n if (next) this.seek(next.start);\r\n } else {\r\n const previous = [...this.chapterList].reverse().find((c) => c.start < t - 1);\r\n this.seek(previous?.start ?? 0);\r\n }\r\n }\r\n\r\n private render(): void {\r\n const state = this.engine?.state() ?? { playing: false, timeS: 0, durationS: 0 };\r\n const { time, fill, thumb, scrub, play, label, menu } = this.els;\r\n const fraction = state.durationS > 0 ? Math.min(1, state.timeS / state.durationS) : 0;\r\n time.textContent = `${formatTime(state.timeS)} / ${formatTime(state.durationS)}`;\r\n fill.style.width = `${fraction * 100}%`;\r\n thumb.style.left = `${fraction * 100}%`;\r\n scrub.setAttribute(\"aria-valuemax\", String(Math.round(state.durationS)));\r\n scrub.setAttribute(\"aria-valuenow\", String(Math.round(state.timeS)));\r\n scrub.setAttribute(\"aria-valuetext\", time.textContent);\r\n const wanted = state.playing ? \"Pause\" : \"Play\";\r\n if (play.getAttribute(\"aria-label\") !== wanted) {\r\n play.innerHTML = state.playing ? ICON.pause : ICON.play;\r\n play.setAttribute(\"aria-label\", wanted);\r\n }\r\n const chapter = chapterAt(this.chapterList, state.timeS);\r\n label.textContent = chapter?.label ?? \"\";\r\n if (chapter?.id !== this.currentChapter?.id) {\r\n this.currentChapter = chapter;\r\n for (const button of Array.from(menu.querySelectorAll(\"button\"))) {\r\n button.setAttribute(\"aria-current\", String(this.chapterList[Number(button.dataset[\"index\"])]?.id === chapter?.id));\r\n }\r\n this.dispatch(\"chapterchange\", { chapter: chapter ?? null });\r\n }\r\n }\r\n\r\n private sizeEngine(): void {\r\n const rect = this.els.frame.getBoundingClientRect();\r\n this.engine?.resize(rect.width, rect.height);\r\n }\r\n\r\n private showError(message: string | null): void {\r\n this.els.error.hidden = message === null;\r\n this.els.error.textContent = message ?? \"\";\r\n }\r\n\r\n private dispatch(type: string, detail?: unknown): void {\r\n this.dispatchEvent(new CustomEvent(type, { detail, bubbles: type !== \"timeupdate\", composed: true }));\r\n }\r\n}\r\n\r\n/** Register the element (default tag `miraiclip-player`). Safe to call twice. */\r\nexport function defineMiraiclipPlayer(tagName = \"miraiclip-player\"): typeof MiraiclipPlayerElement {\r\n if (typeof customElements === \"undefined\") return MiraiclipPlayerElement;\r\n const existing = customElements.get(tagName);\r\n if (existing) return existing as typeof MiraiclipPlayerElement;\r\n customElements.define(tagName, MiraiclipPlayerElement);\r\n return MiraiclipPlayerElement;\r\n}\r\n\r\ndeclare global {\r\n interface HTMLElementTagNameMap {\r\n \"miraiclip-player\": MiraiclipPlayerElement;\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/chapters.ts","../src/engines.ts","../src/element.ts","../src/define.ts"],"sourcesContent":["/**\n * Chapters for the player: from a document's markers, from JSON, or from a\n * WebVTT chapters track. Pure functions — no DOM.\n */\nexport interface PlayerChapter {\n id: string;\n label: string;\n /** Seconds. */\n start: number;\n end: number;\n}\n\ninterface MarkerLike {\n id: string;\n timeUs: number;\n label: string;\n durationUs?: number;\n}\n\n/**\n * A document's markers as chapters (same rule as core's `markerChapters`):\n * each runs for its own `durationUs`, or until the next marker; the last one\n * until `durationS`.\n */\nexport function chaptersFromMarkers(markers: Record<string, MarkerLike> | undefined, durationS: number): PlayerChapter[] {\n const list = Object.values(markers ?? {}).sort((a, b) => a.timeUs - b.timeUs || a.id.localeCompare(b.id));\n return list.map((m, i) => {\n const start = m.timeUs / 1e6;\n const next = list[i + 1];\n const end = m.durationUs !== undefined ? (m.timeUs + m.durationUs) / 1e6 : next ? next.timeUs / 1e6 : Math.max(durationS, start);\n return { id: m.id, label: m.label, start, end };\n });\n}\n\n/** Parse a WebVTT chapters track (\"00:01.000 --> 00:05.000\" cues with a title line). */\nexport function parseVttChapters(vtt: string): PlayerChapter[] {\n const chapters: PlayerChapter[] = [];\n const blocks = vtt.replace(/\\r/g, \"\").split(/\\n{2,}/);\n const time = (t: string): number => {\n const parts = t.trim().split(\":\").map(Number);\n let s = 0;\n for (const p of parts) s = s * 60 + p;\n return s;\n };\n for (const block of blocks) {\n const lines = block.split(\"\\n\").filter((l) => l.trim() !== \"\");\n const at = lines.findIndex((l) => l.includes(\"-->\"));\n if (at < 0) continue;\n const [from, to] = lines[at]!.split(\"-->\").map((s) => s.trim().split(/\\s+/)[0]!);\n const label = lines.slice(at + 1).join(\" \").trim();\n if (!label) continue;\n chapters.push({ id: at > 0 ? lines[0]!.trim() : `c${chapters.length + 1}`, label, start: time(from!), end: time(to!) });\n }\n return chapters.sort((a, b) => a.start - b.start);\n}\n\n/** Accept `[{ label, start, end? }]` (seconds) or `[{ label, timeUs, durationUs? }]` and normalize. */\nexport function normalizeChapters(input: unknown, durationS: number): PlayerChapter[] {\n if (!Array.isArray(input)) return [];\n const raw = input\n .map((c, i) => {\n const o = c as Record<string, unknown>;\n const label = String(o[\"label\"] ?? o[\"title\"] ?? \"\");\n const start = typeof o[\"start\"] === \"number\" ? o[\"start\"] : typeof o[\"timeUs\"] === \"number\" ? o[\"timeUs\"] / 1e6 : NaN;\n const end =\n typeof o[\"end\"] === \"number\"\n ? o[\"end\"]\n : typeof o[\"durationUs\"] === \"number\" && typeof o[\"timeUs\"] === \"number\"\n ? (o[\"timeUs\"] + o[\"durationUs\"]) / 1e6\n : undefined;\n return { id: String(o[\"id\"] ?? `c${i + 1}`), label, start, end };\n })\n .filter((c) => c.label && Number.isFinite(c.start))\n .sort((a, b) => a.start - b.start);\n return raw.map((c, i) => ({ id: c.id, label: c.label, start: c.start, end: c.end ?? raw[i + 1]?.start ?? Math.max(durationS, c.start) }));\n}\n\n/**\n * The chapter playing at `timeS` (the latest one that has started and not\n * ended). Media elements land a seek on a frame boundary, often a few ms\n * before the requested time, so a chapter counts as started `toleranceS`\n * early (default 50 ms).\n */\nexport function chapterAt(chapters: readonly PlayerChapter[], timeS: number, toleranceS = 0.05): PlayerChapter | undefined {\n let found: PlayerChapter | undefined;\n for (const c of chapters) if (c.start <= timeS + toleranceS && timeS < c.end) found = c;\n return found;\n}\n\n/** \"1:05\" / \"1:02:03\". */\nexport function formatTime(seconds: number): string {\n const s = Math.max(0, Math.floor(seconds + 1e-6));\n const h = Math.floor(s / 3600);\n const m = Math.floor((s % 3600) / 60);\n const ss = String(s % 60).padStart(2, \"0\");\n return h > 0 ? `${h}:${String(m).padStart(2, \"0\")}:${ss}` : `${m}:${ss}`;\n}\n","/**\n * Playback engines behind the element: a native <video> for rendered files,\n * and the Miraiclip renderer for live documents (loaded on demand, so a\n * video-only page never downloads the renderer).\n */\nexport interface EngineState {\n playing: boolean;\n timeS: number;\n durationS: number;\n}\n\nexport interface PlaybackEngine {\n readonly element: HTMLElement;\n play(): void;\n pause(): void;\n seek(seconds: number): void;\n state(): EngineState;\n setMuted(muted: boolean): void;\n /** Called on any time/state change. */\n onChange(listener: () => void): void;\n onEnded(listener: () => void): void;\n onError(listener: (message: string) => void): void;\n resize(width: number, height: number): void;\n destroy(): void;\n}\n\nexport function createVideoEngine(src: string, options: { loop: boolean; muted: boolean; poster?: string }): PlaybackEngine {\n const video = document.createElement(\"video\");\n video.src = src;\n video.preload = \"metadata\";\n video.playsInline = true;\n video.loop = options.loop;\n video.muted = options.muted;\n if (options.poster) video.poster = options.poster;\n video.setAttribute(\"part\", \"media\");\n const change: (() => void)[] = [];\n const ended: (() => void)[] = [];\n const errors: ((m: string) => void)[] = [];\n const emit = () => change.forEach((l) => l());\n for (const type of [\"timeupdate\", \"play\", \"pause\", \"durationchange\", \"loadedmetadata\", \"seeked\"]) video.addEventListener(type, emit);\n video.addEventListener(\"ended\", () => ended.forEach((l) => l()));\n video.addEventListener(\"error\", () => errors.forEach((l) => l(video.error?.message || \"the video could not be played\")));\n return {\n element: video,\n play: () => void video.play()?.catch?.((e: Error) => errors.forEach((l) => l(e.message))),\n pause: () => video.pause(),\n seek: (s) => {\n video.currentTime = Math.max(0, s);\n emit();\n },\n state: () => ({ playing: !video.paused && !video.ended, timeS: video.currentTime || 0, durationS: Number.isFinite(video.duration) ? video.duration : 0 }),\n setMuted: (m) => {\n video.muted = m;\n },\n onChange: (l) => change.push(l),\n onEnded: (l) => ended.push(l),\n onError: (l) => errors.push(l),\n resize: () => undefined,\n destroy: () => {\n video.pause();\n video.removeAttribute(\"src\");\n video.load();\n },\n };\n}\n\n/** Rewrite relative asset `src` paths so they resolve against `base` (the document's own URL by default). */\nexport function resolveAssetSources<T extends { assets: Record<string, { src: string }> }>(doc: T, base: string | undefined): T {\n if (!base) return doc;\n const copy = JSON.parse(JSON.stringify(doc)) as T;\n for (const asset of Object.values(copy.assets)) {\n if (/^(?:[a-z][a-z0-9+.-]*:|\\/\\/)/i.test(asset.src)) continue; // absolute, data:, blob:\n try {\n asset.src = new URL(asset.src, base).href;\n } catch {\n // leave as-is\n }\n }\n return copy;\n}\n\ninterface LiveModules {\n core: typeof import(\"@miraiclip/core\");\n renderer: typeof import(\"@miraiclip/renderer\");\n}\n\nlet modules: Promise<LiveModules> | undefined;\n\n/** Load core + renderer once (they are optional peers: only documents need them). */\nexport function loadLiveModules(): Promise<LiveModules> {\n modules ??= Promise.all([import(\"@miraiclip/core\"), import(\"@miraiclip/renderer\")]).then(([core, renderer]) => ({ core, renderer }));\n return modules;\n}\n\nexport async function createDocumentEngine(\n doc: import(\"@miraiclip/core\").ProjectDocument,\n options: { loop: boolean; muted: boolean; loadPlugins: boolean },\n): Promise<PlaybackEngine> {\n const { core, renderer } = await loadLiveModules();\n if (options.loadPlugins && doc.plugins?.length) await renderer.loadDocumentPlugins(doc);\n const project = core.createProject(doc);\n const controller = renderer.createPreviewController(project, { player: { loop: options.loop } });\n if (options.muted) controller.toggleMute();\n const canvas = document.createElement(\"canvas\");\n canvas.setAttribute(\"part\", \"media\");\n const change: (() => void)[] = [];\n const ended: (() => void)[] = [];\n const errors: ((m: string) => void)[] = [];\n let durationUs = 0;\n for (const clip of Object.values(doc.clips)) durationUs = Math.max(durationUs, clip.startUs + clip.durationUs);\n let wasPlaying = false;\n const emit = () => {\n const playing = controller.getState().playing;\n if (wasPlaying && !playing && project.getState().playheadUs >= durationUs - 1) ended.forEach((l) => l());\n wasPlaying = playing;\n change.forEach((l) => l());\n };\n controller.subscribe(() => {\n const error = controller.getState().lastError;\n if (error) errors.forEach((l) => l(error));\n emit();\n });\n const offPlayhead = project.events.on(\"playhead\", emit);\n let attached = false;\n return {\n element: canvas,\n play: () => controller.play(),\n pause: () => controller.pause(),\n seek: (s) => controller.seek(Math.round(Math.max(0, s) * 1e6)),\n state: () => ({ playing: controller.getState().playing, timeS: project.getState().playheadUs / 1e6, durationS: durationUs / 1e6 }),\n setMuted: (m) => {\n if (controller.getState().muted !== m) controller.toggleMute();\n },\n onChange: (l) => change.push(l),\n onEnded: (l) => ended.push(l),\n onError: (l) => errors.push(l),\n resize: (width, height) => {\n if (width <= 0 || height <= 0) return;\n if (!attached) {\n controller.attach(canvas, { width, height });\n attached = true;\n } else controller.resize(width, height);\n },\n destroy: () => {\n offPlayhead();\n controller.destroy();\n },\n };\n}\n","/**\n * <miraiclip-player>: plays a rendered video file or a live Miraiclip\n * document, with a scrubber, chapters (from the document's markers, a JSON\n * list or a WebVTT chapters file) and keyboard control. No framework.\n *\n * <miraiclip-player src=\"launch.mp4\" chapters=\"launch.chapters.vtt\" controls></miraiclip-player>\n * <miraiclip-player document=\"video.miraiclip.json\" controls></miraiclip-player>\n *\n * Events: ready, play, pause, timeupdate, chapterchange ({ chapter }), ended, error ({ message }).\n */\nimport { chapterAt, chaptersFromMarkers, formatTime, normalizeChapters, parseVttChapters, type PlayerChapter } from \"./chapters.js\";\nimport { createDocumentEngine, createVideoEngine, resolveAssetSources, type PlaybackEngine } from \"./engines.js\";\n\ntype Doc = import(\"@miraiclip/core\").ProjectDocument;\n\nconst STYLE = `\n:host{display:block;position:relative;--mc-accent:#22d3ee;--mc-bg:#000;--mc-fg:#fff;--mc-radius:12px;--mc-font:Inter,system-ui,sans-serif;color:var(--mc-fg);font-family:var(--mc-font)}\n:host([hidden]){display:none}\n.frame{position:relative;width:100%;aspect-ratio:var(--mc-aspect,16/9);background:var(--mc-bg);border-radius:var(--mc-radius);overflow:hidden;outline:none}\n.stage,.stage>*{position:absolute;inset:0;width:100%;height:100%;display:block;object-fit:contain}\n.big{position:absolute;inset:0;margin:auto;width:76px;height:76px;border-radius:50%;border:0;background:#000a;color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center}\n.big svg{width:34px;height:34px;margin-left:4px}\n:host([data-playing]) .big,:host(:not([data-ready])) .big{display:none}\n.bar{position:absolute;left:0;right:0;bottom:0;display:flex;align-items:center;gap:10px;padding:28px 12px 10px;background:linear-gradient(transparent,#000c);opacity:0;transition:opacity .2s}\n:host([controls]) .frame:hover .bar,:host([controls]) .frame:focus-within .bar,:host([controls]:not([data-playing])) .bar{opacity:1}\n:host(:not([controls])) .bar{display:none}\nbutton.icon{background:none;border:0;color:inherit;width:34px;height:34px;padding:5px;cursor:pointer;border-radius:8px;flex:none}\nbutton.icon:hover,button.icon:focus-visible{background:#ffffff22;outline:none}\nbutton svg{width:100%;height:100%;fill:currentColor}\n.time{font-size:13px;font-variant-numeric:tabular-nums;white-space:nowrap;opacity:.9}\n.scrub{position:relative;flex:1;height:22px;cursor:pointer;outline:none;touch-action:none}\n.rail{position:absolute;left:0;right:0;top:9px;height:4px;border-radius:2px;background:#ffffff40}\n.fill{position:absolute;left:0;top:9px;height:4px;border-radius:2px;background:var(--mc-accent);width:0}\n.tick{position:absolute;top:7px;width:2px;height:8px;background:#fff;opacity:.85;border-radius:1px}\n.thumb{position:absolute;top:4px;width:14px;height:14px;margin-left:-7px;border-radius:50%;background:#fff;box-shadow:0 1px 4px #0008;left:0}\n.scrub:focus-visible .thumb{outline:2px solid var(--mc-accent);outline-offset:2px}\n.label{font-size:13px;max-width:30%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;opacity:.9}\n.menu{position:absolute;right:10px;bottom:56px;max-height:60%;overflow:auto;margin:0;padding:6px;list-style:none;border-radius:10px;background:#111827f2;box-shadow:0 10px 30px #0009;min-width:200px}\n.menu[hidden]{display:none}\n.menu button{display:flex;gap:10px;width:100%;padding:8px 10px;border:0;border-radius:6px;background:none;color:inherit;font:inherit;font-size:14px;text-align:left;cursor:pointer}\n.menu button:hover,.menu button:focus-visible{background:#ffffff1f;outline:none}\n.menu button[aria-current=\"true\"]{color:var(--mc-accent)}\n.menu .t{opacity:.7;font-variant-numeric:tabular-nums}\n.error{position:absolute;left:12px;right:12px;top:12px;padding:8px 12px;border-radius:8px;background:#7f1d1dee;font-size:13px}\n.error[hidden]{display:none}\n`;\n\nconst ICON = {\n play: '<svg viewBox=\"0 0 24 24\"><path d=\"M8 5v14l11-7z\"/></svg>',\n pause: '<svg viewBox=\"0 0 24 24\"><path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/></svg>',\n chapters: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 6h2v2H4zm4 0h12v2H8zM4 11h2v2H4zm4 0h12v2H8zm-4 5h2v2H4zm4 0h12v2H8z\"/></svg>',\n sound: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 9v6h4l5 4V5L8 9zm12.5 3a4.5 4.5 0 0 0-2.5-4v8a4.5 4.5 0 0 0 2.5-4z\"/></svg>',\n muted: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 9v6h4l5 4V5L8 9zm16.6 0L19 7.4 16.4 10 13.8 7.4 12.4 9l2.6 2.6-2.6 2.6 1.4 1.4 2.6-2.6 2.6 2.6 1.6-1.6-2.6-2.6z\"/></svg>',\n};\n\nconst BaseElement: typeof HTMLElement = typeof HTMLElement === \"undefined\" ? (class {} as unknown as typeof HTMLElement) : HTMLElement;\n\nexport class MiraiclipPlayerElement extends BaseElement {\n static get observedAttributes(): string[] {\n return [\"src\", \"document\", \"chapters\", \"poster\", \"loop\", \"muted\", \"assets-base\"];\n }\n\n private readonly root: ShadowRoot;\n private engine: PlaybackEngine | undefined;\n private loadToken = 0;\n private docValue: Doc | undefined;\n private chapterList: PlayerChapter[] = [];\n private explicitChapters: PlayerChapter[] | undefined;\n private currentChapter: PlayerChapter | undefined;\n private resizeObserver: ResizeObserver | undefined;\n private dragging = false;\n private readonly els: Record<\"frame\" | \"stage\" | \"big\" | \"play\" | \"time\" | \"scrub\" | \"fill\" | \"thumb\" | \"ticks\" | \"label\" | \"chaptersBtn\" | \"menu\" | \"mute\" | \"error\", HTMLElement>;\n\n constructor() {\n super();\n this.root = this.attachShadow({ mode: \"open\" });\n this.root.innerHTML = `<style>${STYLE}</style>\n<div class=\"frame\" part=\"frame\" tabindex=\"0\">\n <div class=\"stage\" part=\"stage\"></div>\n <button class=\"big\" part=\"big-play\" aria-label=\"Play\">${ICON.play}</button>\n <div class=\"bar\" part=\"controls\">\n <button class=\"icon play\" aria-label=\"Play\">${ICON.play}</button>\n <span class=\"time\" part=\"time\">0:00 / 0:00</span>\n <div class=\"scrub\" part=\"scrubber\" role=\"slider\" tabindex=\"0\" aria-label=\"Seek\" aria-valuemin=\"0\" aria-valuemax=\"0\" aria-valuenow=\"0\">\n <div class=\"rail\"></div><div class=\"fill\"></div><div class=\"ticks\"></div><div class=\"thumb\"></div>\n </div>\n <span class=\"label\" part=\"chapter-label\"></span>\n <button class=\"icon chapters\" aria-label=\"Chapters\" aria-haspopup=\"true\" aria-expanded=\"false\" hidden>${ICON.chapters}</button>\n <button class=\"icon mute\" aria-label=\"Mute\">${ICON.sound}</button>\n </div>\n <ol class=\"menu\" part=\"chapters\" hidden></ol>\n <div class=\"error\" part=\"error\" role=\"alert\" hidden></div>\n</div>`;\n const $ = (sel: string) => this.root.querySelector(sel) as HTMLElement;\n this.els = {\n frame: $(\".frame\"),\n stage: $(\".stage\"),\n big: $(\".big\"),\n play: $(\".play\"),\n time: $(\".time\"),\n scrub: $(\".scrub\"),\n fill: $(\".fill\"),\n thumb: $(\".thumb\"),\n ticks: $(\".ticks\"),\n label: $(\".label\"),\n chaptersBtn: $(\".chapters\"),\n menu: $(\".menu\"),\n mute: $(\".mute\"),\n error: $(\".error\"),\n };\n this.wire();\n }\n\n // ------------------------------------------------------------------ API\n\n /** A project document to play live (takes precedence over `document`/`src` attributes). */\n get document(): Doc | undefined {\n return this.docValue;\n }\n set document(doc: Doc | undefined) {\n this.docValue = doc;\n void this.load();\n }\n\n /** Chapters in seconds. Set to override markers / the `chapters` attribute. */\n get chapters(): PlayerChapter[] {\n return [...this.chapterList];\n }\n set chapters(value: PlayerChapter[] | undefined) {\n this.explicitChapters = value ? normalizeChapters(value, this.duration) : undefined;\n this.refreshChapters();\n }\n\n get currentTime(): number {\n return this.engine?.state().timeS ?? 0;\n }\n set currentTime(seconds: number) {\n this.seek(seconds);\n }\n get duration(): number {\n return this.engine?.state().durationS ?? 0;\n }\n get paused(): boolean {\n return !(this.engine?.state().playing ?? false);\n }\n get currentChapterInfo(): PlayerChapter | undefined {\n return this.currentChapter;\n }\n\n play(): void {\n this.engine?.play();\n }\n pause(): void {\n this.engine?.pause();\n }\n toggle(): void {\n if (this.paused) this.play();\n else this.pause();\n }\n seek(seconds: number): void {\n this.engine?.seek(Math.min(Math.max(0, seconds), this.duration || seconds));\n this.render();\n }\n /** Jump to a chapter by id or index. */\n goToChapter(which: string | number): void {\n const chapter = typeof which === \"number\" ? this.chapterList[which] : this.chapterList.find((c) => c.id === which);\n if (chapter) this.seek(chapter.start);\n }\n\n // ------------------------------------------------------------------ lifecycle\n\n connectedCallback(): void {\n if (typeof ResizeObserver !== \"undefined\") {\n this.resizeObserver = new ResizeObserver(() => this.sizeEngine());\n this.resizeObserver.observe(this.els.frame);\n }\n if (!this.engine) void this.load();\n }\n\n disconnectedCallback(): void {\n this.resizeObserver?.disconnect();\n this.resizeObserver = undefined;\n this.engine?.destroy();\n this.engine = undefined;\n }\n\n attributeChangedCallback(name: string, previous: string | null, next: string | null): void {\n if (previous === next || !this.isConnected) return;\n if (name === \"src\" || name === \"document\" || name === \"assets-base\") void this.load();\n else if (name === \"chapters\") void this.loadChapterAttribute();\n else if (name === \"muted\") this.engine?.setMuted(next !== null);\n }\n\n // ------------------------------------------------------------------ loading\n\n private async load(): Promise<void> {\n const token = ++this.loadToken;\n this.engine?.destroy();\n this.engine = undefined;\n this.removeAttribute(\"data-ready\");\n this.showError(null);\n this.els.stage.replaceChildren();\n const loop = this.hasAttribute(\"loop\");\n const muted = this.hasAttribute(\"muted\");\n try {\n let engine: PlaybackEngine | undefined;\n let doc = this.docValue;\n const docUrl = this.getAttribute(\"document\");\n if (!doc && docUrl) {\n const response = await fetch(docUrl);\n if (!response.ok) throw new Error(`could not load ${docUrl} (HTTP ${response.status})`);\n doc = (await response.json()) as Doc;\n if (token !== this.loadToken) return;\n }\n if (doc) {\n const base = this.getAttribute(\"assets-base\") ?? (docUrl ? new URL(docUrl, location.href).href : undefined);\n const resolved = resolveAssetSources(doc, base);\n this.style.setProperty(\"--mc-aspect\", `${resolved.settings.width}/${resolved.settings.height}`);\n engine = await createDocumentEngine(resolved, { loop, muted, loadPlugins: this.hasAttribute(\"plugins\") });\n this.docChapters = (durationS) => chaptersFromMarkers(resolved.markers, durationS);\n } else {\n const src = this.getAttribute(\"src\");\n if (!src) return;\n const poster = this.getAttribute(\"poster\");\n engine = createVideoEngine(src, { loop, muted, ...(poster ? { poster } : {}) });\n this.docChapters = undefined;\n const video = engine.element as HTMLVideoElement;\n video.addEventListener(\"loadedmetadata\", () => {\n if (video.videoWidth && video.videoHeight) this.style.setProperty(\"--mc-aspect\", `${video.videoWidth}/${video.videoHeight}`);\n this.refreshChapters();\n });\n }\n if (token !== this.loadToken) {\n engine.destroy();\n return;\n }\n this.engine = engine;\n this.els.stage.append(engine.element);\n let wasPlaying = false;\n engine.onChange(() => {\n const playing = engine.state().playing;\n if (playing !== wasPlaying) {\n wasPlaying = playing;\n this.toggleAttribute(\"data-playing\", playing);\n this.dispatch(playing ? \"play\" : \"pause\");\n }\n this.render();\n this.dispatch(\"timeupdate\");\n });\n engine.onEnded(() => this.dispatch(\"ended\"));\n engine.onError((message) => {\n this.showError(message);\n this.dispatch(\"error\", { message });\n });\n this.sizeEngine();\n await this.loadChapterAttribute();\n this.refreshChapters();\n this.setAttribute(\"data-ready\", \"\");\n this.render();\n this.dispatch(\"ready\");\n if (this.hasAttribute(\"autoplay\")) engine.play();\n } catch (error) {\n if (token !== this.loadToken) return;\n const message = error instanceof Error ? error.message : String(error);\n this.showError(message);\n this.dispatch(\"error\", { message });\n }\n }\n\n private docChapters: ((durationS: number) => PlayerChapter[]) | undefined;\n private attributeChapters: PlayerChapter[] | undefined;\n\n private async loadChapterAttribute(): Promise<void> {\n const url = this.getAttribute(\"chapters\");\n if (!url) {\n this.attributeChapters = undefined;\n return;\n }\n try {\n const response = await fetch(url);\n const text = await response.text();\n this.attributeChapters = text.trimStart().startsWith(\"WEBVTT\") ? parseVttChapters(text) : normalizeChapters(JSON.parse(text), this.duration);\n } catch {\n this.attributeChapters = undefined;\n }\n this.refreshChapters();\n }\n\n private refreshChapters(): void {\n const duration = this.duration;\n this.chapterList = this.explicitChapters ?? this.attributeChapters ?? this.docChapters?.(duration) ?? [];\n const { ticks, menu, chaptersBtn } = this.els;\n ticks.replaceChildren();\n menu.replaceChildren();\n chaptersBtn.hidden = this.chapterList.length === 0;\n for (const [i, chapter] of this.chapterList.entries()) {\n if (duration > 0 && chapter.start > 0) {\n const tick = document.createElement(\"div\");\n tick.className = \"tick\";\n tick.style.left = `${(chapter.start / duration) * 100}%`;\n tick.title = chapter.label;\n ticks.append(tick);\n }\n const li = document.createElement(\"li\");\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.dataset[\"index\"] = String(i);\n button.innerHTML = `<span class=\"t\"></span><span class=\"n\"></span>`;\n (button.querySelector(\".t\") as HTMLElement).textContent = formatTime(chapter.start);\n (button.querySelector(\".n\") as HTMLElement).textContent = chapter.label;\n li.append(button);\n menu.append(li);\n }\n this.currentChapter = undefined;\n this.render();\n }\n\n // ------------------------------------------------------------------ UI\n\n private wire(): void {\n const { big, play, scrub, chaptersBtn, menu, mute, frame } = this.els;\n big.addEventListener(\"click\", () => this.play());\n play.addEventListener(\"click\", () => this.toggle());\n this.els.stage.addEventListener(\"click\", () => this.toggle());\n mute.addEventListener(\"click\", () => {\n const muted = !this.hasAttribute(\"muted\");\n this.toggleAttribute(\"muted\", muted);\n this.engine?.setMuted(muted);\n mute.innerHTML = muted ? ICON.muted : ICON.sound;\n mute.setAttribute(\"aria-label\", muted ? \"Unmute\" : \"Mute\");\n });\n chaptersBtn.addEventListener(\"click\", () => {\n const open = menu.hidden;\n menu.hidden = !open;\n chaptersBtn.setAttribute(\"aria-expanded\", String(open));\n if (open) (menu.querySelector<HTMLButtonElement>('button[aria-current=\"true\"]') ?? menu.querySelector<HTMLButtonElement>(\"button\"))?.focus();\n });\n menu.addEventListener(\"click\", (event) => {\n const button = (event.target as HTMLElement).closest(\"button\");\n if (!button) return;\n this.goToChapter(Number(button.dataset[\"index\"]));\n menu.hidden = true;\n chaptersBtn.setAttribute(\"aria-expanded\", \"false\");\n });\n const seekFromPointer = (event: PointerEvent): void => {\n const rect = scrub.getBoundingClientRect();\n if (rect.width <= 0) return;\n this.seek(((event.clientX - rect.left) / rect.width) * this.duration);\n };\n scrub.addEventListener(\"pointerdown\", (event) => {\n this.dragging = true;\n scrub.setPointerCapture?.(event.pointerId);\n seekFromPointer(event);\n });\n scrub.addEventListener(\"pointermove\", (event) => {\n if (this.dragging) seekFromPointer(event);\n });\n scrub.addEventListener(\"pointerup\", () => {\n this.dragging = false;\n });\n const keys = (event: KeyboardEvent): void => {\n const step = event.shiftKey ? 10 : 5;\n if (event.key === \" \" || event.key === \"k\") this.toggle();\n else if (event.key === \"ArrowRight\" || event.key === \"l\") this.seek(this.currentTime + step);\n else if (event.key === \"ArrowLeft\" || event.key === \"j\") this.seek(this.currentTime - step);\n else if (event.key === \"Home\") this.seek(0);\n else if (event.key === \"End\") this.seek(this.duration);\n else if (event.key === \"]\" ) this.stepChapter(1);\n else if (event.key === \"[\") this.stepChapter(-1);\n else if (event.key === \"Escape\") menu.hidden = true;\n else return;\n event.preventDefault();\n };\n frame.addEventListener(\"keydown\", keys);\n }\n\n private stepChapter(direction: 1 | -1): void {\n if (this.chapterList.length === 0) return;\n const t = this.currentTime;\n if (direction > 0) {\n const next = this.chapterList.find((c) => c.start > t + 0.25);\n if (next) this.seek(next.start);\n } else {\n const previous = [...this.chapterList].reverse().find((c) => c.start < t - 1);\n this.seek(previous?.start ?? 0);\n }\n }\n\n private render(): void {\n const state = this.engine?.state() ?? { playing: false, timeS: 0, durationS: 0 };\n const { time, fill, thumb, scrub, play, label, menu } = this.els;\n const fraction = state.durationS > 0 ? Math.min(1, state.timeS / state.durationS) : 0;\n time.textContent = `${formatTime(state.timeS)} / ${formatTime(state.durationS)}`;\n fill.style.width = `${fraction * 100}%`;\n thumb.style.left = `${fraction * 100}%`;\n scrub.setAttribute(\"aria-valuemax\", String(Math.round(state.durationS)));\n scrub.setAttribute(\"aria-valuenow\", String(Math.round(state.timeS)));\n scrub.setAttribute(\"aria-valuetext\", time.textContent);\n const wanted = state.playing ? \"Pause\" : \"Play\";\n if (play.getAttribute(\"aria-label\") !== wanted) {\n play.innerHTML = state.playing ? ICON.pause : ICON.play;\n play.setAttribute(\"aria-label\", wanted);\n }\n const chapter = chapterAt(this.chapterList, state.timeS);\n label.textContent = chapter?.label ?? \"\";\n if (chapter?.id !== this.currentChapter?.id) {\n this.currentChapter = chapter;\n for (const button of Array.from(menu.querySelectorAll(\"button\"))) {\n button.setAttribute(\"aria-current\", String(this.chapterList[Number(button.dataset[\"index\"])]?.id === chapter?.id));\n }\n this.dispatch(\"chapterchange\", { chapter: chapter ?? null });\n }\n }\n\n private sizeEngine(): void {\n const rect = this.els.frame.getBoundingClientRect();\n this.engine?.resize(rect.width, rect.height);\n }\n\n private showError(message: string | null): void {\n this.els.error.hidden = message === null;\n this.els.error.textContent = message ?? \"\";\n }\n\n private dispatch(type: string, detail?: unknown): void {\n this.dispatchEvent(new CustomEvent(type, { detail, bubbles: type !== \"timeupdate\", composed: true }));\n }\n}\n\n/** Register the element (default tag `miraiclip-player`). Safe to call twice. */\nexport function defineMiraiclipPlayer(tagName = \"miraiclip-player\"): typeof MiraiclipPlayerElement {\n if (typeof customElements === \"undefined\") return MiraiclipPlayerElement;\n const existing = customElements.get(tagName);\n if (existing) return existing as typeof MiraiclipPlayerElement;\n customElements.define(tagName, MiraiclipPlayerElement);\n return MiraiclipPlayerElement;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"miraiclip-player\": MiraiclipPlayerElement;\n }\n}\n","/** Side-effect entry: `import \"@miraiclip/player/define\"` registers <miraiclip-player>. */\nimport { defineMiraiclipPlayer } from \"./element.js\";\n\ndefineMiraiclipPlayer();\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/chapters.ts","../src/engines.ts","../src/element.ts","../src/define.ts"],"sourcesContent":["/**\r\n * Chapters for the player: from a document's markers, from JSON, or from a\r\n * WebVTT chapters track. Pure functions — no DOM.\r\n */\r\nexport interface PlayerChapter {\r\n id: string;\r\n label: string;\r\n /** Seconds. */\r\n start: number;\r\n end: number;\r\n}\r\n\r\ninterface MarkerLike {\r\n id: string;\r\n timeUs: number;\r\n label: string;\r\n durationUs?: number;\r\n}\r\n\r\n/**\r\n * A document's markers as chapters (same rule as core's `markerChapters`):\r\n * each runs for its own `durationUs`, or until the next marker; the last one\r\n * until `durationS`.\r\n */\r\nexport function chaptersFromMarkers(markers: Record<string, MarkerLike> | undefined, durationS: number): PlayerChapter[] {\r\n const list = Object.values(markers ?? {}).sort((a, b) => a.timeUs - b.timeUs || a.id.localeCompare(b.id));\r\n return list.map((m, i) => {\r\n const start = m.timeUs / 1e6;\r\n const next = list[i + 1];\r\n const end = m.durationUs !== undefined ? (m.timeUs + m.durationUs) / 1e6 : next ? next.timeUs / 1e6 : Math.max(durationS, start);\r\n return { id: m.id, label: m.label, start, end };\r\n });\r\n}\r\n\r\n/** Parse a WebVTT chapters track (\"00:01.000 --> 00:05.000\" cues with a title line). */\r\nexport function parseVttChapters(vtt: string): PlayerChapter[] {\r\n const chapters: PlayerChapter[] = [];\r\n const blocks = vtt.replace(/\\r/g, \"\").split(/\\n{2,}/);\r\n const time = (t: string): number => {\r\n const parts = t.trim().split(\":\").map(Number);\r\n let s = 0;\r\n for (const p of parts) s = s * 60 + p;\r\n return s;\r\n };\r\n for (const block of blocks) {\r\n const lines = block.split(\"\\n\").filter((l) => l.trim() !== \"\");\r\n const at = lines.findIndex((l) => l.includes(\"-->\"));\r\n if (at < 0) continue;\r\n const [from, to] = lines[at]!.split(\"-->\").map((s) => s.trim().split(/\\s+/)[0]!);\r\n const label = lines.slice(at + 1).join(\" \").trim();\r\n if (!label) continue;\r\n chapters.push({ id: at > 0 ? lines[0]!.trim() : `c${chapters.length + 1}`, label, start: time(from!), end: time(to!) });\r\n }\r\n return chapters.sort((a, b) => a.start - b.start);\r\n}\r\n\r\n/** Accept `[{ label, start, end? }]` (seconds) or `[{ label, timeUs, durationUs? }]` and normalize. */\r\nexport function normalizeChapters(input: unknown, durationS: number): PlayerChapter[] {\r\n if (!Array.isArray(input)) return [];\r\n const raw = input\r\n .map((c, i) => {\r\n const o = c as Record<string, unknown>;\r\n const label = String(o[\"label\"] ?? o[\"title\"] ?? \"\");\r\n const start = typeof o[\"start\"] === \"number\" ? o[\"start\"] : typeof o[\"timeUs\"] === \"number\" ? o[\"timeUs\"] / 1e6 : NaN;\r\n const end =\r\n typeof o[\"end\"] === \"number\"\r\n ? o[\"end\"]\r\n : typeof o[\"durationUs\"] === \"number\" && typeof o[\"timeUs\"] === \"number\"\r\n ? (o[\"timeUs\"] + o[\"durationUs\"]) / 1e6\r\n : undefined;\r\n return { id: String(o[\"id\"] ?? `c${i + 1}`), label, start, end };\r\n })\r\n .filter((c) => c.label && Number.isFinite(c.start))\r\n .sort((a, b) => a.start - b.start);\r\n return raw.map((c, i) => ({ id: c.id, label: c.label, start: c.start, end: c.end ?? raw[i + 1]?.start ?? Math.max(durationS, c.start) }));\r\n}\r\n\r\n/**\r\n * The chapter playing at `timeS` (the latest one that has started and not\r\n * ended). Media elements land a seek on a frame boundary, often a few ms\r\n * before the requested time, so a chapter counts as started `toleranceS`\r\n * early (default 50 ms).\r\n */\r\nexport function chapterAt(chapters: readonly PlayerChapter[], timeS: number, toleranceS = 0.05): PlayerChapter | undefined {\r\n let found: PlayerChapter | undefined;\r\n for (const c of chapters) if (c.start <= timeS + toleranceS && timeS < c.end) found = c;\r\n return found;\r\n}\r\n\r\n/** \"1:05\" / \"1:02:03\". */\r\nexport function formatTime(seconds: number): string {\r\n const s = Math.max(0, Math.floor(seconds + 1e-6));\r\n const h = Math.floor(s / 3600);\r\n const m = Math.floor((s % 3600) / 60);\r\n const ss = String(s % 60).padStart(2, \"0\");\r\n return h > 0 ? `${h}:${String(m).padStart(2, \"0\")}:${ss}` : `${m}:${ss}`;\r\n}\r\n","/**\r\n * Playback engines behind the element: a native <video> for rendered files,\r\n * and the Miraiclip renderer for live documents (loaded on demand, so a\r\n * video-only page never downloads the renderer).\r\n */\r\nexport interface EngineState {\r\n playing: boolean;\r\n timeS: number;\r\n durationS: number;\r\n}\r\n\r\nexport interface PlaybackEngine {\r\n readonly element: HTMLElement;\r\n play(): void;\r\n pause(): void;\r\n seek(seconds: number): void;\r\n state(): EngineState;\r\n setMuted(muted: boolean): void;\r\n /** Called on any time/state change. */\r\n onChange(listener: () => void): void;\r\n onEnded(listener: () => void): void;\r\n onError(listener: (message: string) => void): void;\r\n resize(width: number, height: number): void;\r\n destroy(): void;\r\n}\r\n\r\nexport function createVideoEngine(src: string, options: { loop: boolean; muted: boolean; poster?: string }): PlaybackEngine {\r\n const video = document.createElement(\"video\");\r\n video.src = src;\r\n video.preload = \"metadata\";\r\n video.playsInline = true;\r\n video.loop = options.loop;\r\n video.muted = options.muted;\r\n if (options.poster) video.poster = options.poster;\r\n video.setAttribute(\"part\", \"media\");\r\n const change: (() => void)[] = [];\r\n const ended: (() => void)[] = [];\r\n const errors: ((m: string) => void)[] = [];\r\n const emit = () => change.forEach((l) => l());\r\n for (const type of [\"timeupdate\", \"play\", \"pause\", \"durationchange\", \"loadedmetadata\", \"seeked\"]) video.addEventListener(type, emit);\r\n video.addEventListener(\"ended\", () => ended.forEach((l) => l()));\r\n video.addEventListener(\"error\", () => errors.forEach((l) => l(video.error?.message || \"the video could not be played\")));\r\n return {\r\n element: video,\r\n play: () => void video.play()?.catch?.((e: Error) => errors.forEach((l) => l(e.message))),\r\n pause: () => video.pause(),\r\n seek: (s) => {\r\n video.currentTime = Math.max(0, s);\r\n emit();\r\n },\r\n state: () => ({ playing: !video.paused && !video.ended, timeS: video.currentTime || 0, durationS: Number.isFinite(video.duration) ? video.duration : 0 }),\r\n setMuted: (m) => {\r\n video.muted = m;\r\n },\r\n onChange: (l) => change.push(l),\r\n onEnded: (l) => ended.push(l),\r\n onError: (l) => errors.push(l),\r\n resize: () => undefined,\r\n destroy: () => {\r\n video.pause();\r\n video.removeAttribute(\"src\");\r\n video.load();\r\n },\r\n };\r\n}\r\n\r\n/** Rewrite relative asset `src` paths so they resolve against `base` (the document's own URL by default). */\r\nexport function resolveAssetSources<T extends { assets: Record<string, { src: string }> }>(doc: T, base: string | undefined): T {\r\n if (!base) return doc;\r\n const copy = JSON.parse(JSON.stringify(doc)) as T;\r\n for (const asset of Object.values(copy.assets)) {\r\n if (/^(?:[a-z][a-z0-9+.-]*:|\\/\\/)/i.test(asset.src)) continue; // absolute, data:, blob:\r\n try {\r\n asset.src = new URL(asset.src, base).href;\r\n } catch {\r\n // leave as-is\r\n }\r\n }\r\n return copy;\r\n}\r\n\r\ninterface LiveModules {\r\n core: typeof import(\"@miraiclip/core\");\r\n renderer: typeof import(\"@miraiclip/renderer\");\r\n}\r\n\r\nlet modules: Promise<LiveModules> | undefined;\r\n\r\n/** Load core + renderer once (they are optional peers: only documents need them). */\r\nexport function loadLiveModules(): Promise<LiveModules> {\r\n modules ??= Promise.all([import(\"@miraiclip/core\"), import(\"@miraiclip/renderer\")]).then(([core, renderer]) => ({ core, renderer }));\r\n return modules;\r\n}\r\n\r\nexport async function createDocumentEngine(\r\n doc: import(\"@miraiclip/core\").ProjectDocument,\r\n options: { loop: boolean; muted: boolean; loadPlugins: boolean },\r\n): Promise<PlaybackEngine> {\r\n const { core, renderer } = await loadLiveModules();\r\n if (options.loadPlugins && doc.plugins?.length) await renderer.loadDocumentPlugins(doc);\r\n const project = core.createProject(doc);\r\n const controller = renderer.createPreviewController(project, { player: { loop: options.loop } });\r\n if (options.muted) controller.toggleMute();\r\n const canvas = document.createElement(\"canvas\");\r\n canvas.setAttribute(\"part\", \"media\");\r\n const change: (() => void)[] = [];\r\n const ended: (() => void)[] = [];\r\n const errors: ((m: string) => void)[] = [];\r\n let durationUs = 0;\r\n for (const clip of Object.values(doc.clips)) durationUs = Math.max(durationUs, clip.startUs + clip.durationUs);\r\n let wasPlaying = false;\r\n const emit = () => {\r\n const playing = controller.getState().playing;\r\n if (wasPlaying && !playing && project.getState().playheadUs >= durationUs - 1) ended.forEach((l) => l());\r\n wasPlaying = playing;\r\n change.forEach((l) => l());\r\n };\r\n controller.subscribe(() => {\r\n const error = controller.getState().lastError;\r\n if (error) errors.forEach((l) => l(error));\r\n emit();\r\n });\r\n const offPlayhead = project.events.on(\"playhead\", emit);\r\n let attached = false;\r\n return {\r\n element: canvas,\r\n play: () => controller.play(),\r\n pause: () => controller.pause(),\r\n seek: (s) => controller.seek(Math.round(Math.max(0, s) * 1e6)),\r\n state: () => ({ playing: controller.getState().playing, timeS: project.getState().playheadUs / 1e6, durationS: durationUs / 1e6 }),\r\n setMuted: (m) => {\r\n if (controller.getState().muted !== m) controller.toggleMute();\r\n },\r\n onChange: (l) => change.push(l),\r\n onEnded: (l) => ended.push(l),\r\n onError: (l) => errors.push(l),\r\n resize: (width, height) => {\r\n if (width <= 0 || height <= 0) return;\r\n if (!attached) {\r\n controller.attach(canvas, { width, height });\r\n attached = true;\r\n } else controller.resize(width, height);\r\n },\r\n destroy: () => {\r\n offPlayhead();\r\n controller.destroy();\r\n },\r\n };\r\n}\r\n","/**\r\n * <miraiclip-player>: plays a rendered video file or a live Miraiclip\r\n * document, with a scrubber, chapters (from the document's markers, a JSON\r\n * list or a WebVTT chapters file) and keyboard control. No framework.\r\n *\r\n * <miraiclip-player src=\"launch.mp4\" chapters=\"launch.chapters.vtt\" controls></miraiclip-player>\r\n * <miraiclip-player document=\"video.miraiclip.json\" controls></miraiclip-player>\r\n *\r\n * Events: ready, play, pause, timeupdate, chapterchange ({ chapter }), ended, error ({ message }).\r\n */\r\nimport { chapterAt, chaptersFromMarkers, formatTime, normalizeChapters, parseVttChapters, type PlayerChapter } from \"./chapters.js\";\r\nimport { createDocumentEngine, createVideoEngine, resolveAssetSources, type PlaybackEngine } from \"./engines.js\";\r\n\r\ntype Doc = import(\"@miraiclip/core\").ProjectDocument;\r\n\r\nconst STYLE = `\r\n:host{display:block;position:relative;--mc-accent:#22d3ee;--mc-bg:#000;--mc-fg:#fff;--mc-radius:12px;--mc-font:Inter,system-ui,sans-serif;color:var(--mc-fg);font-family:var(--mc-font)}\r\n:host([hidden]){display:none}\r\n.frame{position:relative;width:100%;aspect-ratio:var(--mc-aspect,16/9);background:var(--mc-bg);border-radius:var(--mc-radius);overflow:hidden;outline:none}\r\n.stage,.stage>*{position:absolute;inset:0;width:100%;height:100%;display:block;object-fit:contain}\r\n.big{position:absolute;inset:0;margin:auto;width:76px;height:76px;border-radius:50%;border:0;background:#000a;color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center}\r\n.big svg{width:34px;height:34px;margin-left:4px}\r\n:host([data-playing]) .big,:host(:not([data-ready])) .big{display:none}\r\n.bar{position:absolute;left:0;right:0;bottom:0;display:flex;align-items:center;gap:10px;padding:28px 12px 10px;background:linear-gradient(transparent,#000c);opacity:0;transition:opacity .2s}\r\n:host([controls]) .frame:hover .bar,:host([controls]) .frame:focus-within .bar,:host([controls]:not([data-playing])) .bar{opacity:1}\r\n:host(:not([controls])) .bar{display:none}\r\nbutton.icon{background:none;border:0;color:inherit;width:34px;height:34px;padding:5px;cursor:pointer;border-radius:8px;flex:none}\r\nbutton.icon:hover,button.icon:focus-visible{background:#ffffff22;outline:none}\r\nbutton svg{width:100%;height:100%;fill:currentColor}\r\n.time{font-size:13px;font-variant-numeric:tabular-nums;white-space:nowrap;opacity:.9}\r\n.scrub{position:relative;flex:1;height:22px;cursor:pointer;outline:none;touch-action:none}\r\n.rail{position:absolute;left:0;right:0;top:9px;height:4px;border-radius:2px;background:#ffffff40}\r\n.fill{position:absolute;left:0;top:9px;height:4px;border-radius:2px;background:var(--mc-accent);width:0}\r\n.tick{position:absolute;top:7px;width:2px;height:8px;background:#fff;opacity:.85;border-radius:1px}\r\n.thumb{position:absolute;top:4px;width:14px;height:14px;margin-left:-7px;border-radius:50%;background:#fff;box-shadow:0 1px 4px #0008;left:0}\r\n.scrub:focus-visible .thumb{outline:2px solid var(--mc-accent);outline-offset:2px}\r\n.label{font-size:13px;max-width:30%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;opacity:.9}\r\n.menu{position:absolute;right:10px;bottom:56px;max-height:60%;overflow:auto;margin:0;padding:6px;list-style:none;border-radius:10px;background:#111827f2;box-shadow:0 10px 30px #0009;min-width:200px}\r\n.menu[hidden]{display:none}\r\n.menu button{display:flex;gap:10px;width:100%;padding:8px 10px;border:0;border-radius:6px;background:none;color:inherit;font:inherit;font-size:14px;text-align:left;cursor:pointer}\r\n.menu button:hover,.menu button:focus-visible{background:#ffffff1f;outline:none}\r\n.menu button[aria-current=\"true\"]{color:var(--mc-accent)}\r\n.menu .t{opacity:.7;font-variant-numeric:tabular-nums}\r\n.error{position:absolute;left:12px;right:12px;top:12px;padding:8px 12px;border-radius:8px;background:#7f1d1dee;font-size:13px}\r\n.error[hidden]{display:none}\r\n`;\r\n\r\nconst ICON = {\r\n play: '<svg viewBox=\"0 0 24 24\"><path d=\"M8 5v14l11-7z\"/></svg>',\r\n pause: '<svg viewBox=\"0 0 24 24\"><path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/></svg>',\r\n chapters: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 6h2v2H4zm4 0h12v2H8zM4 11h2v2H4zm4 0h12v2H8zm-4 5h2v2H4zm4 0h12v2H8z\"/></svg>',\r\n sound: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 9v6h4l5 4V5L8 9zm12.5 3a4.5 4.5 0 0 0-2.5-4v8a4.5 4.5 0 0 0 2.5-4z\"/></svg>',\r\n muted: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 9v6h4l5 4V5L8 9zm16.6 0L19 7.4 16.4 10 13.8 7.4 12.4 9l2.6 2.6-2.6 2.6 1.4 1.4 2.6-2.6 2.6 2.6 1.6-1.6-2.6-2.6z\"/></svg>',\r\n};\r\n\r\nconst BaseElement: typeof HTMLElement = typeof HTMLElement === \"undefined\" ? (class {} as unknown as typeof HTMLElement) : HTMLElement;\r\n\r\nexport class MiraiclipPlayerElement extends BaseElement {\r\n static get observedAttributes(): string[] {\r\n return [\"src\", \"document\", \"chapters\", \"poster\", \"loop\", \"muted\", \"assets-base\"];\r\n }\r\n\r\n private readonly root: ShadowRoot;\r\n private engine: PlaybackEngine | undefined;\r\n private loadToken = 0;\r\n private docValue: Doc | undefined;\r\n private chapterList: PlayerChapter[] = [];\r\n private explicitChapters: PlayerChapter[] | undefined;\r\n private currentChapter: PlayerChapter | undefined;\r\n private resizeObserver: ResizeObserver | undefined;\r\n private dragging = false;\r\n private readonly els: Record<\"frame\" | \"stage\" | \"big\" | \"play\" | \"time\" | \"scrub\" | \"fill\" | \"thumb\" | \"ticks\" | \"label\" | \"chaptersBtn\" | \"menu\" | \"mute\" | \"error\", HTMLElement>;\r\n\r\n constructor() {\r\n super();\r\n this.root = this.attachShadow({ mode: \"open\" });\r\n this.root.innerHTML = `<style>${STYLE}</style>\r\n<div class=\"frame\" part=\"frame\" tabindex=\"0\">\r\n <div class=\"stage\" part=\"stage\"></div>\r\n <button class=\"big\" part=\"big-play\" aria-label=\"Play\">${ICON.play}</button>\r\n <div class=\"bar\" part=\"controls\">\r\n <button class=\"icon play\" aria-label=\"Play\">${ICON.play}</button>\r\n <span class=\"time\" part=\"time\">0:00 / 0:00</span>\r\n <div class=\"scrub\" part=\"scrubber\" role=\"slider\" tabindex=\"0\" aria-label=\"Seek\" aria-valuemin=\"0\" aria-valuemax=\"0\" aria-valuenow=\"0\">\r\n <div class=\"rail\"></div><div class=\"fill\"></div><div class=\"ticks\"></div><div class=\"thumb\"></div>\r\n </div>\r\n <span class=\"label\" part=\"chapter-label\"></span>\r\n <button class=\"icon chapters\" aria-label=\"Chapters\" aria-haspopup=\"true\" aria-expanded=\"false\" hidden>${ICON.chapters}</button>\r\n <button class=\"icon mute\" aria-label=\"Mute\">${ICON.sound}</button>\r\n </div>\r\n <ol class=\"menu\" part=\"chapters\" hidden></ol>\r\n <div class=\"error\" part=\"error\" role=\"alert\" hidden></div>\r\n</div>`;\r\n const $ = (sel: string) => this.root.querySelector(sel) as HTMLElement;\r\n this.els = {\r\n frame: $(\".frame\"),\r\n stage: $(\".stage\"),\r\n big: $(\".big\"),\r\n play: $(\".play\"),\r\n time: $(\".time\"),\r\n scrub: $(\".scrub\"),\r\n fill: $(\".fill\"),\r\n thumb: $(\".thumb\"),\r\n ticks: $(\".ticks\"),\r\n label: $(\".label\"),\r\n chaptersBtn: $(\".chapters\"),\r\n menu: $(\".menu\"),\r\n mute: $(\".mute\"),\r\n error: $(\".error\"),\r\n };\r\n this.wire();\r\n }\r\n\r\n // ------------------------------------------------------------------ API\r\n\r\n /** A project document to play live (takes precedence over `document`/`src` attributes). */\r\n get document(): Doc | undefined {\r\n return this.docValue;\r\n }\r\n set document(doc: Doc | undefined) {\r\n this.docValue = doc;\r\n void this.load();\r\n }\r\n\r\n /** Chapters in seconds. Set to override markers / the `chapters` attribute. */\r\n get chapters(): PlayerChapter[] {\r\n return [...this.chapterList];\r\n }\r\n set chapters(value: PlayerChapter[] | undefined) {\r\n this.explicitChapters = value ? normalizeChapters(value, this.duration) : undefined;\r\n this.refreshChapters();\r\n }\r\n\r\n get currentTime(): number {\r\n return this.engine?.state().timeS ?? 0;\r\n }\r\n set currentTime(seconds: number) {\r\n this.seek(seconds);\r\n }\r\n get duration(): number {\r\n return this.engine?.state().durationS ?? 0;\r\n }\r\n get paused(): boolean {\r\n return !(this.engine?.state().playing ?? false);\r\n }\r\n get currentChapterInfo(): PlayerChapter | undefined {\r\n return this.currentChapter;\r\n }\r\n\r\n play(): void {\r\n this.engine?.play();\r\n }\r\n pause(): void {\r\n this.engine?.pause();\r\n }\r\n toggle(): void {\r\n if (this.paused) this.play();\r\n else this.pause();\r\n }\r\n seek(seconds: number): void {\r\n this.engine?.seek(Math.min(Math.max(0, seconds), this.duration || seconds));\r\n this.render();\r\n }\r\n /** Jump to a chapter by id or index. */\r\n goToChapter(which: string | number): void {\r\n const chapter = typeof which === \"number\" ? this.chapterList[which] : this.chapterList.find((c) => c.id === which);\r\n if (chapter) this.seek(chapter.start);\r\n }\r\n\r\n // ------------------------------------------------------------------ lifecycle\r\n\r\n connectedCallback(): void {\r\n if (typeof ResizeObserver !== \"undefined\") {\r\n this.resizeObserver = new ResizeObserver(() => this.sizeEngine());\r\n this.resizeObserver.observe(this.els.frame);\r\n }\r\n if (!this.engine) void this.load();\r\n }\r\n\r\n disconnectedCallback(): void {\r\n this.resizeObserver?.disconnect();\r\n this.resizeObserver = undefined;\r\n this.engine?.destroy();\r\n this.engine = undefined;\r\n }\r\n\r\n attributeChangedCallback(name: string, previous: string | null, next: string | null): void {\r\n if (previous === next || !this.isConnected) return;\r\n if (name === \"src\" || name === \"document\" || name === \"assets-base\") void this.load();\r\n else if (name === \"chapters\") void this.loadChapterAttribute();\r\n else if (name === \"muted\") this.engine?.setMuted(next !== null);\r\n }\r\n\r\n // ------------------------------------------------------------------ loading\r\n\r\n private async load(): Promise<void> {\r\n const token = ++this.loadToken;\r\n this.engine?.destroy();\r\n this.engine = undefined;\r\n this.removeAttribute(\"data-ready\");\r\n this.showError(null);\r\n this.els.stage.replaceChildren();\r\n const loop = this.hasAttribute(\"loop\");\r\n const muted = this.hasAttribute(\"muted\");\r\n try {\r\n let engine: PlaybackEngine | undefined;\r\n let doc = this.docValue;\r\n const docUrl = this.getAttribute(\"document\");\r\n if (!doc && docUrl) {\r\n const response = await fetch(docUrl);\r\n if (!response.ok) throw new Error(`could not load ${docUrl} (HTTP ${response.status})`);\r\n doc = (await response.json()) as Doc;\r\n if (token !== this.loadToken) return;\r\n }\r\n if (doc) {\r\n const base = this.getAttribute(\"assets-base\") ?? (docUrl ? new URL(docUrl, location.href).href : undefined);\r\n const resolved = resolveAssetSources(doc, base);\r\n this.style.setProperty(\"--mc-aspect\", `${resolved.settings.width}/${resolved.settings.height}`);\r\n engine = await createDocumentEngine(resolved, { loop, muted, loadPlugins: this.hasAttribute(\"plugins\") });\r\n this.docChapters = (durationS) => chaptersFromMarkers(resolved.markers, durationS);\r\n } else {\r\n const src = this.getAttribute(\"src\");\r\n if (!src) return;\r\n const poster = this.getAttribute(\"poster\");\r\n engine = createVideoEngine(src, { loop, muted, ...(poster ? { poster } : {}) });\r\n this.docChapters = undefined;\r\n const video = engine.element as HTMLVideoElement;\r\n video.addEventListener(\"loadedmetadata\", () => {\r\n if (video.videoWidth && video.videoHeight) this.style.setProperty(\"--mc-aspect\", `${video.videoWidth}/${video.videoHeight}`);\r\n this.refreshChapters();\r\n });\r\n }\r\n if (token !== this.loadToken) {\r\n engine.destroy();\r\n return;\r\n }\r\n this.engine = engine;\r\n this.els.stage.append(engine.element);\r\n let wasPlaying = false;\r\n engine.onChange(() => {\r\n const playing = engine.state().playing;\r\n if (playing !== wasPlaying) {\r\n wasPlaying = playing;\r\n this.toggleAttribute(\"data-playing\", playing);\r\n this.dispatch(playing ? \"play\" : \"pause\");\r\n }\r\n this.render();\r\n this.dispatch(\"timeupdate\");\r\n });\r\n engine.onEnded(() => this.dispatch(\"ended\"));\r\n engine.onError((message) => {\r\n this.showError(message);\r\n this.dispatch(\"error\", { message });\r\n });\r\n this.sizeEngine();\r\n await this.loadChapterAttribute();\r\n this.refreshChapters();\r\n this.setAttribute(\"data-ready\", \"\");\r\n this.render();\r\n this.dispatch(\"ready\");\r\n if (this.hasAttribute(\"autoplay\")) engine.play();\r\n } catch (error) {\r\n if (token !== this.loadToken) return;\r\n const message = error instanceof Error ? error.message : String(error);\r\n this.showError(message);\r\n this.dispatch(\"error\", { message });\r\n }\r\n }\r\n\r\n private docChapters: ((durationS: number) => PlayerChapter[]) | undefined;\r\n private attributeChapters: PlayerChapter[] | undefined;\r\n\r\n private async loadChapterAttribute(): Promise<void> {\r\n const url = this.getAttribute(\"chapters\");\r\n if (!url) {\r\n this.attributeChapters = undefined;\r\n return;\r\n }\r\n try {\r\n const response = await fetch(url);\r\n const text = await response.text();\r\n this.attributeChapters = text.trimStart().startsWith(\"WEBVTT\") ? parseVttChapters(text) : normalizeChapters(JSON.parse(text), this.duration);\r\n } catch {\r\n this.attributeChapters = undefined;\r\n }\r\n this.refreshChapters();\r\n }\r\n\r\n private refreshChapters(): void {\r\n const duration = this.duration;\r\n this.chapterList = this.explicitChapters ?? this.attributeChapters ?? this.docChapters?.(duration) ?? [];\r\n const { ticks, menu, chaptersBtn } = this.els;\r\n ticks.replaceChildren();\r\n menu.replaceChildren();\r\n chaptersBtn.hidden = this.chapterList.length === 0;\r\n for (const [i, chapter] of this.chapterList.entries()) {\r\n if (duration > 0 && chapter.start > 0) {\r\n const tick = document.createElement(\"div\");\r\n tick.className = \"tick\";\r\n tick.style.left = `${(chapter.start / duration) * 100}%`;\r\n tick.title = chapter.label;\r\n ticks.append(tick);\r\n }\r\n const li = document.createElement(\"li\");\r\n const button = document.createElement(\"button\");\r\n button.type = \"button\";\r\n button.dataset[\"index\"] = String(i);\r\n button.innerHTML = `<span class=\"t\"></span><span class=\"n\"></span>`;\r\n (button.querySelector(\".t\") as HTMLElement).textContent = formatTime(chapter.start);\r\n (button.querySelector(\".n\") as HTMLElement).textContent = chapter.label;\r\n li.append(button);\r\n menu.append(li);\r\n }\r\n this.currentChapter = undefined;\r\n this.render();\r\n }\r\n\r\n // ------------------------------------------------------------------ UI\r\n\r\n private wire(): void {\r\n const { big, play, scrub, chaptersBtn, menu, mute, frame } = this.els;\r\n big.addEventListener(\"click\", () => this.play());\r\n play.addEventListener(\"click\", () => this.toggle());\r\n this.els.stage.addEventListener(\"click\", () => this.toggle());\r\n mute.addEventListener(\"click\", () => {\r\n const muted = !this.hasAttribute(\"muted\");\r\n this.toggleAttribute(\"muted\", muted);\r\n this.engine?.setMuted(muted);\r\n mute.innerHTML = muted ? ICON.muted : ICON.sound;\r\n mute.setAttribute(\"aria-label\", muted ? \"Unmute\" : \"Mute\");\r\n });\r\n chaptersBtn.addEventListener(\"click\", () => {\r\n const open = menu.hidden;\r\n menu.hidden = !open;\r\n chaptersBtn.setAttribute(\"aria-expanded\", String(open));\r\n if (open) (menu.querySelector<HTMLButtonElement>('button[aria-current=\"true\"]') ?? menu.querySelector<HTMLButtonElement>(\"button\"))?.focus();\r\n });\r\n menu.addEventListener(\"click\", (event) => {\r\n const button = (event.target as HTMLElement).closest(\"button\");\r\n if (!button) return;\r\n this.goToChapter(Number(button.dataset[\"index\"]));\r\n menu.hidden = true;\r\n chaptersBtn.setAttribute(\"aria-expanded\", \"false\");\r\n });\r\n const seekFromPointer = (event: PointerEvent): void => {\r\n const rect = scrub.getBoundingClientRect();\r\n if (rect.width <= 0) return;\r\n this.seek(((event.clientX - rect.left) / rect.width) * this.duration);\r\n };\r\n scrub.addEventListener(\"pointerdown\", (event) => {\r\n this.dragging = true;\r\n scrub.setPointerCapture?.(event.pointerId);\r\n seekFromPointer(event);\r\n });\r\n scrub.addEventListener(\"pointermove\", (event) => {\r\n if (this.dragging) seekFromPointer(event);\r\n });\r\n scrub.addEventListener(\"pointerup\", () => {\r\n this.dragging = false;\r\n });\r\n const keys = (event: KeyboardEvent): void => {\r\n const step = event.shiftKey ? 10 : 5;\r\n if (event.key === \" \" || event.key === \"k\") this.toggle();\r\n else if (event.key === \"ArrowRight\" || event.key === \"l\") this.seek(this.currentTime + step);\r\n else if (event.key === \"ArrowLeft\" || event.key === \"j\") this.seek(this.currentTime - step);\r\n else if (event.key === \"Home\") this.seek(0);\r\n else if (event.key === \"End\") this.seek(this.duration);\r\n else if (event.key === \"]\" ) this.stepChapter(1);\r\n else if (event.key === \"[\") this.stepChapter(-1);\r\n else if (event.key === \"Escape\") menu.hidden = true;\r\n else return;\r\n event.preventDefault();\r\n };\r\n frame.addEventListener(\"keydown\", keys);\r\n }\r\n\r\n private stepChapter(direction: 1 | -1): void {\r\n if (this.chapterList.length === 0) return;\r\n const t = this.currentTime;\r\n if (direction > 0) {\r\n const next = this.chapterList.find((c) => c.start > t + 0.25);\r\n if (next) this.seek(next.start);\r\n } else {\r\n const previous = [...this.chapterList].reverse().find((c) => c.start < t - 1);\r\n this.seek(previous?.start ?? 0);\r\n }\r\n }\r\n\r\n private render(): void {\r\n const state = this.engine?.state() ?? { playing: false, timeS: 0, durationS: 0 };\r\n const { time, fill, thumb, scrub, play, label, menu } = this.els;\r\n const fraction = state.durationS > 0 ? Math.min(1, state.timeS / state.durationS) : 0;\r\n time.textContent = `${formatTime(state.timeS)} / ${formatTime(state.durationS)}`;\r\n fill.style.width = `${fraction * 100}%`;\r\n thumb.style.left = `${fraction * 100}%`;\r\n scrub.setAttribute(\"aria-valuemax\", String(Math.round(state.durationS)));\r\n scrub.setAttribute(\"aria-valuenow\", String(Math.round(state.timeS)));\r\n scrub.setAttribute(\"aria-valuetext\", time.textContent);\r\n const wanted = state.playing ? \"Pause\" : \"Play\";\r\n if (play.getAttribute(\"aria-label\") !== wanted) {\r\n play.innerHTML = state.playing ? ICON.pause : ICON.play;\r\n play.setAttribute(\"aria-label\", wanted);\r\n }\r\n const chapter = chapterAt(this.chapterList, state.timeS);\r\n label.textContent = chapter?.label ?? \"\";\r\n if (chapter?.id !== this.currentChapter?.id) {\r\n this.currentChapter = chapter;\r\n for (const button of Array.from(menu.querySelectorAll(\"button\"))) {\r\n button.setAttribute(\"aria-current\", String(this.chapterList[Number(button.dataset[\"index\"])]?.id === chapter?.id));\r\n }\r\n this.dispatch(\"chapterchange\", { chapter: chapter ?? null });\r\n }\r\n }\r\n\r\n private sizeEngine(): void {\r\n const rect = this.els.frame.getBoundingClientRect();\r\n this.engine?.resize(rect.width, rect.height);\r\n }\r\n\r\n private showError(message: string | null): void {\r\n this.els.error.hidden = message === null;\r\n this.els.error.textContent = message ?? \"\";\r\n }\r\n\r\n private dispatch(type: string, detail?: unknown): void {\r\n this.dispatchEvent(new CustomEvent(type, { detail, bubbles: type !== \"timeupdate\", composed: true }));\r\n }\r\n}\r\n\r\n/** Register the element (default tag `miraiclip-player`). Safe to call twice. */\r\nexport function defineMiraiclipPlayer(tagName = \"miraiclip-player\"): typeof MiraiclipPlayerElement {\r\n if (typeof customElements === \"undefined\") return MiraiclipPlayerElement;\r\n const existing = customElements.get(tagName);\r\n if (existing) return existing as typeof MiraiclipPlayerElement;\r\n customElements.define(tagName, MiraiclipPlayerElement);\r\n return MiraiclipPlayerElement;\r\n}\r\n\r\ndeclare global {\r\n interface HTMLElementTagNameMap {\r\n \"miraiclip-player\": MiraiclipPlayerElement;\r\n }\r\n}\r\n","/** Side-effect entry: `import \"@miraiclip/player/define\"` registers <miraiclip-player>. */\r\nimport { defineMiraiclipPlayer } from \"./element.js\";\r\n\r\ndefineMiraiclipPlayer();\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAwBO,SAAS,oBAAoB,SAAiD,WAAoC;AACvH,QAAM,OAAO,OAAO,OAAO,WAAW,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,cAAc,EAAE,EAAE,CAAC;AACxG,SAAO,KAAK,IAAI,CAAC,GAAG,MAAM;AACxB,UAAM,QAAQ,EAAE,SAAS;AACzB,UAAM,OAAO,KAAK,IAAI,CAAC;AACvB,UAAM,MAAM,EAAE,eAAe,UAAa,EAAE,SAAS,EAAE,cAAc,MAAM,OAAO,KAAK,SAAS,MAAM,KAAK,IAAI,WAAW,KAAK;AAC/H,WAAO,EAAE,IAAI,EAAE,IAAI,OAAO,EAAE,OAAO,OAAO,IAAI;AAAA,EAChD,CAAC;AACH;AAGO,SAAS,iBAAiB,KAA8B;AAC7D,QAAM,WAA4B,CAAC;AACnC,QAAM,SAAS,IAAI,QAAQ,OAAO,EAAE,EAAE,MAAM,QAAQ;AACpD,QAAM,OAAO,CAAC,MAAsB;AAClC,UAAM,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,EAAE,IAAI,MAAM;AAC5C,QAAI,IAAI;AACR,eAAW,KAAK,MAAO,KAAI,IAAI,KAAK;AACpC,WAAO;AAAA,EACT;AACA,aAAW,SAAS,QAAQ;AAC1B,UAAM,QAAQ,MAAM,MAAM,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,MAAM,EAAE;AAC7D,UAAM,KAAK,MAAM,UAAU,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC;AACnD,QAAI,KAAK,EAAG;AACZ,UAAM,CAAC,MAAM,EAAE,IAAI,MAAM,EAAE,EAAG,MAAM,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,EAAE,CAAC,CAAE;AAC/E,UAAM,QAAQ,MAAM,MAAM,KAAK,CAAC,EAAE,KAAK,GAAG,EAAE,KAAK;AACjD,QAAI,CAAC,MAAO;AACZ,aAAS,KAAK,EAAE,IAAI,KAAK,IAAI,MAAM,CAAC,EAAG,KAAK,IAAI,IAAI,SAAS,SAAS,CAAC,IAAI,OAAO,OAAO,KAAK,IAAK,GAAG,KAAK,KAAK,EAAG,EAAE,CAAC;AAAA,EACxH;AACA,SAAO,SAAS,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AAClD;AAGO,SAAS,kBAAkB,OAAgB,WAAoC;AACpF,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,QAAO,CAAC;AACnC,QAAM,MAAM,MACT,IAAI,CAAC,GAAG,MAAM;AACb,UAAM,IAAI;AACV,UAAM,QAAQ,OAAO,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK,EAAE;AACnD,UAAM,QAAQ,OAAO,EAAE,OAAO,MAAM,WAAW,EAAE,OAAO,IAAI,OAAO,EAAE,QAAQ,MAAM,WAAW,EAAE,QAAQ,IAAI,MAAM;AAClH,UAAM,MACJ,OAAO,EAAE,KAAK,MAAM,WAChB,EAAE,KAAK,IACP,OAAO,EAAE,YAAY,MAAM,YAAY,OAAO,EAAE,QAAQ,MAAM,YAC3D,EAAE,QAAQ,IAAI,EAAE,YAAY,KAAK,MAClC;AACR,WAAO,EAAE,IAAI,OAAO,EAAE,IAAI,KAAK,IAAI,IAAI,CAAC,EAAE,GAAG,OAAO,OAAO,IAAI;AAAA,EACjE,CAAC,EACA,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,SAAS,EAAE,KAAK,CAAC,EACjD,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AACnC,SAAO,IAAI,IAAI,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,IAAI,OAAO,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK,EAAE,OAAO,IAAI,IAAI,CAAC,GAAG,SAAS,KAAK,IAAI,WAAW,EAAE,KAAK,EAAE,EAAE;AAC1I;AAQO,SAAS,UAAU,UAAoC,OAAe,aAAa,MAAiC;AACzH,MAAI;AACJ,aAAW,KAAK,SAAU,KAAI,EAAE,SAAS,QAAQ,cAAc,QAAQ,EAAE,IAAK,SAAQ;AACtF,SAAO;AACT;AAGO,SAAS,WAAW,SAAyB;AAClD,QAAM,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,UAAU,IAAI,CAAC;AAChD,QAAM,IAAI,KAAK,MAAM,IAAI,IAAI;AAC7B,QAAM,IAAI,KAAK,MAAO,IAAI,OAAQ,EAAE;AACpC,QAAM,KAAK,OAAO,IAAI,EAAE,EAAE,SAAS,GAAG,GAAG;AACzC,SAAO,IAAI,IAAI,GAAG,CAAC,IAAI,OAAO,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,IAAI,EAAE;AACxE;;;ACtEO,SAAS,kBAAkB,KAAa,SAA6E;AAC1H,QAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,QAAM,MAAM;AACZ,QAAM,UAAU;AAChB,QAAM,cAAc;AACpB,QAAM,OAAO,QAAQ;AACrB,QAAM,QAAQ,QAAQ;AACtB,MAAI,QAAQ,OAAQ,OAAM,SAAS,QAAQ;AAC3C,QAAM,aAAa,QAAQ,OAAO;AAClC,QAAM,SAAyB,CAAC;AAChC,QAAM,QAAwB,CAAC;AAC/B,QAAM,SAAkC,CAAC;AACzC,QAAM,OAAO,MAAM,OAAO,QAAQ,CAAC,MAAM,EAAE,CAAC;AAC5C,aAAW,QAAQ,CAAC,cAAc,QAAQ,SAAS,kBAAkB,kBAAkB,QAAQ,EAAG,OAAM,iBAAiB,MAAM,IAAI;AACnI,QAAM,iBAAiB,SAAS,MAAM,MAAM,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;AAC/D,QAAM,iBAAiB,SAAS,MAAM,OAAO,QAAQ,CAAC,MAAM,EAAE,MAAM,OAAO,WAAW,+BAA+B,CAAC,CAAC;AACvH,SAAO;AAAA,IACL,SAAS;AAAA,IACT,MAAM,MAAM,KAAK,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAa,OAAO,QAAQ,CAAC,MAAM,EAAE,EAAE,OAAO,CAAC,CAAC;AAAA,IACxF,OAAO,MAAM,MAAM,MAAM;AAAA,IACzB,MAAM,CAAC,MAAM;AACX,YAAM,cAAc,KAAK,IAAI,GAAG,CAAC;AACjC,WAAK;AAAA,IACP;AAAA,IACA,OAAO,OAAO,EAAE,SAAS,CAAC,MAAM,UAAU,CAAC,MAAM,OAAO,OAAO,MAAM,eAAe,GAAG,WAAW,OAAO,SAAS,MAAM,QAAQ,IAAI,MAAM,WAAW,EAAE;AAAA,IACvJ,UAAU,CAAC,MAAM;AACf,YAAM,QAAQ;AAAA,IAChB;AAAA,IACA,UAAU,CAAC,MAAM,OAAO,KAAK,CAAC;AAAA,IAC9B,SAAS,CAAC,MAAM,MAAM,KAAK,CAAC;AAAA,IAC5B,SAAS,CAAC,MAAM,OAAO,KAAK,CAAC;AAAA,IAC7B,QAAQ,MAAM;AAAA,IACd,SAAS,MAAM;AACb,YAAM,MAAM;AACZ,YAAM,gBAAgB,KAAK;AAC3B,YAAM,KAAK;AAAA,IACb;AAAA,EACF;AACF;AAGO,SAAS,oBAA2E,KAAQ,MAA6B;AAC9H,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,OAAO,KAAK,MAAM,KAAK,UAAU,GAAG,CAAC;AAC3C,aAAW,SAAS,OAAO,OAAO,KAAK,MAAM,GAAG;AAC9C,QAAI,gCAAgC,KAAK,MAAM,GAAG,EAAG;AACrD,QAAI;AACF,YAAM,MAAM,IAAI,IAAI,MAAM,KAAK,IAAI,EAAE;AAAA,IACvC,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAOA,IAAI;AAGG,SAAS,kBAAwC;AACtD,cAAY,QAAQ,IAAI,CAAC,OAAO,iBAAiB,GAAG,OAAO,qBAAqB,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,MAAM,QAAQ,OAAO,EAAE,MAAM,SAAS,EAAE;AACnI,SAAO;AACT;AAEA,eAAsB,qBACpB,KACA,SACyB;AACzB,QAAM,EAAE,MAAM,SAAS,IAAI,MAAM,gBAAgB;AACjD,MAAI,QAAQ,eAAe,IAAI,SAAS,OAAQ,OAAM,SAAS,oBAAoB,GAAG;AACtF,QAAM,UAAU,KAAK,cAAc,GAAG;AACtC,QAAM,aAAa,SAAS,wBAAwB,SAAS,EAAE,QAAQ,EAAE,MAAM,QAAQ,KAAK,EAAE,CAAC;AAC/F,MAAI,QAAQ,MAAO,YAAW,WAAW;AACzC,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,SAAO,aAAa,QAAQ,OAAO;AACnC,QAAM,SAAyB,CAAC;AAChC,QAAM,QAAwB,CAAC;AAC/B,QAAM,SAAkC,CAAC;AACzC,MAAI,aAAa;AACjB,aAAW,QAAQ,OAAO,OAAO,IAAI,KAAK,EAAG,cAAa,KAAK,IAAI,YAAY,KAAK,UAAU,KAAK,UAAU;AAC7G,MAAI,aAAa;AACjB,QAAM,OAAO,MAAM;AACjB,UAAM,UAAU,WAAW,SAAS,EAAE;AACtC,QAAI,cAAc,CAAC,WAAW,QAAQ,SAAS,EAAE,cAAc,aAAa,EAAG,OAAM,QAAQ,CAAC,MAAM,EAAE,CAAC;AACvG,iBAAa;AACb,WAAO,QAAQ,CAAC,MAAM,EAAE,CAAC;AAAA,EAC3B;AACA,aAAW,UAAU,MAAM;AACzB,UAAM,QAAQ,WAAW,SAAS,EAAE;AACpC,QAAI,MAAO,QAAO,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;AACzC,SAAK;AAAA,EACP,CAAC;AACD,QAAM,cAAc,QAAQ,OAAO,GAAG,YAAY,IAAI;AACtD,MAAI,WAAW;AACf,SAAO;AAAA,IACL,SAAS;AAAA,IACT,MAAM,MAAM,WAAW,KAAK;AAAA,IAC5B,OAAO,MAAM,WAAW,MAAM;AAAA,IAC9B,MAAM,CAAC,MAAM,WAAW,KAAK,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,IAAI,GAAG,CAAC;AAAA,IAC7D,OAAO,OAAO,EAAE,SAAS,WAAW,SAAS,EAAE,SAAS,OAAO,QAAQ,SAAS,EAAE,aAAa,KAAK,WAAW,aAAa,IAAI;AAAA,IAChI,UAAU,CAAC,MAAM;AACf,UAAI,WAAW,SAAS,EAAE,UAAU,EAAG,YAAW,WAAW;AAAA,IAC/D;AAAA,IACA,UAAU,CAAC,MAAM,OAAO,KAAK,CAAC;AAAA,IAC9B,SAAS,CAAC,MAAM,MAAM,KAAK,CAAC;AAAA,IAC5B,SAAS,CAAC,MAAM,OAAO,KAAK,CAAC;AAAA,IAC7B,QAAQ,CAAC,OAAO,WAAW;AACzB,UAAI,SAAS,KAAK,UAAU,EAAG;AAC/B,UAAI,CAAC,UAAU;AACb,mBAAW,OAAO,QAAQ,EAAE,OAAO,OAAO,CAAC;AAC3C,mBAAW;AAAA,MACb,MAAO,YAAW,OAAO,OAAO,MAAM;AAAA,IACxC;AAAA,IACA,SAAS,MAAM;AACb,kBAAY;AACZ,iBAAW,QAAQ;AAAA,IACrB;AAAA,EACF;AACF;;;ACrIA,IAAM,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgCd,IAAM,OAAO;AAAA,EACX,MAAM;AAAA,EACN,OAAO;AAAA,EACP,UAAU;AAAA,EACV,OAAO;AAAA,EACP,OAAO;AACT;AAEA,IAAM,cAAkC,OAAO,gBAAgB,cAAe,MAAM;AAAC,IAAsC;AAEpH,IAAM,yBAAN,cAAqC,YAAY;AAAA,EACtD,WAAW,qBAA+B;AACxC,WAAO,CAAC,OAAO,YAAY,YAAY,UAAU,QAAQ,SAAS,aAAa;AAAA,EACjF;AAAA,EAEiB;AAAA,EACT;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA,cAA+B,CAAC;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACF;AAAA,EAEjB,cAAc;AACZ,UAAM;AACN,SAAK,OAAO,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAC9C,SAAK,KAAK,YAAY,UAAU,KAAK;AAAA;AAAA;AAAA,0DAGiB,KAAK,IAAI;AAAA;AAAA,kDAEjB,KAAK,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4GAMiD,KAAK,QAAQ;AAAA,kDACvE,KAAK,KAAK;AAAA;AAAA;AAAA;AAAA;AAKxD,UAAM,IAAI,CAAC,QAAgB,KAAK,KAAK,cAAc,GAAG;AACtD,SAAK,MAAM;AAAA,MACT,OAAO,EAAE,QAAQ;AAAA,MACjB,OAAO,EAAE,QAAQ;AAAA,MACjB,KAAK,EAAE,MAAM;AAAA,MACb,MAAM,EAAE,OAAO;AAAA,MACf,MAAM,EAAE,OAAO;AAAA,MACf,OAAO,EAAE,QAAQ;AAAA,MACjB,MAAM,EAAE,OAAO;AAAA,MACf,OAAO,EAAE,QAAQ;AAAA,MACjB,OAAO,EAAE,QAAQ;AAAA,MACjB,OAAO,EAAE,QAAQ;AAAA,MACjB,aAAa,EAAE,WAAW;AAAA,MAC1B,MAAM,EAAE,OAAO;AAAA,MACf,MAAM,EAAE,OAAO;AAAA,MACf,OAAO,EAAE,QAAQ;AAAA,IACnB;AACA,SAAK,KAAK;AAAA,EACZ;AAAA;AAAA;AAAA,EAKA,IAAI,WAA4B;AAC9B,WAAO,KAAK;AAAA,EACd;AAAA,EACA,IAAI,SAAS,KAAsB;AACjC,SAAK,WAAW;AAChB,SAAK,KAAK,KAAK;AAAA,EACjB;AAAA;AAAA,EAGA,IAAI,WAA4B;AAC9B,WAAO,CAAC,GAAG,KAAK,WAAW;AAAA,EAC7B;AAAA,EACA,IAAI,SAAS,OAAoC;AAC/C,SAAK,mBAAmB,QAAQ,kBAAkB,OAAO,KAAK,QAAQ,IAAI;AAC1E,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEA,IAAI,cAAsB;AACxB,WAAO,KAAK,QAAQ,MAAM,EAAE,SAAS;AAAA,EACvC;AAAA,EACA,IAAI,YAAY,SAAiB;AAC/B,SAAK,KAAK,OAAO;AAAA,EACnB;AAAA,EACA,IAAI,WAAmB;AACrB,WAAO,KAAK,QAAQ,MAAM,EAAE,aAAa;AAAA,EAC3C;AAAA,EACA,IAAI,SAAkB;AACpB,WAAO,EAAE,KAAK,QAAQ,MAAM,EAAE,WAAW;AAAA,EAC3C;AAAA,EACA,IAAI,qBAAgD;AAClD,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,OAAa;AACX,SAAK,QAAQ,KAAK;AAAA,EACpB;AAAA,EACA,QAAc;AACZ,SAAK,QAAQ,MAAM;AAAA,EACrB;AAAA,EACA,SAAe;AACb,QAAI,KAAK,OAAQ,MAAK,KAAK;AAAA,QACtB,MAAK,MAAM;AAAA,EAClB;AAAA,EACA,KAAK,SAAuB;AAC1B,SAAK,QAAQ,KAAK,KAAK,IAAI,KAAK,IAAI,GAAG,OAAO,GAAG,KAAK,YAAY,OAAO,CAAC;AAC1E,SAAK,OAAO;AAAA,EACd;AAAA;AAAA,EAEA,YAAY,OAA8B;AACxC,UAAM,UAAU,OAAO,UAAU,WAAW,KAAK,YAAY,KAAK,IAAI,KAAK,YAAY,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK;AACjH,QAAI,QAAS,MAAK,KAAK,QAAQ,KAAK;AAAA,EACtC;AAAA;AAAA,EAIA,oBAA0B;AACxB,QAAI,OAAO,mBAAmB,aAAa;AACzC,WAAK,iBAAiB,IAAI,eAAe,MAAM,KAAK,WAAW,CAAC;AAChE,WAAK,eAAe,QAAQ,KAAK,IAAI,KAAK;AAAA,IAC5C;AACA,QAAI,CAAC,KAAK,OAAQ,MAAK,KAAK,KAAK;AAAA,EACnC;AAAA,EAEA,uBAA6B;AAC3B,SAAK,gBAAgB,WAAW;AAChC,SAAK,iBAAiB;AACtB,SAAK,QAAQ,QAAQ;AACrB,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,yBAAyB,MAAc,UAAyB,MAA2B;AACzF,QAAI,aAAa,QAAQ,CAAC,KAAK,YAAa;AAC5C,QAAI,SAAS,SAAS,SAAS,cAAc,SAAS,cAAe,MAAK,KAAK,KAAK;AAAA,aAC3E,SAAS,WAAY,MAAK,KAAK,qBAAqB;AAAA,aACpD,SAAS,QAAS,MAAK,QAAQ,SAAS,SAAS,IAAI;AAAA,EAChE;AAAA;AAAA,EAIA,MAAc,OAAsB;AAClC,UAAM,QAAQ,EAAE,KAAK;AACrB,SAAK,QAAQ,QAAQ;AACrB,SAAK,SAAS;AACd,SAAK,gBAAgB,YAAY;AACjC,SAAK,UAAU,IAAI;AACnB,SAAK,IAAI,MAAM,gBAAgB;AAC/B,UAAM,OAAO,KAAK,aAAa,MAAM;AACrC,UAAM,QAAQ,KAAK,aAAa,OAAO;AACvC,QAAI;AACF,UAAI;AACJ,UAAI,MAAM,KAAK;AACf,YAAM,SAAS,KAAK,aAAa,UAAU;AAC3C,UAAI,CAAC,OAAO,QAAQ;AAClB,cAAM,WAAW,MAAM,MAAM,MAAM;AACnC,YAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,kBAAkB,MAAM,UAAU,SAAS,MAAM,GAAG;AACtF,cAAO,MAAM,SAAS,KAAK;AAC3B,YAAI,UAAU,KAAK,UAAW;AAAA,MAChC;AACA,UAAI,KAAK;AACP,cAAM,OAAO,KAAK,aAAa,aAAa,MAAM,SAAS,IAAI,IAAI,QAAQ,SAAS,IAAI,EAAE,OAAO;AACjG,cAAM,WAAW,oBAAoB,KAAK,IAAI;AAC9C,aAAK,MAAM,YAAY,eAAe,GAAG,SAAS,SAAS,KAAK,IAAI,SAAS,SAAS,MAAM,EAAE;AAC9F,iBAAS,MAAM,qBAAqB,UAAU,EAAE,MAAM,OAAO,aAAa,KAAK,aAAa,SAAS,EAAE,CAAC;AACxG,aAAK,cAAc,CAAC,cAAc,oBAAoB,SAAS,SAAS,SAAS;AAAA,MACnF,OAAO;AACL,cAAM,MAAM,KAAK,aAAa,KAAK;AACnC,YAAI,CAAC,IAAK;AACV,cAAM,SAAS,KAAK,aAAa,QAAQ;AACzC,iBAAS,kBAAkB,KAAK,EAAE,MAAM,OAAO,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC,EAAG,CAAC;AAC9E,aAAK,cAAc;AACnB,cAAM,QAAQ,OAAO;AACrB,cAAM,iBAAiB,kBAAkB,MAAM;AAC7C,cAAI,MAAM,cAAc,MAAM,YAAa,MAAK,MAAM,YAAY,eAAe,GAAG,MAAM,UAAU,IAAI,MAAM,WAAW,EAAE;AAC3H,eAAK,gBAAgB;AAAA,QACvB,CAAC;AAAA,MACH;AACA,UAAI,UAAU,KAAK,WAAW;AAC5B,eAAO,QAAQ;AACf;AAAA,MACF;AACA,WAAK,SAAS;AACd,WAAK,IAAI,MAAM,OAAO,OAAO,OAAO;AACpC,UAAI,aAAa;AACjB,aAAO,SAAS,MAAM;AACpB,cAAM,UAAU,OAAO,MAAM,EAAE;AAC/B,YAAI,YAAY,YAAY;AAC1B,uBAAa;AACb,eAAK,gBAAgB,gBAAgB,OAAO;AAC5C,eAAK,SAAS,UAAU,SAAS,OAAO;AAAA,QAC1C;AACA,aAAK,OAAO;AACZ,aAAK,SAAS,YAAY;AAAA,MAC5B,CAAC;AACD,aAAO,QAAQ,MAAM,KAAK,SAAS,OAAO,CAAC;AAC3C,aAAO,QAAQ,CAAC,YAAY;AAC1B,aAAK,UAAU,OAAO;AACtB,aAAK,SAAS,SAAS,EAAE,QAAQ,CAAC;AAAA,MACpC,CAAC;AACD,WAAK,WAAW;AAChB,YAAM,KAAK,qBAAqB;AAChC,WAAK,gBAAgB;AACrB,WAAK,aAAa,cAAc,EAAE;AAClC,WAAK,OAAO;AACZ,WAAK,SAAS,OAAO;AACrB,UAAI,KAAK,aAAa,UAAU,EAAG,QAAO,KAAK;AAAA,IACjD,SAAS,OAAO;AACd,UAAI,UAAU,KAAK,UAAW;AAC9B,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,WAAK,UAAU,OAAO;AACtB,WAAK,SAAS,SAAS,EAAE,QAAQ,CAAC;AAAA,IACpC;AAAA,EACF;AAAA,EAEQ;AAAA,EACA;AAAA,EAER,MAAc,uBAAsC;AAClD,UAAM,MAAM,KAAK,aAAa,UAAU;AACxC,QAAI,CAAC,KAAK;AACR,WAAK,oBAAoB;AACzB;AAAA,IACF;AACA,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,GAAG;AAChC,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,WAAK,oBAAoB,KAAK,UAAU,EAAE,WAAW,QAAQ,IAAI,iBAAiB,IAAI,IAAI,kBAAkB,KAAK,MAAM,IAAI,GAAG,KAAK,QAAQ;AAAA,IAC7I,QAAQ;AACN,WAAK,oBAAoB;AAAA,IAC3B;AACA,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEQ,kBAAwB;AAC9B,UAAM,WAAW,KAAK;AACtB,SAAK,cAAc,KAAK,oBAAoB,KAAK,qBAAqB,KAAK,cAAc,QAAQ,KAAK,CAAC;AACvG,UAAM,EAAE,OAAO,MAAM,YAAY,IAAI,KAAK;AAC1C,UAAM,gBAAgB;AACtB,SAAK,gBAAgB;AACrB,gBAAY,SAAS,KAAK,YAAY,WAAW;AACjD,eAAW,CAAC,GAAG,OAAO,KAAK,KAAK,YAAY,QAAQ,GAAG;AACrD,UAAI,WAAW,KAAK,QAAQ,QAAQ,GAAG;AACrC,cAAM,OAAO,SAAS,cAAc,KAAK;AACzC,aAAK,YAAY;AACjB,aAAK,MAAM,OAAO,GAAI,QAAQ,QAAQ,WAAY,GAAG;AACrD,aAAK,QAAQ,QAAQ;AACrB,cAAM,OAAO,IAAI;AAAA,MACnB;AACA,YAAM,KAAK,SAAS,cAAc,IAAI;AACtC,YAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,aAAO,OAAO;AACd,aAAO,QAAQ,OAAO,IAAI,OAAO,CAAC;AAClC,aAAO,YAAY;AACnB,MAAC,OAAO,cAAc,IAAI,EAAkB,cAAc,WAAW,QAAQ,KAAK;AAClF,MAAC,OAAO,cAAc,IAAI,EAAkB,cAAc,QAAQ;AAClE,SAAG,OAAO,MAAM;AAChB,WAAK,OAAO,EAAE;AAAA,IAChB;AACA,SAAK,iBAAiB;AACtB,SAAK,OAAO;AAAA,EACd;AAAA;AAAA,EAIQ,OAAa;AACnB,UAAM,EAAE,KAAK,MAAM,OAAO,aAAa,MAAM,MAAM,MAAM,IAAI,KAAK;AAClE,QAAI,iBAAiB,SAAS,MAAM,KAAK,KAAK,CAAC;AAC/C,SAAK,iBAAiB,SAAS,MAAM,KAAK,OAAO,CAAC;AAClD,SAAK,IAAI,MAAM,iBAAiB,SAAS,MAAM,KAAK,OAAO,CAAC;AAC5D,SAAK,iBAAiB,SAAS,MAAM;AACnC,YAAM,QAAQ,CAAC,KAAK,aAAa,OAAO;AACxC,WAAK,gBAAgB,SAAS,KAAK;AACnC,WAAK,QAAQ,SAAS,KAAK;AAC3B,WAAK,YAAY,QAAQ,KAAK,QAAQ,KAAK;AAC3C,WAAK,aAAa,cAAc,QAAQ,WAAW,MAAM;AAAA,IAC3D,CAAC;AACD,gBAAY,iBAAiB,SAAS,MAAM;AAC1C,YAAM,OAAO,KAAK;AAClB,WAAK,SAAS,CAAC;AACf,kBAAY,aAAa,iBAAiB,OAAO,IAAI,CAAC;AACtD,UAAI,KAAM,EAAC,KAAK,cAAiC,6BAA6B,KAAK,KAAK,cAAiC,QAAQ,IAAI,MAAM;AAAA,IAC7I,CAAC;AACD,SAAK,iBAAiB,SAAS,CAAC,UAAU;AACxC,YAAM,SAAU,MAAM,OAAuB,QAAQ,QAAQ;AAC7D,UAAI,CAAC,OAAQ;AACb,WAAK,YAAY,OAAO,OAAO,QAAQ,OAAO,CAAC,CAAC;AAChD,WAAK,SAAS;AACd,kBAAY,aAAa,iBAAiB,OAAO;AAAA,IACnD,CAAC;AACD,UAAM,kBAAkB,CAAC,UAA8B;AACrD,YAAM,OAAO,MAAM,sBAAsB;AACzC,UAAI,KAAK,SAAS,EAAG;AACrB,WAAK,MAAO,MAAM,UAAU,KAAK,QAAQ,KAAK,QAAS,KAAK,QAAQ;AAAA,IACtE;AACA,UAAM,iBAAiB,eAAe,CAAC,UAAU;AAC/C,WAAK,WAAW;AAChB,YAAM,oBAAoB,MAAM,SAAS;AACzC,sBAAgB,KAAK;AAAA,IACvB,CAAC;AACD,UAAM,iBAAiB,eAAe,CAAC,UAAU;AAC/C,UAAI,KAAK,SAAU,iBAAgB,KAAK;AAAA,IAC1C,CAAC;AACD,UAAM,iBAAiB,aAAa,MAAM;AACxC,WAAK,WAAW;AAAA,IAClB,CAAC;AACD,UAAM,OAAO,CAAC,UAA+B;AAC3C,YAAM,OAAO,MAAM,WAAW,KAAK;AACnC,UAAI,MAAM,QAAQ,OAAO,MAAM,QAAQ,IAAK,MAAK,OAAO;AAAA,eAC/C,MAAM,QAAQ,gBAAgB,MAAM,QAAQ,IAAK,MAAK,KAAK,KAAK,cAAc,IAAI;AAAA,eAClF,MAAM,QAAQ,eAAe,MAAM,QAAQ,IAAK,MAAK,KAAK,KAAK,cAAc,IAAI;AAAA,eACjF,MAAM,QAAQ,OAAQ,MAAK,KAAK,CAAC;AAAA,eACjC,MAAM,QAAQ,MAAO,MAAK,KAAK,KAAK,QAAQ;AAAA,eAC5C,MAAM,QAAQ,IAAM,MAAK,YAAY,CAAC;AAAA,eACtC,MAAM,QAAQ,IAAK,MAAK,YAAY,EAAE;AAAA,eACtC,MAAM,QAAQ,SAAU,MAAK,SAAS;AAAA,UAC1C;AACL,YAAM,eAAe;AAAA,IACvB;AACA,UAAM,iBAAiB,WAAW,IAAI;AAAA,EACxC;AAAA,EAEQ,YAAY,WAAyB;AAC3C,QAAI,KAAK,YAAY,WAAW,EAAG;AACnC,UAAM,IAAI,KAAK;AACf,QAAI,YAAY,GAAG;AACjB,YAAM,OAAO,KAAK,YAAY,KAAK,CAAC,MAAM,EAAE,QAAQ,IAAI,IAAI;AAC5D,UAAI,KAAM,MAAK,KAAK,KAAK,KAAK;AAAA,IAChC,OAAO;AACL,YAAM,WAAW,CAAC,GAAG,KAAK,WAAW,EAAE,QAAQ,EAAE,KAAK,CAAC,MAAM,EAAE,QAAQ,IAAI,CAAC;AAC5E,WAAK,KAAK,UAAU,SAAS,CAAC;AAAA,IAChC;AAAA,EACF;AAAA,EAEQ,SAAe;AACrB,UAAM,QAAQ,KAAK,QAAQ,MAAM,KAAK,EAAE,SAAS,OAAO,OAAO,GAAG,WAAW,EAAE;AAC/E,UAAM,EAAE,MAAM,MAAM,OAAO,OAAO,MAAM,OAAO,KAAK,IAAI,KAAK;AAC7D,UAAM,WAAW,MAAM,YAAY,IAAI,KAAK,IAAI,GAAG,MAAM,QAAQ,MAAM,SAAS,IAAI;AACpF,SAAK,cAAc,GAAG,WAAW,MAAM,KAAK,CAAC,MAAM,WAAW,MAAM,SAAS,CAAC;AAC9E,SAAK,MAAM,QAAQ,GAAG,WAAW,GAAG;AACpC,UAAM,MAAM,OAAO,GAAG,WAAW,GAAG;AACpC,UAAM,aAAa,iBAAiB,OAAO,KAAK,MAAM,MAAM,SAAS,CAAC,CAAC;AACvE,UAAM,aAAa,iBAAiB,OAAO,KAAK,MAAM,MAAM,KAAK,CAAC,CAAC;AACnE,UAAM,aAAa,kBAAkB,KAAK,WAAW;AACrD,UAAM,SAAS,MAAM,UAAU,UAAU;AACzC,QAAI,KAAK,aAAa,YAAY,MAAM,QAAQ;AAC9C,WAAK,YAAY,MAAM,UAAU,KAAK,QAAQ,KAAK;AACnD,WAAK,aAAa,cAAc,MAAM;AAAA,IACxC;AACA,UAAM,UAAU,UAAU,KAAK,aAAa,MAAM,KAAK;AACvD,UAAM,cAAc,SAAS,SAAS;AACtC,QAAI,SAAS,OAAO,KAAK,gBAAgB,IAAI;AAC3C,WAAK,iBAAiB;AACtB,iBAAW,UAAU,MAAM,KAAK,KAAK,iBAAiB,QAAQ,CAAC,GAAG;AAChE,eAAO,aAAa,gBAAgB,OAAO,KAAK,YAAY,OAAO,OAAO,QAAQ,OAAO,CAAC,CAAC,GAAG,OAAO,SAAS,EAAE,CAAC;AAAA,MACnH;AACA,WAAK,SAAS,iBAAiB,EAAE,SAAS,WAAW,KAAK,CAAC;AAAA,IAC7D;AAAA,EACF;AAAA,EAEQ,aAAmB;AACzB,UAAM,OAAO,KAAK,IAAI,MAAM,sBAAsB;AAClD,SAAK,QAAQ,OAAO,KAAK,OAAO,KAAK,MAAM;AAAA,EAC7C;AAAA,EAEQ,UAAU,SAA8B;AAC9C,SAAK,IAAI,MAAM,SAAS,YAAY;AACpC,SAAK,IAAI,MAAM,cAAc,WAAW;AAAA,EAC1C;AAAA,EAEQ,SAAS,MAAc,QAAwB;AACrD,SAAK,cAAc,IAAI,YAAY,MAAM,EAAE,QAAQ,SAAS,SAAS,cAAc,UAAU,KAAK,CAAC,CAAC;AAAA,EACtG;AACF;AAGO,SAAS,sBAAsB,UAAU,oBAAmD;AACjG,MAAI,OAAO,mBAAmB,YAAa,QAAO;AAClD,QAAM,WAAW,eAAe,IAAI,OAAO;AAC3C,MAAI,SAAU,QAAO;AACrB,iBAAe,OAAO,SAAS,sBAAsB;AACrD,SAAO;AACT;;;ACjbA,sBAAsB;","names":[]}
package/dist/define.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  defineMiraiclipPlayer
3
- } from "./chunk-TMHYBF54.js";
3
+ } from "./chunk-XKPHDDUA.js";
4
4
 
5
5
  // src/define.ts
6
6
  defineMiraiclipPlayer();
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/define.ts"],"sourcesContent":["/** Side-effect entry: `import \"@miraiclip/player/define\"` registers <miraiclip-player>. */\nimport { defineMiraiclipPlayer } from \"./element.js\";\n\ndefineMiraiclipPlayer();\n"],"mappings":";;;;;AAGA,sBAAsB;","names":[]}
1
+ {"version":3,"sources":["../src/define.ts"],"sourcesContent":["/** Side-effect entry: `import \"@miraiclip/player/define\"` registers <miraiclip-player>. */\r\nimport { defineMiraiclipPlayer } from \"./element.js\";\r\n\r\ndefineMiraiclipPlayer();\r\n"],"mappings":";;;;;AAGA,sBAAsB;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/chapters.ts","../src/engines.ts","../src/element.ts"],"sourcesContent":["export { MiraiclipPlayerElement, defineMiraiclipPlayer } from \"./element.js\";\nexport { chapterAt, chaptersFromMarkers, formatTime, normalizeChapters, parseVttChapters } from \"./chapters.js\";\nexport type { PlayerChapter } from \"./chapters.js\";\nexport { resolveAssetSources } from \"./engines.js\";\n","/**\n * Chapters for the player: from a document's markers, from JSON, or from a\n * WebVTT chapters track. Pure functions — no DOM.\n */\nexport interface PlayerChapter {\n id: string;\n label: string;\n /** Seconds. */\n start: number;\n end: number;\n}\n\ninterface MarkerLike {\n id: string;\n timeUs: number;\n label: string;\n durationUs?: number;\n}\n\n/**\n * A document's markers as chapters (same rule as core's `markerChapters`):\n * each runs for its own `durationUs`, or until the next marker; the last one\n * until `durationS`.\n */\nexport function chaptersFromMarkers(markers: Record<string, MarkerLike> | undefined, durationS: number): PlayerChapter[] {\n const list = Object.values(markers ?? {}).sort((a, b) => a.timeUs - b.timeUs || a.id.localeCompare(b.id));\n return list.map((m, i) => {\n const start = m.timeUs / 1e6;\n const next = list[i + 1];\n const end = m.durationUs !== undefined ? (m.timeUs + m.durationUs) / 1e6 : next ? next.timeUs / 1e6 : Math.max(durationS, start);\n return { id: m.id, label: m.label, start, end };\n });\n}\n\n/** Parse a WebVTT chapters track (\"00:01.000 --> 00:05.000\" cues with a title line). */\nexport function parseVttChapters(vtt: string): PlayerChapter[] {\n const chapters: PlayerChapter[] = [];\n const blocks = vtt.replace(/\\r/g, \"\").split(/\\n{2,}/);\n const time = (t: string): number => {\n const parts = t.trim().split(\":\").map(Number);\n let s = 0;\n for (const p of parts) s = s * 60 + p;\n return s;\n };\n for (const block of blocks) {\n const lines = block.split(\"\\n\").filter((l) => l.trim() !== \"\");\n const at = lines.findIndex((l) => l.includes(\"-->\"));\n if (at < 0) continue;\n const [from, to] = lines[at]!.split(\"-->\").map((s) => s.trim().split(/\\s+/)[0]!);\n const label = lines.slice(at + 1).join(\" \").trim();\n if (!label) continue;\n chapters.push({ id: at > 0 ? lines[0]!.trim() : `c${chapters.length + 1}`, label, start: time(from!), end: time(to!) });\n }\n return chapters.sort((a, b) => a.start - b.start);\n}\n\n/** Accept `[{ label, start, end? }]` (seconds) or `[{ label, timeUs, durationUs? }]` and normalize. */\nexport function normalizeChapters(input: unknown, durationS: number): PlayerChapter[] {\n if (!Array.isArray(input)) return [];\n const raw = input\n .map((c, i) => {\n const o = c as Record<string, unknown>;\n const label = String(o[\"label\"] ?? o[\"title\"] ?? \"\");\n const start = typeof o[\"start\"] === \"number\" ? o[\"start\"] : typeof o[\"timeUs\"] === \"number\" ? o[\"timeUs\"] / 1e6 : NaN;\n const end =\n typeof o[\"end\"] === \"number\"\n ? o[\"end\"]\n : typeof o[\"durationUs\"] === \"number\" && typeof o[\"timeUs\"] === \"number\"\n ? (o[\"timeUs\"] + o[\"durationUs\"]) / 1e6\n : undefined;\n return { id: String(o[\"id\"] ?? `c${i + 1}`), label, start, end };\n })\n .filter((c) => c.label && Number.isFinite(c.start))\n .sort((a, b) => a.start - b.start);\n return raw.map((c, i) => ({ id: c.id, label: c.label, start: c.start, end: c.end ?? raw[i + 1]?.start ?? Math.max(durationS, c.start) }));\n}\n\n/**\n * The chapter playing at `timeS` (the latest one that has started and not\n * ended). Media elements land a seek on a frame boundary, often a few ms\n * before the requested time, so a chapter counts as started `toleranceS`\n * early (default 50 ms).\n */\nexport function chapterAt(chapters: readonly PlayerChapter[], timeS: number, toleranceS = 0.05): PlayerChapter | undefined {\n let found: PlayerChapter | undefined;\n for (const c of chapters) if (c.start <= timeS + toleranceS && timeS < c.end) found = c;\n return found;\n}\n\n/** \"1:05\" / \"1:02:03\". */\nexport function formatTime(seconds: number): string {\n const s = Math.max(0, Math.floor(seconds + 1e-6));\n const h = Math.floor(s / 3600);\n const m = Math.floor((s % 3600) / 60);\n const ss = String(s % 60).padStart(2, \"0\");\n return h > 0 ? `${h}:${String(m).padStart(2, \"0\")}:${ss}` : `${m}:${ss}`;\n}\n","/**\n * Playback engines behind the element: a native <video> for rendered files,\n * and the Miraiclip renderer for live documents (loaded on demand, so a\n * video-only page never downloads the renderer).\n */\nexport interface EngineState {\n playing: boolean;\n timeS: number;\n durationS: number;\n}\n\nexport interface PlaybackEngine {\n readonly element: HTMLElement;\n play(): void;\n pause(): void;\n seek(seconds: number): void;\n state(): EngineState;\n setMuted(muted: boolean): void;\n /** Called on any time/state change. */\n onChange(listener: () => void): void;\n onEnded(listener: () => void): void;\n onError(listener: (message: string) => void): void;\n resize(width: number, height: number): void;\n destroy(): void;\n}\n\nexport function createVideoEngine(src: string, options: { loop: boolean; muted: boolean; poster?: string }): PlaybackEngine {\n const video = document.createElement(\"video\");\n video.src = src;\n video.preload = \"metadata\";\n video.playsInline = true;\n video.loop = options.loop;\n video.muted = options.muted;\n if (options.poster) video.poster = options.poster;\n video.setAttribute(\"part\", \"media\");\n const change: (() => void)[] = [];\n const ended: (() => void)[] = [];\n const errors: ((m: string) => void)[] = [];\n const emit = () => change.forEach((l) => l());\n for (const type of [\"timeupdate\", \"play\", \"pause\", \"durationchange\", \"loadedmetadata\", \"seeked\"]) video.addEventListener(type, emit);\n video.addEventListener(\"ended\", () => ended.forEach((l) => l()));\n video.addEventListener(\"error\", () => errors.forEach((l) => l(video.error?.message || \"the video could not be played\")));\n return {\n element: video,\n play: () => void video.play()?.catch?.((e: Error) => errors.forEach((l) => l(e.message))),\n pause: () => video.pause(),\n seek: (s) => {\n video.currentTime = Math.max(0, s);\n emit();\n },\n state: () => ({ playing: !video.paused && !video.ended, timeS: video.currentTime || 0, durationS: Number.isFinite(video.duration) ? video.duration : 0 }),\n setMuted: (m) => {\n video.muted = m;\n },\n onChange: (l) => change.push(l),\n onEnded: (l) => ended.push(l),\n onError: (l) => errors.push(l),\n resize: () => undefined,\n destroy: () => {\n video.pause();\n video.removeAttribute(\"src\");\n video.load();\n },\n };\n}\n\n/** Rewrite relative asset `src` paths so they resolve against `base` (the document's own URL by default). */\nexport function resolveAssetSources<T extends { assets: Record<string, { src: string }> }>(doc: T, base: string | undefined): T {\n if (!base) return doc;\n const copy = JSON.parse(JSON.stringify(doc)) as T;\n for (const asset of Object.values(copy.assets)) {\n if (/^(?:[a-z][a-z0-9+.-]*:|\\/\\/)/i.test(asset.src)) continue; // absolute, data:, blob:\n try {\n asset.src = new URL(asset.src, base).href;\n } catch {\n // leave as-is\n }\n }\n return copy;\n}\n\ninterface LiveModules {\n core: typeof import(\"@miraiclip/core\");\n renderer: typeof import(\"@miraiclip/renderer\");\n}\n\nlet modules: Promise<LiveModules> | undefined;\n\n/** Load core + renderer once (they are optional peers: only documents need them). */\nexport function loadLiveModules(): Promise<LiveModules> {\n modules ??= Promise.all([import(\"@miraiclip/core\"), import(\"@miraiclip/renderer\")]).then(([core, renderer]) => ({ core, renderer }));\n return modules;\n}\n\nexport async function createDocumentEngine(\n doc: import(\"@miraiclip/core\").ProjectDocument,\n options: { loop: boolean; muted: boolean; loadPlugins: boolean },\n): Promise<PlaybackEngine> {\n const { core, renderer } = await loadLiveModules();\n if (options.loadPlugins && doc.plugins?.length) await renderer.loadDocumentPlugins(doc);\n const project = core.createProject(doc);\n const controller = renderer.createPreviewController(project, { player: { loop: options.loop } });\n if (options.muted) controller.toggleMute();\n const canvas = document.createElement(\"canvas\");\n canvas.setAttribute(\"part\", \"media\");\n const change: (() => void)[] = [];\n const ended: (() => void)[] = [];\n const errors: ((m: string) => void)[] = [];\n let durationUs = 0;\n for (const clip of Object.values(doc.clips)) durationUs = Math.max(durationUs, clip.startUs + clip.durationUs);\n let wasPlaying = false;\n const emit = () => {\n const playing = controller.getState().playing;\n if (wasPlaying && !playing && project.getState().playheadUs >= durationUs - 1) ended.forEach((l) => l());\n wasPlaying = playing;\n change.forEach((l) => l());\n };\n controller.subscribe(() => {\n const error = controller.getState().lastError;\n if (error) errors.forEach((l) => l(error));\n emit();\n });\n const offPlayhead = project.events.on(\"playhead\", emit);\n let attached = false;\n return {\n element: canvas,\n play: () => controller.play(),\n pause: () => controller.pause(),\n seek: (s) => controller.seek(Math.round(Math.max(0, s) * 1e6)),\n state: () => ({ playing: controller.getState().playing, timeS: project.getState().playheadUs / 1e6, durationS: durationUs / 1e6 }),\n setMuted: (m) => {\n if (controller.getState().muted !== m) controller.toggleMute();\n },\n onChange: (l) => change.push(l),\n onEnded: (l) => ended.push(l),\n onError: (l) => errors.push(l),\n resize: (width, height) => {\n if (width <= 0 || height <= 0) return;\n if (!attached) {\n controller.attach(canvas, { width, height });\n attached = true;\n } else controller.resize(width, height);\n },\n destroy: () => {\n offPlayhead();\n controller.destroy();\n },\n };\n}\n","/**\n * <miraiclip-player>: plays a rendered video file or a live Miraiclip\n * document, with a scrubber, chapters (from the document's markers, a JSON\n * list or a WebVTT chapters file) and keyboard control. No framework.\n *\n * <miraiclip-player src=\"launch.mp4\" chapters=\"launch.chapters.vtt\" controls></miraiclip-player>\n * <miraiclip-player document=\"video.miraiclip.json\" controls></miraiclip-player>\n *\n * Events: ready, play, pause, timeupdate, chapterchange ({ chapter }), ended, error ({ message }).\n */\nimport { chapterAt, chaptersFromMarkers, formatTime, normalizeChapters, parseVttChapters, type PlayerChapter } from \"./chapters.js\";\nimport { createDocumentEngine, createVideoEngine, resolveAssetSources, type PlaybackEngine } from \"./engines.js\";\n\ntype Doc = import(\"@miraiclip/core\").ProjectDocument;\n\nconst STYLE = `\n:host{display:block;position:relative;--mc-accent:#22d3ee;--mc-bg:#000;--mc-fg:#fff;--mc-radius:12px;--mc-font:Inter,system-ui,sans-serif;color:var(--mc-fg);font-family:var(--mc-font)}\n:host([hidden]){display:none}\n.frame{position:relative;width:100%;aspect-ratio:var(--mc-aspect,16/9);background:var(--mc-bg);border-radius:var(--mc-radius);overflow:hidden;outline:none}\n.stage,.stage>*{position:absolute;inset:0;width:100%;height:100%;display:block;object-fit:contain}\n.big{position:absolute;inset:0;margin:auto;width:76px;height:76px;border-radius:50%;border:0;background:#000a;color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center}\n.big svg{width:34px;height:34px;margin-left:4px}\n:host([data-playing]) .big,:host(:not([data-ready])) .big{display:none}\n.bar{position:absolute;left:0;right:0;bottom:0;display:flex;align-items:center;gap:10px;padding:28px 12px 10px;background:linear-gradient(transparent,#000c);opacity:0;transition:opacity .2s}\n:host([controls]) .frame:hover .bar,:host([controls]) .frame:focus-within .bar,:host([controls]:not([data-playing])) .bar{opacity:1}\n:host(:not([controls])) .bar{display:none}\nbutton.icon{background:none;border:0;color:inherit;width:34px;height:34px;padding:5px;cursor:pointer;border-radius:8px;flex:none}\nbutton.icon:hover,button.icon:focus-visible{background:#ffffff22;outline:none}\nbutton svg{width:100%;height:100%;fill:currentColor}\n.time{font-size:13px;font-variant-numeric:tabular-nums;white-space:nowrap;opacity:.9}\n.scrub{position:relative;flex:1;height:22px;cursor:pointer;outline:none;touch-action:none}\n.rail{position:absolute;left:0;right:0;top:9px;height:4px;border-radius:2px;background:#ffffff40}\n.fill{position:absolute;left:0;top:9px;height:4px;border-radius:2px;background:var(--mc-accent);width:0}\n.tick{position:absolute;top:7px;width:2px;height:8px;background:#fff;opacity:.85;border-radius:1px}\n.thumb{position:absolute;top:4px;width:14px;height:14px;margin-left:-7px;border-radius:50%;background:#fff;box-shadow:0 1px 4px #0008;left:0}\n.scrub:focus-visible .thumb{outline:2px solid var(--mc-accent);outline-offset:2px}\n.label{font-size:13px;max-width:30%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;opacity:.9}\n.menu{position:absolute;right:10px;bottom:56px;max-height:60%;overflow:auto;margin:0;padding:6px;list-style:none;border-radius:10px;background:#111827f2;box-shadow:0 10px 30px #0009;min-width:200px}\n.menu[hidden]{display:none}\n.menu button{display:flex;gap:10px;width:100%;padding:8px 10px;border:0;border-radius:6px;background:none;color:inherit;font:inherit;font-size:14px;text-align:left;cursor:pointer}\n.menu button:hover,.menu button:focus-visible{background:#ffffff1f;outline:none}\n.menu button[aria-current=\"true\"]{color:var(--mc-accent)}\n.menu .t{opacity:.7;font-variant-numeric:tabular-nums}\n.error{position:absolute;left:12px;right:12px;top:12px;padding:8px 12px;border-radius:8px;background:#7f1d1dee;font-size:13px}\n.error[hidden]{display:none}\n`;\n\nconst ICON = {\n play: '<svg viewBox=\"0 0 24 24\"><path d=\"M8 5v14l11-7z\"/></svg>',\n pause: '<svg viewBox=\"0 0 24 24\"><path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/></svg>',\n chapters: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 6h2v2H4zm4 0h12v2H8zM4 11h2v2H4zm4 0h12v2H8zm-4 5h2v2H4zm4 0h12v2H8z\"/></svg>',\n sound: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 9v6h4l5 4V5L8 9zm12.5 3a4.5 4.5 0 0 0-2.5-4v8a4.5 4.5 0 0 0 2.5-4z\"/></svg>',\n muted: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 9v6h4l5 4V5L8 9zm16.6 0L19 7.4 16.4 10 13.8 7.4 12.4 9l2.6 2.6-2.6 2.6 1.4 1.4 2.6-2.6 2.6 2.6 1.6-1.6-2.6-2.6z\"/></svg>',\n};\n\nconst BaseElement: typeof HTMLElement = typeof HTMLElement === \"undefined\" ? (class {} as unknown as typeof HTMLElement) : HTMLElement;\n\nexport class MiraiclipPlayerElement extends BaseElement {\n static get observedAttributes(): string[] {\n return [\"src\", \"document\", \"chapters\", \"poster\", \"loop\", \"muted\", \"assets-base\"];\n }\n\n private readonly root: ShadowRoot;\n private engine: PlaybackEngine | undefined;\n private loadToken = 0;\n private docValue: Doc | undefined;\n private chapterList: PlayerChapter[] = [];\n private explicitChapters: PlayerChapter[] | undefined;\n private currentChapter: PlayerChapter | undefined;\n private resizeObserver: ResizeObserver | undefined;\n private dragging = false;\n private readonly els: Record<\"frame\" | \"stage\" | \"big\" | \"play\" | \"time\" | \"scrub\" | \"fill\" | \"thumb\" | \"ticks\" | \"label\" | \"chaptersBtn\" | \"menu\" | \"mute\" | \"error\", HTMLElement>;\n\n constructor() {\n super();\n this.root = this.attachShadow({ mode: \"open\" });\n this.root.innerHTML = `<style>${STYLE}</style>\n<div class=\"frame\" part=\"frame\" tabindex=\"0\">\n <div class=\"stage\" part=\"stage\"></div>\n <button class=\"big\" part=\"big-play\" aria-label=\"Play\">${ICON.play}</button>\n <div class=\"bar\" part=\"controls\">\n <button class=\"icon play\" aria-label=\"Play\">${ICON.play}</button>\n <span class=\"time\" part=\"time\">0:00 / 0:00</span>\n <div class=\"scrub\" part=\"scrubber\" role=\"slider\" tabindex=\"0\" aria-label=\"Seek\" aria-valuemin=\"0\" aria-valuemax=\"0\" aria-valuenow=\"0\">\n <div class=\"rail\"></div><div class=\"fill\"></div><div class=\"ticks\"></div><div class=\"thumb\"></div>\n </div>\n <span class=\"label\" part=\"chapter-label\"></span>\n <button class=\"icon chapters\" aria-label=\"Chapters\" aria-haspopup=\"true\" aria-expanded=\"false\" hidden>${ICON.chapters}</button>\n <button class=\"icon mute\" aria-label=\"Mute\">${ICON.sound}</button>\n </div>\n <ol class=\"menu\" part=\"chapters\" hidden></ol>\n <div class=\"error\" part=\"error\" role=\"alert\" hidden></div>\n</div>`;\n const $ = (sel: string) => this.root.querySelector(sel) as HTMLElement;\n this.els = {\n frame: $(\".frame\"),\n stage: $(\".stage\"),\n big: $(\".big\"),\n play: $(\".play\"),\n time: $(\".time\"),\n scrub: $(\".scrub\"),\n fill: $(\".fill\"),\n thumb: $(\".thumb\"),\n ticks: $(\".ticks\"),\n label: $(\".label\"),\n chaptersBtn: $(\".chapters\"),\n menu: $(\".menu\"),\n mute: $(\".mute\"),\n error: $(\".error\"),\n };\n this.wire();\n }\n\n // ------------------------------------------------------------------ API\n\n /** A project document to play live (takes precedence over `document`/`src` attributes). */\n get document(): Doc | undefined {\n return this.docValue;\n }\n set document(doc: Doc | undefined) {\n this.docValue = doc;\n void this.load();\n }\n\n /** Chapters in seconds. Set to override markers / the `chapters` attribute. */\n get chapters(): PlayerChapter[] {\n return [...this.chapterList];\n }\n set chapters(value: PlayerChapter[] | undefined) {\n this.explicitChapters = value ? normalizeChapters(value, this.duration) : undefined;\n this.refreshChapters();\n }\n\n get currentTime(): number {\n return this.engine?.state().timeS ?? 0;\n }\n set currentTime(seconds: number) {\n this.seek(seconds);\n }\n get duration(): number {\n return this.engine?.state().durationS ?? 0;\n }\n get paused(): boolean {\n return !(this.engine?.state().playing ?? false);\n }\n get currentChapterInfo(): PlayerChapter | undefined {\n return this.currentChapter;\n }\n\n play(): void {\n this.engine?.play();\n }\n pause(): void {\n this.engine?.pause();\n }\n toggle(): void {\n if (this.paused) this.play();\n else this.pause();\n }\n seek(seconds: number): void {\n this.engine?.seek(Math.min(Math.max(0, seconds), this.duration || seconds));\n this.render();\n }\n /** Jump to a chapter by id or index. */\n goToChapter(which: string | number): void {\n const chapter = typeof which === \"number\" ? this.chapterList[which] : this.chapterList.find((c) => c.id === which);\n if (chapter) this.seek(chapter.start);\n }\n\n // ------------------------------------------------------------------ lifecycle\n\n connectedCallback(): void {\n if (typeof ResizeObserver !== \"undefined\") {\n this.resizeObserver = new ResizeObserver(() => this.sizeEngine());\n this.resizeObserver.observe(this.els.frame);\n }\n if (!this.engine) void this.load();\n }\n\n disconnectedCallback(): void {\n this.resizeObserver?.disconnect();\n this.resizeObserver = undefined;\n this.engine?.destroy();\n this.engine = undefined;\n }\n\n attributeChangedCallback(name: string, previous: string | null, next: string | null): void {\n if (previous === next || !this.isConnected) return;\n if (name === \"src\" || name === \"document\" || name === \"assets-base\") void this.load();\n else if (name === \"chapters\") void this.loadChapterAttribute();\n else if (name === \"muted\") this.engine?.setMuted(next !== null);\n }\n\n // ------------------------------------------------------------------ loading\n\n private async load(): Promise<void> {\n const token = ++this.loadToken;\n this.engine?.destroy();\n this.engine = undefined;\n this.removeAttribute(\"data-ready\");\n this.showError(null);\n this.els.stage.replaceChildren();\n const loop = this.hasAttribute(\"loop\");\n const muted = this.hasAttribute(\"muted\");\n try {\n let engine: PlaybackEngine | undefined;\n let doc = this.docValue;\n const docUrl = this.getAttribute(\"document\");\n if (!doc && docUrl) {\n const response = await fetch(docUrl);\n if (!response.ok) throw new Error(`could not load ${docUrl} (HTTP ${response.status})`);\n doc = (await response.json()) as Doc;\n if (token !== this.loadToken) return;\n }\n if (doc) {\n const base = this.getAttribute(\"assets-base\") ?? (docUrl ? new URL(docUrl, location.href).href : undefined);\n const resolved = resolveAssetSources(doc, base);\n this.style.setProperty(\"--mc-aspect\", `${resolved.settings.width}/${resolved.settings.height}`);\n engine = await createDocumentEngine(resolved, { loop, muted, loadPlugins: this.hasAttribute(\"plugins\") });\n this.docChapters = (durationS) => chaptersFromMarkers(resolved.markers, durationS);\n } else {\n const src = this.getAttribute(\"src\");\n if (!src) return;\n const poster = this.getAttribute(\"poster\");\n engine = createVideoEngine(src, { loop, muted, ...(poster ? { poster } : {}) });\n this.docChapters = undefined;\n const video = engine.element as HTMLVideoElement;\n video.addEventListener(\"loadedmetadata\", () => {\n if (video.videoWidth && video.videoHeight) this.style.setProperty(\"--mc-aspect\", `${video.videoWidth}/${video.videoHeight}`);\n this.refreshChapters();\n });\n }\n if (token !== this.loadToken) {\n engine.destroy();\n return;\n }\n this.engine = engine;\n this.els.stage.append(engine.element);\n let wasPlaying = false;\n engine.onChange(() => {\n const playing = engine.state().playing;\n if (playing !== wasPlaying) {\n wasPlaying = playing;\n this.toggleAttribute(\"data-playing\", playing);\n this.dispatch(playing ? \"play\" : \"pause\");\n }\n this.render();\n this.dispatch(\"timeupdate\");\n });\n engine.onEnded(() => this.dispatch(\"ended\"));\n engine.onError((message) => {\n this.showError(message);\n this.dispatch(\"error\", { message });\n });\n this.sizeEngine();\n await this.loadChapterAttribute();\n this.refreshChapters();\n this.setAttribute(\"data-ready\", \"\");\n this.render();\n this.dispatch(\"ready\");\n if (this.hasAttribute(\"autoplay\")) engine.play();\n } catch (error) {\n if (token !== this.loadToken) return;\n const message = error instanceof Error ? error.message : String(error);\n this.showError(message);\n this.dispatch(\"error\", { message });\n }\n }\n\n private docChapters: ((durationS: number) => PlayerChapter[]) | undefined;\n private attributeChapters: PlayerChapter[] | undefined;\n\n private async loadChapterAttribute(): Promise<void> {\n const url = this.getAttribute(\"chapters\");\n if (!url) {\n this.attributeChapters = undefined;\n return;\n }\n try {\n const response = await fetch(url);\n const text = await response.text();\n this.attributeChapters = text.trimStart().startsWith(\"WEBVTT\") ? parseVttChapters(text) : normalizeChapters(JSON.parse(text), this.duration);\n } catch {\n this.attributeChapters = undefined;\n }\n this.refreshChapters();\n }\n\n private refreshChapters(): void {\n const duration = this.duration;\n this.chapterList = this.explicitChapters ?? this.attributeChapters ?? this.docChapters?.(duration) ?? [];\n const { ticks, menu, chaptersBtn } = this.els;\n ticks.replaceChildren();\n menu.replaceChildren();\n chaptersBtn.hidden = this.chapterList.length === 0;\n for (const [i, chapter] of this.chapterList.entries()) {\n if (duration > 0 && chapter.start > 0) {\n const tick = document.createElement(\"div\");\n tick.className = \"tick\";\n tick.style.left = `${(chapter.start / duration) * 100}%`;\n tick.title = chapter.label;\n ticks.append(tick);\n }\n const li = document.createElement(\"li\");\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.dataset[\"index\"] = String(i);\n button.innerHTML = `<span class=\"t\"></span><span class=\"n\"></span>`;\n (button.querySelector(\".t\") as HTMLElement).textContent = formatTime(chapter.start);\n (button.querySelector(\".n\") as HTMLElement).textContent = chapter.label;\n li.append(button);\n menu.append(li);\n }\n this.currentChapter = undefined;\n this.render();\n }\n\n // ------------------------------------------------------------------ UI\n\n private wire(): void {\n const { big, play, scrub, chaptersBtn, menu, mute, frame } = this.els;\n big.addEventListener(\"click\", () => this.play());\n play.addEventListener(\"click\", () => this.toggle());\n this.els.stage.addEventListener(\"click\", () => this.toggle());\n mute.addEventListener(\"click\", () => {\n const muted = !this.hasAttribute(\"muted\");\n this.toggleAttribute(\"muted\", muted);\n this.engine?.setMuted(muted);\n mute.innerHTML = muted ? ICON.muted : ICON.sound;\n mute.setAttribute(\"aria-label\", muted ? \"Unmute\" : \"Mute\");\n });\n chaptersBtn.addEventListener(\"click\", () => {\n const open = menu.hidden;\n menu.hidden = !open;\n chaptersBtn.setAttribute(\"aria-expanded\", String(open));\n if (open) (menu.querySelector<HTMLButtonElement>('button[aria-current=\"true\"]') ?? menu.querySelector<HTMLButtonElement>(\"button\"))?.focus();\n });\n menu.addEventListener(\"click\", (event) => {\n const button = (event.target as HTMLElement).closest(\"button\");\n if (!button) return;\n this.goToChapter(Number(button.dataset[\"index\"]));\n menu.hidden = true;\n chaptersBtn.setAttribute(\"aria-expanded\", \"false\");\n });\n const seekFromPointer = (event: PointerEvent): void => {\n const rect = scrub.getBoundingClientRect();\n if (rect.width <= 0) return;\n this.seek(((event.clientX - rect.left) / rect.width) * this.duration);\n };\n scrub.addEventListener(\"pointerdown\", (event) => {\n this.dragging = true;\n scrub.setPointerCapture?.(event.pointerId);\n seekFromPointer(event);\n });\n scrub.addEventListener(\"pointermove\", (event) => {\n if (this.dragging) seekFromPointer(event);\n });\n scrub.addEventListener(\"pointerup\", () => {\n this.dragging = false;\n });\n const keys = (event: KeyboardEvent): void => {\n const step = event.shiftKey ? 10 : 5;\n if (event.key === \" \" || event.key === \"k\") this.toggle();\n else if (event.key === \"ArrowRight\" || event.key === \"l\") this.seek(this.currentTime + step);\n else if (event.key === \"ArrowLeft\" || event.key === \"j\") this.seek(this.currentTime - step);\n else if (event.key === \"Home\") this.seek(0);\n else if (event.key === \"End\") this.seek(this.duration);\n else if (event.key === \"]\" ) this.stepChapter(1);\n else if (event.key === \"[\") this.stepChapter(-1);\n else if (event.key === \"Escape\") menu.hidden = true;\n else return;\n event.preventDefault();\n };\n frame.addEventListener(\"keydown\", keys);\n }\n\n private stepChapter(direction: 1 | -1): void {\n if (this.chapterList.length === 0) return;\n const t = this.currentTime;\n if (direction > 0) {\n const next = this.chapterList.find((c) => c.start > t + 0.25);\n if (next) this.seek(next.start);\n } else {\n const previous = [...this.chapterList].reverse().find((c) => c.start < t - 1);\n this.seek(previous?.start ?? 0);\n }\n }\n\n private render(): void {\n const state = this.engine?.state() ?? { playing: false, timeS: 0, durationS: 0 };\n const { time, fill, thumb, scrub, play, label, menu } = this.els;\n const fraction = state.durationS > 0 ? Math.min(1, state.timeS / state.durationS) : 0;\n time.textContent = `${formatTime(state.timeS)} / ${formatTime(state.durationS)}`;\n fill.style.width = `${fraction * 100}%`;\n thumb.style.left = `${fraction * 100}%`;\n scrub.setAttribute(\"aria-valuemax\", String(Math.round(state.durationS)));\n scrub.setAttribute(\"aria-valuenow\", String(Math.round(state.timeS)));\n scrub.setAttribute(\"aria-valuetext\", time.textContent);\n const wanted = state.playing ? \"Pause\" : \"Play\";\n if (play.getAttribute(\"aria-label\") !== wanted) {\n play.innerHTML = state.playing ? ICON.pause : ICON.play;\n play.setAttribute(\"aria-label\", wanted);\n }\n const chapter = chapterAt(this.chapterList, state.timeS);\n label.textContent = chapter?.label ?? \"\";\n if (chapter?.id !== this.currentChapter?.id) {\n this.currentChapter = chapter;\n for (const button of Array.from(menu.querySelectorAll(\"button\"))) {\n button.setAttribute(\"aria-current\", String(this.chapterList[Number(button.dataset[\"index\"])]?.id === chapter?.id));\n }\n this.dispatch(\"chapterchange\", { chapter: chapter ?? null });\n }\n }\n\n private sizeEngine(): void {\n const rect = this.els.frame.getBoundingClientRect();\n this.engine?.resize(rect.width, rect.height);\n }\n\n private showError(message: string | null): void {\n this.els.error.hidden = message === null;\n this.els.error.textContent = message ?? \"\";\n }\n\n private dispatch(type: string, detail?: unknown): void {\n this.dispatchEvent(new CustomEvent(type, { detail, bubbles: type !== \"timeupdate\", composed: true }));\n }\n}\n\n/** Register the element (default tag `miraiclip-player`). Safe to call twice. */\nexport function defineMiraiclipPlayer(tagName = \"miraiclip-player\"): typeof MiraiclipPlayerElement {\n if (typeof customElements === \"undefined\") return MiraiclipPlayerElement;\n const existing = customElements.get(tagName);\n if (existing) return existing as typeof MiraiclipPlayerElement;\n customElements.define(tagName, MiraiclipPlayerElement);\n return MiraiclipPlayerElement;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"miraiclip-player\": MiraiclipPlayerElement;\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/chapters.ts","../src/engines.ts","../src/element.ts"],"sourcesContent":["export { MiraiclipPlayerElement, defineMiraiclipPlayer } from \"./element.js\";\r\nexport { chapterAt, chaptersFromMarkers, formatTime, normalizeChapters, parseVttChapters } from \"./chapters.js\";\r\nexport type { PlayerChapter } from \"./chapters.js\";\r\nexport { resolveAssetSources } from \"./engines.js\";\r\n","/**\r\n * Chapters for the player: from a document's markers, from JSON, or from a\r\n * WebVTT chapters track. Pure functions — no DOM.\r\n */\r\nexport interface PlayerChapter {\r\n id: string;\r\n label: string;\r\n /** Seconds. */\r\n start: number;\r\n end: number;\r\n}\r\n\r\ninterface MarkerLike {\r\n id: string;\r\n timeUs: number;\r\n label: string;\r\n durationUs?: number;\r\n}\r\n\r\n/**\r\n * A document's markers as chapters (same rule as core's `markerChapters`):\r\n * each runs for its own `durationUs`, or until the next marker; the last one\r\n * until `durationS`.\r\n */\r\nexport function chaptersFromMarkers(markers: Record<string, MarkerLike> | undefined, durationS: number): PlayerChapter[] {\r\n const list = Object.values(markers ?? {}).sort((a, b) => a.timeUs - b.timeUs || a.id.localeCompare(b.id));\r\n return list.map((m, i) => {\r\n const start = m.timeUs / 1e6;\r\n const next = list[i + 1];\r\n const end = m.durationUs !== undefined ? (m.timeUs + m.durationUs) / 1e6 : next ? next.timeUs / 1e6 : Math.max(durationS, start);\r\n return { id: m.id, label: m.label, start, end };\r\n });\r\n}\r\n\r\n/** Parse a WebVTT chapters track (\"00:01.000 --> 00:05.000\" cues with a title line). */\r\nexport function parseVttChapters(vtt: string): PlayerChapter[] {\r\n const chapters: PlayerChapter[] = [];\r\n const blocks = vtt.replace(/\\r/g, \"\").split(/\\n{2,}/);\r\n const time = (t: string): number => {\r\n const parts = t.trim().split(\":\").map(Number);\r\n let s = 0;\r\n for (const p of parts) s = s * 60 + p;\r\n return s;\r\n };\r\n for (const block of blocks) {\r\n const lines = block.split(\"\\n\").filter((l) => l.trim() !== \"\");\r\n const at = lines.findIndex((l) => l.includes(\"-->\"));\r\n if (at < 0) continue;\r\n const [from, to] = lines[at]!.split(\"-->\").map((s) => s.trim().split(/\\s+/)[0]!);\r\n const label = lines.slice(at + 1).join(\" \").trim();\r\n if (!label) continue;\r\n chapters.push({ id: at > 0 ? lines[0]!.trim() : `c${chapters.length + 1}`, label, start: time(from!), end: time(to!) });\r\n }\r\n return chapters.sort((a, b) => a.start - b.start);\r\n}\r\n\r\n/** Accept `[{ label, start, end? }]` (seconds) or `[{ label, timeUs, durationUs? }]` and normalize. */\r\nexport function normalizeChapters(input: unknown, durationS: number): PlayerChapter[] {\r\n if (!Array.isArray(input)) return [];\r\n const raw = input\r\n .map((c, i) => {\r\n const o = c as Record<string, unknown>;\r\n const label = String(o[\"label\"] ?? o[\"title\"] ?? \"\");\r\n const start = typeof o[\"start\"] === \"number\" ? o[\"start\"] : typeof o[\"timeUs\"] === \"number\" ? o[\"timeUs\"] / 1e6 : NaN;\r\n const end =\r\n typeof o[\"end\"] === \"number\"\r\n ? o[\"end\"]\r\n : typeof o[\"durationUs\"] === \"number\" && typeof o[\"timeUs\"] === \"number\"\r\n ? (o[\"timeUs\"] + o[\"durationUs\"]) / 1e6\r\n : undefined;\r\n return { id: String(o[\"id\"] ?? `c${i + 1}`), label, start, end };\r\n })\r\n .filter((c) => c.label && Number.isFinite(c.start))\r\n .sort((a, b) => a.start - b.start);\r\n return raw.map((c, i) => ({ id: c.id, label: c.label, start: c.start, end: c.end ?? raw[i + 1]?.start ?? Math.max(durationS, c.start) }));\r\n}\r\n\r\n/**\r\n * The chapter playing at `timeS` (the latest one that has started and not\r\n * ended). Media elements land a seek on a frame boundary, often a few ms\r\n * before the requested time, so a chapter counts as started `toleranceS`\r\n * early (default 50 ms).\r\n */\r\nexport function chapterAt(chapters: readonly PlayerChapter[], timeS: number, toleranceS = 0.05): PlayerChapter | undefined {\r\n let found: PlayerChapter | undefined;\r\n for (const c of chapters) if (c.start <= timeS + toleranceS && timeS < c.end) found = c;\r\n return found;\r\n}\r\n\r\n/** \"1:05\" / \"1:02:03\". */\r\nexport function formatTime(seconds: number): string {\r\n const s = Math.max(0, Math.floor(seconds + 1e-6));\r\n const h = Math.floor(s / 3600);\r\n const m = Math.floor((s % 3600) / 60);\r\n const ss = String(s % 60).padStart(2, \"0\");\r\n return h > 0 ? `${h}:${String(m).padStart(2, \"0\")}:${ss}` : `${m}:${ss}`;\r\n}\r\n","/**\r\n * Playback engines behind the element: a native <video> for rendered files,\r\n * and the Miraiclip renderer for live documents (loaded on demand, so a\r\n * video-only page never downloads the renderer).\r\n */\r\nexport interface EngineState {\r\n playing: boolean;\r\n timeS: number;\r\n durationS: number;\r\n}\r\n\r\nexport interface PlaybackEngine {\r\n readonly element: HTMLElement;\r\n play(): void;\r\n pause(): void;\r\n seek(seconds: number): void;\r\n state(): EngineState;\r\n setMuted(muted: boolean): void;\r\n /** Called on any time/state change. */\r\n onChange(listener: () => void): void;\r\n onEnded(listener: () => void): void;\r\n onError(listener: (message: string) => void): void;\r\n resize(width: number, height: number): void;\r\n destroy(): void;\r\n}\r\n\r\nexport function createVideoEngine(src: string, options: { loop: boolean; muted: boolean; poster?: string }): PlaybackEngine {\r\n const video = document.createElement(\"video\");\r\n video.src = src;\r\n video.preload = \"metadata\";\r\n video.playsInline = true;\r\n video.loop = options.loop;\r\n video.muted = options.muted;\r\n if (options.poster) video.poster = options.poster;\r\n video.setAttribute(\"part\", \"media\");\r\n const change: (() => void)[] = [];\r\n const ended: (() => void)[] = [];\r\n const errors: ((m: string) => void)[] = [];\r\n const emit = () => change.forEach((l) => l());\r\n for (const type of [\"timeupdate\", \"play\", \"pause\", \"durationchange\", \"loadedmetadata\", \"seeked\"]) video.addEventListener(type, emit);\r\n video.addEventListener(\"ended\", () => ended.forEach((l) => l()));\r\n video.addEventListener(\"error\", () => errors.forEach((l) => l(video.error?.message || \"the video could not be played\")));\r\n return {\r\n element: video,\r\n play: () => void video.play()?.catch?.((e: Error) => errors.forEach((l) => l(e.message))),\r\n pause: () => video.pause(),\r\n seek: (s) => {\r\n video.currentTime = Math.max(0, s);\r\n emit();\r\n },\r\n state: () => ({ playing: !video.paused && !video.ended, timeS: video.currentTime || 0, durationS: Number.isFinite(video.duration) ? video.duration : 0 }),\r\n setMuted: (m) => {\r\n video.muted = m;\r\n },\r\n onChange: (l) => change.push(l),\r\n onEnded: (l) => ended.push(l),\r\n onError: (l) => errors.push(l),\r\n resize: () => undefined,\r\n destroy: () => {\r\n video.pause();\r\n video.removeAttribute(\"src\");\r\n video.load();\r\n },\r\n };\r\n}\r\n\r\n/** Rewrite relative asset `src` paths so they resolve against `base` (the document's own URL by default). */\r\nexport function resolveAssetSources<T extends { assets: Record<string, { src: string }> }>(doc: T, base: string | undefined): T {\r\n if (!base) return doc;\r\n const copy = JSON.parse(JSON.stringify(doc)) as T;\r\n for (const asset of Object.values(copy.assets)) {\r\n if (/^(?:[a-z][a-z0-9+.-]*:|\\/\\/)/i.test(asset.src)) continue; // absolute, data:, blob:\r\n try {\r\n asset.src = new URL(asset.src, base).href;\r\n } catch {\r\n // leave as-is\r\n }\r\n }\r\n return copy;\r\n}\r\n\r\ninterface LiveModules {\r\n core: typeof import(\"@miraiclip/core\");\r\n renderer: typeof import(\"@miraiclip/renderer\");\r\n}\r\n\r\nlet modules: Promise<LiveModules> | undefined;\r\n\r\n/** Load core + renderer once (they are optional peers: only documents need them). */\r\nexport function loadLiveModules(): Promise<LiveModules> {\r\n modules ??= Promise.all([import(\"@miraiclip/core\"), import(\"@miraiclip/renderer\")]).then(([core, renderer]) => ({ core, renderer }));\r\n return modules;\r\n}\r\n\r\nexport async function createDocumentEngine(\r\n doc: import(\"@miraiclip/core\").ProjectDocument,\r\n options: { loop: boolean; muted: boolean; loadPlugins: boolean },\r\n): Promise<PlaybackEngine> {\r\n const { core, renderer } = await loadLiveModules();\r\n if (options.loadPlugins && doc.plugins?.length) await renderer.loadDocumentPlugins(doc);\r\n const project = core.createProject(doc);\r\n const controller = renderer.createPreviewController(project, { player: { loop: options.loop } });\r\n if (options.muted) controller.toggleMute();\r\n const canvas = document.createElement(\"canvas\");\r\n canvas.setAttribute(\"part\", \"media\");\r\n const change: (() => void)[] = [];\r\n const ended: (() => void)[] = [];\r\n const errors: ((m: string) => void)[] = [];\r\n let durationUs = 0;\r\n for (const clip of Object.values(doc.clips)) durationUs = Math.max(durationUs, clip.startUs + clip.durationUs);\r\n let wasPlaying = false;\r\n const emit = () => {\r\n const playing = controller.getState().playing;\r\n if (wasPlaying && !playing && project.getState().playheadUs >= durationUs - 1) ended.forEach((l) => l());\r\n wasPlaying = playing;\r\n change.forEach((l) => l());\r\n };\r\n controller.subscribe(() => {\r\n const error = controller.getState().lastError;\r\n if (error) errors.forEach((l) => l(error));\r\n emit();\r\n });\r\n const offPlayhead = project.events.on(\"playhead\", emit);\r\n let attached = false;\r\n return {\r\n element: canvas,\r\n play: () => controller.play(),\r\n pause: () => controller.pause(),\r\n seek: (s) => controller.seek(Math.round(Math.max(0, s) * 1e6)),\r\n state: () => ({ playing: controller.getState().playing, timeS: project.getState().playheadUs / 1e6, durationS: durationUs / 1e6 }),\r\n setMuted: (m) => {\r\n if (controller.getState().muted !== m) controller.toggleMute();\r\n },\r\n onChange: (l) => change.push(l),\r\n onEnded: (l) => ended.push(l),\r\n onError: (l) => errors.push(l),\r\n resize: (width, height) => {\r\n if (width <= 0 || height <= 0) return;\r\n if (!attached) {\r\n controller.attach(canvas, { width, height });\r\n attached = true;\r\n } else controller.resize(width, height);\r\n },\r\n destroy: () => {\r\n offPlayhead();\r\n controller.destroy();\r\n },\r\n };\r\n}\r\n","/**\r\n * <miraiclip-player>: plays a rendered video file or a live Miraiclip\r\n * document, with a scrubber, chapters (from the document's markers, a JSON\r\n * list or a WebVTT chapters file) and keyboard control. No framework.\r\n *\r\n * <miraiclip-player src=\"launch.mp4\" chapters=\"launch.chapters.vtt\" controls></miraiclip-player>\r\n * <miraiclip-player document=\"video.miraiclip.json\" controls></miraiclip-player>\r\n *\r\n * Events: ready, play, pause, timeupdate, chapterchange ({ chapter }), ended, error ({ message }).\r\n */\r\nimport { chapterAt, chaptersFromMarkers, formatTime, normalizeChapters, parseVttChapters, type PlayerChapter } from \"./chapters.js\";\r\nimport { createDocumentEngine, createVideoEngine, resolveAssetSources, type PlaybackEngine } from \"./engines.js\";\r\n\r\ntype Doc = import(\"@miraiclip/core\").ProjectDocument;\r\n\r\nconst STYLE = `\r\n:host{display:block;position:relative;--mc-accent:#22d3ee;--mc-bg:#000;--mc-fg:#fff;--mc-radius:12px;--mc-font:Inter,system-ui,sans-serif;color:var(--mc-fg);font-family:var(--mc-font)}\r\n:host([hidden]){display:none}\r\n.frame{position:relative;width:100%;aspect-ratio:var(--mc-aspect,16/9);background:var(--mc-bg);border-radius:var(--mc-radius);overflow:hidden;outline:none}\r\n.stage,.stage>*{position:absolute;inset:0;width:100%;height:100%;display:block;object-fit:contain}\r\n.big{position:absolute;inset:0;margin:auto;width:76px;height:76px;border-radius:50%;border:0;background:#000a;color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center}\r\n.big svg{width:34px;height:34px;margin-left:4px}\r\n:host([data-playing]) .big,:host(:not([data-ready])) .big{display:none}\r\n.bar{position:absolute;left:0;right:0;bottom:0;display:flex;align-items:center;gap:10px;padding:28px 12px 10px;background:linear-gradient(transparent,#000c);opacity:0;transition:opacity .2s}\r\n:host([controls]) .frame:hover .bar,:host([controls]) .frame:focus-within .bar,:host([controls]:not([data-playing])) .bar{opacity:1}\r\n:host(:not([controls])) .bar{display:none}\r\nbutton.icon{background:none;border:0;color:inherit;width:34px;height:34px;padding:5px;cursor:pointer;border-radius:8px;flex:none}\r\nbutton.icon:hover,button.icon:focus-visible{background:#ffffff22;outline:none}\r\nbutton svg{width:100%;height:100%;fill:currentColor}\r\n.time{font-size:13px;font-variant-numeric:tabular-nums;white-space:nowrap;opacity:.9}\r\n.scrub{position:relative;flex:1;height:22px;cursor:pointer;outline:none;touch-action:none}\r\n.rail{position:absolute;left:0;right:0;top:9px;height:4px;border-radius:2px;background:#ffffff40}\r\n.fill{position:absolute;left:0;top:9px;height:4px;border-radius:2px;background:var(--mc-accent);width:0}\r\n.tick{position:absolute;top:7px;width:2px;height:8px;background:#fff;opacity:.85;border-radius:1px}\r\n.thumb{position:absolute;top:4px;width:14px;height:14px;margin-left:-7px;border-radius:50%;background:#fff;box-shadow:0 1px 4px #0008;left:0}\r\n.scrub:focus-visible .thumb{outline:2px solid var(--mc-accent);outline-offset:2px}\r\n.label{font-size:13px;max-width:30%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;opacity:.9}\r\n.menu{position:absolute;right:10px;bottom:56px;max-height:60%;overflow:auto;margin:0;padding:6px;list-style:none;border-radius:10px;background:#111827f2;box-shadow:0 10px 30px #0009;min-width:200px}\r\n.menu[hidden]{display:none}\r\n.menu button{display:flex;gap:10px;width:100%;padding:8px 10px;border:0;border-radius:6px;background:none;color:inherit;font:inherit;font-size:14px;text-align:left;cursor:pointer}\r\n.menu button:hover,.menu button:focus-visible{background:#ffffff1f;outline:none}\r\n.menu button[aria-current=\"true\"]{color:var(--mc-accent)}\r\n.menu .t{opacity:.7;font-variant-numeric:tabular-nums}\r\n.error{position:absolute;left:12px;right:12px;top:12px;padding:8px 12px;border-radius:8px;background:#7f1d1dee;font-size:13px}\r\n.error[hidden]{display:none}\r\n`;\r\n\r\nconst ICON = {\r\n play: '<svg viewBox=\"0 0 24 24\"><path d=\"M8 5v14l11-7z\"/></svg>',\r\n pause: '<svg viewBox=\"0 0 24 24\"><path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/></svg>',\r\n chapters: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 6h2v2H4zm4 0h12v2H8zM4 11h2v2H4zm4 0h12v2H8zm-4 5h2v2H4zm4 0h12v2H8z\"/></svg>',\r\n sound: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 9v6h4l5 4V5L8 9zm12.5 3a4.5 4.5 0 0 0-2.5-4v8a4.5 4.5 0 0 0 2.5-4z\"/></svg>',\r\n muted: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 9v6h4l5 4V5L8 9zm16.6 0L19 7.4 16.4 10 13.8 7.4 12.4 9l2.6 2.6-2.6 2.6 1.4 1.4 2.6-2.6 2.6 2.6 1.6-1.6-2.6-2.6z\"/></svg>',\r\n};\r\n\r\nconst BaseElement: typeof HTMLElement = typeof HTMLElement === \"undefined\" ? (class {} as unknown as typeof HTMLElement) : HTMLElement;\r\n\r\nexport class MiraiclipPlayerElement extends BaseElement {\r\n static get observedAttributes(): string[] {\r\n return [\"src\", \"document\", \"chapters\", \"poster\", \"loop\", \"muted\", \"assets-base\"];\r\n }\r\n\r\n private readonly root: ShadowRoot;\r\n private engine: PlaybackEngine | undefined;\r\n private loadToken = 0;\r\n private docValue: Doc | undefined;\r\n private chapterList: PlayerChapter[] = [];\r\n private explicitChapters: PlayerChapter[] | undefined;\r\n private currentChapter: PlayerChapter | undefined;\r\n private resizeObserver: ResizeObserver | undefined;\r\n private dragging = false;\r\n private readonly els: Record<\"frame\" | \"stage\" | \"big\" | \"play\" | \"time\" | \"scrub\" | \"fill\" | \"thumb\" | \"ticks\" | \"label\" | \"chaptersBtn\" | \"menu\" | \"mute\" | \"error\", HTMLElement>;\r\n\r\n constructor() {\r\n super();\r\n this.root = this.attachShadow({ mode: \"open\" });\r\n this.root.innerHTML = `<style>${STYLE}</style>\r\n<div class=\"frame\" part=\"frame\" tabindex=\"0\">\r\n <div class=\"stage\" part=\"stage\"></div>\r\n <button class=\"big\" part=\"big-play\" aria-label=\"Play\">${ICON.play}</button>\r\n <div class=\"bar\" part=\"controls\">\r\n <button class=\"icon play\" aria-label=\"Play\">${ICON.play}</button>\r\n <span class=\"time\" part=\"time\">0:00 / 0:00</span>\r\n <div class=\"scrub\" part=\"scrubber\" role=\"slider\" tabindex=\"0\" aria-label=\"Seek\" aria-valuemin=\"0\" aria-valuemax=\"0\" aria-valuenow=\"0\">\r\n <div class=\"rail\"></div><div class=\"fill\"></div><div class=\"ticks\"></div><div class=\"thumb\"></div>\r\n </div>\r\n <span class=\"label\" part=\"chapter-label\"></span>\r\n <button class=\"icon chapters\" aria-label=\"Chapters\" aria-haspopup=\"true\" aria-expanded=\"false\" hidden>${ICON.chapters}</button>\r\n <button class=\"icon mute\" aria-label=\"Mute\">${ICON.sound}</button>\r\n </div>\r\n <ol class=\"menu\" part=\"chapters\" hidden></ol>\r\n <div class=\"error\" part=\"error\" role=\"alert\" hidden></div>\r\n</div>`;\r\n const $ = (sel: string) => this.root.querySelector(sel) as HTMLElement;\r\n this.els = {\r\n frame: $(\".frame\"),\r\n stage: $(\".stage\"),\r\n big: $(\".big\"),\r\n play: $(\".play\"),\r\n time: $(\".time\"),\r\n scrub: $(\".scrub\"),\r\n fill: $(\".fill\"),\r\n thumb: $(\".thumb\"),\r\n ticks: $(\".ticks\"),\r\n label: $(\".label\"),\r\n chaptersBtn: $(\".chapters\"),\r\n menu: $(\".menu\"),\r\n mute: $(\".mute\"),\r\n error: $(\".error\"),\r\n };\r\n this.wire();\r\n }\r\n\r\n // ------------------------------------------------------------------ API\r\n\r\n /** A project document to play live (takes precedence over `document`/`src` attributes). */\r\n get document(): Doc | undefined {\r\n return this.docValue;\r\n }\r\n set document(doc: Doc | undefined) {\r\n this.docValue = doc;\r\n void this.load();\r\n }\r\n\r\n /** Chapters in seconds. Set to override markers / the `chapters` attribute. */\r\n get chapters(): PlayerChapter[] {\r\n return [...this.chapterList];\r\n }\r\n set chapters(value: PlayerChapter[] | undefined) {\r\n this.explicitChapters = value ? normalizeChapters(value, this.duration) : undefined;\r\n this.refreshChapters();\r\n }\r\n\r\n get currentTime(): number {\r\n return this.engine?.state().timeS ?? 0;\r\n }\r\n set currentTime(seconds: number) {\r\n this.seek(seconds);\r\n }\r\n get duration(): number {\r\n return this.engine?.state().durationS ?? 0;\r\n }\r\n get paused(): boolean {\r\n return !(this.engine?.state().playing ?? false);\r\n }\r\n get currentChapterInfo(): PlayerChapter | undefined {\r\n return this.currentChapter;\r\n }\r\n\r\n play(): void {\r\n this.engine?.play();\r\n }\r\n pause(): void {\r\n this.engine?.pause();\r\n }\r\n toggle(): void {\r\n if (this.paused) this.play();\r\n else this.pause();\r\n }\r\n seek(seconds: number): void {\r\n this.engine?.seek(Math.min(Math.max(0, seconds), this.duration || seconds));\r\n this.render();\r\n }\r\n /** Jump to a chapter by id or index. */\r\n goToChapter(which: string | number): void {\r\n const chapter = typeof which === \"number\" ? this.chapterList[which] : this.chapterList.find((c) => c.id === which);\r\n if (chapter) this.seek(chapter.start);\r\n }\r\n\r\n // ------------------------------------------------------------------ lifecycle\r\n\r\n connectedCallback(): void {\r\n if (typeof ResizeObserver !== \"undefined\") {\r\n this.resizeObserver = new ResizeObserver(() => this.sizeEngine());\r\n this.resizeObserver.observe(this.els.frame);\r\n }\r\n if (!this.engine) void this.load();\r\n }\r\n\r\n disconnectedCallback(): void {\r\n this.resizeObserver?.disconnect();\r\n this.resizeObserver = undefined;\r\n this.engine?.destroy();\r\n this.engine = undefined;\r\n }\r\n\r\n attributeChangedCallback(name: string, previous: string | null, next: string | null): void {\r\n if (previous === next || !this.isConnected) return;\r\n if (name === \"src\" || name === \"document\" || name === \"assets-base\") void this.load();\r\n else if (name === \"chapters\") void this.loadChapterAttribute();\r\n else if (name === \"muted\") this.engine?.setMuted(next !== null);\r\n }\r\n\r\n // ------------------------------------------------------------------ loading\r\n\r\n private async load(): Promise<void> {\r\n const token = ++this.loadToken;\r\n this.engine?.destroy();\r\n this.engine = undefined;\r\n this.removeAttribute(\"data-ready\");\r\n this.showError(null);\r\n this.els.stage.replaceChildren();\r\n const loop = this.hasAttribute(\"loop\");\r\n const muted = this.hasAttribute(\"muted\");\r\n try {\r\n let engine: PlaybackEngine | undefined;\r\n let doc = this.docValue;\r\n const docUrl = this.getAttribute(\"document\");\r\n if (!doc && docUrl) {\r\n const response = await fetch(docUrl);\r\n if (!response.ok) throw new Error(`could not load ${docUrl} (HTTP ${response.status})`);\r\n doc = (await response.json()) as Doc;\r\n if (token !== this.loadToken) return;\r\n }\r\n if (doc) {\r\n const base = this.getAttribute(\"assets-base\") ?? (docUrl ? new URL(docUrl, location.href).href : undefined);\r\n const resolved = resolveAssetSources(doc, base);\r\n this.style.setProperty(\"--mc-aspect\", `${resolved.settings.width}/${resolved.settings.height}`);\r\n engine = await createDocumentEngine(resolved, { loop, muted, loadPlugins: this.hasAttribute(\"plugins\") });\r\n this.docChapters = (durationS) => chaptersFromMarkers(resolved.markers, durationS);\r\n } else {\r\n const src = this.getAttribute(\"src\");\r\n if (!src) return;\r\n const poster = this.getAttribute(\"poster\");\r\n engine = createVideoEngine(src, { loop, muted, ...(poster ? { poster } : {}) });\r\n this.docChapters = undefined;\r\n const video = engine.element as HTMLVideoElement;\r\n video.addEventListener(\"loadedmetadata\", () => {\r\n if (video.videoWidth && video.videoHeight) this.style.setProperty(\"--mc-aspect\", `${video.videoWidth}/${video.videoHeight}`);\r\n this.refreshChapters();\r\n });\r\n }\r\n if (token !== this.loadToken) {\r\n engine.destroy();\r\n return;\r\n }\r\n this.engine = engine;\r\n this.els.stage.append(engine.element);\r\n let wasPlaying = false;\r\n engine.onChange(() => {\r\n const playing = engine.state().playing;\r\n if (playing !== wasPlaying) {\r\n wasPlaying = playing;\r\n this.toggleAttribute(\"data-playing\", playing);\r\n this.dispatch(playing ? \"play\" : \"pause\");\r\n }\r\n this.render();\r\n this.dispatch(\"timeupdate\");\r\n });\r\n engine.onEnded(() => this.dispatch(\"ended\"));\r\n engine.onError((message) => {\r\n this.showError(message);\r\n this.dispatch(\"error\", { message });\r\n });\r\n this.sizeEngine();\r\n await this.loadChapterAttribute();\r\n this.refreshChapters();\r\n this.setAttribute(\"data-ready\", \"\");\r\n this.render();\r\n this.dispatch(\"ready\");\r\n if (this.hasAttribute(\"autoplay\")) engine.play();\r\n } catch (error) {\r\n if (token !== this.loadToken) return;\r\n const message = error instanceof Error ? error.message : String(error);\r\n this.showError(message);\r\n this.dispatch(\"error\", { message });\r\n }\r\n }\r\n\r\n private docChapters: ((durationS: number) => PlayerChapter[]) | undefined;\r\n private attributeChapters: PlayerChapter[] | undefined;\r\n\r\n private async loadChapterAttribute(): Promise<void> {\r\n const url = this.getAttribute(\"chapters\");\r\n if (!url) {\r\n this.attributeChapters = undefined;\r\n return;\r\n }\r\n try {\r\n const response = await fetch(url);\r\n const text = await response.text();\r\n this.attributeChapters = text.trimStart().startsWith(\"WEBVTT\") ? parseVttChapters(text) : normalizeChapters(JSON.parse(text), this.duration);\r\n } catch {\r\n this.attributeChapters = undefined;\r\n }\r\n this.refreshChapters();\r\n }\r\n\r\n private refreshChapters(): void {\r\n const duration = this.duration;\r\n this.chapterList = this.explicitChapters ?? this.attributeChapters ?? this.docChapters?.(duration) ?? [];\r\n const { ticks, menu, chaptersBtn } = this.els;\r\n ticks.replaceChildren();\r\n menu.replaceChildren();\r\n chaptersBtn.hidden = this.chapterList.length === 0;\r\n for (const [i, chapter] of this.chapterList.entries()) {\r\n if (duration > 0 && chapter.start > 0) {\r\n const tick = document.createElement(\"div\");\r\n tick.className = \"tick\";\r\n tick.style.left = `${(chapter.start / duration) * 100}%`;\r\n tick.title = chapter.label;\r\n ticks.append(tick);\r\n }\r\n const li = document.createElement(\"li\");\r\n const button = document.createElement(\"button\");\r\n button.type = \"button\";\r\n button.dataset[\"index\"] = String(i);\r\n button.innerHTML = `<span class=\"t\"></span><span class=\"n\"></span>`;\r\n (button.querySelector(\".t\") as HTMLElement).textContent = formatTime(chapter.start);\r\n (button.querySelector(\".n\") as HTMLElement).textContent = chapter.label;\r\n li.append(button);\r\n menu.append(li);\r\n }\r\n this.currentChapter = undefined;\r\n this.render();\r\n }\r\n\r\n // ------------------------------------------------------------------ UI\r\n\r\n private wire(): void {\r\n const { big, play, scrub, chaptersBtn, menu, mute, frame } = this.els;\r\n big.addEventListener(\"click\", () => this.play());\r\n play.addEventListener(\"click\", () => this.toggle());\r\n this.els.stage.addEventListener(\"click\", () => this.toggle());\r\n mute.addEventListener(\"click\", () => {\r\n const muted = !this.hasAttribute(\"muted\");\r\n this.toggleAttribute(\"muted\", muted);\r\n this.engine?.setMuted(muted);\r\n mute.innerHTML = muted ? ICON.muted : ICON.sound;\r\n mute.setAttribute(\"aria-label\", muted ? \"Unmute\" : \"Mute\");\r\n });\r\n chaptersBtn.addEventListener(\"click\", () => {\r\n const open = menu.hidden;\r\n menu.hidden = !open;\r\n chaptersBtn.setAttribute(\"aria-expanded\", String(open));\r\n if (open) (menu.querySelector<HTMLButtonElement>('button[aria-current=\"true\"]') ?? menu.querySelector<HTMLButtonElement>(\"button\"))?.focus();\r\n });\r\n menu.addEventListener(\"click\", (event) => {\r\n const button = (event.target as HTMLElement).closest(\"button\");\r\n if (!button) return;\r\n this.goToChapter(Number(button.dataset[\"index\"]));\r\n menu.hidden = true;\r\n chaptersBtn.setAttribute(\"aria-expanded\", \"false\");\r\n });\r\n const seekFromPointer = (event: PointerEvent): void => {\r\n const rect = scrub.getBoundingClientRect();\r\n if (rect.width <= 0) return;\r\n this.seek(((event.clientX - rect.left) / rect.width) * this.duration);\r\n };\r\n scrub.addEventListener(\"pointerdown\", (event) => {\r\n this.dragging = true;\r\n scrub.setPointerCapture?.(event.pointerId);\r\n seekFromPointer(event);\r\n });\r\n scrub.addEventListener(\"pointermove\", (event) => {\r\n if (this.dragging) seekFromPointer(event);\r\n });\r\n scrub.addEventListener(\"pointerup\", () => {\r\n this.dragging = false;\r\n });\r\n const keys = (event: KeyboardEvent): void => {\r\n const step = event.shiftKey ? 10 : 5;\r\n if (event.key === \" \" || event.key === \"k\") this.toggle();\r\n else if (event.key === \"ArrowRight\" || event.key === \"l\") this.seek(this.currentTime + step);\r\n else if (event.key === \"ArrowLeft\" || event.key === \"j\") this.seek(this.currentTime - step);\r\n else if (event.key === \"Home\") this.seek(0);\r\n else if (event.key === \"End\") this.seek(this.duration);\r\n else if (event.key === \"]\" ) this.stepChapter(1);\r\n else if (event.key === \"[\") this.stepChapter(-1);\r\n else if (event.key === \"Escape\") menu.hidden = true;\r\n else return;\r\n event.preventDefault();\r\n };\r\n frame.addEventListener(\"keydown\", keys);\r\n }\r\n\r\n private stepChapter(direction: 1 | -1): void {\r\n if (this.chapterList.length === 0) return;\r\n const t = this.currentTime;\r\n if (direction > 0) {\r\n const next = this.chapterList.find((c) => c.start > t + 0.25);\r\n if (next) this.seek(next.start);\r\n } else {\r\n const previous = [...this.chapterList].reverse().find((c) => c.start < t - 1);\r\n this.seek(previous?.start ?? 0);\r\n }\r\n }\r\n\r\n private render(): void {\r\n const state = this.engine?.state() ?? { playing: false, timeS: 0, durationS: 0 };\r\n const { time, fill, thumb, scrub, play, label, menu } = this.els;\r\n const fraction = state.durationS > 0 ? Math.min(1, state.timeS / state.durationS) : 0;\r\n time.textContent = `${formatTime(state.timeS)} / ${formatTime(state.durationS)}`;\r\n fill.style.width = `${fraction * 100}%`;\r\n thumb.style.left = `${fraction * 100}%`;\r\n scrub.setAttribute(\"aria-valuemax\", String(Math.round(state.durationS)));\r\n scrub.setAttribute(\"aria-valuenow\", String(Math.round(state.timeS)));\r\n scrub.setAttribute(\"aria-valuetext\", time.textContent);\r\n const wanted = state.playing ? \"Pause\" : \"Play\";\r\n if (play.getAttribute(\"aria-label\") !== wanted) {\r\n play.innerHTML = state.playing ? ICON.pause : ICON.play;\r\n play.setAttribute(\"aria-label\", wanted);\r\n }\r\n const chapter = chapterAt(this.chapterList, state.timeS);\r\n label.textContent = chapter?.label ?? \"\";\r\n if (chapter?.id !== this.currentChapter?.id) {\r\n this.currentChapter = chapter;\r\n for (const button of Array.from(menu.querySelectorAll(\"button\"))) {\r\n button.setAttribute(\"aria-current\", String(this.chapterList[Number(button.dataset[\"index\"])]?.id === chapter?.id));\r\n }\r\n this.dispatch(\"chapterchange\", { chapter: chapter ?? null });\r\n }\r\n }\r\n\r\n private sizeEngine(): void {\r\n const rect = this.els.frame.getBoundingClientRect();\r\n this.engine?.resize(rect.width, rect.height);\r\n }\r\n\r\n private showError(message: string | null): void {\r\n this.els.error.hidden = message === null;\r\n this.els.error.textContent = message ?? \"\";\r\n }\r\n\r\n private dispatch(type: string, detail?: unknown): void {\r\n this.dispatchEvent(new CustomEvent(type, { detail, bubbles: type !== \"timeupdate\", composed: true }));\r\n }\r\n}\r\n\r\n/** Register the element (default tag `miraiclip-player`). Safe to call twice. */\r\nexport function defineMiraiclipPlayer(tagName = \"miraiclip-player\"): typeof MiraiclipPlayerElement {\r\n if (typeof customElements === \"undefined\") return MiraiclipPlayerElement;\r\n const existing = customElements.get(tagName);\r\n if (existing) return existing as typeof MiraiclipPlayerElement;\r\n customElements.define(tagName, MiraiclipPlayerElement);\r\n return MiraiclipPlayerElement;\r\n}\r\n\r\ndeclare global {\r\n interface HTMLElementTagNameMap {\r\n \"miraiclip-player\": MiraiclipPlayerElement;\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  normalizeChapters,
8
8
  parseVttChapters,
9
9
  resolveAssetSources
10
- } from "./chunk-TMHYBF54.js";
10
+ } from "./chunk-XKPHDDUA.js";
11
11
  export {
12
12
  MiraiclipPlayerElement,
13
13
  chapterAt,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miraiclip/player",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "<miraiclip-player>: a framework-free web component that plays a Miraiclip project live or a rendered MP4/WebM, with chapters from timeline markers.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -50,8 +50,8 @@
50
50
  "access": "public"
51
51
  },
52
52
  "peerDependencies": {
53
- "@miraiclip/core": "^0.7.0",
54
- "@miraiclip/renderer": "^0.9.1"
53
+ "@miraiclip/renderer": "^0.11.0",
54
+ "@miraiclip/core": "^0.8.0"
55
55
  },
56
56
  "peerDependenciesMeta": {
57
57
  "@miraiclip/core": {
@@ -69,8 +69,8 @@
69
69
  "tsup": "^8.5.1",
70
70
  "typescript": "^5.9.3",
71
71
  "vitest": "^3.2.4",
72
- "@miraiclip/core": "^0.7.0",
73
- "@miraiclip/renderer": "^0.9.1"
72
+ "@miraiclip/core": "^0.8.0",
73
+ "@miraiclip/renderer": "^0.11.0"
74
74
  },
75
75
  "keywords": [
76
76
  "video",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/chapters.ts","../src/engines.ts","../src/element.ts"],"sourcesContent":["/**\n * Chapters for the player: from a document's markers, from JSON, or from a\n * WebVTT chapters track. Pure functions — no DOM.\n */\nexport interface PlayerChapter {\n id: string;\n label: string;\n /** Seconds. */\n start: number;\n end: number;\n}\n\ninterface MarkerLike {\n id: string;\n timeUs: number;\n label: string;\n durationUs?: number;\n}\n\n/**\n * A document's markers as chapters (same rule as core's `markerChapters`):\n * each runs for its own `durationUs`, or until the next marker; the last one\n * until `durationS`.\n */\nexport function chaptersFromMarkers(markers: Record<string, MarkerLike> | undefined, durationS: number): PlayerChapter[] {\n const list = Object.values(markers ?? {}).sort((a, b) => a.timeUs - b.timeUs || a.id.localeCompare(b.id));\n return list.map((m, i) => {\n const start = m.timeUs / 1e6;\n const next = list[i + 1];\n const end = m.durationUs !== undefined ? (m.timeUs + m.durationUs) / 1e6 : next ? next.timeUs / 1e6 : Math.max(durationS, start);\n return { id: m.id, label: m.label, start, end };\n });\n}\n\n/** Parse a WebVTT chapters track (\"00:01.000 --> 00:05.000\" cues with a title line). */\nexport function parseVttChapters(vtt: string): PlayerChapter[] {\n const chapters: PlayerChapter[] = [];\n const blocks = vtt.replace(/\\r/g, \"\").split(/\\n{2,}/);\n const time = (t: string): number => {\n const parts = t.trim().split(\":\").map(Number);\n let s = 0;\n for (const p of parts) s = s * 60 + p;\n return s;\n };\n for (const block of blocks) {\n const lines = block.split(\"\\n\").filter((l) => l.trim() !== \"\");\n const at = lines.findIndex((l) => l.includes(\"-->\"));\n if (at < 0) continue;\n const [from, to] = lines[at]!.split(\"-->\").map((s) => s.trim().split(/\\s+/)[0]!);\n const label = lines.slice(at + 1).join(\" \").trim();\n if (!label) continue;\n chapters.push({ id: at > 0 ? lines[0]!.trim() : `c${chapters.length + 1}`, label, start: time(from!), end: time(to!) });\n }\n return chapters.sort((a, b) => a.start - b.start);\n}\n\n/** Accept `[{ label, start, end? }]` (seconds) or `[{ label, timeUs, durationUs? }]` and normalize. */\nexport function normalizeChapters(input: unknown, durationS: number): PlayerChapter[] {\n if (!Array.isArray(input)) return [];\n const raw = input\n .map((c, i) => {\n const o = c as Record<string, unknown>;\n const label = String(o[\"label\"] ?? o[\"title\"] ?? \"\");\n const start = typeof o[\"start\"] === \"number\" ? o[\"start\"] : typeof o[\"timeUs\"] === \"number\" ? o[\"timeUs\"] / 1e6 : NaN;\n const end =\n typeof o[\"end\"] === \"number\"\n ? o[\"end\"]\n : typeof o[\"durationUs\"] === \"number\" && typeof o[\"timeUs\"] === \"number\"\n ? (o[\"timeUs\"] + o[\"durationUs\"]) / 1e6\n : undefined;\n return { id: String(o[\"id\"] ?? `c${i + 1}`), label, start, end };\n })\n .filter((c) => c.label && Number.isFinite(c.start))\n .sort((a, b) => a.start - b.start);\n return raw.map((c, i) => ({ id: c.id, label: c.label, start: c.start, end: c.end ?? raw[i + 1]?.start ?? Math.max(durationS, c.start) }));\n}\n\n/**\n * The chapter playing at `timeS` (the latest one that has started and not\n * ended). Media elements land a seek on a frame boundary, often a few ms\n * before the requested time, so a chapter counts as started `toleranceS`\n * early (default 50 ms).\n */\nexport function chapterAt(chapters: readonly PlayerChapter[], timeS: number, toleranceS = 0.05): PlayerChapter | undefined {\n let found: PlayerChapter | undefined;\n for (const c of chapters) if (c.start <= timeS + toleranceS && timeS < c.end) found = c;\n return found;\n}\n\n/** \"1:05\" / \"1:02:03\". */\nexport function formatTime(seconds: number): string {\n const s = Math.max(0, Math.floor(seconds + 1e-6));\n const h = Math.floor(s / 3600);\n const m = Math.floor((s % 3600) / 60);\n const ss = String(s % 60).padStart(2, \"0\");\n return h > 0 ? `${h}:${String(m).padStart(2, \"0\")}:${ss}` : `${m}:${ss}`;\n}\n","/**\n * Playback engines behind the element: a native <video> for rendered files,\n * and the Miraiclip renderer for live documents (loaded on demand, so a\n * video-only page never downloads the renderer).\n */\nexport interface EngineState {\n playing: boolean;\n timeS: number;\n durationS: number;\n}\n\nexport interface PlaybackEngine {\n readonly element: HTMLElement;\n play(): void;\n pause(): void;\n seek(seconds: number): void;\n state(): EngineState;\n setMuted(muted: boolean): void;\n /** Called on any time/state change. */\n onChange(listener: () => void): void;\n onEnded(listener: () => void): void;\n onError(listener: (message: string) => void): void;\n resize(width: number, height: number): void;\n destroy(): void;\n}\n\nexport function createVideoEngine(src: string, options: { loop: boolean; muted: boolean; poster?: string }): PlaybackEngine {\n const video = document.createElement(\"video\");\n video.src = src;\n video.preload = \"metadata\";\n video.playsInline = true;\n video.loop = options.loop;\n video.muted = options.muted;\n if (options.poster) video.poster = options.poster;\n video.setAttribute(\"part\", \"media\");\n const change: (() => void)[] = [];\n const ended: (() => void)[] = [];\n const errors: ((m: string) => void)[] = [];\n const emit = () => change.forEach((l) => l());\n for (const type of [\"timeupdate\", \"play\", \"pause\", \"durationchange\", \"loadedmetadata\", \"seeked\"]) video.addEventListener(type, emit);\n video.addEventListener(\"ended\", () => ended.forEach((l) => l()));\n video.addEventListener(\"error\", () => errors.forEach((l) => l(video.error?.message || \"the video could not be played\")));\n return {\n element: video,\n play: () => void video.play()?.catch?.((e: Error) => errors.forEach((l) => l(e.message))),\n pause: () => video.pause(),\n seek: (s) => {\n video.currentTime = Math.max(0, s);\n emit();\n },\n state: () => ({ playing: !video.paused && !video.ended, timeS: video.currentTime || 0, durationS: Number.isFinite(video.duration) ? video.duration : 0 }),\n setMuted: (m) => {\n video.muted = m;\n },\n onChange: (l) => change.push(l),\n onEnded: (l) => ended.push(l),\n onError: (l) => errors.push(l),\n resize: () => undefined,\n destroy: () => {\n video.pause();\n video.removeAttribute(\"src\");\n video.load();\n },\n };\n}\n\n/** Rewrite relative asset `src` paths so they resolve against `base` (the document's own URL by default). */\nexport function resolveAssetSources<T extends { assets: Record<string, { src: string }> }>(doc: T, base: string | undefined): T {\n if (!base) return doc;\n const copy = JSON.parse(JSON.stringify(doc)) as T;\n for (const asset of Object.values(copy.assets)) {\n if (/^(?:[a-z][a-z0-9+.-]*:|\\/\\/)/i.test(asset.src)) continue; // absolute, data:, blob:\n try {\n asset.src = new URL(asset.src, base).href;\n } catch {\n // leave as-is\n }\n }\n return copy;\n}\n\ninterface LiveModules {\n core: typeof import(\"@miraiclip/core\");\n renderer: typeof import(\"@miraiclip/renderer\");\n}\n\nlet modules: Promise<LiveModules> | undefined;\n\n/** Load core + renderer once (they are optional peers: only documents need them). */\nexport function loadLiveModules(): Promise<LiveModules> {\n modules ??= Promise.all([import(\"@miraiclip/core\"), import(\"@miraiclip/renderer\")]).then(([core, renderer]) => ({ core, renderer }));\n return modules;\n}\n\nexport async function createDocumentEngine(\n doc: import(\"@miraiclip/core\").ProjectDocument,\n options: { loop: boolean; muted: boolean; loadPlugins: boolean },\n): Promise<PlaybackEngine> {\n const { core, renderer } = await loadLiveModules();\n if (options.loadPlugins && doc.plugins?.length) await renderer.loadDocumentPlugins(doc);\n const project = core.createProject(doc);\n const controller = renderer.createPreviewController(project, { player: { loop: options.loop } });\n if (options.muted) controller.toggleMute();\n const canvas = document.createElement(\"canvas\");\n canvas.setAttribute(\"part\", \"media\");\n const change: (() => void)[] = [];\n const ended: (() => void)[] = [];\n const errors: ((m: string) => void)[] = [];\n let durationUs = 0;\n for (const clip of Object.values(doc.clips)) durationUs = Math.max(durationUs, clip.startUs + clip.durationUs);\n let wasPlaying = false;\n const emit = () => {\n const playing = controller.getState().playing;\n if (wasPlaying && !playing && project.getState().playheadUs >= durationUs - 1) ended.forEach((l) => l());\n wasPlaying = playing;\n change.forEach((l) => l());\n };\n controller.subscribe(() => {\n const error = controller.getState().lastError;\n if (error) errors.forEach((l) => l(error));\n emit();\n });\n const offPlayhead = project.events.on(\"playhead\", emit);\n let attached = false;\n return {\n element: canvas,\n play: () => controller.play(),\n pause: () => controller.pause(),\n seek: (s) => controller.seek(Math.round(Math.max(0, s) * 1e6)),\n state: () => ({ playing: controller.getState().playing, timeS: project.getState().playheadUs / 1e6, durationS: durationUs / 1e6 }),\n setMuted: (m) => {\n if (controller.getState().muted !== m) controller.toggleMute();\n },\n onChange: (l) => change.push(l),\n onEnded: (l) => ended.push(l),\n onError: (l) => errors.push(l),\n resize: (width, height) => {\n if (width <= 0 || height <= 0) return;\n if (!attached) {\n controller.attach(canvas, { width, height });\n attached = true;\n } else controller.resize(width, height);\n },\n destroy: () => {\n offPlayhead();\n controller.destroy();\n },\n };\n}\n","/**\n * <miraiclip-player>: plays a rendered video file or a live Miraiclip\n * document, with a scrubber, chapters (from the document's markers, a JSON\n * list or a WebVTT chapters file) and keyboard control. No framework.\n *\n * <miraiclip-player src=\"launch.mp4\" chapters=\"launch.chapters.vtt\" controls></miraiclip-player>\n * <miraiclip-player document=\"video.miraiclip.json\" controls></miraiclip-player>\n *\n * Events: ready, play, pause, timeupdate, chapterchange ({ chapter }), ended, error ({ message }).\n */\nimport { chapterAt, chaptersFromMarkers, formatTime, normalizeChapters, parseVttChapters, type PlayerChapter } from \"./chapters.js\";\nimport { createDocumentEngine, createVideoEngine, resolveAssetSources, type PlaybackEngine } from \"./engines.js\";\n\ntype Doc = import(\"@miraiclip/core\").ProjectDocument;\n\nconst STYLE = `\n:host{display:block;position:relative;--mc-accent:#22d3ee;--mc-bg:#000;--mc-fg:#fff;--mc-radius:12px;--mc-font:Inter,system-ui,sans-serif;color:var(--mc-fg);font-family:var(--mc-font)}\n:host([hidden]){display:none}\n.frame{position:relative;width:100%;aspect-ratio:var(--mc-aspect,16/9);background:var(--mc-bg);border-radius:var(--mc-radius);overflow:hidden;outline:none}\n.stage,.stage>*{position:absolute;inset:0;width:100%;height:100%;display:block;object-fit:contain}\n.big{position:absolute;inset:0;margin:auto;width:76px;height:76px;border-radius:50%;border:0;background:#000a;color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center}\n.big svg{width:34px;height:34px;margin-left:4px}\n:host([data-playing]) .big,:host(:not([data-ready])) .big{display:none}\n.bar{position:absolute;left:0;right:0;bottom:0;display:flex;align-items:center;gap:10px;padding:28px 12px 10px;background:linear-gradient(transparent,#000c);opacity:0;transition:opacity .2s}\n:host([controls]) .frame:hover .bar,:host([controls]) .frame:focus-within .bar,:host([controls]:not([data-playing])) .bar{opacity:1}\n:host(:not([controls])) .bar{display:none}\nbutton.icon{background:none;border:0;color:inherit;width:34px;height:34px;padding:5px;cursor:pointer;border-radius:8px;flex:none}\nbutton.icon:hover,button.icon:focus-visible{background:#ffffff22;outline:none}\nbutton svg{width:100%;height:100%;fill:currentColor}\n.time{font-size:13px;font-variant-numeric:tabular-nums;white-space:nowrap;opacity:.9}\n.scrub{position:relative;flex:1;height:22px;cursor:pointer;outline:none;touch-action:none}\n.rail{position:absolute;left:0;right:0;top:9px;height:4px;border-radius:2px;background:#ffffff40}\n.fill{position:absolute;left:0;top:9px;height:4px;border-radius:2px;background:var(--mc-accent);width:0}\n.tick{position:absolute;top:7px;width:2px;height:8px;background:#fff;opacity:.85;border-radius:1px}\n.thumb{position:absolute;top:4px;width:14px;height:14px;margin-left:-7px;border-radius:50%;background:#fff;box-shadow:0 1px 4px #0008;left:0}\n.scrub:focus-visible .thumb{outline:2px solid var(--mc-accent);outline-offset:2px}\n.label{font-size:13px;max-width:30%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;opacity:.9}\n.menu{position:absolute;right:10px;bottom:56px;max-height:60%;overflow:auto;margin:0;padding:6px;list-style:none;border-radius:10px;background:#111827f2;box-shadow:0 10px 30px #0009;min-width:200px}\n.menu[hidden]{display:none}\n.menu button{display:flex;gap:10px;width:100%;padding:8px 10px;border:0;border-radius:6px;background:none;color:inherit;font:inherit;font-size:14px;text-align:left;cursor:pointer}\n.menu button:hover,.menu button:focus-visible{background:#ffffff1f;outline:none}\n.menu button[aria-current=\"true\"]{color:var(--mc-accent)}\n.menu .t{opacity:.7;font-variant-numeric:tabular-nums}\n.error{position:absolute;left:12px;right:12px;top:12px;padding:8px 12px;border-radius:8px;background:#7f1d1dee;font-size:13px}\n.error[hidden]{display:none}\n`;\n\nconst ICON = {\n play: '<svg viewBox=\"0 0 24 24\"><path d=\"M8 5v14l11-7z\"/></svg>',\n pause: '<svg viewBox=\"0 0 24 24\"><path d=\"M6 5h4v14H6zM14 5h4v14h-4z\"/></svg>',\n chapters: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 6h2v2H4zm4 0h12v2H8zM4 11h2v2H4zm4 0h12v2H8zm-4 5h2v2H4zm4 0h12v2H8z\"/></svg>',\n sound: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 9v6h4l5 4V5L8 9zm12.5 3a4.5 4.5 0 0 0-2.5-4v8a4.5 4.5 0 0 0 2.5-4z\"/></svg>',\n muted: '<svg viewBox=\"0 0 24 24\"><path d=\"M4 9v6h4l5 4V5L8 9zm16.6 0L19 7.4 16.4 10 13.8 7.4 12.4 9l2.6 2.6-2.6 2.6 1.4 1.4 2.6-2.6 2.6 2.6 1.6-1.6-2.6-2.6z\"/></svg>',\n};\n\nconst BaseElement: typeof HTMLElement = typeof HTMLElement === \"undefined\" ? (class {} as unknown as typeof HTMLElement) : HTMLElement;\n\nexport class MiraiclipPlayerElement extends BaseElement {\n static get observedAttributes(): string[] {\n return [\"src\", \"document\", \"chapters\", \"poster\", \"loop\", \"muted\", \"assets-base\"];\n }\n\n private readonly root: ShadowRoot;\n private engine: PlaybackEngine | undefined;\n private loadToken = 0;\n private docValue: Doc | undefined;\n private chapterList: PlayerChapter[] = [];\n private explicitChapters: PlayerChapter[] | undefined;\n private currentChapter: PlayerChapter | undefined;\n private resizeObserver: ResizeObserver | undefined;\n private dragging = false;\n private readonly els: Record<\"frame\" | \"stage\" | \"big\" | \"play\" | \"time\" | \"scrub\" | \"fill\" | \"thumb\" | \"ticks\" | \"label\" | \"chaptersBtn\" | \"menu\" | \"mute\" | \"error\", HTMLElement>;\n\n constructor() {\n super();\n this.root = this.attachShadow({ mode: \"open\" });\n this.root.innerHTML = `<style>${STYLE}</style>\n<div class=\"frame\" part=\"frame\" tabindex=\"0\">\n <div class=\"stage\" part=\"stage\"></div>\n <button class=\"big\" part=\"big-play\" aria-label=\"Play\">${ICON.play}</button>\n <div class=\"bar\" part=\"controls\">\n <button class=\"icon play\" aria-label=\"Play\">${ICON.play}</button>\n <span class=\"time\" part=\"time\">0:00 / 0:00</span>\n <div class=\"scrub\" part=\"scrubber\" role=\"slider\" tabindex=\"0\" aria-label=\"Seek\" aria-valuemin=\"0\" aria-valuemax=\"0\" aria-valuenow=\"0\">\n <div class=\"rail\"></div><div class=\"fill\"></div><div class=\"ticks\"></div><div class=\"thumb\"></div>\n </div>\n <span class=\"label\" part=\"chapter-label\"></span>\n <button class=\"icon chapters\" aria-label=\"Chapters\" aria-haspopup=\"true\" aria-expanded=\"false\" hidden>${ICON.chapters}</button>\n <button class=\"icon mute\" aria-label=\"Mute\">${ICON.sound}</button>\n </div>\n <ol class=\"menu\" part=\"chapters\" hidden></ol>\n <div class=\"error\" part=\"error\" role=\"alert\" hidden></div>\n</div>`;\n const $ = (sel: string) => this.root.querySelector(sel) as HTMLElement;\n this.els = {\n frame: $(\".frame\"),\n stage: $(\".stage\"),\n big: $(\".big\"),\n play: $(\".play\"),\n time: $(\".time\"),\n scrub: $(\".scrub\"),\n fill: $(\".fill\"),\n thumb: $(\".thumb\"),\n ticks: $(\".ticks\"),\n label: $(\".label\"),\n chaptersBtn: $(\".chapters\"),\n menu: $(\".menu\"),\n mute: $(\".mute\"),\n error: $(\".error\"),\n };\n this.wire();\n }\n\n // ------------------------------------------------------------------ API\n\n /** A project document to play live (takes precedence over `document`/`src` attributes). */\n get document(): Doc | undefined {\n return this.docValue;\n }\n set document(doc: Doc | undefined) {\n this.docValue = doc;\n void this.load();\n }\n\n /** Chapters in seconds. Set to override markers / the `chapters` attribute. */\n get chapters(): PlayerChapter[] {\n return [...this.chapterList];\n }\n set chapters(value: PlayerChapter[] | undefined) {\n this.explicitChapters = value ? normalizeChapters(value, this.duration) : undefined;\n this.refreshChapters();\n }\n\n get currentTime(): number {\n return this.engine?.state().timeS ?? 0;\n }\n set currentTime(seconds: number) {\n this.seek(seconds);\n }\n get duration(): number {\n return this.engine?.state().durationS ?? 0;\n }\n get paused(): boolean {\n return !(this.engine?.state().playing ?? false);\n }\n get currentChapterInfo(): PlayerChapter | undefined {\n return this.currentChapter;\n }\n\n play(): void {\n this.engine?.play();\n }\n pause(): void {\n this.engine?.pause();\n }\n toggle(): void {\n if (this.paused) this.play();\n else this.pause();\n }\n seek(seconds: number): void {\n this.engine?.seek(Math.min(Math.max(0, seconds), this.duration || seconds));\n this.render();\n }\n /** Jump to a chapter by id or index. */\n goToChapter(which: string | number): void {\n const chapter = typeof which === \"number\" ? this.chapterList[which] : this.chapterList.find((c) => c.id === which);\n if (chapter) this.seek(chapter.start);\n }\n\n // ------------------------------------------------------------------ lifecycle\n\n connectedCallback(): void {\n if (typeof ResizeObserver !== \"undefined\") {\n this.resizeObserver = new ResizeObserver(() => this.sizeEngine());\n this.resizeObserver.observe(this.els.frame);\n }\n if (!this.engine) void this.load();\n }\n\n disconnectedCallback(): void {\n this.resizeObserver?.disconnect();\n this.resizeObserver = undefined;\n this.engine?.destroy();\n this.engine = undefined;\n }\n\n attributeChangedCallback(name: string, previous: string | null, next: string | null): void {\n if (previous === next || !this.isConnected) return;\n if (name === \"src\" || name === \"document\" || name === \"assets-base\") void this.load();\n else if (name === \"chapters\") void this.loadChapterAttribute();\n else if (name === \"muted\") this.engine?.setMuted(next !== null);\n }\n\n // ------------------------------------------------------------------ loading\n\n private async load(): Promise<void> {\n const token = ++this.loadToken;\n this.engine?.destroy();\n this.engine = undefined;\n this.removeAttribute(\"data-ready\");\n this.showError(null);\n this.els.stage.replaceChildren();\n const loop = this.hasAttribute(\"loop\");\n const muted = this.hasAttribute(\"muted\");\n try {\n let engine: PlaybackEngine | undefined;\n let doc = this.docValue;\n const docUrl = this.getAttribute(\"document\");\n if (!doc && docUrl) {\n const response = await fetch(docUrl);\n if (!response.ok) throw new Error(`could not load ${docUrl} (HTTP ${response.status})`);\n doc = (await response.json()) as Doc;\n if (token !== this.loadToken) return;\n }\n if (doc) {\n const base = this.getAttribute(\"assets-base\") ?? (docUrl ? new URL(docUrl, location.href).href : undefined);\n const resolved = resolveAssetSources(doc, base);\n this.style.setProperty(\"--mc-aspect\", `${resolved.settings.width}/${resolved.settings.height}`);\n engine = await createDocumentEngine(resolved, { loop, muted, loadPlugins: this.hasAttribute(\"plugins\") });\n this.docChapters = (durationS) => chaptersFromMarkers(resolved.markers, durationS);\n } else {\n const src = this.getAttribute(\"src\");\n if (!src) return;\n const poster = this.getAttribute(\"poster\");\n engine = createVideoEngine(src, { loop, muted, ...(poster ? { poster } : {}) });\n this.docChapters = undefined;\n const video = engine.element as HTMLVideoElement;\n video.addEventListener(\"loadedmetadata\", () => {\n if (video.videoWidth && video.videoHeight) this.style.setProperty(\"--mc-aspect\", `${video.videoWidth}/${video.videoHeight}`);\n this.refreshChapters();\n });\n }\n if (token !== this.loadToken) {\n engine.destroy();\n return;\n }\n this.engine = engine;\n this.els.stage.append(engine.element);\n let wasPlaying = false;\n engine.onChange(() => {\n const playing = engine.state().playing;\n if (playing !== wasPlaying) {\n wasPlaying = playing;\n this.toggleAttribute(\"data-playing\", playing);\n this.dispatch(playing ? \"play\" : \"pause\");\n }\n this.render();\n this.dispatch(\"timeupdate\");\n });\n engine.onEnded(() => this.dispatch(\"ended\"));\n engine.onError((message) => {\n this.showError(message);\n this.dispatch(\"error\", { message });\n });\n this.sizeEngine();\n await this.loadChapterAttribute();\n this.refreshChapters();\n this.setAttribute(\"data-ready\", \"\");\n this.render();\n this.dispatch(\"ready\");\n if (this.hasAttribute(\"autoplay\")) engine.play();\n } catch (error) {\n if (token !== this.loadToken) return;\n const message = error instanceof Error ? error.message : String(error);\n this.showError(message);\n this.dispatch(\"error\", { message });\n }\n }\n\n private docChapters: ((durationS: number) => PlayerChapter[]) | undefined;\n private attributeChapters: PlayerChapter[] | undefined;\n\n private async loadChapterAttribute(): Promise<void> {\n const url = this.getAttribute(\"chapters\");\n if (!url) {\n this.attributeChapters = undefined;\n return;\n }\n try {\n const response = await fetch(url);\n const text = await response.text();\n this.attributeChapters = text.trimStart().startsWith(\"WEBVTT\") ? parseVttChapters(text) : normalizeChapters(JSON.parse(text), this.duration);\n } catch {\n this.attributeChapters = undefined;\n }\n this.refreshChapters();\n }\n\n private refreshChapters(): void {\n const duration = this.duration;\n this.chapterList = this.explicitChapters ?? this.attributeChapters ?? this.docChapters?.(duration) ?? [];\n const { ticks, menu, chaptersBtn } = this.els;\n ticks.replaceChildren();\n menu.replaceChildren();\n chaptersBtn.hidden = this.chapterList.length === 0;\n for (const [i, chapter] of this.chapterList.entries()) {\n if (duration > 0 && chapter.start > 0) {\n const tick = document.createElement(\"div\");\n tick.className = \"tick\";\n tick.style.left = `${(chapter.start / duration) * 100}%`;\n tick.title = chapter.label;\n ticks.append(tick);\n }\n const li = document.createElement(\"li\");\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.dataset[\"index\"] = String(i);\n button.innerHTML = `<span class=\"t\"></span><span class=\"n\"></span>`;\n (button.querySelector(\".t\") as HTMLElement).textContent = formatTime(chapter.start);\n (button.querySelector(\".n\") as HTMLElement).textContent = chapter.label;\n li.append(button);\n menu.append(li);\n }\n this.currentChapter = undefined;\n this.render();\n }\n\n // ------------------------------------------------------------------ UI\n\n private wire(): void {\n const { big, play, scrub, chaptersBtn, menu, mute, frame } = this.els;\n big.addEventListener(\"click\", () => this.play());\n play.addEventListener(\"click\", () => this.toggle());\n this.els.stage.addEventListener(\"click\", () => this.toggle());\n mute.addEventListener(\"click\", () => {\n const muted = !this.hasAttribute(\"muted\");\n this.toggleAttribute(\"muted\", muted);\n this.engine?.setMuted(muted);\n mute.innerHTML = muted ? ICON.muted : ICON.sound;\n mute.setAttribute(\"aria-label\", muted ? \"Unmute\" : \"Mute\");\n });\n chaptersBtn.addEventListener(\"click\", () => {\n const open = menu.hidden;\n menu.hidden = !open;\n chaptersBtn.setAttribute(\"aria-expanded\", String(open));\n if (open) (menu.querySelector<HTMLButtonElement>('button[aria-current=\"true\"]') ?? menu.querySelector<HTMLButtonElement>(\"button\"))?.focus();\n });\n menu.addEventListener(\"click\", (event) => {\n const button = (event.target as HTMLElement).closest(\"button\");\n if (!button) return;\n this.goToChapter(Number(button.dataset[\"index\"]));\n menu.hidden = true;\n chaptersBtn.setAttribute(\"aria-expanded\", \"false\");\n });\n const seekFromPointer = (event: PointerEvent): void => {\n const rect = scrub.getBoundingClientRect();\n if (rect.width <= 0) return;\n this.seek(((event.clientX - rect.left) / rect.width) * this.duration);\n };\n scrub.addEventListener(\"pointerdown\", (event) => {\n this.dragging = true;\n scrub.setPointerCapture?.(event.pointerId);\n seekFromPointer(event);\n });\n scrub.addEventListener(\"pointermove\", (event) => {\n if (this.dragging) seekFromPointer(event);\n });\n scrub.addEventListener(\"pointerup\", () => {\n this.dragging = false;\n });\n const keys = (event: KeyboardEvent): void => {\n const step = event.shiftKey ? 10 : 5;\n if (event.key === \" \" || event.key === \"k\") this.toggle();\n else if (event.key === \"ArrowRight\" || event.key === \"l\") this.seek(this.currentTime + step);\n else if (event.key === \"ArrowLeft\" || event.key === \"j\") this.seek(this.currentTime - step);\n else if (event.key === \"Home\") this.seek(0);\n else if (event.key === \"End\") this.seek(this.duration);\n else if (event.key === \"]\" ) this.stepChapter(1);\n else if (event.key === \"[\") this.stepChapter(-1);\n else if (event.key === \"Escape\") menu.hidden = true;\n else return;\n event.preventDefault();\n };\n frame.addEventListener(\"keydown\", keys);\n }\n\n private stepChapter(direction: 1 | -1): void {\n if (this.chapterList.length === 0) return;\n const t = this.currentTime;\n if (direction > 0) {\n const next = this.chapterList.find((c) => c.start > t + 0.25);\n if (next) this.seek(next.start);\n } else {\n const previous = [...this.chapterList].reverse().find((c) => c.start < t - 1);\n this.seek(previous?.start ?? 0);\n }\n }\n\n private render(): void {\n const state = this.engine?.state() ?? { playing: false, timeS: 0, durationS: 0 };\n const { time, fill, thumb, scrub, play, label, menu } = this.els;\n const fraction = state.durationS > 0 ? Math.min(1, state.timeS / state.durationS) : 0;\n time.textContent = `${formatTime(state.timeS)} / ${formatTime(state.durationS)}`;\n fill.style.width = `${fraction * 100}%`;\n thumb.style.left = `${fraction * 100}%`;\n scrub.setAttribute(\"aria-valuemax\", String(Math.round(state.durationS)));\n scrub.setAttribute(\"aria-valuenow\", String(Math.round(state.timeS)));\n scrub.setAttribute(\"aria-valuetext\", time.textContent);\n const wanted = state.playing ? \"Pause\" : \"Play\";\n if (play.getAttribute(\"aria-label\") !== wanted) {\n play.innerHTML = state.playing ? ICON.pause : ICON.play;\n play.setAttribute(\"aria-label\", wanted);\n }\n const chapter = chapterAt(this.chapterList, state.timeS);\n label.textContent = chapter?.label ?? \"\";\n if (chapter?.id !== this.currentChapter?.id) {\n this.currentChapter = chapter;\n for (const button of Array.from(menu.querySelectorAll(\"button\"))) {\n button.setAttribute(\"aria-current\", String(this.chapterList[Number(button.dataset[\"index\"])]?.id === chapter?.id));\n }\n this.dispatch(\"chapterchange\", { chapter: chapter ?? null });\n }\n }\n\n private sizeEngine(): void {\n const rect = this.els.frame.getBoundingClientRect();\n this.engine?.resize(rect.width, rect.height);\n }\n\n private showError(message: string | null): void {\n this.els.error.hidden = message === null;\n this.els.error.textContent = message ?? \"\";\n }\n\n private dispatch(type: string, detail?: unknown): void {\n this.dispatchEvent(new CustomEvent(type, { detail, bubbles: type !== \"timeupdate\", composed: true }));\n }\n}\n\n/** Register the element (default tag `miraiclip-player`). Safe to call twice. */\nexport function defineMiraiclipPlayer(tagName = \"miraiclip-player\"): typeof MiraiclipPlayerElement {\n if (typeof customElements === \"undefined\") return MiraiclipPlayerElement;\n const existing = customElements.get(tagName);\n if (existing) return existing as typeof MiraiclipPlayerElement;\n customElements.define(tagName, MiraiclipPlayerElement);\n return MiraiclipPlayerElement;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"miraiclip-player\": MiraiclipPlayerElement;\n }\n}\n"],"mappings":"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","names":[]}