@mattebox/player-diagnostics 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/dist/cdn/mattebox-player-diagnostics.min.js +1 -1
  2. package/dist/charts.js +1 -2
  3. package/dist/dom.js +1 -2
  4. package/dist/element.d.ts +13 -2
  5. package/dist/element.d.ts.map +1 -1
  6. package/dist/element.js +35 -11
  7. package/dist/element.js.map +1 -1
  8. package/dist/es2015/_virtual/{_@oxc-project_runtime@0.154.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/asyncToGenerator.js +1 -2
  9. package/dist/es2015/_virtual/{_@oxc-project_runtime@0.154.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/defineProperty.js +1 -2
  10. package/dist/es2015/_virtual/{_@oxc-project_runtime@0.154.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/objectSpread2.js +1 -2
  11. package/dist/es2015/_virtual/{_@oxc-project_runtime@0.154.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/toPrimitive.js +1 -2
  12. package/dist/es2015/_virtual/{_@oxc-project_runtime@0.154.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/toPropertyKey.js +1 -2
  13. package/dist/es2015/_virtual/{_@oxc-project_runtime@0.154.0 → _@oxc-project_runtime@0.155.0}/helpers/esm/typeof.js +1 -2
  14. package/dist/es2015/charts.js +1 -2
  15. package/dist/es2015/dom.js +1 -2
  16. package/dist/es2015/element.js +39 -17
  17. package/dist/es2015/format.js +1 -2
  18. package/dist/es2015/host.js +1 -2
  19. package/dist/es2015/icon.js +1 -2
  20. package/dist/es2015/index.js +1 -2
  21. package/dist/es2015/locales/ar.js +8 -0
  22. package/dist/es2015/locales/ca.js +8 -0
  23. package/dist/es2015/locales/de.js +8 -0
  24. package/dist/es2015/locales/es.js +8 -0
  25. package/dist/es2015/locales/fr.js +8 -0
  26. package/dist/es2015/locales/hi.js +8 -0
  27. package/dist/es2015/locales/it.js +8 -0
  28. package/dist/es2015/locales/ja.js +8 -0
  29. package/dist/es2015/locales/ko.js +8 -0
  30. package/dist/es2015/locales/pt.js +8 -0
  31. package/dist/es2015/locales/zh.js +8 -0
  32. package/dist/es2015/report.js +16 -4
  33. package/dist/es2015/sampler.js +1 -2
  34. package/dist/es2015/style.js +0 -1
  35. package/dist/es2015/support.js +3 -4
  36. package/dist/es2015/trace.js +2 -3
  37. package/dist/format.js +1 -2
  38. package/dist/host.js +1 -2
  39. package/dist/icon.js +1 -2
  40. package/dist/index.d.ts +2 -1
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +1 -2
  43. package/dist/index.js.map +1 -1
  44. package/dist/locales/ar.d.ts +8 -0
  45. package/dist/locales/ar.d.ts.map +1 -0
  46. package/dist/locales/ar.js +10 -0
  47. package/dist/locales/ar.js.map +1 -0
  48. package/dist/locales/ca.d.ts +8 -0
  49. package/dist/locales/ca.d.ts.map +1 -0
  50. package/dist/locales/ca.js +10 -0
  51. package/dist/locales/ca.js.map +1 -0
  52. package/dist/locales/de.d.ts +8 -0
  53. package/dist/locales/de.d.ts.map +1 -0
  54. package/dist/locales/de.js +10 -0
  55. package/dist/locales/de.js.map +1 -0
  56. package/dist/locales/es.d.ts +8 -0
  57. package/dist/locales/es.d.ts.map +1 -0
  58. package/dist/locales/es.js +10 -0
  59. package/dist/locales/es.js.map +1 -0
  60. package/dist/locales/fr.d.ts +8 -0
  61. package/dist/locales/fr.d.ts.map +1 -0
  62. package/dist/locales/fr.js +10 -0
  63. package/dist/locales/fr.js.map +1 -0
  64. package/dist/locales/hi.d.ts +8 -0
  65. package/dist/locales/hi.d.ts.map +1 -0
  66. package/dist/locales/hi.js +10 -0
  67. package/dist/locales/hi.js.map +1 -0
  68. package/dist/locales/it.d.ts +8 -0
  69. package/dist/locales/it.d.ts.map +1 -0
  70. package/dist/locales/it.js +10 -0
  71. package/dist/locales/it.js.map +1 -0
  72. package/dist/locales/ja.d.ts +8 -0
  73. package/dist/locales/ja.d.ts.map +1 -0
  74. package/dist/locales/ja.js +10 -0
  75. package/dist/locales/ja.js.map +1 -0
  76. package/dist/locales/ko.d.ts +8 -0
  77. package/dist/locales/ko.d.ts.map +1 -0
  78. package/dist/locales/ko.js +10 -0
  79. package/dist/locales/ko.js.map +1 -0
  80. package/dist/locales/pt.d.ts +8 -0
  81. package/dist/locales/pt.d.ts.map +1 -0
  82. package/dist/locales/pt.js +10 -0
  83. package/dist/locales/pt.js.map +1 -0
  84. package/dist/locales/zh.d.ts +8 -0
  85. package/dist/locales/zh.d.ts.map +1 -0
  86. package/dist/locales/zh.js +10 -0
  87. package/dist/locales/zh.js.map +1 -0
  88. package/dist/messages.d.ts +22 -0
  89. package/dist/messages.d.ts.map +1 -0
  90. package/dist/report.d.ts +9 -0
  91. package/dist/report.d.ts.map +1 -1
  92. package/dist/report.js +12 -4
  93. package/dist/report.js.map +1 -1
  94. package/dist/sampler.js +1 -2
  95. package/dist/style.js +0 -1
  96. package/dist/support.js +1 -2
  97. package/dist/trace.js +1 -2
  98. package/package.json +9 -4
@@ -1 +1 @@
1
- {"version":3,"file":"element.js","names":[],"sources":["../src/element.ts"],"sourcesContent":["/**\n * <mbx-diagnostics>: what the session is doing, for an integrator's eyes.\n * In the bar it is a button that opens a panel over the picture; anywhere\n * else inside the player, the panel itself, open, in flow. Four pages:\n * the playback facts, the charts, the engine's state, and what the browser\n * supports. A button copies the report, and `report()` hands it to a page,\n * which also gets it as a `report` event on every fatal error, so a page\n * sends it where its errors go.\n *\n * The sampler runs while the element is attached, panel open or not, so\n * the counts cover the session, and keeps the session's trace, slimmed,\n * from the engine's `trace` events, or off its ring where it emits none. The pages redraw twice a second while\n * shown, the charts four times. The browser is probed once, the first\n * time its page is shown: the key-system probes are not free, and Firefox\n * asks the viewer about a DRM module it has not enabled.\n *\n * A native session has no engine, and the engine's rules say a native\n * session has no diagnostics beyond the video's own: the playback page and\n * the buffer, stall and frame charts read the video, the rest says so.\n *\n * No class fields: the ES2015 build would lower them into helpers, and the\n * player's emit check bans helpers. State is declared, then assigned.\n */\nimport type { Mattebox } from 'mattebox';\nimport type { Charts, ChartTab, Palette } from './charts.js';\nimport { CHART_TABS, createCharts } from './charts.js';\nimport { setChildren } from './dom.js';\nimport { bitrate, clock, fixed, ranges, rendition, share } from './format.js';\nimport type { PlayerError, PlayerHost } from './host.js';\nimport { findPlayer, whenPlayer } from './host.js';\nimport { icon } from './icon.js';\nimport type { DiagnosticsReport, QoeFigures } from './report.js';\nimport { buildReport } from './report.js';\nimport type { Sampler } from './sampler.js';\nimport { createSampler } from './sampler.js';\nimport { STYLE } from './style.js';\nimport type { Cell, Support } from './support.js';\nimport { probeSupport } from './support.js';\n\nconst POLL_MS = 500;\nconst DRAW_MS = 250;\nconst WINDOW = 120;\nconst WINDOWS = [30, 60, 120, 300, 600];\n/** The panel's width in em, when the picture has the room. */\nconst PANEL_WIDTH = 35;\n/** Air between the panel's top and the picture's, and around it. */\nconst AIR = 8;\n\nconst TABS = ['playback', 'charts', 'engine', 'browser'] as const;\ntype Tab = (typeof TABS)[number];\nconst TAB_NAMES: Readonly<Record<Tab, string>> = {\n playback: 'Playback',\n charts: 'Charts',\n engine: 'Engine',\n browser: 'Browser',\n};\n\nconst READY_STATES = ['nothing', 'metadata', 'current data', 'future data', 'enough data'];\nconst NETWORK_STATES = ['empty', 'idle', 'loading', 'no source'];\n\ntype Pair = readonly [string, string];\n\ninterface Section {\n readonly title: string | null;\n readonly rows: readonly Pair[];\n /** A line instead of rows, for a section with nothing to list. */\n readonly note?: string | undefined;\n}\n\n/** The optional namespaces the pages read, cast once, the way the player does it. */\ninterface Namespaces {\n readonly live?: { readonly latency: number | null; readonly atEdge: boolean };\n readonly qoe?: QoeFigures;\n readonly drm?: {\n readonly keySystem: string | null;\n readonly sessions: ReadonlyArray<{ readonly keyId: string; readonly status: string }>;\n };\n}\n\nfunction el<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n part: string,\n text?: string,\n): HTMLElementTagNameMap[K] {\n const node = document.createElement(tag);\n node.setAttribute('part', part);\n if (text !== undefined) node.textContent = text;\n return node;\n}\n\nfunction button(part: string, text?: string): HTMLButtonElement {\n const node = el('button', part, text);\n node.type = 'button';\n return node;\n}\n\n/** A cell's text and its part: a tick, a cross, a dash, or the text itself. */\nfunction cell(value: Cell): [string, string] {\n switch (value) {\n case 'yes':\n return ['✓', 'cell ok'];\n case 'no':\n return ['✕', 'cell bad'];\n case 'na':\n return ['–', 'cell na'];\n case 'maybe':\n return ['maybe', 'cell maybe'];\n default:\n return [value, 'cell'];\n }\n}\n\nexport class MbxDiagnostics extends HTMLElement {\n static get observedAttributes(): readonly string[] {\n return ['label', 'label-copy', 'label-copied', 'window'];\n }\n\n declare private player: PlayerHost | null;\n declare private cancel: (() => void) | null;\n declare private offs: Array<() => void>;\n declare private readonly sampler: Sampler;\n declare private readonly charts: Charts;\n declare private timer: ReturnType<typeof setInterval> | undefined;\n declare private drawer: ReturnType<typeof setInterval> | undefined;\n declare private copied: ReturnType<typeof setTimeout> | undefined;\n declare private tab: Tab;\n declare private chartTab: ChartTab;\n declare private support: Support | null;\n declare private probing: boolean;\n\n declare private readonly toggle: HTMLButtonElement;\n declare private readonly panel: HTMLElement;\n declare private readonly body: HTMLElement;\n declare private readonly tabs: Record<Tab, HTMLButtonElement>;\n declare private readonly pages: Record<Tab, HTMLElement>;\n declare private readonly copy: HTMLButtonElement;\n declare private readonly canvas: HTMLCanvasElement;\n declare private readonly chartTabs: Record<ChartTab, HTMLButtonElement>;\n declare private readonly windowSelect: HTMLSelectElement;\n declare private readonly outside: (event: PointerEvent) => void;\n declare private readonly refit: () => void;\n\n constructor() {\n super();\n this.player = null;\n this.cancel = null;\n this.offs = [];\n this.sampler = createSampler();\n this.timer = undefined;\n this.drawer = undefined;\n this.copied = undefined;\n this.tab = 'playback';\n this.chartTab = 'buffer';\n this.support = null;\n this.probing = false;\n\n const root = this.attachShadow({ mode: 'open' });\n const style = document.createElement('style');\n style.textContent = STYLE;\n\n this.toggle = button('button');\n this.toggle.setAttribute('aria-haspopup', 'dialog');\n this.toggle.setAttribute('aria-expanded', 'false');\n const slot = document.createElement('slot');\n slot.name = 'icon';\n slot.append(icon());\n this.toggle.append(slot);\n this.toggle.addEventListener('click', () => {\n if (this.panel.hidden) this.show();\n else this.hide();\n });\n\n this.panel = el('div', 'panel');\n this.panel.setAttribute('role', 'dialog');\n this.panel.hidden = true;\n this.panel.addEventListener('keydown', (event) => {\n if (event.key !== 'Escape' || this.hasAttribute('inline')) return;\n event.preventDefault();\n this.hide();\n this.toggle.focus();\n });\n\n const head = el('div', 'head');\n // A tablist holds tabs alone (WAI-ARIA 1.2), so copy and close sit beside it.\n const tablist = el('div', 'tabs');\n tablist.setAttribute('role', 'tablist');\n head.append(tablist);\n this.tabs = {} as Record<Tab, HTMLButtonElement>;\n this.pages = {} as Record<Tab, HTMLElement>;\n this.body = el('div', 'body');\n // It scrolls, so a keyboard has to reach it (WCAG 2.2 SC 2.1.1).\n this.body.tabIndex = 0;\n for (const tab of TABS) {\n const node = button(`tab tab-${tab}`, TAB_NAMES[tab]);\n node.setAttribute('role', 'tab');\n node.addEventListener('click', () => {\n this.select(tab);\n });\n this.tabs[tab] = node;\n tablist.append(node);\n const page = el('div', `page page-${tab}`);\n page.setAttribute('role', 'tabpanel');\n page.hidden = true;\n this.pages[tab] = page;\n this.body.append(page);\n }\n this.copy = button('copy');\n this.copy.addEventListener('click', () => {\n void this.copyReport();\n });\n const close = button('close', '✕');\n close.setAttribute('aria-label', 'Close');\n close.addEventListener('click', () => {\n this.hide();\n this.toggle.focus();\n });\n head.append(this.copy, close);\n\n // The charts page: its own tabs, the window, the canvas, the legend and the readout.\n const chartTabs = el('div', 'chart-tabs');\n // The tabs in a tablist of their own, the window select beside it.\n const chartTablist = el('div', 'chart-tablist');\n chartTablist.setAttribute('role', 'tablist');\n chartTabs.append(chartTablist);\n this.chartTabs = {} as Record<ChartTab, HTMLButtonElement>;\n for (const tab of CHART_TABS) {\n const node = button(`chart-tab chart-tab-${tab}`, tab);\n node.setAttribute('role', 'tab');\n node.addEventListener('click', () => {\n this.chartTab = tab;\n this.markChartTab();\n this.draw();\n });\n this.chartTabs[tab] = node;\n chartTablist.append(node);\n }\n this.windowSelect = el('select', 'window');\n this.windowSelect.setAttribute('aria-label', 'Window');\n for (const seconds of WINDOWS) {\n const option = document.createElement('option');\n option.value = String(seconds);\n option.textContent = seconds < 60 ? `${seconds} s` : `${seconds / 60} min`;\n this.windowSelect.append(option);\n }\n this.windowSelect.addEventListener('change', () => {\n this.draw();\n });\n chartTabs.append(this.windowSelect);\n this.canvas = el('canvas', 'chart');\n const legend = el('div', 'legend');\n const readout = el('p', 'readout');\n this.pages.charts.append(chartTabs, this.canvas, legend, readout);\n this.charts = createCharts(this.canvas, legend, readout);\n\n this.outside = (event: PointerEvent): void => {\n if (!event.composedPath().includes(this)) this.hide();\n };\n this.refit = (): void => {\n this.fit();\n };\n\n this.panel.append(head, this.body);\n root.append(style, this.toggle, this.panel);\n this.markTab();\n this.markChartTab();\n this.renderLabels();\n }\n\n connectedCallback(): void {\n // In the bar, a button and a popup; anywhere else, the panel in flow.\n this.toggleAttribute('inline', this.parentElement?.localName !== 'mbx-control-bar');\n const found = findPlayer(this);\n if (found === null) return;\n this.cancel?.();\n this.cancel = whenPlayer(found, (player) => {\n if (!this.isConnected) return;\n this.attach(player);\n });\n }\n\n disconnectedCallback(): void {\n this.cancel?.();\n this.cancel = null;\n this.detach();\n }\n\n attributeChangedCallback(): void {\n this.renderLabels();\n }\n\n private attach(player: PlayerHost): void {\n this.player = player;\n this.sampler.attach(player.video, player.engine);\n const on = (target: EventTarget, name: string, fn: (event: Event) => void): void => {\n target.addEventListener(name, fn);\n this.offs.push(() => {\n target.removeEventListener(name, fn);\n });\n };\n on(player, 'sourcechange', () => {\n this.sampler.attach(player.video, player.engine);\n this.poll();\n });\n on(player, 'error', (event) => {\n const error = (event as CustomEvent<PlayerError>).detail;\n if (error.fatal) {\n this.dispatchEvent(\n new CustomEvent('report', { detail: this.report(), bubbles: true, composed: true }),\n );\n }\n });\n this.timer = setInterval(() => {\n this.poll();\n }, POLL_MS);\n this.poll();\n if (this.hasAttribute('inline')) this.show();\n }\n\n private detach(): void {\n for (const off of this.offs) off();\n this.offs = [];\n clearInterval(this.timer);\n this.timer = undefined;\n this.sampler.release();\n this.hide();\n this.player = null;\n }\n\n /** The report, as of now. */\n report(): DiagnosticsReport {\n const player = this.player;\n if (player === null) throw new Error('mbx-diagnostics is not inside a player');\n return buildReport({\n player,\n samples: this.sampler.samples,\n marks: this.sampler.marks,\n counters: this.sampler.counters,\n history: this.sampler.history,\n support: this.support,\n });\n }\n\n private poll(): void {\n const player = this.player;\n if (player === null) return;\n this.sampler.poll(player.video, player.engine);\n if (!this.panel.hidden) this.paint();\n }\n\n private show(): void {\n if (!this.panel.hidden) return;\n this.panel.hidden = false;\n if (!this.hasAttribute('inline')) {\n this.setAttribute('open', '');\n this.toggle.setAttribute('aria-expanded', 'true');\n document.addEventListener('pointerdown', this.outside, true);\n window.addEventListener('resize', this.refit);\n this.fit();\n }\n this.paint();\n this.drawer = setInterval(() => {\n this.draw();\n }, DRAW_MS);\n if (!this.hasAttribute('inline')) this.tabs[this.tab].focus();\n }\n\n private hide(): void {\n clearInterval(this.drawer);\n this.drawer = undefined;\n if (this.panel.hidden) return;\n this.panel.hidden = true;\n this.removeAttribute('open');\n this.toggle.setAttribute('aria-expanded', 'false');\n document.removeEventListener('pointerdown', this.outside, true);\n window.removeEventListener('resize', this.refit);\n }\n\n /** The panel never leaves the picture: fitted to the room above the button, within the player's width. */\n private fit(): void {\n const player = this.player;\n if (player === null) return;\n const box = player.getBoundingClientRect();\n const host = this.getBoundingClientRect();\n const top = player.video.getBoundingClientRect().top;\n const room = host.top - top - AIR;\n this.panel.style.maxHeight = `${Math.max(120, Math.floor(room))}px`;\n const em = Number.parseFloat(getComputedStyle(this).fontSize) || 16;\n const width = Math.min(PANEL_WIDTH * em, Math.max(200, box.width - AIR * 2));\n this.panel.style.width = `${width}px`;\n const left = Math.max(box.left + AIR, Math.min(host.right - width, box.right - AIR - width));\n this.panel.style.right = 'auto';\n this.panel.style.left = `${left - host.left}px`;\n }\n\n private select(tab: Tab): void {\n this.tab = tab;\n this.markTab();\n this.tabs[tab].focus();\n this.paint();\n this.draw();\n }\n\n private markTab(): void {\n for (const tab of TABS) {\n this.tabs[tab].setAttribute('aria-selected', String(tab === this.tab));\n this.tabs[tab].tabIndex = tab === this.tab ? 0 : -1;\n this.pages[tab].hidden = tab !== this.tab;\n }\n }\n\n private markChartTab(): void {\n for (const tab of CHART_TABS) {\n this.chartTabs[tab].setAttribute('aria-selected', String(tab === this.chartTab));\n }\n }\n\n private renderLabels(): void {\n this.toggle.setAttribute('aria-label', this.getAttribute('label') ?? 'Diagnostics');\n this.panel.setAttribute('aria-label', this.getAttribute('label') ?? 'Diagnostics');\n if (this.copied === undefined) {\n this.copy.textContent = this.getAttribute('label-copy') ?? 'Copy report';\n }\n const raw = Number(this.getAttribute('window'));\n const seconds = Number.isFinite(raw) && raw > 0 ? raw : WINDOW;\n if (!WINDOWS.includes(seconds)) {\n const option = document.createElement('option');\n option.value = String(seconds);\n option.textContent = `${seconds} s`;\n this.windowSelect.append(option);\n }\n this.windowSelect.value = String(seconds);\n }\n\n private async copyReport(): Promise<void> {\n const text = JSON.stringify(this.report(), null, 2);\n try {\n await navigator.clipboard.writeText(text);\n } catch {\n return;\n }\n this.copy.textContent = this.getAttribute('label-copied') ?? 'Copied';\n clearTimeout(this.copied);\n this.copied = setTimeout(() => {\n this.copied = undefined;\n this.renderLabels();\n }, 1500);\n }\n\n /** The colours the charts draw with, off the element's tokens. */\n private palette(): Palette {\n const computed = getComputedStyle(this);\n const token = (name: string, fallback: string): string =>\n computed.getPropertyValue(name).trim() || fallback;\n return {\n ink: token('--mbx-text', '#f3ede4'),\n muted: token('--mbx-muted', '#aaa49a'),\n accent: token('--mbx-accent', '#e0543c'),\n warn: token('--mbx-chart-3', '#d9a83f'),\n series: [1, 2, 3, 4, 5].map((i) => token(`--mbx-chart-${i}`, '#888')),\n };\n }\n\n private draw(): void {\n const player = this.player;\n if (player === null || this.panel.hidden || this.tab !== 'charts') return;\n this.charts.draw({\n tab: this.chartTab,\n window: Number(this.windowSelect.value) || WINDOW,\n samples: this.sampler.samples,\n marks: this.sampler.marks,\n counters: this.sampler.counters,\n video: player.video,\n engine: player.engine,\n palette: this.palette(),\n });\n }\n\n /** The page shown, from the facts of the moment. */\n private paint(): void {\n const player = this.player;\n if (player === null || this.panel.hidden) return;\n switch (this.tab) {\n case 'playback':\n this.sections(this.pages.playback, this.playbackSections(player));\n break;\n case 'engine':\n this.sections(this.pages.engine, this.engineSections(player.engine));\n break;\n case 'browser':\n this.browser();\n break;\n default:\n break;\n }\n }\n\n private playbackSections(player: PlayerHost): Section[] {\n const video = player.video;\n const engine = player.engine;\n const quality = video.getVideoPlaybackQuality?.();\n const counters = this.sampler.counters;\n const last = this.sampler.samples[this.sampler.samples.length - 1];\n const state = video.paused ? (video.ended ? 'ended' : 'paused') : 'playing';\n const source: Pair[] = [\n ['source', player.getAttribute('src') ?? '–'],\n ['type', player.getAttribute('type') ?? '–'],\n ['handler', player.player?.session?.handler ?? '–'],\n ];\n if (engine !== null) source.push(['phase', engine.stats.snapshot().lifecycle.phase]);\n if (player.error !== null) {\n source.push(['error', `${player.error.category}: ${player.error.code}`]);\n } else if (video.error !== null) {\n source.push(['media error', `${video.error.code} ${video.error.message}`]);\n }\n const playback: Pair[] = [\n ['time', `${clock(video.currentTime)} of ${clock(video.duration)}`],\n [\n 'state',\n `${state}, ${READY_STATES[video.readyState] ?? video.readyState}, network ${NETWORK_STATES[video.networkState] ?? video.networkState}`,\n ],\n ['rate', `${video.playbackRate}×`],\n ['picture', video.videoWidth > 0 ? `${video.videoWidth}×${video.videoHeight}` : '–'],\n [\n 'frames',\n quality === undefined\n ? '–'\n : `${quality.totalVideoFrames} decoded, ${quality.droppedVideoFrames} dropped (${share(quality.droppedVideoFrames, quality.totalVideoFrames)})`,\n ],\n ['buffered', `${fixed(last?.ahead ?? 0)}s ahead: ${ranges(playbackRanges(video))}`],\n ['stalls', `${counters.stalls}, ${counters.stalledSeconds.toFixed(1)}s in total`],\n ];\n if (engine !== null) {\n const snapshot = engine.stats.snapshot();\n playback.push(\n [\n 'throughput',\n `slow ${bitrate(snapshot.stats.throughputEwma)}, fast ${bitrate(snapshot.stats.throughputFastEwma)}`,\n ],\n ['playing', rendition(engine.quality.playing)],\n ['switches', String(counters.switches)],\n );\n }\n // The engine's own figures, when its qoe stage is in the stack: they\n // count from the load, where the stalls above count from the sampling.\n const qoe = (engine as Namespaces | null)?.qoe;\n return [\n { title: 'Source', rows: source },\n { title: 'Playback', rows: playback },\n ...(qoe === undefined\n ? []\n : [\n {\n title: 'Quality of experience',\n rows: [\n ['startup', qoe.startupTime === null ? '–' : `${qoe.startupTime.toFixed(2)}s`],\n ['rebuffers', `${qoe.rebuffers}, ${qoe.rebufferDuration.toFixed(1)}s in total`],\n ['quality changes', String(qoe.switches)],\n ] satisfies Pair[],\n },\n ]),\n ];\n }\n\n private engineSections(engine: Mattebox | null): Section[] {\n if (engine === null) {\n return [\n {\n title: null,\n rows: [],\n note: 'A native session: the browser plays this, and there is no engine to ask.',\n },\n ];\n }\n const state = engine.stats.snapshot();\n const optional = engine as Namespaces;\n const live = state.live;\n const rows: Pair[] = [\n ['capabilities', [...engine.capabilities()].join(', ') || 'none'],\n ['phase', state.lifecycle.phase],\n ['buffer goal', `${state.scheduling.bufferGoal}s`],\n [\n 'throughput',\n `slow ${bitrate(state.stats.throughputEwma)}, fast ${bitrate(state.stats.throughputFastEwma)}`,\n ],\n [\n 'in flight',\n [...state.scheduling.inflight.values()].map((r) => `${r.trackId} #${r.seq}`).join(', ') ||\n 'none',\n ],\n [\n 'live',\n live === null\n ? 'VOD'\n : `window ${clock(live.span.start)} to ${clock(live.span.end)}, edge ${clock(live.edge)}${\n optional.live?.latency === null || optional.live?.latency === undefined\n ? ''\n : `, ${optional.live.latency.toFixed(1)}s behind`\n }`,\n ],\n ['trace', `${engine.stats.trace().length} entries`],\n ];\n const error = engine.error;\n if (error !== null) rows.push(['last error', `${error.category}: ${error.code}`]);\n\n const buffers: Pair[] = [...state.buffers.entries()].map(([id, buffer]) => [\n id.replace('sb:', ''),\n `${buffer.codecs}: ${ranges(buffer.ranges)}${buffer.pendingAppends > 0 ? `, ${buffer.pendingAppends} pending` : ''}`,\n ]);\n\n const tracks: Pair[] = engine.tracks.available.map((track) => {\n const active = engine.tracks.active(track.contentType)?.id === track.id;\n const name = [track.lang, track.role].filter((v) => v !== undefined).join(' · ');\n return [\n `${track.contentType}${active ? ' ●' : ''}`,\n `${track.id}${name === '' ? '' : ` (${name})`}, ${track.renditions.length} renditions`,\n ];\n });\n\n const playing = engine.quality.playing?.id ?? null;\n const active = engine.quality.active?.id ?? null;\n const allowed = new Set(engine.quality.allowed.map((r) => r.id));\n const renditions: Pair[] = engine.quality.renditions.map((r) => {\n const flags = [\n r.id === playing ? 'playing' : '',\n r.id === active && active !== playing ? 'next' : '',\n r.id === engine.quality.pinned ? 'pinned' : '',\n allowed.has(r.id) ? '' : 'capped',\n ].filter((f) => f !== '');\n const size = r.width !== undefined && r.height !== undefined ? `${r.width}×${r.height}` : '';\n const text = [\n size,\n bitrate(r.bitrate),\n r.frameRate === undefined ? '' : `${r.frameRate} fps`,\n r.codecs ?? '',\n ]\n .filter((v) => v !== '')\n .join(', ');\n return [r.id, `${text}${flags.length > 0 ? ` [${flags.join(', ')}]` : ''}`];\n });\n\n const drm: Pair[] =\n optional.drm === undefined\n ? []\n : [\n ['key system', optional.drm.keySystem ?? 'none yet'],\n [\n 'keys',\n optional.drm.sessions.length === 0\n ? 'none yet'\n : optional.drm.sessions.map((s) => `${s.keyId}: ${s.status}`).join(', '),\n ],\n ];\n\n return [\n { title: 'Engine', rows },\n {\n title: 'Source buffers',\n rows: buffers,\n note: buffers.length === 0 ? 'none yet' : undefined,\n },\n { title: 'Tracks', rows: tracks, note: tracks.length === 0 ? 'none yet' : undefined },\n {\n title: 'Renditions',\n rows: renditions,\n note: renditions.length === 0 ? 'none yet' : undefined,\n },\n ...(optional.drm === undefined ? [] : [{ title: 'DRM', rows: drm }]),\n ];\n }\n\n /**\n * Draws sections into a page, reusing what is there: a heading and a\n * list per section, a row per pair, texts replaced only where they\n * changed, so a page open for an hour neither flickers nor grows.\n */\n private sections(page: HTMLElement, sections: readonly Section[]): void {\n const wanted = sections.flatMap((section) => {\n const nodes: Array<['heading' | 'note' | 'rows', string | readonly Pair[]]> = [];\n if (section.title !== null) nodes.push(['heading', section.title]);\n if (section.note !== undefined && section.rows.length === 0)\n nodes.push(['note', section.note]);\n else nodes.push(['rows', section.rows]);\n return nodes;\n });\n while (page.childElementCount > wanted.length) page.lastElementChild?.remove();\n wanted.forEach(([kind, content], i) => {\n let node = page.children[i] as HTMLElement | undefined;\n const tag = kind === 'heading' ? 'h3' : kind === 'note' ? 'p' : 'dl';\n if (node === undefined || node.localName !== tag) {\n const fresh = el(tag, kind);\n if (node === undefined) page.append(fresh);\n else node.replaceWith(fresh);\n node = fresh;\n }\n if (typeof content === 'string') {\n if (node.textContent !== content) node.textContent = content;\n return;\n }\n // Rows: a key and a value each, as children of the list in pairs.\n while (node.childElementCount > content.length * 2) node.lastElementChild?.remove();\n content.forEach(([key, value], j) => {\n let dt = node.children[j * 2] as HTMLElement | undefined;\n let dd = node.children[j * 2 + 1] as HTMLElement | undefined;\n if (dt === undefined || dd === undefined) {\n dt = el('dt', 'key');\n dd = el('dd', 'value');\n node.append(dt, dd);\n }\n if (dt.textContent !== key) dt.textContent = key;\n if (dd.textContent !== value) dd.textContent = value;\n });\n });\n }\n\n /** The browser page: probed once, the first time it is shown, then a set of tables. */\n private browser(): void {\n const page = this.pages.browser;\n if (this.support !== null || this.probing) return;\n this.probing = true;\n setChildren(page, el('p', 'note', 'Probing the browser…'));\n void probeSupport().then((support) => {\n this.support = support;\n this.probing = false;\n this.tables(page, support);\n });\n }\n\n private tables(page: HTMLElement, support: Support): void {\n setChildren(page);\n page.append(el('h3', 'heading', 'Platform'), el('p', 'note', support.userAgent));\n const platform = el('table', 'table');\n for (const row of support.platform) {\n const tr = document.createElement('tr');\n const [text, part] = cell(row.value);\n tr.append(el('th', 'cell label', row.label), el('td', part, text));\n platform.append(tr);\n }\n page.append(platform);\n\n page.append(el('h3', 'heading', 'Codecs'));\n const codecs = el('table', 'table');\n const head = document.createElement('tr');\n for (const name of ['Codec', 'MSE fMP4', 'MSE WebM', '<video>', 'Smooth', 'Efficient']) {\n head.append(el('th', 'cell label', name));\n }\n codecs.append(head);\n for (const row of support.codecs) {\n const tr = document.createElement('tr');\n const label = el('th', 'cell label', row.label);\n label.title = row.codec;\n tr.append(label);\n for (const value of [row.mse, row.webm, row.element, row.smooth, row.efficient]) {\n const [text, part] = cell(value);\n tr.append(el('td', part, text));\n }\n codecs.append(tr);\n }\n page.append(codecs);\n\n page.append(el('h3', 'heading', 'DRM'));\n const drm = el('table', 'table');\n const drmHead = document.createElement('tr');\n for (const name of [\n 'System',\n 'Available',\n 'Level',\n 'Schemes',\n 'Persistent',\n 'Identifier',\n 'HDCP',\n ]) {\n drmHead.append(el('th', 'cell label', name));\n }\n drm.append(drmHead);\n for (const row of support.drm) {\n const tr = document.createElement('tr');\n const label = el('th', 'cell label', row.label);\n if (row.keySystem !== null) label.title = row.keySystem;\n tr.append(label);\n const values: Cell[] = [\n row.keySystem === null ? 'no' : 'yes',\n row.level,\n row.schemes,\n row.persistent,\n row.identifier,\n row.hdcp,\n ];\n for (const value of values) {\n const [text, part] = cell(value);\n tr.append(el('td', part, text));\n }\n drm.append(tr);\n }\n page.append(drm);\n }\n}\n\nfunction playbackRanges(video: HTMLVideoElement): Array<{ start: number; end: number }> {\n return Array.from({ length: video.buffered.length }, (_, i) => ({\n start: video.buffered.start(i),\n end: video.buffered.end(i),\n }));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"element.js","names":[],"sources":["../src/element.ts"],"sourcesContent":["/**\n * <mbx-diagnostics>: what the session is doing, for an integrator's eyes.\n * In the bar it is a button that opens a panel over the picture; anywhere\n * else inside the player, the panel itself, open, in flow. Four pages:\n * the playback facts, the charts, the engine's state, and what the browser\n * supports. A button copies the report, and `report()` hands it to a page,\n * which also gets it as a `report` event on every fatal error, so a page\n * sends it where its errors go.\n *\n * The sampler runs while the element is attached, panel open or not, so\n * the counts cover the session, and keeps the session's trace, slimmed,\n * from the engine's `trace` events, or off its ring where it emits none. The pages redraw twice a second while\n * shown, the charts four times. The browser is probed once, the first\n * time its page is shown: the key-system probes are not free, and Firefox\n * asks the viewer about a DRM module it has not enabled.\n *\n * A native session has no engine, and the engine's rules say a native\n * session has no diagnostics beyond the video's own: the playback page and\n * the buffer, stall and frame charts read the video, the rest says so.\n *\n * No class fields: the ES2015 build would lower them into helpers, and the\n * player's emit check bans helpers. State is declared, then assigned.\n */\nimport type { Messages } from '@mattebox/player';\nimport type { Mattebox } from 'mattebox';\nimport type { Charts, ChartTab, Palette } from './charts.js';\nimport { CHART_TABS, createCharts } from './charts.js';\nimport { setChildren } from './dom.js';\nimport { bitrate, clock, fixed, ranges, rendition, share } from './format.js';\nimport type { PlayerError, PlayerHost } from './host.js';\nimport { findPlayer, whenPlayer } from './host.js';\nimport { icon } from './icon.js';\nimport type {} from './messages.js';\nimport type { DiagnosticsReport, QoeFigures } from './report.js';\nimport { buildReport, trackersOf } from './report.js';\nimport type { Sampler } from './sampler.js';\nimport { createSampler } from './sampler.js';\nimport { STYLE } from './style.js';\nimport type { Cell, Support } from './support.js';\nimport { probeSupport } from './support.js';\n\nconst POLL_MS = 500;\nconst DRAW_MS = 250;\nconst WINDOW = 120;\nconst WINDOWS = [30, 60, 120, 300, 600];\n/** The panel's width in em, when the picture has the room. */\nconst PANEL_WIDTH = 35;\n/** Air between the panel's top and the picture's, and around it. */\nconst AIR = 8;\n\nconst TABS = ['playback', 'charts', 'engine', 'browser'] as const;\ntype Tab = (typeof TABS)[number];\nconst TAB_NAMES: Readonly<Record<Tab, string>> = {\n playback: 'Playback',\n charts: 'Charts',\n engine: 'Engine',\n browser: 'Browser',\n};\n\nconst READY_STATES = ['nothing', 'metadata', 'current data', 'future data', 'enough data'];\nconst NETWORK_STATES = ['empty', 'idle', 'loading', 'no source'];\n\ntype Pair = readonly [string, string];\n\ninterface Section {\n readonly title: string | null;\n readonly rows: readonly Pair[];\n /** A line instead of rows, for a section with nothing to list. */\n readonly note?: string | undefined;\n}\n\n/** The optional namespaces the pages read, cast once, the way the player does it. */\ninterface Namespaces {\n readonly live?: { readonly latency: number | null; readonly atEdge: boolean };\n readonly qoe?: QoeFigures;\n readonly drm?: {\n readonly keySystem: string | null;\n readonly sessions: ReadonlyArray<{ readonly keyId: string; readonly status: string }>;\n };\n}\n\nfunction el<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n part: string,\n text?: string,\n): HTMLElementTagNameMap[K] {\n const node = document.createElement(tag);\n node.setAttribute('part', part);\n if (text !== undefined) node.textContent = text;\n return node;\n}\n\nfunction button(part: string, text?: string): HTMLButtonElement {\n const node = el('button', part, text);\n node.type = 'button';\n return node;\n}\n\n/** A cell's text and its part: a tick, a cross, a dash, or the text itself. */\nfunction cell(value: Cell): [string, string] {\n switch (value) {\n case 'yes':\n return ['✓', 'cell ok'];\n case 'no':\n return ['✕', 'cell bad'];\n case 'na':\n return ['–', 'cell na'];\n case 'maybe':\n return ['maybe', 'cell maybe'];\n default:\n return [value, 'cell'];\n }\n}\n\nexport class MbxDiagnostics extends HTMLElement {\n static get observedAttributes(): readonly string[] {\n return ['label', 'label-copy', 'label-copied', 'label-close', 'window'];\n }\n\n declare private player: PlayerHost | null;\n declare private cancel: (() => void) | null;\n declare private offs: Array<() => void>;\n declare private readonly sampler: Sampler;\n declare private readonly charts: Charts;\n declare private timer: ReturnType<typeof setInterval> | undefined;\n declare private drawer: ReturnType<typeof setInterval> | undefined;\n declare private copied: ReturnType<typeof setTimeout> | undefined;\n declare private tab: Tab;\n declare private chartTab: ChartTab;\n declare private support: Support | null;\n declare private probing: boolean;\n\n declare private readonly toggle: HTMLButtonElement;\n declare private readonly panel: HTMLElement;\n declare private readonly body: HTMLElement;\n declare private readonly tabs: Record<Tab, HTMLButtonElement>;\n declare private readonly pages: Record<Tab, HTMLElement>;\n declare private readonly copy: HTMLButtonElement;\n declare private readonly closer: HTMLButtonElement;\n declare private readonly canvas: HTMLCanvasElement;\n declare private readonly chartTabs: Record<ChartTab, HTMLButtonElement>;\n declare private readonly windowSelect: HTMLSelectElement;\n declare private readonly outside: (event: PointerEvent) => void;\n declare private readonly refit: () => void;\n\n constructor() {\n super();\n this.player = null;\n this.cancel = null;\n this.offs = [];\n this.sampler = createSampler();\n this.timer = undefined;\n this.drawer = undefined;\n this.copied = undefined;\n this.tab = 'playback';\n this.chartTab = 'buffer';\n this.support = null;\n this.probing = false;\n\n const root = this.attachShadow({ mode: 'open' });\n const style = document.createElement('style');\n style.textContent = STYLE;\n\n this.toggle = button('button');\n this.toggle.setAttribute('aria-haspopup', 'dialog');\n this.toggle.setAttribute('aria-expanded', 'false');\n const slot = document.createElement('slot');\n slot.name = 'icon';\n slot.append(icon());\n this.toggle.append(slot);\n this.toggle.addEventListener('click', () => {\n if (this.panel.hidden) this.show();\n else this.hide();\n });\n\n this.panel = el('div', 'panel');\n this.panel.setAttribute('role', 'dialog');\n this.panel.hidden = true;\n this.panel.addEventListener('keydown', (event) => {\n if (event.key !== 'Escape' || this.hasAttribute('inline')) return;\n event.preventDefault();\n this.hide();\n this.toggle.focus();\n });\n\n const head = el('div', 'head');\n // A tablist holds tabs alone (WAI-ARIA 1.2), so copy and close sit beside it.\n const tablist = el('div', 'tabs');\n tablist.setAttribute('role', 'tablist');\n head.append(tablist);\n this.tabs = {} as Record<Tab, HTMLButtonElement>;\n this.pages = {} as Record<Tab, HTMLElement>;\n this.body = el('div', 'body');\n // It scrolls, so a keyboard has to reach it (WCAG 2.2 SC 2.1.1).\n this.body.tabIndex = 0;\n for (const tab of TABS) {\n const node = button(`tab tab-${tab}`, TAB_NAMES[tab]);\n node.setAttribute('role', 'tab');\n node.addEventListener('click', () => {\n this.select(tab);\n });\n this.tabs[tab] = node;\n tablist.append(node);\n const page = el('div', `page page-${tab}`);\n page.setAttribute('role', 'tabpanel');\n page.hidden = true;\n this.pages[tab] = page;\n this.body.append(page);\n }\n this.copy = button('copy');\n this.copy.addEventListener('click', () => {\n void this.copyReport();\n });\n this.closer = button('close', '✕');\n this.closer.addEventListener('click', () => {\n this.hide();\n this.toggle.focus();\n });\n head.append(this.copy, this.closer);\n\n // The charts page: its own tabs, the window, the canvas, the legend and the readout.\n const chartTabs = el('div', 'chart-tabs');\n // The tabs in a tablist of their own, the window select beside it.\n const chartTablist = el('div', 'chart-tablist');\n chartTablist.setAttribute('role', 'tablist');\n chartTabs.append(chartTablist);\n this.chartTabs = {} as Record<ChartTab, HTMLButtonElement>;\n for (const tab of CHART_TABS) {\n const node = button(`chart-tab chart-tab-${tab}`, tab);\n node.setAttribute('role', 'tab');\n node.addEventListener('click', () => {\n this.chartTab = tab;\n this.markChartTab();\n this.draw();\n });\n this.chartTabs[tab] = node;\n chartTablist.append(node);\n }\n this.windowSelect = el('select', 'window');\n this.windowSelect.setAttribute('aria-label', 'Window');\n for (const seconds of WINDOWS) {\n const option = document.createElement('option');\n option.value = String(seconds);\n option.textContent = seconds < 60 ? `${seconds} s` : `${seconds / 60} min`;\n this.windowSelect.append(option);\n }\n this.windowSelect.addEventListener('change', () => {\n this.draw();\n });\n chartTabs.append(this.windowSelect);\n this.canvas = el('canvas', 'chart');\n const legend = el('div', 'legend');\n const readout = el('p', 'readout');\n this.pages.charts.append(chartTabs, this.canvas, legend, readout);\n this.charts = createCharts(this.canvas, legend, readout);\n\n this.outside = (event: PointerEvent): void => {\n if (!event.composedPath().includes(this)) this.hide();\n };\n this.refit = (): void => {\n this.fit();\n };\n\n this.panel.append(head, this.body);\n root.append(style, this.toggle, this.panel);\n this.markTab();\n this.markChartTab();\n this.renderLabels();\n }\n\n connectedCallback(): void {\n // In the bar, a button and a popup; anywhere else, the panel in flow.\n this.toggleAttribute('inline', this.parentElement?.localName !== 'mbx-control-bar');\n const found = findPlayer(this);\n if (found === null) return;\n this.cancel?.();\n this.cancel = whenPlayer(found, (player) => {\n if (!this.isConnected) return;\n this.attach(player);\n });\n }\n\n disconnectedCallback(): void {\n this.cancel?.();\n this.cancel = null;\n this.detach();\n }\n\n attributeChangedCallback(): void {\n this.renderLabels();\n }\n\n private attach(player: PlayerHost): void {\n this.player = player;\n this.renderLabels();\n this.sampler.attach(player.video, player.engine);\n const on = (target: EventTarget, name: string, fn: (event: Event) => void): void => {\n target.addEventListener(name, fn);\n this.offs.push(() => {\n target.removeEventListener(name, fn);\n });\n };\n on(player, 'localechange', () => {\n this.renderLabels();\n });\n on(player, 'sourcechange', () => {\n this.sampler.attach(player.video, player.engine);\n this.poll();\n });\n on(player, 'error', (event) => {\n const error = (event as CustomEvent<PlayerError>).detail;\n if (error.fatal) {\n this.dispatchEvent(\n new CustomEvent('report', { detail: this.report(), bubbles: true, composed: true }),\n );\n }\n });\n this.timer = setInterval(() => {\n this.poll();\n }, POLL_MS);\n this.poll();\n if (this.hasAttribute('inline')) this.show();\n }\n\n private detach(): void {\n for (const off of this.offs) off();\n this.offs = [];\n clearInterval(this.timer);\n this.timer = undefined;\n this.sampler.release();\n this.hide();\n this.player = null;\n }\n\n /** The report, as of now. */\n report(): DiagnosticsReport {\n const player = this.player;\n if (player === null) throw new Error('mbx-diagnostics is not inside a player');\n return buildReport({\n player,\n samples: this.sampler.samples,\n marks: this.sampler.marks,\n counters: this.sampler.counters,\n history: this.sampler.history,\n support: this.support,\n });\n }\n\n private poll(): void {\n const player = this.player;\n if (player === null) return;\n this.sampler.poll(player.video, player.engine);\n if (!this.panel.hidden) this.paint();\n }\n\n /**\n * Opens the panel, as the button does. The player carries `diagnostics`\n * while it is open, so the error screen leaves the bar it sits in shown.\n */\n show(): void {\n if (!this.panel.hidden) return;\n this.panel.hidden = false;\n if (!this.hasAttribute('inline')) {\n // Before the fit below, which measures the bar the attribute shows.\n this.player?.toggleAttribute('diagnostics', true);\n this.setAttribute('open', '');\n this.toggle.setAttribute('aria-expanded', 'true');\n document.addEventListener('pointerdown', this.outside, true);\n window.addEventListener('resize', this.refit);\n this.fit();\n }\n this.paint();\n this.drawer = setInterval(() => {\n this.draw();\n }, DRAW_MS);\n if (!this.hasAttribute('inline')) this.tabs[this.tab].focus();\n }\n\n /** Closes the panel. An inline panel stays open while it is in the player. */\n hide(): void {\n clearInterval(this.drawer);\n this.drawer = undefined;\n if (this.panel.hidden) return;\n this.panel.hidden = true;\n this.player?.removeAttribute('diagnostics');\n this.removeAttribute('open');\n this.toggle.setAttribute('aria-expanded', 'false');\n document.removeEventListener('pointerdown', this.outside, true);\n window.removeEventListener('resize', this.refit);\n }\n\n /** The panel never leaves the picture: fitted to the room above the button, within the player's width. */\n private fit(): void {\n const player = this.player;\n if (player === null) return;\n const box = player.getBoundingClientRect();\n const host = this.getBoundingClientRect();\n const top = player.video.getBoundingClientRect().top;\n const room = host.top - top - AIR;\n this.panel.style.maxHeight = `${Math.max(120, Math.floor(room))}px`;\n const em = Number.parseFloat(getComputedStyle(this).fontSize) || 16;\n const width = Math.min(PANEL_WIDTH * em, Math.max(200, box.width - AIR * 2));\n this.panel.style.width = `${width}px`;\n const left = Math.max(box.left + AIR, Math.min(host.right - width, box.right - AIR - width));\n this.panel.style.right = 'auto';\n this.panel.style.left = `${left - host.left}px`;\n }\n\n private select(tab: Tab): void {\n this.tab = tab;\n this.markTab();\n this.tabs[tab].focus();\n this.paint();\n this.draw();\n }\n\n private markTab(): void {\n for (const tab of TABS) {\n this.tabs[tab].setAttribute('aria-selected', String(tab === this.tab));\n this.tabs[tab].tabIndex = tab === this.tab ? 0 : -1;\n this.pages[tab].hidden = tab !== this.tab;\n }\n }\n\n private markChartTab(): void {\n for (const tab of CHART_TABS) {\n this.chartTabs[tab].setAttribute('aria-selected', String(tab === this.chartTab));\n }\n }\n\n /**\n * A word of the panel's frame: its `attribute` when the page set one,\n * else the player's language's `id`, else `english`.\n */\n private word(attribute: string, id: keyof Messages, english: string): string {\n return this.getAttribute(attribute) ?? this.player?.message(id) ?? english;\n }\n\n private renderLabels(): void {\n const name = this.word('label', 'diagnostics', 'Diagnostics');\n this.toggle.setAttribute('aria-label', name);\n this.panel.setAttribute('aria-label', name);\n this.closer.setAttribute('aria-label', this.word('label-close', 'diagnosticsClose', 'Close'));\n if (this.copied === undefined) {\n this.copy.textContent = this.word('label-copy', 'diagnosticsCopy', 'Copy report');\n }\n const raw = Number(this.getAttribute('window'));\n const seconds = Number.isFinite(raw) && raw > 0 ? raw : WINDOW;\n if (!WINDOWS.includes(seconds)) {\n const option = document.createElement('option');\n option.value = String(seconds);\n option.textContent = `${seconds} s`;\n this.windowSelect.append(option);\n }\n this.windowSelect.value = String(seconds);\n }\n\n private async copyReport(): Promise<void> {\n const text = JSON.stringify(this.report(), null, 2);\n try {\n await navigator.clipboard.writeText(text);\n } catch {\n return;\n }\n this.copy.textContent = this.word('label-copied', 'diagnosticsCopied', 'Copied');\n clearTimeout(this.copied);\n this.copied = setTimeout(() => {\n this.copied = undefined;\n this.renderLabels();\n }, 1500);\n }\n\n /** The colours the charts draw with, off the element's tokens. */\n private palette(): Palette {\n const computed = getComputedStyle(this);\n const token = (name: string, fallback: string): string =>\n computed.getPropertyValue(name).trim() || fallback;\n return {\n ink: token('--mbx-text', '#f3ede4'),\n muted: token('--mbx-muted', '#aaa49a'),\n accent: token('--mbx-accent', '#e0543c'),\n warn: token('--mbx-chart-3', '#d9a83f'),\n series: [1, 2, 3, 4, 5].map((i) => token(`--mbx-chart-${i}`, '#888')),\n };\n }\n\n private draw(): void {\n const player = this.player;\n if (player === null || this.panel.hidden || this.tab !== 'charts') return;\n this.charts.draw({\n tab: this.chartTab,\n window: Number(this.windowSelect.value) || WINDOW,\n samples: this.sampler.samples,\n marks: this.sampler.marks,\n counters: this.sampler.counters,\n video: player.video,\n engine: player.engine,\n palette: this.palette(),\n });\n }\n\n /** The page shown, from the facts of the moment. */\n private paint(): void {\n const player = this.player;\n if (player === null || this.panel.hidden) return;\n switch (this.tab) {\n case 'playback':\n this.sections(this.pages.playback, this.playbackSections(player));\n break;\n case 'engine':\n this.sections(this.pages.engine, this.engineSections(player.engine));\n break;\n case 'browser':\n this.browser();\n break;\n default:\n break;\n }\n }\n\n private playbackSections(player: PlayerHost): Section[] {\n const video = player.video;\n const engine = player.engine;\n const quality = video.getVideoPlaybackQuality?.();\n const counters = this.sampler.counters;\n const last = this.sampler.samples[this.sampler.samples.length - 1];\n const state = video.paused ? (video.ended ? 'ended' : 'paused') : 'playing';\n const source: Pair[] = [\n ['source', player.getAttribute('src') ?? '–'],\n ['type', player.getAttribute('type') ?? '–'],\n ['handler', player.player?.session?.handler ?? '–'],\n ];\n if (engine !== null) source.push(['phase', engine.stats.snapshot().lifecycle.phase]);\n if (player.error !== null) {\n source.push(['error', `${player.error.category}: ${player.error.code}`]);\n } else if (video.error !== null) {\n source.push(['media error', `${video.error.code} ${video.error.message}`]);\n }\n const playback: Pair[] = [\n ['time', `${clock(video.currentTime)} of ${clock(video.duration)}`],\n [\n 'state',\n `${state}, ${READY_STATES[video.readyState] ?? video.readyState}, network ${NETWORK_STATES[video.networkState] ?? video.networkState}`,\n ],\n ['rate', `${video.playbackRate}×`],\n ['picture', video.videoWidth > 0 ? `${video.videoWidth}×${video.videoHeight}` : '–'],\n [\n 'frames',\n quality === undefined\n ? '–'\n : `${quality.totalVideoFrames} decoded, ${quality.droppedVideoFrames} dropped (${share(quality.droppedVideoFrames, quality.totalVideoFrames)})`,\n ],\n ['buffered', `${fixed(last?.ahead ?? 0)}s ahead: ${ranges(playbackRanges(video))}`],\n ['stalls', `${counters.stalls}, ${counters.stalledSeconds.toFixed(1)}s in total`],\n ];\n if (engine !== null) {\n const snapshot = engine.stats.snapshot();\n playback.push(\n [\n 'throughput',\n `slow ${bitrate(snapshot.stats.throughputEwma)}, fast ${bitrate(snapshot.stats.throughputFastEwma)}`,\n ],\n ['playing', rendition(engine.quality.playing)],\n ['switches', String(counters.switches)],\n );\n }\n // The engine's own figures, when its qoe stage is in the stack: they\n // count from the load, where the stalls above count from the sampling.\n const qoe = (engine as Namespaces | null)?.qoe;\n // The ids support asks a viewer for, to find the session in the tracker's own data.\n const trackers = trackersOf(player).map(\n (tracker): Pair => [\n tracker.name,\n [\n tracker.session === null ? '' : `session ${tracker.session}`,\n tracker.device === null ? '' : `device ${tracker.device}`,\n ]\n .filter((part) => part !== '')\n .join(', ') || '–',\n ],\n );\n return [\n { title: 'Source', rows: source },\n { title: 'Playback', rows: playback },\n ...(trackers.length === 0 ? [] : [{ title: 'Trackers', rows: trackers }]),\n ...(qoe === undefined\n ? []\n : [\n {\n title: 'Quality of experience',\n rows: [\n ['startup', qoe.startupTime === null ? '–' : `${qoe.startupTime.toFixed(2)}s`],\n ['rebuffers', `${qoe.rebuffers}, ${qoe.rebufferDuration.toFixed(1)}s in total`],\n ['quality changes', String(qoe.switches)],\n ] satisfies Pair[],\n },\n ]),\n ];\n }\n\n private engineSections(engine: Mattebox | null): Section[] {\n if (engine === null) {\n return [\n {\n title: null,\n rows: [],\n note: 'A native session: the browser plays this, and there is no engine to ask.',\n },\n ];\n }\n const state = engine.stats.snapshot();\n const optional = engine as Namespaces;\n const live = state.live;\n const rows: Pair[] = [\n ['capabilities', [...engine.capabilities()].join(', ') || 'none'],\n ['phase', state.lifecycle.phase],\n ['buffer goal', `${state.scheduling.bufferGoal}s`],\n [\n 'throughput',\n `slow ${bitrate(state.stats.throughputEwma)}, fast ${bitrate(state.stats.throughputFastEwma)}`,\n ],\n [\n 'in flight',\n [...state.scheduling.inflight.values()].map((r) => `${r.trackId} #${r.seq}`).join(', ') ||\n 'none',\n ],\n [\n 'live',\n live === null\n ? 'VOD'\n : `window ${clock(live.span.start)} to ${clock(live.span.end)}, edge ${clock(live.edge)}${\n optional.live?.latency === null || optional.live?.latency === undefined\n ? ''\n : `, ${optional.live.latency.toFixed(1)}s behind`\n }`,\n ],\n ['trace', `${engine.stats.trace().length} entries`],\n ];\n const error = engine.error;\n if (error !== null) rows.push(['last error', `${error.category}: ${error.code}`]);\n\n const buffers: Pair[] = [...state.buffers.entries()].map(([id, buffer]) => [\n id.replace('sb:', ''),\n `${buffer.codecs}: ${ranges(buffer.ranges)}${buffer.pendingAppends > 0 ? `, ${buffer.pendingAppends} pending` : ''}`,\n ]);\n\n const tracks: Pair[] = engine.tracks.available.map((track) => {\n const active = engine.tracks.active(track.contentType)?.id === track.id;\n const name = [track.lang, track.role].filter((v) => v !== undefined).join(' · ');\n return [\n `${track.contentType}${active ? ' ●' : ''}`,\n `${track.id}${name === '' ? '' : ` (${name})`}, ${track.renditions.length} renditions`,\n ];\n });\n\n const playing = engine.quality.playing?.id ?? null;\n const active = engine.quality.active?.id ?? null;\n const allowed = new Set(engine.quality.allowed.map((r) => r.id));\n const renditions: Pair[] = engine.quality.renditions.map((r) => {\n const flags = [\n r.id === playing ? 'playing' : '',\n r.id === active && active !== playing ? 'next' : '',\n r.id === engine.quality.pinned ? 'pinned' : '',\n allowed.has(r.id) ? '' : 'capped',\n ].filter((f) => f !== '');\n const size = r.width !== undefined && r.height !== undefined ? `${r.width}×${r.height}` : '';\n const text = [\n size,\n bitrate(r.bitrate),\n r.frameRate === undefined ? '' : `${r.frameRate} fps`,\n r.codecs ?? '',\n ]\n .filter((v) => v !== '')\n .join(', ');\n return [r.id, `${text}${flags.length > 0 ? ` [${flags.join(', ')}]` : ''}`];\n });\n\n const drm: Pair[] =\n optional.drm === undefined\n ? []\n : [\n ['key system', optional.drm.keySystem ?? 'none yet'],\n [\n 'keys',\n optional.drm.sessions.length === 0\n ? 'none yet'\n : optional.drm.sessions.map((s) => `${s.keyId}: ${s.status}`).join(', '),\n ],\n ];\n\n return [\n { title: 'Engine', rows },\n {\n title: 'Source buffers',\n rows: buffers,\n note: buffers.length === 0 ? 'none yet' : undefined,\n },\n { title: 'Tracks', rows: tracks, note: tracks.length === 0 ? 'none yet' : undefined },\n {\n title: 'Renditions',\n rows: renditions,\n note: renditions.length === 0 ? 'none yet' : undefined,\n },\n ...(optional.drm === undefined ? [] : [{ title: 'DRM', rows: drm }]),\n ];\n }\n\n /**\n * Draws sections into a page, reusing what is there: a heading and a\n * list per section, a row per pair, texts replaced only where they\n * changed, so a page open for an hour neither flickers nor grows.\n */\n private sections(page: HTMLElement, sections: readonly Section[]): void {\n const wanted = sections.flatMap((section) => {\n const nodes: Array<['heading' | 'note' | 'rows', string | readonly Pair[]]> = [];\n if (section.title !== null) nodes.push(['heading', section.title]);\n if (section.note !== undefined && section.rows.length === 0)\n nodes.push(['note', section.note]);\n else nodes.push(['rows', section.rows]);\n return nodes;\n });\n while (page.childElementCount > wanted.length) page.lastElementChild?.remove();\n wanted.forEach(([kind, content], i) => {\n let node = page.children[i] as HTMLElement | undefined;\n const tag = kind === 'heading' ? 'h3' : kind === 'note' ? 'p' : 'dl';\n if (node === undefined || node.localName !== tag) {\n const fresh = el(tag, kind);\n if (node === undefined) page.append(fresh);\n else node.replaceWith(fresh);\n node = fresh;\n }\n if (typeof content === 'string') {\n if (node.textContent !== content) node.textContent = content;\n return;\n }\n // Rows: a key and a value each, as children of the list in pairs.\n while (node.childElementCount > content.length * 2) node.lastElementChild?.remove();\n content.forEach(([key, value], j) => {\n let dt = node.children[j * 2] as HTMLElement | undefined;\n let dd = node.children[j * 2 + 1] as HTMLElement | undefined;\n if (dt === undefined || dd === undefined) {\n dt = el('dt', 'key');\n dd = el('dd', 'value');\n node.append(dt, dd);\n }\n if (dt.textContent !== key) dt.textContent = key;\n if (dd.textContent !== value) dd.textContent = value;\n });\n });\n }\n\n /** The browser page: probed once, the first time it is shown, then a set of tables. */\n private browser(): void {\n const page = this.pages.browser;\n if (this.support !== null || this.probing) return;\n this.probing = true;\n setChildren(page, el('p', 'note', 'Probing the browser…'));\n void probeSupport().then((support) => {\n this.support = support;\n this.probing = false;\n this.tables(page, support);\n });\n }\n\n private tables(page: HTMLElement, support: Support): void {\n setChildren(page);\n page.append(el('h3', 'heading', 'Platform'), el('p', 'note', support.userAgent));\n const platform = el('table', 'table');\n for (const row of support.platform) {\n const tr = document.createElement('tr');\n const [text, part] = cell(row.value);\n tr.append(el('th', 'cell label', row.label), el('td', part, text));\n platform.append(tr);\n }\n page.append(platform);\n\n page.append(el('h3', 'heading', 'Codecs'));\n const codecs = el('table', 'table');\n const head = document.createElement('tr');\n for (const name of ['Codec', 'MSE fMP4', 'MSE WebM', '<video>', 'Smooth', 'Efficient']) {\n head.append(el('th', 'cell label', name));\n }\n codecs.append(head);\n for (const row of support.codecs) {\n const tr = document.createElement('tr');\n const label = el('th', 'cell label', row.label);\n label.title = row.codec;\n tr.append(label);\n for (const value of [row.mse, row.webm, row.element, row.smooth, row.efficient]) {\n const [text, part] = cell(value);\n tr.append(el('td', part, text));\n }\n codecs.append(tr);\n }\n page.append(codecs);\n\n page.append(el('h3', 'heading', 'DRM'));\n const drm = el('table', 'table');\n const drmHead = document.createElement('tr');\n for (const name of [\n 'System',\n 'Available',\n 'Level',\n 'Schemes',\n 'Persistent',\n 'Identifier',\n 'HDCP',\n ]) {\n drmHead.append(el('th', 'cell label', name));\n }\n drm.append(drmHead);\n for (const row of support.drm) {\n const tr = document.createElement('tr');\n const label = el('th', 'cell label', row.label);\n if (row.keySystem !== null) label.title = row.keySystem;\n tr.append(label);\n const values: Cell[] = [\n row.keySystem === null ? 'no' : 'yes',\n row.level,\n row.schemes,\n row.persistent,\n row.identifier,\n row.hdcp,\n ];\n for (const value of values) {\n const [text, part] = cell(value);\n tr.append(el('td', part, text));\n }\n drm.append(tr);\n }\n page.append(drm);\n }\n}\n\nfunction playbackRanges(video: HTMLVideoElement): Array<{ start: number; end: number }> {\n return Array.from({ length: video.buffered.length }, (_, i) => ({\n start: video.buffered.start(i),\n end: video.buffered.end(i),\n }));\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- //#region \0@oxc-project+runtime@0.154.0/helpers/esm/asyncToGenerator.js
1
+ // \0@oxc-project+runtime@0.155.0/helpers/esm/asyncToGenerator.js
2
2
  function asyncGeneratorStep(n, t, e, r, o, a, c) {
3
3
  try {
4
4
  var i = n[a](c), u = i.value;
@@ -23,5 +23,4 @@ function _asyncToGenerator(n) {
23
23
  });
24
24
  };
25
25
  }
26
- //#endregion
27
26
  export { _asyncToGenerator as default };
@@ -1,5 +1,5 @@
1
1
  import toPropertyKey from "./toPropertyKey.js";
2
- //#region \0@oxc-project+runtime@0.154.0/helpers/esm/defineProperty.js
2
+ // \0@oxc-project+runtime@0.155.0/helpers/esm/defineProperty.js
3
3
  function _defineProperty(e, r, t) {
4
4
  return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
5
5
  value: t,
@@ -8,5 +8,4 @@ function _defineProperty(e, r, t) {
8
8
  writable: !0
9
9
  }) : e[r] = t, e;
10
10
  }
11
- //#endregion
12
11
  export { _defineProperty as default };
@@ -1,5 +1,5 @@
1
1
  import _defineProperty from "./defineProperty.js";
2
- //#region \0@oxc-project+runtime@0.154.0/helpers/esm/objectSpread2.js
2
+ // \0@oxc-project+runtime@0.155.0/helpers/esm/objectSpread2.js
3
3
  function ownKeys(e, r) {
4
4
  var t = Object.keys(e);
5
5
  if (Object.getOwnPropertySymbols) {
@@ -21,5 +21,4 @@ function _objectSpread2(e) {
21
21
  }
22
22
  return e;
23
23
  }
24
- //#endregion
25
24
  export { _objectSpread2 as default };
@@ -1,5 +1,5 @@
1
1
  import _typeof from "./typeof.js";
2
- //#region \0@oxc-project+runtime@0.154.0/helpers/esm/toPrimitive.js
2
+ // \0@oxc-project+runtime@0.155.0/helpers/esm/toPrimitive.js
3
3
  function toPrimitive(t, r) {
4
4
  if ("object" != _typeof(t) || !t) return t;
5
5
  var e = t[Symbol.toPrimitive];
@@ -10,5 +10,4 @@ function toPrimitive(t, r) {
10
10
  }
11
11
  return ("string" === r ? String : Number)(t);
12
12
  }
13
- //#endregion
14
13
  export { toPrimitive as default };
@@ -1,9 +1,8 @@
1
1
  import _typeof from "./typeof.js";
2
2
  import toPrimitive from "./toPrimitive.js";
3
- //#region \0@oxc-project+runtime@0.154.0/helpers/esm/toPropertyKey.js
3
+ // \0@oxc-project+runtime@0.155.0/helpers/esm/toPropertyKey.js
4
4
  function toPropertyKey(t) {
5
5
  var i = toPrimitive(t, "string");
6
6
  return "symbol" == _typeof(i) ? i : i + "";
7
7
  }
8
- //#endregion
9
8
  export { toPropertyKey as default };
@@ -1,4 +1,4 @@
1
- //#region \0@oxc-project+runtime@0.154.0/helpers/esm/typeof.js
1
+ // \0@oxc-project+runtime@0.155.0/helpers/esm/typeof.js
2
2
  function _typeof(o) {
3
3
  "@babel/helpers - typeof";
4
4
  return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
@@ -7,5 +7,4 @@ function _typeof(o) {
7
7
  return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
8
8
  }, _typeof(o);
9
9
  }
10
- //#endregion
11
10
  export { _typeof as default };
@@ -1,6 +1,6 @@
1
1
  import { setChildren } from "./dom.js";
2
2
  import { bitrate, clock } from "./format.js";
3
- //#region src/charts.ts
3
+ // src/charts.ts
4
4
  const CHART_TABS = [
5
5
  "buffer",
6
6
  "throughput",
@@ -377,5 +377,4 @@ function createCharts(canvas, legend, readout) {
377
377
  }
378
378
  } };
379
379
  }
380
- //#endregion
381
380
  export { CHART_TABS, createCharts };
@@ -1,4 +1,4 @@
1
- //#region src/dom.ts
1
+ // src/dom.ts
2
2
  /**
3
3
  * Replaces the children of `node`: the ones it has go, then `children` come.
4
4
  * `replaceChildren` does this in one call, from Chromium 86 (AGENTS.md,
@@ -9,5 +9,4 @@ function setChildren(node, ...children) {
9
9
  while (node.firstChild !== null) node.firstChild.remove();
10
10
  node.append(...children);
11
11
  }
12
- //#endregion
13
12
  export { setChildren };
@@ -3,12 +3,12 @@ import { bitrate, clock, fixed, ranges, rendition, share } from "./format.js";
3
3
  import { CHART_TABS, createCharts } from "./charts.js";
4
4
  import { findPlayer, whenPlayer } from "./host.js";
5
5
  import { icon } from "./icon.js";
6
- import { buildReport } from "./report.js";
6
+ import { buildReport, trackersOf } from "./report.js";
7
7
  import { createSampler } from "./sampler.js";
8
8
  import { STYLE } from "./style.js";
9
- import _asyncToGenerator from "./_virtual/_@oxc-project_runtime@0.154.0/helpers/esm/asyncToGenerator.js";
9
+ import _asyncToGenerator from "./_virtual/_@oxc-project_runtime@0.155.0/helpers/esm/asyncToGenerator.js";
10
10
  import { probeSupport } from "./support.js";
11
- //#region src/element.ts
11
+ // src/element.ts
12
12
  const POLL_MS = 500;
13
13
  const DRAW_MS = 250;
14
14
  const WINDOW = 120;
@@ -75,6 +75,7 @@ var MbxDiagnostics = class extends HTMLElement {
75
75
  "label",
76
76
  "label-copy",
77
77
  "label-copied",
78
+ "label-close",
78
79
  "window"
79
80
  ];
80
81
  }
@@ -140,13 +141,12 @@ var MbxDiagnostics = class extends HTMLElement {
140
141
  this.copy.addEventListener("click", () => {
141
142
  this.copyReport();
142
143
  });
143
- const close = button("close", "✕");
144
- close.setAttribute("aria-label", "Close");
145
- close.addEventListener("click", () => {
144
+ this.closer = button("close", "✕");
145
+ this.closer.addEventListener("click", () => {
146
146
  this.hide();
147
147
  this.toggle.focus();
148
148
  });
149
- head.append(this.copy, close);
149
+ head.append(this.copy, this.closer);
150
150
  const chartTabs = el("div", "chart-tabs");
151
151
  const chartTablist = el("div", "chart-tablist");
152
152
  chartTablist.setAttribute("role", "tablist");
@@ -214,6 +214,7 @@ var MbxDiagnostics = class extends HTMLElement {
214
214
  }
215
215
  attach(player) {
216
216
  this.player = player;
217
+ this.renderLabels();
217
218
  this.sampler.attach(player.video, player.engine);
218
219
  const on = (target, name, fn) => {
219
220
  target.addEventListener(name, fn);
@@ -221,6 +222,9 @@ var MbxDiagnostics = class extends HTMLElement {
221
222
  target.removeEventListener(name, fn);
222
223
  });
223
224
  };
225
+ on(player, "localechange", () => {
226
+ this.renderLabels();
227
+ });
224
228
  on(player, "sourcechange", () => {
225
229
  this.sampler.attach(player.video, player.engine);
226
230
  this.poll();
@@ -266,10 +270,16 @@ var MbxDiagnostics = class extends HTMLElement {
266
270
  this.sampler.poll(player.video, player.engine);
267
271
  if (!this.panel.hidden) this.paint();
268
272
  }
273
+ /**
274
+ * Opens the panel, as the button does. The player carries `diagnostics`
275
+ * while it is open, so the error screen leaves the bar it sits in shown.
276
+ */
269
277
  show() {
270
278
  if (!this.panel.hidden) return;
271
279
  this.panel.hidden = false;
272
280
  if (!this.hasAttribute("inline")) {
281
+ var _this$player;
282
+ (_this$player = this.player) === null || _this$player === void 0 || _this$player.toggleAttribute("diagnostics", true);
273
283
  this.setAttribute("open", "");
274
284
  this.toggle.setAttribute("aria-expanded", "true");
275
285
  document.addEventListener("pointerdown", this.outside, true);
@@ -282,11 +292,14 @@ var MbxDiagnostics = class extends HTMLElement {
282
292
  }, DRAW_MS);
283
293
  if (!this.hasAttribute("inline")) this.tabs[this.tab].focus();
284
294
  }
295
+ /** Closes the panel. An inline panel stays open while it is in the player. */
285
296
  hide() {
297
+ var _this$player2;
286
298
  clearInterval(this.drawer);
287
299
  this.drawer = void 0;
288
300
  if (this.panel.hidden) return;
289
301
  this.panel.hidden = true;
302
+ (_this$player2 = this.player) === null || _this$player2 === void 0 || _this$player2.removeAttribute("diagnostics");
290
303
  this.removeAttribute("open");
291
304
  this.toggle.setAttribute("aria-expanded", "false");
292
305
  document.removeEventListener("pointerdown", this.outside, true);
@@ -325,14 +338,20 @@ var MbxDiagnostics = class extends HTMLElement {
325
338
  markChartTab() {
326
339
  for (const tab of CHART_TABS) this.chartTabs[tab].setAttribute("aria-selected", String(tab === this.chartTab));
327
340
  }
341
+ /**
342
+ * A word of the panel's frame: its `attribute` when the page set one,
343
+ * else the player's language's `id`, else `english`.
344
+ */
345
+ word(attribute, id, english) {
346
+ var _ref, _this$getAttribute, _this$player3;
347
+ return (_ref = (_this$getAttribute = this.getAttribute(attribute)) !== null && _this$getAttribute !== void 0 ? _this$getAttribute : (_this$player3 = this.player) === null || _this$player3 === void 0 ? void 0 : _this$player3.message(id)) !== null && _ref !== void 0 ? _ref : english;
348
+ }
328
349
  renderLabels() {
329
- var _this$getAttribute, _this$getAttribute2;
330
- this.toggle.setAttribute("aria-label", (_this$getAttribute = this.getAttribute("label")) !== null && _this$getAttribute !== void 0 ? _this$getAttribute : "Diagnostics");
331
- this.panel.setAttribute("aria-label", (_this$getAttribute2 = this.getAttribute("label")) !== null && _this$getAttribute2 !== void 0 ? _this$getAttribute2 : "Diagnostics");
332
- if (this.copied === void 0) {
333
- var _this$getAttribute3;
334
- this.copy.textContent = (_this$getAttribute3 = this.getAttribute("label-copy")) !== null && _this$getAttribute3 !== void 0 ? _this$getAttribute3 : "Copy report";
335
- }
350
+ const name = this.word("label", "diagnostics", "Diagnostics");
351
+ this.toggle.setAttribute("aria-label", name);
352
+ this.panel.setAttribute("aria-label", name);
353
+ this.closer.setAttribute("aria-label", this.word("label-close", "diagnosticsClose", "Close"));
354
+ if (this.copied === void 0) this.copy.textContent = this.word("label-copy", "diagnosticsCopy", "Copy report");
336
355
  const raw = Number(this.getAttribute("window"));
337
356
  const seconds = Number.isFinite(raw) && raw > 0 ? raw : WINDOW;
338
357
  if (!WINDOWS.includes(seconds)) {
@@ -346,14 +365,13 @@ var MbxDiagnostics = class extends HTMLElement {
346
365
  copyReport() {
347
366
  var _this = this;
348
367
  return _asyncToGenerator(function* () {
349
- var _this$getAttribute4;
350
368
  const text = JSON.stringify(_this.report(), null, 2);
351
369
  try {
352
370
  yield navigator.clipboard.writeText(text);
353
371
  } catch (_unused) {
354
372
  return;
355
373
  }
356
- _this.copy.textContent = (_this$getAttribute4 = _this.getAttribute("label-copied")) !== null && _this$getAttribute4 !== void 0 ? _this$getAttribute4 : "Copied";
374
+ _this.copy.textContent = _this.word("label-copied", "diagnosticsCopied", "Copied");
357
375
  clearTimeout(_this.copied);
358
376
  _this.copied = setTimeout(() => {
359
377
  _this.copied = void 0;
@@ -437,6 +455,7 @@ var MbxDiagnostics = class extends HTMLElement {
437
455
  playback.push(["throughput", `slow ${bitrate(snapshot.stats.throughputEwma)}, fast ${bitrate(snapshot.stats.throughputFastEwma)}`], ["playing", rendition(engine.quality.playing)], ["switches", String(counters.switches)]);
438
456
  }
439
457
  const qoe = engine === null || engine === void 0 ? void 0 : engine.qoe;
458
+ const trackers = trackersOf(player).map((tracker) => [tracker.name, [tracker.session === null ? "" : `session ${tracker.session}`, tracker.device === null ? "" : `device ${tracker.device}`].filter((part) => part !== "").join(", ") || "–"]);
440
459
  return [
441
460
  {
442
461
  title: "Source",
@@ -446,6 +465,10 @@ var MbxDiagnostics = class extends HTMLElement {
446
465
  title: "Playback",
447
466
  rows: playback
448
467
  },
468
+ ...trackers.length === 0 ? [] : [{
469
+ title: "Trackers",
470
+ rows: trackers
471
+ }],
449
472
  ...qoe === void 0 ? [] : [{
450
473
  title: "Quality of experience",
451
474
  rows: [
@@ -667,5 +690,4 @@ function playbackRanges(video) {
667
690
  end: video.buffered.end(i)
668
691
  }));
669
692
  }
670
- //#endregion
671
693
  export { MbxDiagnostics };
@@ -1,4 +1,4 @@
1
- //#region src/format.ts
1
+ // src/format.ts
2
2
  /** The numbers as the panel and the charts print them. Pure. */
3
3
  /** Bits per second as "3.2 Mbps", "640 kbps" or "0 bps". */
4
4
  function bitrate(bps) {
@@ -34,5 +34,4 @@ function rendition(r) {
34
34
  if (r === null) return "–";
35
35
  return r.height !== void 0 ? `${r.height}p, ${bitrate(r.bitrate)}` : bitrate(r.bitrate);
36
36
  }
37
- //#endregion
38
37
  export { bitrate, clock, fixed, ranges, rendition, share };
@@ -1,4 +1,4 @@
1
- //#region src/host.ts
1
+ // src/host.ts
2
2
  const PLAYER = "mattebox-player";
3
3
  /** The parent, or the host of the shadow root `node` sits in, or null at the document. */
4
4
  function above(node) {
@@ -30,5 +30,4 @@ function whenPlayer(found, fn) {
30
30
  waiting = false;
31
31
  };
32
32
  }
33
- //#endregion
34
33
  export { PLAYER, findPlayer, whenPlayer };
@@ -1,4 +1,4 @@
1
- //#region src/icon.ts
1
+ // src/icon.ts
2
2
  /**
3
3
  * The glyph, Mattebox's own drawing in the player's style: a filled path
4
4
  * on a 32-unit viewBox.
@@ -18,5 +18,4 @@ function icon() {
18
18
  svg.append(path);
19
19
  return svg;
20
20
  }
21
- //#endregion
22
21
  export { icon };
@@ -1,6 +1,6 @@
1
1
  import { probeSupport } from "./support.js";
2
2
  import { MbxDiagnostics } from "./element.js";
3
- //#region src/index.ts
3
+ // src/index.ts
4
4
  /**
5
5
  * @mattebox/player-diagnostics: <mbx-diagnostics>, a control for the
6
6
  * player's bar, or a panel under the video, that shows what the session
@@ -11,5 +11,4 @@ import { MbxDiagnostics } from "./element.js";
11
11
  */
12
12
  const DIAGNOSTICS = "mbx-diagnostics";
13
13
  if (customElements.get("mbx-diagnostics") === void 0) customElements.define(DIAGNOSTICS, MbxDiagnostics);
14
- //#endregion
15
14
  export { DIAGNOSTICS, MbxDiagnostics, probeSupport };
@@ -0,0 +1,8 @@
1
+ // src/locales/ar.ts
2
+ var ar_default = {
3
+ diagnostics: "التشخيص",
4
+ diagnosticsCopy: "نسخ التقرير",
5
+ diagnosticsCopied: "تم النسخ",
6
+ diagnosticsClose: "إغلاق"
7
+ };
8
+ export { ar_default as default };
@@ -0,0 +1,8 @@
1
+ // src/locales/ca.ts
2
+ var ca_default = {
3
+ diagnostics: "Diagnòstic",
4
+ diagnosticsCopy: "Copia l’informe",
5
+ diagnosticsCopied: "Copiat",
6
+ diagnosticsClose: "Tanca"
7
+ };
8
+ export { ca_default as default };
@@ -0,0 +1,8 @@
1
+ // src/locales/de.ts
2
+ var de_default = {
3
+ diagnostics: "Diagnose",
4
+ diagnosticsCopy: "Bericht kopieren",
5
+ diagnosticsCopied: "Kopiert",
6
+ diagnosticsClose: "Schließen"
7
+ };
8
+ export { de_default as default };
@@ -0,0 +1,8 @@
1
+ // src/locales/es.ts
2
+ var es_default = {
3
+ diagnostics: "Diagnóstico",
4
+ diagnosticsCopy: "Copiar el informe",
5
+ diagnosticsCopied: "Copiado",
6
+ diagnosticsClose: "Cerrar"
7
+ };
8
+ export { es_default as default };
@@ -0,0 +1,8 @@
1
+ // src/locales/fr.ts
2
+ var fr_default = {
3
+ diagnostics: "Diagnostic",
4
+ diagnosticsCopy: "Copier le rapport",
5
+ diagnosticsCopied: "Copié",
6
+ diagnosticsClose: "Fermer"
7
+ };
8
+ export { fr_default as default };
@@ -0,0 +1,8 @@
1
+ // src/locales/hi.ts
2
+ var hi_default = {
3
+ diagnostics: "निदान",
4
+ diagnosticsCopy: "रिपोर्ट कॉपी करें",
5
+ diagnosticsCopied: "कॉपी हो गई",
6
+ diagnosticsClose: "बंद करें"
7
+ };
8
+ export { hi_default as default };
@@ -0,0 +1,8 @@
1
+ // src/locales/it.ts
2
+ var it_default = {
3
+ diagnostics: "Diagnostica",
4
+ diagnosticsCopy: "Copia il rapporto",
5
+ diagnosticsCopied: "Copiato",
6
+ diagnosticsClose: "Chiudi"
7
+ };
8
+ export { it_default as default };
@@ -0,0 +1,8 @@
1
+ // src/locales/ja.ts
2
+ var ja_default = {
3
+ diagnostics: "診断",
4
+ diagnosticsCopy: "レポートをコピー",
5
+ diagnosticsCopied: "コピーしました",
6
+ diagnosticsClose: "閉じる"
7
+ };
8
+ export { ja_default as default };