| type ReadyWatcher = { |
| observer?: MutationObserver |
| token: number |
| } |
|
|
| export function createReadyWatcher(): ReadyWatcher { |
| return { token: 0 } |
| } |
|
|
| export function clearReadyWatcher(state: ReadyWatcher) { |
| state.observer?.disconnect() |
| state.observer = undefined |
| } |
|
|
| export function getViewerHost(container: HTMLElement | undefined) { |
| if (!container) return |
| const host = container.querySelector("diffs-container") |
| if (!(host instanceof HTMLElement)) return |
| return host |
| } |
|
|
| export function getViewerRoot(container: HTMLElement | undefined) { |
| return getViewerHost(container)?.shadowRoot ?? undefined |
| } |
|
|
| export function applyViewerScheme(host: HTMLElement | undefined) { |
| if (!host) return |
| if (typeof document === "undefined") return |
|
|
| const scheme = document.documentElement.dataset.colorScheme |
| if (scheme === "dark" || scheme === "light") { |
| host.dataset.colorScheme = scheme |
| return |
| } |
|
|
| host.removeAttribute("data-color-scheme") |
| } |
|
|
| export function observeViewerScheme(getHost: () => HTMLElement | undefined) { |
| if (typeof document === "undefined") return () => {} |
|
|
| applyViewerScheme(getHost()) |
| if (typeof MutationObserver === "undefined") return () => {} |
|
|
| const root = document.documentElement |
| const monitor = new MutationObserver(() => applyViewerScheme(getHost())) |
| monitor.observe(root, { attributes: true, attributeFilter: ["data-color-scheme"] }) |
| return () => monitor.disconnect() |
| } |
|
|
| export function notifyShadowReady(opts: { |
| state: ReadyWatcher |
| container: HTMLElement |
| getRoot: () => ShadowRoot | undefined |
| isReady: (root: ShadowRoot) => boolean |
| onReady: () => void |
| settleFrames?: number |
| }) { |
| clearReadyWatcher(opts.state) |
| opts.state.token += 1 |
|
|
| const token = opts.state.token |
| const settle = Math.max(0, opts.settleFrames ?? 0) |
|
|
| const runReady = () => { |
| const step = (left: number) => { |
| if (token !== opts.state.token) return |
| if (left <= 0) { |
| opts.onReady() |
| return |
| } |
| requestAnimationFrame(() => step(left - 1)) |
| } |
|
|
| requestAnimationFrame(() => step(settle)) |
| } |
|
|
| const observeRoot = (root: ShadowRoot) => { |
| if (opts.isReady(root)) { |
| runReady() |
| return |
| } |
|
|
| if (typeof MutationObserver === "undefined") return |
|
|
| clearReadyWatcher(opts.state) |
| opts.state.observer = new MutationObserver(() => { |
| if (token !== opts.state.token) return |
| if (!opts.isReady(root)) return |
|
|
| clearReadyWatcher(opts.state) |
| runReady() |
| }) |
| opts.state.observer.observe(root, { childList: true, subtree: true }) |
| } |
|
|
| const root = opts.getRoot() |
| if (!root) { |
| if (typeof MutationObserver === "undefined") return |
|
|
| opts.state.observer = new MutationObserver(() => { |
| if (token !== opts.state.token) return |
|
|
| const next = opts.getRoot() |
| if (!next) return |
|
|
| observeRoot(next) |
| }) |
| opts.state.observer.observe(opts.container, { childList: true, subtree: true }) |
| return |
| } |
|
|
| observeRoot(root) |
| } |
|
|