supervision 0.1.7 → 0.2.0-next.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 (215) hide show
  1. package/dist/detections/chunked-detection-frame-source.d.ts.map +1 -1
  2. package/dist/index.d.ts +11 -4
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +8856 -4559
  5. package/dist/index.js.map +1 -1
  6. package/dist/mask-preparation.worker.js +310 -164
  7. package/dist/mask-preparation.worker.js.map +1 -1
  8. package/dist/media/display-pixel-ratio.d.ts +15 -0
  9. package/dist/media/display-pixel-ratio.d.ts.map +1 -0
  10. package/dist/media/engine-import-failure.d.ts +16 -0
  11. package/dist/media/engine-import-failure.d.ts.map +1 -0
  12. package/dist/media/media-condition-probe.d.ts +51 -0
  13. package/dist/media/media-condition-probe.d.ts.map +1 -0
  14. package/dist/media/media-conditions.d.ts +28 -0
  15. package/dist/media/media-conditions.d.ts.map +1 -0
  16. package/dist/media/media-errors.d.ts +6 -0
  17. package/dist/media/media-errors.d.ts.map +1 -1
  18. package/dist/media/media-normalization.d.ts.map +1 -1
  19. package/dist/media/media-probe.d.ts.map +1 -1
  20. package/dist/media/media-source.d.ts +17 -0
  21. package/dist/media/media-source.d.ts.map +1 -1
  22. package/dist/media/mediabunny-media-source.d.ts.map +1 -1
  23. package/dist/media/video-engine-media-source.d.ts +44 -0
  24. package/dist/media/video-engine-media-source.d.ts.map +1 -0
  25. package/dist/render-preparation/mask-frame-artifact.d.ts +46 -12
  26. package/dist/render-preparation/mask-frame-artifact.d.ts.map +1 -1
  27. package/dist/render-preparation/mask-frame-compositor.d.ts +5 -5
  28. package/dist/render-preparation/mask-frame-compositor.d.ts.map +1 -1
  29. package/dist/render-preparation/mask-frame-preparer.d.ts.map +1 -1
  30. package/dist/render-preparation/mask-preparation-worker-count.d.ts +4 -4
  31. package/dist/render-preparation/mask-preparation-worker-protocol.d.ts +12 -3
  32. package/dist/render-preparation/mask-preparation-worker-protocol.d.ts.map +1 -1
  33. package/dist/render-preparation/prepared-render-window.d.ts +23 -0
  34. package/dist/render-preparation/prepared-render-window.d.ts.map +1 -1
  35. package/dist/render-preparation/prepared-window-timeline.d.ts +2 -6
  36. package/dist/render-preparation/prepared-window-timeline.d.ts.map +1 -1
  37. package/dist/renderers/mask-palette.d.ts +14 -0
  38. package/dist/renderers/mask-palette.d.ts.map +1 -0
  39. package/dist/renderers/mask-vertex.d.ts +14 -0
  40. package/dist/renderers/mask-vertex.d.ts.map +1 -0
  41. package/dist/renderers/media-renderer-core.d.ts.map +1 -1
  42. package/dist/renderers/media-renderer-scene.d.ts +37 -0
  43. package/dist/renderers/media-renderer-scene.d.ts.map +1 -1
  44. package/dist/renderers/media-renderer-state.d.ts +11 -1
  45. package/dist/renderers/media-renderer-state.d.ts.map +1 -1
  46. package/dist/renderers/media-renderer-transport.d.ts +59 -0
  47. package/dist/renderers/media-renderer-transport.d.ts.map +1 -0
  48. package/dist/renderers/pixi-box-layer.d.ts +5 -0
  49. package/dist/renderers/pixi-box-layer.d.ts.map +1 -1
  50. package/dist/renderers/pixi-focus-layer.d.ts +18 -2
  51. package/dist/renderers/pixi-focus-layer.d.ts.map +1 -1
  52. package/dist/renderers/pixi-frame-present.d.ts +77 -0
  53. package/dist/renderers/pixi-frame-present.d.ts.map +1 -0
  54. package/dist/renderers/pixi-id-mask-shader.d.ts +12 -2
  55. package/dist/renderers/pixi-id-mask-shader.d.ts.map +1 -1
  56. package/dist/renderers/pixi-interaction-layer.d.ts.map +1 -1
  57. package/dist/renderers/pixi-interaction-presentation-layer.d.ts +13 -2
  58. package/dist/renderers/pixi-interaction-presentation-layer.d.ts.map +1 -1
  59. package/dist/renderers/pixi-mask-halo.d.ts +24 -0
  60. package/dist/renderers/pixi-mask-halo.d.ts.map +1 -1
  61. package/dist/renderers/pixi-mask-layer.d.ts +71 -8
  62. package/dist/renderers/pixi-mask-layer.d.ts.map +1 -1
  63. package/dist/renderers/pixi-media-scene.d.ts +67 -0
  64. package/dist/renderers/pixi-media-scene.d.ts.map +1 -1
  65. package/dist/renderers/pixi-polygon-layer.d.ts +7 -2
  66. package/dist/renderers/pixi-polygon-layer.d.ts.map +1 -1
  67. package/dist/renderers/pixi-region-coverage-mask.d.ts +91 -0
  68. package/dist/renderers/pixi-region-coverage-mask.d.ts.map +1 -0
  69. package/dist/renderers/pixi-region-effect.d.ts +42 -0
  70. package/dist/renderers/pixi-region-effect.d.ts.map +1 -0
  71. package/dist/renderers/pixi-region-layer.d.ts +68 -2
  72. package/dist/renderers/pixi-region-layer.d.ts.map +1 -1
  73. package/dist/renderers/pixi-vector-layer.d.ts.map +1 -1
  74. package/dist/renderers/prepared-annotation-window.d.ts +47 -0
  75. package/dist/renderers/prepared-annotation-window.d.ts.map +1 -0
  76. package/dist/renderers/presented-frame-channel.d.ts +107 -0
  77. package/dist/renderers/presented-frame-channel.d.ts.map +1 -0
  78. package/dist/renderers/scene-render-scheduler.d.ts +20 -0
  79. package/dist/renderers/scene-render-scheduler.d.ts.map +1 -0
  80. package/dist/sessions/media-session-defaults.d.ts +10 -6
  81. package/dist/sessions/media-session-defaults.d.ts.map +1 -1
  82. package/dist/sessions/media-session-media.d.ts.map +1 -1
  83. package/dist/sessions/media-session-state.d.ts.map +1 -1
  84. package/dist/sessions/media-session.d.ts.map +1 -1
  85. package/dist/tracking.worker.js +58 -3
  86. package/dist/tracking.worker.js.map +1 -1
  87. package/dist/types/media-conditions.d.ts +149 -0
  88. package/dist/types/media-conditions.d.ts.map +1 -0
  89. package/dist/types/media-normalization.d.ts +34 -0
  90. package/dist/types/media-normalization.d.ts.map +1 -1
  91. package/dist/types/media-renderer.d.ts +31 -1
  92. package/dist/types/media-renderer.d.ts.map +1 -1
  93. package/dist/types/media-session.d.ts +89 -8
  94. package/dist/types/media-session.d.ts.map +1 -1
  95. package/dist/types/render-preparation.d.ts +101 -4
  96. package/dist/types/render-preparation.d.ts.map +1 -1
  97. package/dist/web-video-engine/analysis-session.d.ts +76 -0
  98. package/dist/web-video-engine/analysis-session.d.ts.map +1 -0
  99. package/dist/web-video-engine/analysis.d.ts +7 -0
  100. package/dist/web-video-engine/analysis.d.ts.map +1 -0
  101. package/dist/web-video-engine/analysis.js +1678 -0
  102. package/dist/web-video-engine/analysis.js.map +1 -0
  103. package/dist/web-video-engine/cache-budget.d.ts +19 -0
  104. package/dist/web-video-engine/cache-budget.d.ts.map +1 -0
  105. package/dist/web-video-engine/canvas-sink-scrub-cursor.d.ts +90 -0
  106. package/dist/web-video-engine/canvas-sink-scrub-cursor.d.ts.map +1 -0
  107. package/dist/web-video-engine/clock.d.ts +83 -0
  108. package/dist/web-video-engine/clock.d.ts.map +1 -0
  109. package/dist/web-video-engine/constants.d.ts +253 -0
  110. package/dist/web-video-engine/constants.d.ts.map +1 -0
  111. package/dist/web-video-engine/create-scrub-cursor.d.ts +62 -0
  112. package/dist/web-video-engine/create-scrub-cursor.d.ts.map +1 -0
  113. package/dist/web-video-engine/decode-resolution.d.ts +115 -0
  114. package/dist/web-video-engine/decode-resolution.d.ts.map +1 -0
  115. package/dist/web-video-engine/decode-scheduler.d.ts +356 -0
  116. package/dist/web-video-engine/decode-scheduler.d.ts.map +1 -0
  117. package/dist/web-video-engine/decode-session.d.ts +276 -0
  118. package/dist/web-video-engine/decode-session.d.ts.map +1 -0
  119. package/dist/web-video-engine/decode-source.d.ts +250 -0
  120. package/dist/web-video-engine/decode-source.d.ts.map +1 -0
  121. package/dist/web-video-engine/diagnostics-store.d.ts +18 -0
  122. package/dist/web-video-engine/diagnostics-store.d.ts.map +1 -0
  123. package/dist/web-video-engine/diagnostics.d.ts +316 -0
  124. package/dist/web-video-engine/diagnostics.d.ts.map +1 -0
  125. package/dist/web-video-engine/embedded-engine-worker.d.ts +2 -0
  126. package/dist/web-video-engine/embedded-engine-worker.d.ts.map +1 -0
  127. package/dist/web-video-engine/engine-core.d.ts +258 -0
  128. package/dist/web-video-engine/engine-core.d.ts.map +1 -0
  129. package/dist/web-video-engine/engine.d.ts +26 -0
  130. package/dist/web-video-engine/engine.d.ts.map +1 -0
  131. package/dist/web-video-engine/engine.js +873 -0
  132. package/dist/web-video-engine/engine.js.map +1 -0
  133. package/dist/web-video-engine/engine.worker.d.ts +2 -0
  134. package/dist/web-video-engine/engine.worker.d.ts.map +1 -0
  135. package/dist/web-video-engine/engine.worker.js +31320 -0
  136. package/dist/web-video-engine/engine.worker.js.map +1 -0
  137. package/dist/web-video-engine/frame-cache.d.ts +129 -0
  138. package/dist/web-video-engine/frame-cache.d.ts.map +1 -0
  139. package/dist/web-video-engine/frame-extractor.d.ts +28 -0
  140. package/dist/web-video-engine/frame-extractor.d.ts.map +1 -0
  141. package/dist/web-video-engine/frame-timeline-GINI8Gum.js +560 -0
  142. package/dist/web-video-engine/frame-timeline-GINI8Gum.js.map +1 -0
  143. package/dist/web-video-engine/frame-timeline.d.ts +71 -0
  144. package/dist/web-video-engine/frame-timeline.d.ts.map +1 -0
  145. package/dist/web-video-engine/frame-walker.d.ts +74 -0
  146. package/dist/web-video-engine/frame-walker.d.ts.map +1 -0
  147. package/dist/web-video-engine/index.d.ts +3 -0
  148. package/dist/web-video-engine/index.d.ts.map +1 -0
  149. package/dist/web-video-engine/index.js +3 -0
  150. package/dist/web-video-engine/index.js.map +1 -0
  151. package/dist/web-video-engine/key-packet.d.ts +65 -0
  152. package/dist/web-video-engine/key-packet.d.ts.map +1 -0
  153. package/dist/web-video-engine/keyframe-index.d.ts +91 -0
  154. package/dist/web-video-engine/keyframe-index.d.ts.map +1 -0
  155. package/dist/web-video-engine/mirror-store.d.ts +56 -0
  156. package/dist/web-video-engine/mirror-store.d.ts.map +1 -0
  157. package/dist/web-video-engine/renderer.d.ts +94 -0
  158. package/dist/web-video-engine/renderer.d.ts.map +1 -0
  159. package/dist/web-video-engine/rotation.d.ts +35 -0
  160. package/dist/web-video-engine/rotation.d.ts.map +1 -0
  161. package/dist/web-video-engine/scrub-controller.d.ts +376 -0
  162. package/dist/web-video-engine/scrub-controller.d.ts.map +1 -0
  163. package/dist/web-video-engine/scrub-cursor.d.ts +342 -0
  164. package/dist/web-video-engine/scrub-cursor.d.ts.map +1 -0
  165. package/dist/web-video-engine/scrub-trajectory.d.ts +30 -0
  166. package/dist/web-video-engine/scrub-trajectory.d.ts.map +1 -0
  167. package/dist/web-video-engine/source-residency.d.ts +57 -0
  168. package/dist/web-video-engine/source-residency.d.ts.map +1 -0
  169. package/dist/web-video-engine/trace-recorder.d.ts +164 -0
  170. package/dist/web-video-engine/trace-recorder.d.ts.map +1 -0
  171. package/dist/web-video-engine/types.d.ts +204 -0
  172. package/dist/web-video-engine/types.d.ts.map +1 -0
  173. package/dist/web-video-engine/video-engine.d.ts +426 -0
  174. package/dist/web-video-engine/video-engine.d.ts.map +1 -0
  175. package/dist/web-video-engine/webgpu-renderer.d.ts +62 -0
  176. package/dist/web-video-engine/webgpu-renderer.d.ts.map +1 -0
  177. package/dist/web-video-engine/worker-bridge.d.ts +12 -0
  178. package/dist/web-video-engine/worker-bridge.d.ts.map +1 -0
  179. package/dist/web-video-engine/worker-dispatch.d.ts +17 -0
  180. package/dist/web-video-engine/worker-dispatch.d.ts.map +1 -0
  181. package/dist/web-video-engine/worker-protocol.d.ts +267 -0
  182. package/dist/web-video-engine/worker-protocol.d.ts.map +1 -0
  183. package/node_modules/supervision-js-core/dist/detections/buffered-detection-timeline.d.ts.map +1 -1
  184. package/node_modules/supervision-js-core/dist/detections/composite-detection-frame-source.d.ts.map +1 -1
  185. package/node_modules/supervision-js-core/dist/index.d.ts +4 -3
  186. package/node_modules/supervision-js-core/dist/index.d.ts.map +1 -1
  187. package/node_modules/supervision-js-core/dist/index.js +1075 -266
  188. package/node_modules/supervision-js-core/dist/index.js.map +1 -1
  189. package/node_modules/supervision-js-core/dist/styles/default-annotation-presentation.d.ts.map +1 -1
  190. package/node_modules/supervision-js-core/dist/styles/interaction-style.d.ts +4 -36
  191. package/node_modules/supervision-js-core/dist/styles/interaction-style.d.ts.map +1 -1
  192. package/node_modules/supervision-js-core/dist/styles/polyline-style.d.ts +5 -0
  193. package/node_modules/supervision-js-core/dist/styles/polyline-style.d.ts.map +1 -1
  194. package/node_modules/supervision-js-core/dist/types/annotation-renderer.d.ts +92 -7
  195. package/node_modules/supervision-js-core/dist/types/annotation-renderer.d.ts.map +1 -1
  196. package/node_modules/supervision-js-core/dist/types/detection-timeline.d.ts +179 -17
  197. package/node_modules/supervision-js-core/dist/types/detection-timeline.d.ts.map +1 -1
  198. package/node_modules/supervision-js-core/dist/types/media-rendering.d.ts +78 -2
  199. package/node_modules/supervision-js-core/dist/types/media-rendering.d.ts.map +1 -1
  200. package/node_modules/supervision-js-core/dist/types/polyline-style.d.ts +2 -0
  201. package/node_modules/supervision-js-core/dist/types/polyline-style.d.ts.map +1 -1
  202. package/node_modules/supervision-js-core/dist/types/session-lifecycle.d.ts +16 -0
  203. package/node_modules/supervision-js-core/dist/types/session-lifecycle.d.ts.map +1 -1
  204. package/node_modules/supervision-js-core/dist/utils/detection-conversions.d.ts.map +1 -1
  205. package/node_modules/supervision-js-core/dist/utils/detection-frames.d.ts +5 -0
  206. package/node_modules/supervision-js-core/dist/utils/detection-frames.d.ts.map +1 -1
  207. package/node_modules/supervision-js-core/dist/utils/detection-masks.d.ts +9 -0
  208. package/node_modules/supervision-js-core/dist/utils/detection-masks.d.ts.map +1 -1
  209. package/node_modules/supervision-js-core/dist/utils/detection-ranges.d.ts +18 -0
  210. package/node_modules/supervision-js-core/dist/utils/detection-ranges.d.ts.map +1 -0
  211. package/node_modules/supervision-js-core/dist/utils/id-mask-frame.d.ts +15 -2
  212. package/node_modules/supervision-js-core/dist/utils/id-mask-frame.d.ts.map +1 -1
  213. package/node_modules/supervision-js-core/dist/utils/wait-bound.d.ts +7 -0
  214. package/node_modules/supervision-js-core/dist/utils/wait-bound.d.ts.map +1 -0
  215. package/package.json +22 -1
@@ -1 +1 @@
1
- {"version":3,"file":"mask-preparation.worker.js","sources":["../../core/dist/index.js","../src/render-preparation/mask-frame-compositor.ts","../src/render-preparation/mask-frame-artifact.ts","../src/render-preparation/mask-preparation-worker-protocol.ts","../src/render-preparation/mask-preparation.worker.ts"],"sourcesContent":["/** COCO-compatible keypoint visibility values. */\nvar KeypointVisibility;\n(function (KeypointVisibility) {\n KeypointVisibility[KeypointVisibility[\"NotLabeled\"] = 0] = \"NotLabeled\";\n KeypointVisibility[KeypointVisibility[\"Occluded\"] = 1] = \"Occluded\";\n KeypointVisibility[KeypointVisibility[\"Visible\"] = 2] = \"Visible\";\n})(KeypointVisibility || (KeypointVisibility = {}));\nvar DetectionMaskEncoding;\n(function (DetectionMaskEncoding) {\n DetectionMaskEncoding[\"CompressedRle\"] = \"compressedRle\";\n})(DetectionMaskEncoding || (DetectionMaskEncoding = {}));\n\nvar DetectionBufferStatus;\n(function (DetectionBufferStatus) {\n DetectionBufferStatus[\"Idle\"] = \"idle\";\n DetectionBufferStatus[\"Loading\"] = \"loading\";\n DetectionBufferStatus[\"Ready\"] = \"ready\";\n DetectionBufferStatus[\"Error\"] = \"error\";\n DetectionBufferStatus[\"Destroyed\"] = \"destroyed\";\n})(DetectionBufferStatus || (DetectionBufferStatus = {}));\n/**\n * How the renderer selects the active detection frame for a media timestamp.\n */\nvar DetectionFrameSelectionMode;\n(function (DetectionFrameSelectionMode) {\n /**\n * Select the frame whose `[mediaTime, endTime)` interval contains the media\n * time. This is the default for interval annotations and timestamped sources.\n */\n DetectionFrameSelectionMode[\"Interval\"] = \"interval\";\n /**\n * Select from a known inference frame grid using `frameIndex`, `frameRate`,\n * and optional `frameIndexOriginTime`. This is useful when inference was run\n * on normalized frames and playback should snap detections to that grid.\n */\n DetectionFrameSelectionMode[\"NearestFrameIndex\"] = \"nearestFrameIndex\";\n})(DetectionFrameSelectionMode || (DetectionFrameSelectionMode = {}));\nvar DetectionFrameRetentionMode;\n(function (DetectionFrameRetentionMode) {\n /**\n * Keep writable detections only in an in-memory store. Useful for ephemeral\n * live streams where old predictions should disappear when evicted.\n */\n DetectionFrameRetentionMode[\"MemoryOnly\"] = \"memoryOnly\";\n /**\n * Persist every written detection frame. Useful for finite media where seek\n * and replay should not require recomputing detections.\n */\n DetectionFrameRetentionMode[\"PersistAll\"] = \"persistAll\";\n /**\n * Persist only the most recent retention window. Useful for long-running\n * streams where replay is bounded to a recent time horizon.\n */\n DetectionFrameRetentionMode[\"PersistWindow\"] = \"persistWindow\";\n})(DetectionFrameRetentionMode || (DetectionFrameRetentionMode = {}));\n\nfunction copySortedDetectionFrames(detectionFrames) {\n validateDetectionFrames(detectionFrames ?? []);\n return (detectionFrames ?? [])\n .map((frame) => ({\n detections: frame.detections.map((detection) => ({\n ...detection,\n attributes: detection.attributes\n ? [...detection.attributes]\n : undefined,\n keypoints: detection.keypoints\n ? {\n boxRelative: detection.keypoints.boxRelative\n ? [...detection.keypoints.boxRelative]\n : undefined,\n edges: detection.keypoints.edges.map((edge) => [edge[0], edge[1]]),\n points: detection.keypoints.points.map((point) => ({ ...point })),\n visibility: detection.keypoints.visibility\n ? [...detection.keypoints.visibility]\n : undefined,\n }\n : undefined,\n mask: detection.mask ? { ...detection.mask } : undefined,\n metadata: detection.metadata\n ? copyDetectionMetadata(detection.metadata)\n : undefined,\n polygon: detection.polygon\n ? {\n points: detection.polygon.points.map((point) => ({ ...point })),\n }\n : undefined,\n polyline: detection.polyline\n ? {\n points: detection.polyline.points.map((point) => ({ ...point })),\n }\n : undefined,\n rect: detection.rect ? { ...detection.rect } : undefined,\n })),\n coordinateSpace: frame.coordinateSpace\n ? { ...frame.coordinateSpace }\n : undefined,\n endTime: frame.endTime,\n frameIndex: frame.frameIndex,\n mediaTime: frame.mediaTime,\n }))\n .sort((left, right) => left.mediaTime - right.mediaTime);\n}\nfunction copyDetectionMetadata(metadata) {\n const clone = globalThis.structuredClone;\n if (clone) {\n return clone(metadata);\n }\n return copyMetadataValue(metadata);\n}\nfunction copyMetadataValue(value) {\n if (Array.isArray(value)) {\n return value.map(copyMetadataValue);\n }\n if (value && typeof value === \"object\") {\n return Object.fromEntries(Object.entries(value).map(([key, child]) => [\n key,\n copyMetadataValue(child),\n ]));\n }\n return value;\n}\nfunction validateDetectionFrames(detectionFrames) {\n for (const [frameOffset, frame] of detectionFrames.entries()) {\n validateNumber(frame.mediaTime, `frames[${frameOffset}].mediaTime`, {\n min: 0,\n });\n if (frame.endTime !== undefined) {\n validateNumber(frame.endTime, `frames[${frameOffset}].endTime`, {\n exclusiveMin: frame.mediaTime,\n });\n }\n if (frame.frameIndex !== undefined) {\n validateNumber(frame.frameIndex, `frames[${frameOffset}].frameIndex`, {\n integer: true,\n min: 0,\n });\n }\n if (frame.coordinateSpace !== undefined) {\n validateNumber(frame.coordinateSpace.width, `frames[${frameOffset}].coordinateSpace.width`, { exclusiveMin: 0 });\n validateNumber(frame.coordinateSpace.height, `frames[${frameOffset}].coordinateSpace.height`, { exclusiveMin: 0 });\n }\n for (const [detectionOffset, detection] of frame.detections.entries()) {\n const detectionPath = `frames[${frameOffset}].detections[${detectionOffset}]`;\n if (detection.confidence !== undefined) {\n validateNumber(detection.confidence, `${detectionPath}.confidence`, {\n max: 1,\n min: 0,\n });\n }\n if (detection.zIndex !== undefined) {\n validateNumber(detection.zIndex, `${detectionPath}.zIndex`);\n }\n if (detection.trackerId !== undefined) {\n validateNumber(detection.trackerId, `${detectionPath}.trackerId`, {\n integer: true,\n min: 0,\n });\n }\n if (detection.sourceId !== undefined &&\n typeof detection.sourceId !== \"string\") {\n throw new Error(`${detectionPath}.sourceId must be a string.`);\n }\n if (detection.sourceDetectionIndex !== undefined) {\n validateNumber(detection.sourceDetectionIndex, `${detectionPath}.sourceDetectionIndex`, {\n integer: true,\n min: 0,\n });\n }\n if (detection.rect) {\n validateNumber(detection.rect.x, `${detectionPath}.rect.x`);\n validateNumber(detection.rect.y, `${detectionPath}.rect.y`);\n validateNumber(detection.rect.width, `${detectionPath}.rect.width`, {\n exclusiveMin: 0,\n });\n validateNumber(detection.rect.height, `${detectionPath}.rect.height`, {\n exclusiveMin: 0,\n });\n }\n validatePoints(detection.polygon?.points, `${detectionPath}.polygon`, 3);\n validatePoints(detection.polyline?.points, `${detectionPath}.polyline`, 2);\n if (detection.keypoints) {\n validatePoints(detection.keypoints.points, `${detectionPath}.keypoints`, 1);\n if (detection.keypoints.visibility !== undefined &&\n detection.keypoints.visibility.length !==\n detection.keypoints.points.length) {\n throw new Error(`${detectionPath}.keypoints.visibility must match points length.`);\n }\n for (const [edgeOffset, edge] of detection.keypoints.edges.entries()) {\n const edgePath = `${detectionPath}.keypoints.edges[${edgeOffset}]`;\n for (const [endpointOffset, endpoint] of edge.entries()) {\n validateNumber(endpoint, `${edgePath}[${endpointOffset}]`, {\n integer: true,\n min: 0,\n });\n if (endpoint >= detection.keypoints.points.length) {\n throw new Error(`${edgePath}[${endpointOffset}] is out of range.`);\n }\n }\n }\n }\n if (detection.mask) {\n validateNumber(detection.mask.width, `${detectionPath}.mask.width`, {\n integer: true,\n exclusiveMin: 0,\n });\n validateNumber(detection.mask.height, `${detectionPath}.mask.height`, {\n integer: true,\n exclusiveMin: 0,\n });\n if (detection.mask.counts.length === 0) {\n throw new Error(`${detectionPath}.mask.counts must not be empty.`);\n }\n }\n }\n }\n}\nfunction validatePoints(points, path, minimumLength) {\n if (!points) {\n return;\n }\n if (points.length < minimumLength) {\n throw new Error(`${path}.points must contain at least ${minimumLength} points.`);\n }\n for (const [pointOffset, point] of points.entries()) {\n validateNumber(point.x, `${path}.points[${pointOffset}].x`);\n validateNumber(point.y, `${path}.points[${pointOffset}].y`);\n }\n}\nfunction validateNumber(value, path, options = {}) {\n if (!Number.isFinite(value)) {\n throw new Error(`${path} must be a finite number.`);\n }\n if (options.integer && !Number.isInteger(value)) {\n throw new Error(`${path} must be an integer.`);\n }\n if (options.min !== undefined && value < options.min) {\n throw new Error(`${path} must be greater than or equal to ${options.min}.`);\n }\n if (options.exclusiveMin !== undefined && value <= options.exclusiveMin) {\n throw new Error(`${path} must be greater than ${options.exclusiveMin}.`);\n }\n if (options.max !== undefined && value > options.max) {\n throw new Error(`${path} must be less than or equal to ${options.max}.`);\n }\n}\nfunction filterDetectionFramesForRange(detectionFrames, startTime, endTime) {\n return detectionFrames.filter((frame) => detectionFrameOverlapsRange(frame, startTime, endTime));\n}\nfunction selectDetectionFrame(detectionFrames, mediaTime, options = {}) {\n if (options.selectionMode === DetectionFrameSelectionMode.NearestFrameIndex) {\n const selection = selectNearestFrameIndexDetectionFrame(detectionFrames, mediaTime, options.frameRate, options.frameIndexOriginTime);\n if (selection.isApplicable) {\n return selection.frame;\n }\n }\n return selectIntervalDetectionFrame(detectionFrames, mediaTime);\n}\nfunction decodeCompressedRleMask(mask) {\n if (mask.encoding !== DetectionMaskEncoding.CompressedRle) {\n throw new Error(`Unsupported detection mask encoding: ${mask.encoding}`);\n }\n const data = new Uint8Array(mask.width * mask.height);\n const counts = decodeCompressedRleCounts(mask.counts);\n let offset = 0;\n for (let index = 0; index < counts.length; index += 1) {\n const runLength = counts[index] ?? 0;\n const isForeground = index % 2 === 1;\n if (isForeground) {\n for (let runOffset = 0; runOffset < runLength; runOffset += 1) {\n const maskOffset = offset + runOffset;\n const x = Math.floor(maskOffset / mask.height);\n const y = maskOffset % mask.height;\n const rowMajorOffset = y * mask.width + x;\n if (rowMajorOffset < data.length) {\n data[rowMajorOffset] = 1;\n }\n }\n }\n offset += runLength;\n }\n return {\n data,\n height: mask.height,\n width: mask.width,\n };\n}\nfunction selectIntervalDetectionFrame(detectionFrames, mediaTime) {\n let selectedFrame;\n let low = 0;\n let high = detectionFrames.length - 1;\n while (low <= high) {\n const middle = Math.floor((low + high) / 2);\n const frame = detectionFrames[middle];\n if (frame.mediaTime <= mediaTime) {\n selectedFrame = frame;\n low = middle + 1;\n }\n else {\n high = middle - 1;\n }\n }\n return selectedFrame && isDetectionFrameActive(selectedFrame, mediaTime)\n ? selectedFrame\n : undefined;\n}\nfunction selectNearestFrameIndexDetectionFrame(detectionFrames, mediaTime, frameRate, frameIndexOriginTime) {\n if (!frameRate || !Number.isFinite(frameRate) || frameRate <= 0) {\n return { frame: undefined, isApplicable: false };\n }\n const firstIndexedFrame = detectionFrames.find((frame) => frame.frameIndex !== undefined);\n if (!firstIndexedFrame || firstIndexedFrame.frameIndex === undefined) {\n return { frame: undefined, isApplicable: false };\n }\n const originTime = frameIndexOriginTime !== undefined\n ? frameIndexOriginTime\n : firstIndexedFrame.mediaTime - firstIndexedFrame.frameIndex / frameRate;\n const targetFrameIndex = Math.round((mediaTime - originTime) * frameRate);\n let nearestFrame;\n let nearestDistance = Number.POSITIVE_INFINITY;\n for (const frame of detectionFrames) {\n if (frame.frameIndex === undefined) {\n continue;\n }\n const distance = Math.abs(frame.frameIndex - targetFrameIndex);\n if (distance < nearestDistance ||\n (distance === nearestDistance &&\n nearestFrame?.frameIndex !== undefined &&\n frame.frameIndex > nearestFrame.frameIndex)) {\n nearestFrame = frame;\n nearestDistance = distance;\n }\n }\n return {\n frame: nearestDistance <= 1 ? nearestFrame : undefined,\n isApplicable: true,\n };\n}\nfunction isDetectionFrameActive(frame, mediaTime) {\n return (frame.mediaTime <= mediaTime &&\n (frame.endTime === undefined || mediaTime < frame.endTime));\n}\nfunction detectionFrameOverlapsRange(frame, startTime, endTime) {\n if (frame.endTime === undefined) {\n return frame.mediaTime >= startTime && frame.mediaTime <= endTime;\n }\n return frame.mediaTime <= endTime && frame.endTime > startTime;\n}\nfunction decodeCompressedRleCounts(counts) {\n const decoded = [];\n let index = 0;\n while (index < counts.length) {\n let value = 0;\n let shift = 0;\n let charCode;\n do {\n charCode = counts.charCodeAt(index) - 48;\n index += 1;\n value |= (charCode & 0x1f) << shift;\n shift += 5;\n } while (charCode & 0x20);\n if (charCode & 0x10) {\n value |= -1 << shift;\n }\n if (decoded.length > 2) {\n value += decoded[decoded.length - 2] ?? 0;\n }\n decoded.push(value);\n }\n return decoded;\n}\nfunction encodeCompressedRleCounts(counts) {\n return counts\n .map((count, index) => {\n let value = index > 2 ? count - counts[index - 2] : count;\n let encoded = \"\";\n let more = true;\n while (more) {\n let charCode = value & 0x1f;\n value >>= 5;\n more = !((value === 0 && (charCode & 0x10) === 0) ||\n (value === -1 && (charCode & 0x10) !== 0));\n if (more) {\n charCode |= 0x20;\n }\n encoded += String.fromCharCode(charCode + 48);\n }\n return encoded;\n })\n .join(\"\");\n}\n\nfunction createArrayDetectionFrameSource(frames) {\n const sortedFrames = copySortedDetectionFrames(frames);\n return {\n async loadFrames(startTime, endTime) {\n return copySortedDetectionFrames(filterDetectionFramesForRange(sortedFrames, startTime, endTime));\n },\n };\n}\n\nconst DEFAULT_BUFFER_AHEAD_SECONDS = 5;\nconst DEFAULT_BUFFER_BEHIND_SECONDS = 0.5;\nconst bufferedFrameSnapshots = new WeakMap();\n/**\n * Returns the timeline-owned hot-buffer snapshot without copying it.\n *\n * This is an internal platform-adapter fast path. Public callers should use\n * `getBufferedFrames()`, which preserves the existing defensive-copy contract.\n */\nfunction getBufferedDetectionTimelineFrameSnapshot(timeline) {\n return (bufferedFrameSnapshots.get(timeline)?.() ?? timeline.getBufferedFrames());\n}\nfunction createBufferedDetectionTimeline(options) {\n const bufferAheadSeconds = options.bufferAheadSeconds ?? DEFAULT_BUFFER_AHEAD_SECONDS;\n const bufferBehindSeconds = options.bufferBehindSeconds ?? DEFAULT_BUFFER_BEHIND_SECONDS;\n const refreshIntervalSeconds = options.refreshIntervalSeconds === undefined\n ? null\n : Math.max(0, options.refreshIntervalSeconds);\n const playbackGate = options.playbackGate;\n let buffer = [];\n let state = createIdleDetectionBufferState();\n let destroyed = false;\n let loadId = 0;\n let bufferedSourceVersion = null;\n let bufferedVersionRange = null;\n let timelineContext = {\n duration: null,\n loop: false,\n };\n let inFlight;\n let incrementalRefresh;\n let pendingPrefetch;\n let prefetchPump;\n const getSourceVersion = (ranges) => {\n if (!ranges) {\n return options.source.getVersion?.() ?? 0;\n }\n return ranges.reduce((version, range) => Math.max(version, options.source.getVersion?.(range) ?? 0), 0);\n };\n const isBufferFresh = () => bufferedVersionRange !== null &&\n bufferedSourceVersion === getSourceVersion(getBufferedSourceRanges());\n const getLoadRange = (mediaTime) => {\n const comparableMediaTime = getComparableMediaTime(mediaTime);\n const startTime = comparableMediaTime - bufferBehindSeconds;\n const endTime = comparableMediaTime + bufferAheadSeconds;\n return createLoadPlan(startTime, endTime);\n };\n const loadWindow = (mediaTime) => {\n const { endTime, sourceRanges, startTime } = getLoadRange(mediaTime);\n const versionRange = { endTime, startTime };\n const sourceVersion = getSourceVersion(sourceRanges);\n if (inFlight &&\n inFlight.sourceVersion === sourceVersion &&\n rangeContains(inFlight.startTime, inFlight.endTime, startTime, endTime)) {\n return inFlight.promise;\n }\n const currentLoadId = loadId + 1;\n loadId = currentLoadId;\n state = {\n ...state,\n errorMessage: null,\n requestedEndTime: endTime,\n requestedStartTime: startTime,\n status: DetectionBufferStatus.Loading,\n };\n const promise = Promise.all(sourceRanges.map((range) => options.source.loadFrames(range.startTime, range.endTime)))\n .then((frameRanges) => {\n if (destroyed || currentLoadId !== loadId) {\n return;\n }\n const committedSourceVersion = getSourceVersion(sourceRanges);\n const loadedBuffer = copySortedDetectionFrames(frameRanges.flat());\n buffer =\n bufferedSourceVersion !== null &&\n bufferedSourceVersion === committedSourceVersion\n ? reuseBufferedFrameSnapshots(buffer, loadedBuffer)\n : loadedBuffer;\n bufferedVersionRange = versionRange;\n bufferedSourceVersion = committedSourceVersion;\n state = {\n bufferEndTime: endTime,\n bufferStartTime: startTime,\n detectionCount: countDetections(buffer),\n errorMessage: null,\n frameCount: buffer.length,\n requestedEndTime: endTime,\n requestedStartTime: startTime,\n status: DetectionBufferStatus.Ready,\n };\n })\n .catch((error) => {\n if (!destroyed && currentLoadId === loadId) {\n state = {\n ...state,\n errorMessage: getErrorMessage(error),\n status: DetectionBufferStatus.Error,\n };\n }\n throw error;\n })\n .finally(() => {\n if (inFlight?.id === currentLoadId) {\n inFlight = undefined;\n }\n });\n inFlight = {\n endTime,\n id: currentLoadId,\n promise,\n sourceVersion,\n startTime,\n };\n return promise;\n };\n const isBuffered = (mediaTime) => {\n const comparableMediaTime = getComparableMediaTime(mediaTime);\n return (isBufferFresh() &&\n state.bufferStartTime !== null &&\n state.bufferEndTime !== null &&\n comparableMediaTime >= state.bufferStartTime &&\n comparableMediaTime <= state.bufferEndTime);\n };\n const isInsideBufferedRange = (mediaTime) => {\n const comparableMediaTime = getComparableMediaTime(mediaTime);\n return (bufferedVersionRange !== null &&\n state.bufferStartTime !== null &&\n state.bufferEndTime !== null &&\n comparableMediaTime >= state.bufferStartTime &&\n comparableMediaTime <= state.bufferEndTime);\n };\n const refreshBuffer = async (mediaTime) => {\n if (isBuffered(mediaTime)) {\n return;\n }\n if (isInsideBufferedRange(mediaTime) &&\n bufferedSourceVersion !== null &&\n options.source.getChangesSince) {\n if (!incrementalRefresh) {\n incrementalRefresh = applyIncrementalChanges().finally(() => {\n incrementalRefresh = undefined;\n });\n }\n await incrementalRefresh;\n if (isBuffered(mediaTime)) {\n return;\n }\n return refreshBuffer(mediaTime);\n }\n await loadWindow(mediaTime);\n };\n const shouldPrefetch = (mediaTime) => {\n if (!isBuffered(mediaTime)) {\n return true;\n }\n if (shouldRefreshRollingWindow(mediaTime)) {\n return true;\n }\n if (state.bufferEndTime === null || bufferAheadSeconds <= 0) {\n return false;\n }\n return (getComparableMediaTime(mediaTime) + bufferAheadSeconds / 2 >=\n state.bufferEndTime);\n };\n const timeline = {\n async prepare(mediaTime, prepareOptions) {\n if (destroyed) {\n return;\n }\n if (shouldWaitForPlaybackGate(prepareOptions)) {\n await waitForPlaybackGate(mediaTime, prepareOptions);\n if (destroyed) {\n return;\n }\n }\n await refreshBuffer(mediaTime);\n },\n prefetch(mediaTime) {\n if (destroyed || !shouldPrefetch(mediaTime)) {\n return;\n }\n pendingPrefetch = { loadId, mediaTime };\n pumpPrefetchQueue();\n },\n selectFrame(mediaTime) {\n if (!isBuffered(mediaTime)) {\n return undefined;\n }\n return selectDetectionFrame(buffer, getSourceMediaTime(mediaTime), options);\n },\n setTimelineContext(context) {\n timelineContext = context;\n bufferedSourceVersion = null;\n bufferedVersionRange = null;\n },\n getBufferedFrames() {\n return copySortedDetectionFrames(buffer);\n },\n getState() {\n return { ...state };\n },\n destroy() {\n if (destroyed) {\n return;\n }\n destroyed = true;\n pendingPrefetch = undefined;\n buffer = [];\n bufferedSourceVersion = null;\n bufferedVersionRange = null;\n state = {\n ...state,\n bufferEndTime: null,\n bufferStartTime: null,\n detectionCount: 0,\n frameCount: 0,\n status: DetectionBufferStatus.Destroyed,\n };\n options.source.destroy?.();\n },\n };\n bufferedFrameSnapshots.set(timeline, () => buffer);\n return timeline;\n function pumpPrefetchQueue() {\n if (destroyed || prefetchPump) {\n return;\n }\n prefetchPump = drainPrefetchQueue().finally(() => {\n prefetchPump = undefined;\n if (!destroyed && pendingPrefetch) {\n pumpPrefetchQueue();\n }\n });\n }\n async function drainPrefetchQueue() {\n while (!destroyed && pendingPrefetch) {\n if (inFlight) {\n await inFlight.promise.catch(() => undefined);\n continue;\n }\n const request = pendingPrefetch;\n pendingPrefetch = undefined;\n if (request.loadId !== loadId) {\n continue;\n }\n const { mediaTime } = request;\n if (!shouldPrefetch(mediaTime)) {\n continue;\n }\n await (shouldRefreshRollingWindow(mediaTime)\n ? loadWindow(mediaTime)\n : refreshBuffer(mediaTime)).catch(() => undefined);\n }\n }\n async function applyIncrementalChanges() {\n if (destroyed ||\n bufferedSourceVersion === null ||\n !options.source.getChangesSince) {\n return;\n }\n const sourceRanges = getBufferedSourceRanges();\n const incrementalLoadId = loadId;\n const incrementalVersionRange = bufferedVersionRange;\n const changes = options.source.getChangesSince(bufferedSourceVersion, sourceRanges);\n if (changes.requiresReload) {\n bufferedSourceVersion = null;\n return;\n }\n if (changes.ranges.length === 0) {\n bufferedSourceVersion = changes.version;\n return;\n }\n const changedRanges = getOverlappingRanges(changes.ranges, sourceRanges);\n if (changedRanges.length === 0) {\n bufferedSourceVersion = changes.version;\n return;\n }\n try {\n const changedFrameRanges = await Promise.all(changedRanges.map((range) => options.source.loadFrames(range.startTime, range.endTime)));\n if (destroyed ||\n loadId !== incrementalLoadId ||\n bufferedVersionRange !== incrementalVersionRange) {\n return;\n }\n buffer = mergeIncrementalFrames(buffer, changedFrameRanges.flat(), changedRanges);\n bufferedSourceVersion = changes.version;\n state = {\n ...state,\n detectionCount: countDetections(buffer),\n errorMessage: null,\n frameCount: buffer.length,\n status: DetectionBufferStatus.Ready,\n };\n }\n catch (error) {\n if (!destroyed) {\n state = {\n ...state,\n errorMessage: getErrorMessage(error),\n status: DetectionBufferStatus.Error,\n };\n }\n throw error;\n }\n }\n function shouldWaitForPlaybackGate(prepareOptions) {\n return (prepareOptions?.gatePlayback === true &&\n playbackGate?.enabled === true &&\n Boolean(options.source.waitForRange));\n }\n async function waitForPlaybackGate(mediaTime, prepareOptions) {\n if (!playbackGate?.enabled || !options.source.waitForRange) {\n return;\n }\n const requiredAheadSeconds = Math.max(0, playbackGate.requiredAheadSeconds ?? 0);\n const comparableMediaTime = getComparableMediaTime(mediaTime);\n const endTime = getRequiredCoverageEndTime({\n duration: isLoopingTimeline() ? null : prepareOptions?.duration,\n firstTimestamp: prepareOptions?.firstTimestamp,\n mediaTime: comparableMediaTime,\n requiredAheadSeconds,\n });\n if (endTime <= comparableMediaTime) {\n return;\n }\n const coveragePlan = createLoadPlan(comparableMediaTime, endTime);\n state = {\n ...state,\n errorMessage: null,\n requestedEndTime: coveragePlan.endTime,\n requestedStartTime: coveragePlan.startTime,\n status: DetectionBufferStatus.Loading,\n };\n try {\n await Promise.all(coveragePlan.sourceRanges.map((range) => options.source.waitForRange?.(range)));\n }\n catch (error) {\n if (!destroyed) {\n state = {\n ...state,\n errorMessage: getErrorMessage(error),\n status: DetectionBufferStatus.Error,\n };\n }\n throw error;\n }\n }\n function shouldRefreshRollingWindow(mediaTime) {\n if (refreshIntervalSeconds === null ||\n refreshIntervalSeconds <= 0 ||\n state.bufferStartTime === null ||\n state.bufferEndTime === null) {\n return false;\n }\n const { endTime, startTime } = getLoadRange(mediaTime);\n return (Math.abs(startTime - state.bufferStartTime) >= refreshIntervalSeconds ||\n Math.abs(endTime - state.bufferEndTime) >= refreshIntervalSeconds);\n }\n function createLoadPlan(requestedStartTime, requestedEndTime) {\n const startTime = Math.min(requestedStartTime, requestedEndTime);\n const endTime = Math.max(startTime, requestedEndTime);\n if (!isLoopingTimeline()) {\n const clampedStartTime = Math.max(0, startTime);\n const clampedEndTime = Math.max(clampedStartTime, endTime);\n return {\n endTime: clampedEndTime,\n sourceRanges: [\n {\n endTime: clampedEndTime,\n startTime: clampedStartTime,\n },\n ],\n startTime: clampedStartTime,\n };\n }\n const duration = timelineContext.duration ?? 0;\n if (endTime - startTime >= duration) {\n return {\n endTime: duration,\n sourceRanges: [{ endTime: duration, startTime: 0 }],\n startTime: 0,\n };\n }\n return {\n endTime,\n sourceRanges: getLoopingSourceRanges(startTime, endTime, duration),\n startTime,\n };\n }\n function getBufferedSourceRanges() {\n if (!bufferedVersionRange) {\n return [];\n }\n return createLoadPlan(bufferedVersionRange.startTime, bufferedVersionRange.endTime).sourceRanges;\n }\n function isLoopingTimeline() {\n return (timelineContext.loop &&\n timelineContext.duration !== null &&\n timelineContext.duration > 0);\n }\n function getComparableMediaTime(mediaTime) {\n if (!isLoopingTimeline() ||\n state.bufferStartTime === null ||\n state.bufferEndTime === null ||\n timelineContext.duration === null) {\n return mediaTime;\n }\n const duration = timelineContext.duration;\n let comparableMediaTime = mediaTime;\n while (comparableMediaTime < state.bufferStartTime) {\n comparableMediaTime += duration;\n }\n while (comparableMediaTime > state.bufferEndTime) {\n comparableMediaTime -= duration;\n }\n return comparableMediaTime;\n }\n function getSourceMediaTime(mediaTime) {\n if (!isLoopingTimeline() || timelineContext.duration === null) {\n return mediaTime;\n }\n if (mediaTime >= 0 && mediaTime <= timelineContext.duration) {\n return mediaTime;\n }\n return modulo(mediaTime, timelineContext.duration);\n }\n}\nfunction createIdleDetectionBufferState() {\n return {\n bufferEndTime: null,\n bufferStartTime: null,\n detectionCount: 0,\n errorMessage: null,\n frameCount: 0,\n requestedEndTime: null,\n requestedStartTime: null,\n status: DetectionBufferStatus.Idle,\n };\n}\nfunction rangeContains(outerStart, outerEnd, innerStart, innerEnd) {\n return outerStart <= innerStart && innerEnd <= outerEnd;\n}\nfunction countDetections(frames) {\n return frames.reduce((total, frame) => total + frame.detections.length, 0);\n}\nfunction getErrorMessage(error) {\n return error instanceof Error\n ? error.message\n : \"Detection buffer load failed.\";\n}\nfunction getRequiredCoverageEndTime(options) {\n const requestedEndTime = options.mediaTime + options.requiredAheadSeconds;\n if (options.duration === null || options.duration === undefined) {\n return requestedEndTime;\n }\n return Math.min(requestedEndTime, (options.firstTimestamp ?? 0) + Math.max(options.duration, 0));\n}\nfunction getLoopingSourceRanges(startTime, endTime, duration) {\n const normalizedStartTime = modulo(startTime, duration);\n const normalizedEndTime = modulo(endTime, duration);\n const startCycle = Math.floor(startTime / duration);\n const endCycle = Math.floor(endTime / duration);\n if (startCycle === endCycle) {\n return [{ endTime: normalizedEndTime, startTime: normalizedStartTime }];\n }\n const ranges = [];\n if (normalizedStartTime < duration) {\n ranges.push({ endTime: duration, startTime: normalizedStartTime });\n }\n if (normalizedEndTime > 0) {\n ranges.push({ endTime: normalizedEndTime, startTime: 0 });\n }\n return ranges;\n}\nfunction modulo(value, modulus) {\n return ((value % modulus) + modulus) % modulus;\n}\nfunction getOverlappingRanges(changedRanges, bufferedRanges) {\n const intersections = [];\n for (const changedRange of changedRanges) {\n for (const bufferedRange of bufferedRanges) {\n const startTime = Math.max(changedRange.startTime, bufferedRange.startTime);\n const endTime = Math.min(changedRange.endTime, bufferedRange.endTime);\n if (startTime <= endTime) {\n intersections.push({ endTime, startTime });\n }\n }\n }\n return intersections;\n}\nfunction mergeIncrementalFrames(currentFrames, changedFrames, changedRanges) {\n const framesByIdentity = new Map();\n for (const frame of currentFrames) {\n if (changedRanges.some((range) => detectionFrameOverlapsRange(frame, range.startTime, range.endTime))) {\n continue;\n }\n framesByIdentity.set(getDetectionFrameIdentity(frame), frame);\n }\n for (const frame of changedFrames) {\n framesByIdentity.set(getDetectionFrameIdentity(frame), frame);\n }\n return Array.from(framesByIdentity.values()).sort(compareDetectionFrames$1);\n}\nfunction reuseBufferedFrameSnapshots(currentFrames, loadedFrames) {\n const currentFramesByIdentity = new Map(currentFrames.map((frame) => [getDetectionFrameIdentity(frame), frame]));\n return loadedFrames.map((frame) => currentFramesByIdentity.get(getDetectionFrameIdentity(frame)) ?? frame);\n}\nfunction getDetectionFrameIdentity(frame) {\n return frame.frameIndex === undefined\n ? `time:${frame.mediaTime}`\n : `index:${frame.frameIndex}`;\n}\nfunction compareDetectionFrames$1(left, right) {\n if (left.mediaTime !== right.mediaTime) {\n return left.mediaTime - right.mediaTime;\n }\n return (left.frameIndex ?? 0) - (right.frameIndex ?? 0);\n}\n\nfunction createColdDetectionFrameSource(options) {\n return {\n loadFrames(startTime, endTime) {\n return options.store.loadFrames({\n datasetId: options.datasetId,\n endTime,\n startTime,\n });\n },\n };\n}\n\n/**\n * Projects one detection frame from its declared source coordinate space into\n * `target`.\n *\n * Rectangles, polygons, polylines, and keypoints are scaled. Masks are left\n * untouched because a `DetectionMask` already carries the mask-pixel\n * dimensions its `counts` are encoded against; scaling them here would apply\n * the ratio twice. Frames without `coordinateSpace`, with a degenerate space,\n * or already in the target space are returned unchanged so mixed producers can\n * share one source.\n */\nfunction projectDetectionFrame(frame, target) {\n const space = frame.coordinateSpace;\n if (!space ||\n !isUsableCoordinateSpace(space) ||\n !isUsableCoordinateSpace(target) ||\n (space.width === target.width && space.height === target.height)) {\n return frame;\n }\n const scaleX = target.width / space.width;\n const scaleY = target.height / space.height;\n return {\n ...frame,\n coordinateSpace: { height: target.height, width: target.width },\n detections: frame.detections.map((detection) => projectDetection(detection, scaleX, scaleY)),\n };\n}\n/**\n * Projects every frame that declares a source coordinate space into `target`.\n *\n * Frames without coordinate metadata pass through by reference.\n */\nfunction projectDetectionFrames(frames, target) {\n let didProject = false;\n const projectedFrames = frames.map((frame) => {\n const projectedFrame = projectDetectionFrame(frame, target);\n didProject ||= projectedFrame !== frame;\n return projectedFrame;\n });\n return didProject ? projectedFrames : frames;\n}\nfunction projectDetection(detection, scaleX, scaleY) {\n return {\n ...detection,\n ...(detection.rect\n ? { rect: scaleRect(detection.rect, scaleX, scaleY) }\n : {}),\n ...(detection.polygon\n ? { polygon: scalePolygon(detection.polygon, scaleX, scaleY) }\n : {}),\n ...(detection.polyline\n ? { polyline: scalePolyline(detection.polyline, scaleX, scaleY) }\n : {}),\n ...(detection.keypoints\n ? { keypoints: scaleKeypoints(detection.keypoints, scaleX, scaleY) }\n : {}),\n };\n}\nfunction scaleRect(rect, scaleX, scaleY) {\n return {\n height: rect.height * scaleY,\n width: rect.width * scaleX,\n x: rect.x * scaleX,\n y: rect.y * scaleY,\n };\n}\nfunction scalePolygon(polygon, scaleX, scaleY) {\n return { points: scalePoints(polygon.points, scaleX, scaleY) };\n}\nfunction scalePolyline(polyline, scaleX, scaleY) {\n return { points: scalePoints(polyline.points, scaleX, scaleY) };\n}\nfunction scaleKeypoints(keypoints, scaleX, scaleY) {\n return {\n ...keypoints,\n points: scalePoints(keypoints.points, scaleX, scaleY),\n };\n}\nfunction scalePoints(points, scaleX, scaleY) {\n return points.map((point) => ({ x: point.x * scaleX, y: point.y * scaleY }));\n}\nfunction isUsableCoordinateSpace(space) {\n return (Number.isFinite(space.width) &&\n Number.isFinite(space.height) &&\n space.width > 0 &&\n space.height > 0);\n}\n\nfunction createCompositeDetectionFrameSource(options) {\n const sources = normalizeCompositeSources(options.sources);\n return {\n async loadFrames(startTime, endTime, loadOptions) {\n const target = loadOptions?.coordinateSpace;\n const loadedSources = await Promise.all(sources.map(async (source) => {\n const frames = copySortedDetectionFrames(await source.source.loadFrames(startTime, endTime, loadOptions));\n return {\n ...source,\n // Composition flattens child detections into one frame, which can\n // only carry one coordinate space. Each child is projected here,\n // while its own `coordinateSpace` is still attached to its own\n // detections, so children inferred at different sizes compose\n // correctly. Masks keep their intrinsic dimensions.\n frames: target ? projectDetectionFrames(frames, target) : frames,\n };\n }));\n if (options.selectionMode === DetectionFrameSelectionMode.NearestFrameIndex) {\n const nearestFrames = composeNearestFrameIndexFrames(loadedSources, startTime, endTime, options, target);\n if (nearestFrames) {\n return nearestFrames;\n }\n }\n return composeIntervalFrames(loadedSources, startTime, endTime, options, target);\n },\n async waitForRange(range) {\n await Promise.all(sources\n .filter((source) => source.requiredForPlayback)\n .map((source) => source.source.waitForRange?.(range)));\n },\n getAvailableRanges() {\n return mergeRanges$1(sources.flatMap((source) => source.source.getAvailableRanges?.() ?? []));\n },\n getVersion(range) {\n return sources.reduce((version, source) => version + (source.source.getVersion?.(range) ?? 0), 0);\n },\n destroy() {\n for (const source of sources) {\n source.source.destroy?.();\n }\n },\n };\n}\nfunction normalizeCompositeSources(entries) {\n const sourceIds = new Set();\n return entries\n .map((entry, declarationIndex) => {\n if (sourceIds.has(entry.id)) {\n throw new Error(`Duplicate detection source id: ${entry.id}.`);\n }\n sourceIds.add(entry.id);\n const inputCount = [\n entry.frames !== undefined,\n entry.source !== undefined,\n ].filter(Boolean).length;\n if (inputCount !== 1) {\n throw new Error(`Detection source ${entry.id} must provide exactly one input: frames or source.`);\n }\n return {\n declarationIndex,\n id: entry.id,\n order: entry.order ?? 0,\n requiredForPlayback: entry.requiredForPlayback !== false,\n source: entry.source ?? createArrayDetectionFrameSource(entry.frames ?? []),\n sync: entry.sync,\n };\n })\n .sort((left, right) => left.order - right.order ||\n left.declarationIndex - right.declarationIndex);\n}\nfunction composeIntervalFrames(sources, startTime, endTime, options, coordinateSpace) {\n const boundaryTimes = new Set([startTime]);\n for (const source of sources) {\n for (const frame of source.frames) {\n if (frame.mediaTime >= startTime && frame.mediaTime < endTime) {\n boundaryTimes.add(frame.mediaTime);\n }\n if (frame.endTime !== undefined &&\n frame.endTime > startTime &&\n frame.endTime < endTime) {\n boundaryTimes.add(frame.endTime);\n }\n }\n }\n const sortedBoundaryTimes = [...boundaryTimes].sort((left, right) => left - right);\n const frames = [];\n for (const [boundaryIndex, mediaTime] of sortedBoundaryTimes.entries()) {\n if (mediaTime < startTime || mediaTime >= endTime) {\n continue;\n }\n const nextBoundaryTime = sortedBoundaryTimes[boundaryIndex + 1] ?? endTime;\n const endTimeForFrame = Math.min(nextBoundaryTime, endTime);\n const frame = composeFrameAtTime(sources, mediaTime, endTimeForFrame, { ...options, selectionMode: DetectionFrameSelectionMode.Interval }, undefined, coordinateSpace);\n if (frame) {\n frames.push(frame);\n }\n }\n return frames;\n}\nfunction composeNearestFrameIndexFrames(sources, startTime, endTime, options, coordinateSpace) {\n const frameRate = options.frameRate;\n if (!frameRate || !Number.isFinite(frameRate) || frameRate <= 0) {\n return null;\n }\n const indexedFrames = sources.flatMap((source) => source.frames.filter((frame) => frame.frameIndex !== undefined));\n const firstIndexedFrame = indexedFrames[0];\n if (!firstIndexedFrame || firstIndexedFrame.frameIndex === undefined) {\n return null;\n }\n const originTime = options.frameIndexOriginTime ??\n firstIndexedFrame.mediaTime - firstIndexedFrame.frameIndex / frameRate;\n const frameIndexes = [\n ...new Set(indexedFrames\n .map((frame) => frame.frameIndex)\n .filter((frameIndex) => frameIndex !== undefined)),\n ].sort((left, right) => left - right);\n const frames = [];\n for (const frameIndex of frameIndexes) {\n const mediaTime = originTime + frameIndex / frameRate;\n if (mediaTime < startTime || mediaTime >= endTime) {\n continue;\n }\n const frame = composeFrameAtTime(sources, mediaTime, Math.min(mediaTime + 1 / frameRate, endTime), {\n ...options,\n selectionMode: DetectionFrameSelectionMode.NearestFrameIndex,\n }, frameIndex, coordinateSpace);\n if (frame) {\n frames.push(frame);\n }\n }\n return frames;\n}\nfunction composeFrameAtTime(sources, mediaTime, endTime, options, frameIndex, coordinateSpace) {\n const detections = [];\n const activeFrameIndexes = [];\n for (const source of sources) {\n const activeFrame = selectDetectionFrame(source.frames, mediaTime, {\n ...options,\n ...source.sync,\n });\n if (!activeFrame) {\n continue;\n }\n if (activeFrame.frameIndex !== undefined) {\n activeFrameIndexes.push(activeFrame.frameIndex);\n }\n activeFrame.detections.forEach((detection, sourceDetectionIndex) => {\n detections.push(copyDetectionWithSource(detection, source.id, sourceDetectionIndex));\n });\n }\n if (detections.length === 0) {\n return undefined;\n }\n return {\n detections,\n endTime,\n frameIndex: frameIndex ?? resolveComposedFrameIndex(activeFrameIndexes) ?? undefined,\n mediaTime,\n // Children were projected before composition, so a composed frame is\n // already in the coordinate space the renderer presents.\n ...(coordinateSpace ? { coordinateSpace } : {}),\n };\n}\nfunction copyDetectionWithSource(detection, sourceId, sourceDetectionIndex) {\n return {\n ...detection,\n mask: detection.mask ? { ...detection.mask } : undefined,\n metadata: detection.metadata ? { ...detection.metadata } : undefined,\n rect: detection.rect ? { ...detection.rect } : undefined,\n sourceDetectionIndex,\n sourceId,\n };\n}\nfunction resolveComposedFrameIndex(frameIndexes) {\n if (frameIndexes.length === 0) {\n return undefined;\n }\n const firstFrameIndex = frameIndexes[0];\n return frameIndexes.every((frameIndex) => frameIndex === firstFrameIndex)\n ? firstFrameIndex\n : undefined;\n}\nfunction mergeRanges$1(ranges) {\n const sortedRanges = [...ranges].sort((left, right) => left.startTime - right.startTime);\n const mergedRanges = [];\n for (const range of sortedRanges) {\n const lastRange = mergedRanges.at(-1);\n if (!lastRange || range.startTime > lastRange.endTime) {\n mergedRanges.push({ ...range });\n continue;\n }\n mergedRanges[mergedRanges.length - 1] = {\n startTime: lastRange.startTime,\n endTime: Math.max(lastRange.endTime, range.endTime),\n };\n }\n return mergedRanges;\n}\n\nvar AnnotationFrameMutationKind;\n(function (AnnotationFrameMutationKind) {\n AnnotationFrameMutationKind[\"Add\"] = \"add\";\n AnnotationFrameMutationKind[\"Remove\"] = \"remove\";\n AnnotationFrameMutationKind[\"Replace\"] = \"replace\";\n AnnotationFrameMutationKind[\"Transact\"] = \"transact\";\n AnnotationFrameMutationKind[\"Update\"] = \"update\";\n})(AnnotationFrameMutationKind || (AnnotationFrameMutationKind = {}));\nfunction createEditableAnnotationFrameSession(initialFrame) {\n let snapshot = createSnapshot(initialFrame);\n let destroyed = false;\n const listeners = new Set();\n assertStableIds(snapshot);\n const commit = (frame, kind, detectionIds) => {\n assertActive();\n const previous = snapshot;\n const current = createSnapshot(frame);\n assertStableIds(current);\n snapshot = current;\n const mutation = Object.freeze({\n current,\n detectionIds: Object.freeze([...detectionIds]),\n kind,\n previous,\n });\n for (const listener of listeners) {\n listener(mutation);\n }\n return current;\n };\n const session = {\n getSnapshot() {\n assertActive();\n return snapshot;\n },\n add(detection, index = snapshot.detections.length) {\n assertActive();\n const id = requireDetectionId(detection);\n if (findDetectionIndex(snapshot, id) !== -1) {\n throw new Error(`Detection id ${String(id)} already exists.`);\n }\n const detections = [...snapshot.detections];\n const insertionIndex = Math.max(0, Math.min(index, detections.length));\n detections.splice(insertionIndex, 0, detection);\n return commit({ ...snapshot, detections }, AnnotationFrameMutationKind.Add, [id]);\n },\n update(id, update) {\n assertActive();\n const detectionIndex = findDetectionIndex(snapshot, id);\n if (detectionIndex === -1) {\n throw new Error(`Detection id ${String(id)} was not found.`);\n }\n const detections = [...snapshot.detections];\n const current = detections[detectionIndex];\n const next = typeof update === \"function\"\n ? update(current)\n : { ...current, ...update };\n if (next.id !== id) {\n throw new Error(\"Detection updates must preserve the stable id.\");\n }\n detections[detectionIndex] = next;\n return commit({ ...snapshot, detections }, AnnotationFrameMutationKind.Update, [id]);\n },\n remove(id) {\n assertActive();\n const detectionIndex = findDetectionIndex(snapshot, id);\n if (detectionIndex === -1) {\n return snapshot;\n }\n const detections = [...snapshot.detections];\n detections.splice(detectionIndex, 1);\n return commit({ ...snapshot, detections }, AnnotationFrameMutationKind.Remove, [id]);\n },\n replace(frame) {\n return commit(frame, AnnotationFrameMutationKind.Replace, frame.detections.map(requireDetectionId));\n },\n transact(mutate, detectionIds = []) {\n assertActive();\n const detections = copySortedDetectionFrames([snapshot])[0]\n .detections;\n mutate(detections);\n return commit({ ...snapshot, detections }, AnnotationFrameMutationKind.Transact, detectionIds);\n },\n subscribe(listener) {\n assertActive();\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n destroy() {\n destroyed = true;\n listeners.clear();\n },\n };\n return session;\n function assertActive() {\n if (destroyed) {\n throw new Error(\"Editable annotation frame session has been destroyed.\");\n }\n }\n}\nfunction findDetectionIndex(frame, id) {\n return frame.detections.findIndex((detection) => detection.id === id);\n}\nfunction requireDetectionId(detection) {\n if (detection.id === undefined) {\n throw new Error(\"Editable annotation detections require a stable id.\");\n }\n return detection.id;\n}\nfunction assertStableIds(frame) {\n const ids = new Set();\n for (const detection of frame.detections) {\n const id = requireDetectionId(detection);\n if (ids.has(id)) {\n throw new Error(`Detection id ${String(id)} is duplicated.`);\n }\n ids.add(id);\n }\n}\nfunction createSnapshot(frame) {\n const snapshot = copySortedDetectionFrames([frame])[0];\n return deepFreeze(snapshot);\n}\nfunction deepFreeze(value) {\n if (value && typeof value === \"object\" && !Object.isFrozen(value)) {\n Object.freeze(value);\n for (const nested of Object.values(value)) {\n deepFreeze(nested);\n }\n }\n return value;\n}\n\nconst DEFAULT_CHUNK_DURATION_SECONDS = 1;\nfunction createMemoryColdDetectionFrameStore() {\n const datasets = new Map();\n let destroyed = false;\n return {\n async putFrames(options) {\n assertActive();\n const dataset = createDataset(resolveChunkDurationSeconds(options));\n upsertFrames(dataset, copySortedDetectionFrames(options.frames));\n datasets.set(options.datasetId, dataset);\n return createWriteSummary(options.datasetId, dataset);\n },\n async appendFrames(options) {\n assertActive();\n const existingDataset = datasets.get(options.datasetId);\n const chunkDurationSeconds = resolveChunkDurationSeconds(options, existingDataset);\n const dataset = existingDataset ?? createDataset(chunkDurationSeconds);\n upsertFrames(dataset, copySortedDetectionFrames(options.frames));\n datasets.set(options.datasetId, dataset);\n return createWriteSummary(options.datasetId, dataset);\n },\n async loadFrames(options) {\n assertActive();\n const dataset = datasets.get(options.datasetId);\n if (!dataset) {\n return [];\n }\n const startTime = Math.max(0, options.startTime);\n const endTime = Math.max(startTime, options.endTime);\n const frameKeys = new Set();\n const startChunkIndex = getChunkIndex(startTime, dataset.chunkDurationSeconds);\n const endChunkIndex = getChunkIndex(endTime, dataset.chunkDurationSeconds);\n for (let chunkIndex = startChunkIndex; chunkIndex <= endChunkIndex; chunkIndex += 1) {\n for (const frameKey of dataset.frameKeysByChunk.get(chunkIndex) ?? []) {\n frameKeys.add(frameKey);\n }\n }\n const frames = Array.from(frameKeys)\n .map((frameKey) => dataset.framesByKey.get(frameKey)?.frame)\n .filter((frame) => frame !== undefined &&\n detectionFrameOverlapsRange(frame, startTime, endTime));\n return copySortedDetectionFrames(frames);\n },\n async pruneFrames(options) {\n assertActive();\n assertRetentionFloor(options.startTime);\n const dataset = datasets.get(options.datasetId);\n if (!dataset) {\n return createWriteSummary(options.datasetId, createDataset(DEFAULT_CHUNK_DURATION_SECONDS));\n }\n pruneDatasetBefore(dataset, options.startTime);\n return createWriteSummary(options.datasetId, dataset);\n },\n async clearDataset(datasetId) {\n assertActive();\n datasets.delete(datasetId);\n },\n destroy() {\n destroyed = true;\n datasets.clear();\n },\n };\n function assertActive() {\n if (destroyed) {\n throw new Error(\"Memory cold detection frame store has been destroyed.\");\n }\n }\n}\nfunction createDataset(chunkDurationSeconds) {\n return {\n chunkDurationSeconds,\n detectionCount: 0,\n endTime: null,\n frameKeysByChunk: new Map(),\n framesByKey: new Map(),\n startTime: null,\n };\n}\nfunction upsertFrames(dataset, frames) {\n let shouldRecalculateBounds = false;\n for (const frame of frames) {\n const frameKey = getDetectionFrameDedupeKey(frame);\n const existingFrame = dataset.framesByKey.get(frameKey);\n if (existingFrame) {\n removeStoredFrame(dataset, frameKey, existingFrame);\n shouldRecalculateBounds ||= replacementCanShrinkBounds(dataset, existingFrame.frame, frame);\n }\n const chunkIndexes = getFrameChunkIndexes(frame, dataset.chunkDurationSeconds);\n dataset.framesByKey.set(frameKey, { chunkIndexes, frame });\n dataset.detectionCount += frame.detections.length;\n dataset.startTime = Math.min(dataset.startTime ?? frame.mediaTime, frame.mediaTime);\n dataset.endTime = Math.max(dataset.endTime ?? getFrameEndTime$1(frame), getFrameEndTime$1(frame));\n for (const chunkIndex of chunkIndexes) {\n const frameKeys = dataset.frameKeysByChunk.get(chunkIndex) ?? new Set();\n frameKeys.add(frameKey);\n dataset.frameKeysByChunk.set(chunkIndex, frameKeys);\n }\n }\n if (shouldRecalculateBounds) {\n recalculateBounds(dataset);\n }\n}\nfunction removeStoredFrame(dataset, frameKey, storedFrame) {\n dataset.framesByKey.delete(frameKey);\n dataset.detectionCount -= storedFrame.frame.detections.length;\n for (const chunkIndex of storedFrame.chunkIndexes) {\n const frameKeys = dataset.frameKeysByChunk.get(chunkIndex);\n frameKeys?.delete(frameKey);\n if (frameKeys?.size === 0) {\n dataset.frameKeysByChunk.delete(chunkIndex);\n }\n }\n}\n/**\n * Drops frames that end before `startTime`.\n *\n * Only chunks at or below the retention boundary are visited, and the new\n * start bound is read from the lowest surviving chunk, so eviction costs work\n * proportional to the evicted range rather than to retained history.\n */\nfunction pruneDatasetBefore(dataset, startTime) {\n const boundaryChunkIndex = getChunkIndex(Math.max(0, startTime), dataset.chunkDurationSeconds);\n let didRemoveFrame = false;\n for (const chunkIndex of Array.from(dataset.frameKeysByChunk.keys())) {\n if (chunkIndex > boundaryChunkIndex) {\n continue;\n }\n for (const frameKey of Array.from(dataset.frameKeysByChunk.get(chunkIndex) ?? [])) {\n const storedFrame = dataset.framesByKey.get(frameKey);\n if (!storedFrame || isFrameRetainedFrom(storedFrame.frame, startTime)) {\n continue;\n }\n removeStoredFrame(dataset, frameKey, storedFrame);\n didRemoveFrame = true;\n }\n }\n if (!didRemoveFrame) {\n return;\n }\n if (dataset.framesByKey.size === 0) {\n dataset.startTime = null;\n dataset.endTime = null;\n return;\n }\n // Every frame is registered in the chunk its `mediaTime` falls in, so the\n // earliest retained frame lives in the lowest remaining chunk. Removing\n // frames can only raise the start bound; `endTime` is unchanged because only\n // frames ending before `startTime` were dropped.\n dataset.startTime = getEarliestChunkStartTime(dataset);\n}\n/**\n * Mirrors `detectionFrameOverlapsRange` so pruning keeps exactly the frames a\n * load from `startTime` would return.\n */\nfunction isFrameRetainedFrom(frame, startTime) {\n return frame.endTime === undefined\n ? frame.mediaTime >= startTime\n : frame.endTime > startTime;\n}\nfunction getEarliestChunkStartTime(dataset) {\n let earliestChunkIndex = null;\n for (const chunkIndex of dataset.frameKeysByChunk.keys()) {\n if (earliestChunkIndex === null || chunkIndex < earliestChunkIndex) {\n earliestChunkIndex = chunkIndex;\n }\n }\n let startTime = null;\n for (const frameKey of dataset.frameKeysByChunk.get(earliestChunkIndex ?? 0) ?? []) {\n const storedFrame = dataset.framesByKey.get(frameKey);\n if (!storedFrame) {\n continue;\n }\n startTime = Math.min(startTime ?? storedFrame.frame.mediaTime, storedFrame.frame.mediaTime);\n }\n return startTime;\n}\nfunction replacementCanShrinkBounds(dataset, previousFrame, nextFrame) {\n return ((dataset.startTime === previousFrame.mediaTime &&\n nextFrame.mediaTime > previousFrame.mediaTime) ||\n (dataset.endTime === getFrameEndTime$1(previousFrame) &&\n getFrameEndTime$1(nextFrame) < getFrameEndTime$1(previousFrame)));\n}\nfunction recalculateBounds(dataset) {\n let startTime = null;\n let endTime = null;\n for (const { frame } of dataset.framesByKey.values()) {\n startTime = Math.min(startTime ?? frame.mediaTime, frame.mediaTime);\n endTime = Math.max(endTime ?? getFrameEndTime$1(frame), getFrameEndTime$1(frame));\n }\n dataset.startTime = startTime;\n dataset.endTime = endTime;\n}\nfunction resolveChunkDurationSeconds(options, existingDataset) {\n const chunkDurationSeconds = options.chunkDurationSeconds ??\n existingDataset?.chunkDurationSeconds ??\n DEFAULT_CHUNK_DURATION_SECONDS;\n if (chunkDurationSeconds <= 0) {\n throw new Error(\"chunkDurationSeconds must be greater than 0.\");\n }\n if (existingDataset &&\n options.chunkDurationSeconds !== undefined &&\n options.chunkDurationSeconds !== existingDataset.chunkDurationSeconds) {\n throw new Error(\"chunkDurationSeconds must match the existing detection dataset.\");\n }\n return chunkDurationSeconds;\n}\nfunction createWriteSummary(datasetId, dataset) {\n return {\n chunkCount: dataset.frameKeysByChunk.size,\n chunkDurationSeconds: dataset.chunkDurationSeconds,\n datasetId,\n detectionCount: dataset.detectionCount,\n endTime: dataset.endTime,\n frameCount: dataset.framesByKey.size,\n startTime: dataset.startTime,\n };\n}\nfunction getFrameChunkIndexes(frame, chunkDurationSeconds) {\n const startIndex = getChunkIndex(frame.mediaTime, chunkDurationSeconds);\n const endIndex = Math.max(startIndex, Math.ceil(getFrameEndTime$1(frame) / chunkDurationSeconds) - 1);\n return Array.from({ length: endIndex - startIndex + 1 }, (_, offset) => startIndex + offset);\n}\nfunction getFrameEndTime$1(frame) {\n return frame.endTime ?? frame.mediaTime;\n}\nfunction getChunkIndex(mediaTime, chunkDurationSeconds) {\n return Math.floor(mediaTime / chunkDurationSeconds);\n}\nfunction getDetectionFrameDedupeKey(frame) {\n return frame.frameIndex === undefined\n ? `time:${frame.mediaTime}`\n : `index:${frame.frameIndex}`;\n}\n/**\n * Rejects a retention floor that could not describe a real media time.\n *\n * Pruning mutates retained history in place, and `NaN` compares false against\n * every retained frame, so an unvalidated floor would silently erase a dataset\n * instead of evicting a bounded range.\n */\nfunction assertRetentionFloor(startTime) {\n if (!Number.isFinite(startTime) || startTime < 0) {\n throw new RangeError(\"pruneFrames requires a finite, non-negative startTime.\");\n }\n}\n\n/**\n * Wraps a detection source so every loaded frame that declares a source\n * coordinate space is projected into the target space.\n *\n * The target is resolved per load because media dimensions are only known once\n * the media has opened, while sources are composed before that. Frames without\n * `coordinateSpace`, and loads made before a target exists, pass through by\n * reference, so a producer already emitting media-pixel geometry pays nothing.\n *\n * The target is also handed to the wrapped source through\n * `DetectionFrameLoadOptions`. A source that flattens child frames, such as a\n * composite source, needs it to project each child while that child's own\n * `coordinateSpace` is still attached; projecting the flattened result again\n * here is then a no-op.\n *\n * Optional source hooks are forwarded only when the wrapped source implements\n * them, so capability detection keeps working through the wrapper.\n */\nfunction createProjectedDetectionFrameSource(source, resolveTarget) {\n const { getAvailableRanges, getChangesSince, getVersion, waitForRange } = source;\n return {\n async loadFrames(startTime, endTime, options) {\n const target = resolveTarget();\n const frames = await source.loadFrames(startTime, endTime, {\n ...options,\n ...(target ? { coordinateSpace: target } : {}),\n });\n return target ? projectDetectionFrames(frames, target) : frames;\n },\n ...(waitForRange\n ? {\n waitForRange: (range) => waitForRange.call(source, range),\n }\n : {}),\n ...(getAvailableRanges\n ? { getAvailableRanges: () => getAvailableRanges.call(source) }\n : {}),\n ...(getVersion\n ? {\n getVersion: (range) => getVersion.call(source, range),\n }\n : {}),\n ...(getChangesSince\n ? {\n getChangesSince: (version, ranges) => getChangesSince.call(source, version, ranges),\n }\n : {}),\n ...(source.destroy ? { destroy: () => source.destroy?.() } : {}),\n };\n}\n\nconst RANGE_EPSILON_SECONDS = 1e-6;\nconst MAX_CHANGED_RANGE_JOURNAL_LENGTH = 512;\nconst DEFAULT_LIVE_HOLD_SECONDS = 60;\nconst NO_COVERAGE_INVALIDATION = { ranges: [] };\nfunction createWritableDetectionFrameSource(options) {\n const liveHoldSeconds = resolveLiveHoldSeconds(options.live?.holdSeconds);\n let summary = null;\n let latestFrame = null;\n let heldLiveFrame = null;\n // Newest live `mediaTime` accepted so far. It survives retention so a late\n // result cannot reopen coverage the source already moved past.\n let liveFrontierTime = null;\n // Newest media time actually covered by producer-declared data. Live frames\n // are written with a synthetic hold end, so the summary end time cannot be\n // used to place the retention window without pushing it into the future.\n let coverageFrontierTime = null;\n let writeQueue = Promise.resolve();\n let version = 0;\n let allRangeVersion = 0;\n let journalFloorVersion = 0;\n let destroyed = false;\n const changedRanges = [];\n const availableRanges = [];\n const waiters = [];\n const writeOptions = (frames) => ({\n chunkDurationSeconds: options.chunkDurationSeconds,\n datasetId: options.datasetId,\n frames,\n });\n const recordRangeWrite = (nextSummary, changedSourceRanges, invalidation, prunedRange) => {\n summary = nextSummary;\n version += 1;\n for (const changedRange of changedSourceRanges) {\n changedRanges.push({ ...changedRange, version });\n recordAvailableRange(changedRange, availableRanges);\n }\n for (const invalidatedRange of invalidation.ranges) {\n // Coverage a write took away is journaled but never made available: a\n // consumer parked there has to redraw, and nothing new can be loaded.\n changedRanges.push({ ...invalidatedRange, version });\n }\n if (prunedRange) {\n // Evicting old history is a change to that range only. Recording it\n // alongside the append keeps a buffered timeline patching two small\n // ranges instead of invalidating and rebuilding everything it retains.\n changedRanges.push({ ...prunedRange, version });\n dropAvailableRangesBefore(prunedRange.endTime, availableRanges);\n }\n compactChangedRangeJournal();\n resolveCoveredWaiters();\n return nextSummary;\n };\n const recordAllRangesWrite = (nextSummary) => {\n summary = nextSummary;\n version += 1;\n allRangeVersion = version;\n journalFloorVersion = version;\n changedRanges.length = 0;\n availableRanges.length = 0;\n if (nextSummary.startTime !== null && nextSummary.endTime !== null) {\n recordAvailableRange({\n endTime: nextSummary.endTime,\n startTime: nextSummary.startTime,\n }, availableRanges);\n }\n resolveCoveredWaiters();\n return nextSummary;\n };\n return {\n datasetId: options.datasetId,\n async appendFrames(frames) {\n assertActive();\n return enqueueWrite(async () => {\n const nextSummary = await options.store.appendFrames(writeOptions(frames));\n assertActive();\n const changedSourceRanges = getDetectionFrameRanges(frames);\n const invalidation = getInvalidatedCoverage(frames, changedSourceRanges);\n recordLatestFrame(frames);\n recordCoverageFrontierFromFrames(frames);\n return retainAndRecord(nextSummary, changedSourceRanges, invalidation);\n });\n },\n async appendLiveFrame(frame) {\n assertActive();\n return enqueueWrite(async () => {\n if (isStaleLiveFrame(frame)) {\n // A result older than the live frontier no longer describes what is\n // on screen. Writing it would reopen coverage the source already\n // closed, so the newest causal frame simply wins.\n return summary ? { ...summary } : createEmptyWriteSummary();\n }\n const framesToWrite = createLiveFrameWrite(frame);\n const nextSummary = await options.store.appendFrames(writeOptions(framesToWrite));\n assertActive();\n const changedSourceRanges = getDetectionFrameRanges(framesToWrite);\n const invalidation = getInvalidatedCoverage(framesToWrite, changedSourceRanges);\n heldLiveFrame = framesToWrite.at(-1) ?? null;\n liveFrontierTime = Math.max(liveFrontierTime ?? frame.mediaTime, frame.mediaTime);\n recordLatestFrame(framesToWrite);\n // The hold end is a placeholder for \"still current\", not covered data.\n recordCoverageFrontier(getFrameEndTime(frame));\n return retainAndRecord(nextSummary, changedSourceRanges, invalidation);\n });\n },\n async finalizeCoverage(endTime) {\n assertActive();\n if (!Number.isFinite(endTime)) {\n throw new RangeError(\"finalizeCoverage requires a finite endTime.\");\n }\n return enqueueWrite(async () => {\n const frameToFinalize = latestFrame;\n if (!frameToFinalize ||\n // A frame that starts at or after the end of media cannot describe a\n // non-empty terminal interval.\n frameToFinalize.mediaTime >= endTime ||\n getFrameEndTime(frameToFinalize) === endTime) {\n return summary ? { ...summary } : null;\n }\n const previousEndTime = getFrameEndTime(frameToFinalize);\n const finalizedFrame = { ...frameToFinalize, endTime };\n const nextSummary = await options.store.appendFrames(writeOptions([finalizedFrame]));\n assertActive();\n const changedSourceRanges = [\n {\n endTime: Math.max(endTime, previousEndTime),\n startTime: frameToFinalize.mediaTime,\n },\n ];\n const invalidation = getInvalidatedCoverage([finalizedFrame], changedSourceRanges);\n latestFrame = finalizedFrame;\n if (heldLiveFrame &&\n haveSameDetectionFrameIdentity(heldLiveFrame, finalizedFrame)) {\n heldLiveFrame = finalizedFrame;\n }\n coverageFrontierTime = endTime;\n const finalSummary = await retainAndRecord(nextSummary, changedSourceRanges, \n // Finalizing usually closes at the end of media, where the tail an\n // open-ended frame gives up is unreachable. The caller may finalize\n // earlier, though, so report it rather than assume.\n invalidation);\n // Closing a held live frame shortens coverage. Reported availability\n // has to shrink with it, or a playback gate would still believe the\n // source covers time past the end of media.\n clipAvailableRangesAfter(endTime);\n return finalSummary;\n });\n },\n async replaceFrames(frames) {\n assertActive();\n return enqueueWrite(async () => {\n const nextSummary = await options.store.putFrames(writeOptions(frames));\n assertActive();\n latestFrame = null;\n heldLiveFrame = null;\n liveFrontierTime = null;\n coverageFrontierTime = null;\n recordLatestFrame(frames);\n recordCoverageFrontierFromFrames(frames);\n const retention = await applyRetention(nextSummary);\n assertActive();\n return recordAllRangesWrite(retention.kind === \"unchanged\" ? nextSummary : retention.summary);\n });\n },\n async clear() {\n assertActive();\n await enqueueWrite(async () => {\n await options.store.clearDataset(options.datasetId);\n assertActive();\n summary = null;\n latestFrame = null;\n heldLiveFrame = null;\n liveFrontierTime = null;\n coverageFrontierTime = null;\n version += 1;\n allRangeVersion = version;\n journalFloorVersion = version;\n changedRanges.length = 0;\n availableRanges.length = 0;\n });\n },\n async loadFrames(startTime, endTime) {\n assertActive();\n const loadedFrames = await options.store.loadFrames({\n datasetId: options.datasetId,\n endTime,\n startTime,\n });\n assertActive();\n return loadedFrames;\n },\n getSummary() {\n return summary ? { ...summary } : null;\n },\n getAvailableRanges() {\n return availableRanges.map((range) => ({ ...range }));\n },\n waitForRange(range) {\n if (destroyed) {\n return Promise.reject(createDestroyedError());\n }\n if (isRangeCovered(range, availableRanges)) {\n return Promise.resolve();\n }\n return new Promise((resolve, reject) => {\n waiters.push({ range, reject, resolve });\n });\n },\n getVersion(range) {\n if (!range) {\n return version;\n }\n return getRangeVersion(range);\n },\n getChangesSince(previousVersion, ranges) {\n const relevantVersion = ranges.reduce((rangeVersion, range) => Math.max(rangeVersion, getRangeVersion(range)), getReloadFloorVersion());\n if (relevantVersion <= previousVersion) {\n return {\n ranges: [],\n requiresReload: false,\n version: relevantVersion,\n };\n }\n if (previousVersion < getReloadFloorVersion()) {\n return {\n ranges: [],\n requiresReload: true,\n version: relevantVersion,\n };\n }\n // Clipped to the requested ranges: a consumer gets back exactly the part\n // of the change it asked about, and an unbounded invalidation stays a\n // bounded, loadable answer.\n const changedSourceRanges = changedRanges\n .filter((changedRange) => changedRange.version > previousVersion)\n .flatMap((changedRange) => ranges\n .filter((range) => rangesOverlap(range, changedRange))\n .map((range) => {\n const startTime = Math.max(range.startTime, changedRange.startTime);\n const endTime = Math.min(range.endTime, changedRange.endTime);\n return {\n // An unbounded ask against an unbounded invalidation would\n // otherwise hand back an infinite range, and a consumer would\n // take it straight to `loadFrames`. Nothing past the coverage\n // the source knows about can be loaded.\n endTime: Number.isFinite(endTime)\n ? endTime\n : Math.max(startTime, getKnownEndTime()),\n startTime,\n };\n }));\n return {\n ranges: mergeRanges(changedSourceRanges),\n requiresReload: false,\n version: relevantVersion,\n };\n },\n destroy() {\n if (destroyed) {\n return;\n }\n destroyed = true;\n for (const waiter of waiters) {\n waiter.reject(new Error(\"Detection frame source has been destroyed.\"));\n }\n waiters.length = 0;\n options.store.destroy?.();\n },\n };\n function assertActive() {\n if (destroyed) {\n throw createDestroyedError();\n }\n }\n /**\n * Oldest version the journal can still describe incrementally. Anything\n * older has to reload, because replacement, clearing, or journal compaction\n * dropped the intervening changes.\n */\n function getReloadFloorVersion() {\n return Math.max(allRangeVersion, journalFloorVersion);\n }\n /**\n * Latest media time the source can actually serve frames for.\n */\n function getKnownEndTime() {\n return Math.max(coverageFrontierTime ?? 0, summary?.endTime ?? 0);\n }\n function getRangeVersion(range) {\n return changedRanges.reduce((rangeVersion, changedRange) => rangesOverlap(range, changedRange)\n ? Math.max(rangeVersion, changedRange.version)\n : rangeVersion, getReloadFloorVersion());\n }\n /**\n * Runs mutating writes one at a time.\n *\n * Every write reads the held live frame, the latest frame, and the coverage\n * frontier before it awaits storage. Without serialization two concurrent\n * live appends would both observe the pre-write state and both stay open,\n * which is exactly the stale overlay live semantics exist to prevent.\n */\n function enqueueWrite(run) {\n const result = writeQueue.then(() => {\n assertActive();\n return run();\n });\n writeQueue = result.then(() => undefined, () => undefined);\n return result;\n }\n function isStaleLiveFrame(frame) {\n if (liveFrontierTime === null || frame.mediaTime > liveFrontierTime) {\n return false;\n }\n // A frame at the frontier that shares the held frame's identity is a\n // revision of the current result, not a late duplicate of an older one.\n return !(heldLiveFrame && haveSameDetectionFrameIdentity(heldLiveFrame, frame));\n }\n function recordCoverageFrontier(endTime) {\n coverageFrontierTime =\n coverageFrontierTime === null\n ? endTime\n : Math.max(coverageFrontierTime, endTime);\n }\n function recordCoverageFrontierFromFrames(frames) {\n for (const frame of frames) {\n recordCoverageFrontier(getFrameEndTime(frame));\n }\n }\n function clipAvailableRangesAfter(endTime) {\n let writeIndex = 0;\n for (const range of availableRanges) {\n if (range.startTime >= endTime) {\n continue;\n }\n availableRanges[writeIndex] = {\n endTime: Math.min(range.endTime, endTime),\n startTime: range.startTime,\n };\n writeIndex += 1;\n }\n availableRanges.length = writeIndex;\n }\n function createEmptyWriteSummary() {\n return {\n chunkCount: 0,\n chunkDurationSeconds: options.chunkDurationSeconds ?? 1,\n datasetId: options.datasetId,\n detectionCount: 0,\n endTime: null,\n frameCount: 0,\n startTime: null,\n };\n }\n /**\n * Applies retention and records the result as an incremental change when the\n * store can prune in place, or as a full rewrite when it cannot.\n */\n async function retainAndRecord(nextSummary, changedSourceRanges, invalidation = NO_COVERAGE_INVALIDATION) {\n const retention = await applyRetention(nextSummary);\n assertActive();\n if (retention.kind === \"rewritten\") {\n return recordAllRangesWrite(retention.summary);\n }\n if (retention.kind === \"unchanged\") {\n if (changedSourceRanges.length === 0 &&\n invalidation.ranges.length === 0) {\n summary = nextSummary;\n return nextSummary;\n }\n return recordRangeWrite(nextSummary, changedSourceRanges, invalidation);\n }\n forgetFramesBefore(retention.prunedRange.endTime);\n return recordRangeWrite(retention.summary, changedSourceRanges, invalidation, retention.prunedRange);\n }\n async function applyRetention(nextSummary) {\n const retention = options.retention;\n if (retention === undefined ||\n !shouldApplyWindowRetention(retention) ||\n nextSummary.endTime === null) {\n return { kind: \"unchanged\" };\n }\n const retentionWindowSeconds = retention.windowSeconds;\n if (retentionWindowSeconds === undefined ||\n !Number.isFinite(retentionWindowSeconds) ||\n retentionWindowSeconds <= 0) {\n throw new Error(\"retention.windowSeconds must be greater than 0.\");\n }\n // Place the window against real producer coverage. A live frame held open\n // for `holdSeconds` reports a summary end far in the future, and anchoring\n // eviction there would evict everything the producer just wrote.\n const retentionStartTime = Math.max(0, Math.min(coverageFrontierTime ?? nextSummary.endTime, nextSummary.endTime) - retentionWindowSeconds);\n if (nextSummary.startTime !== null &&\n nextSummary.startTime + RANGE_EPSILON_SECONDS >= retentionStartTime) {\n return { kind: \"unchanged\" };\n }\n if (options.store.pruneFrames) {\n return {\n kind: \"pruned\",\n prunedRange: {\n endTime: retentionStartTime,\n startTime: nextSummary.startTime ?? 0,\n },\n summary: await options.store.pruneFrames({\n datasetId: options.datasetId,\n startTime: retentionStartTime,\n }),\n };\n }\n // Stores without in-place pruning still get a correct retention window,\n // at the cost of reloading and rewriting everything they keep.\n const retainedFrames = await options.store.loadFrames({\n datasetId: options.datasetId,\n endTime: nextSummary.endTime,\n startTime: retentionStartTime,\n });\n return {\n kind: \"rewritten\",\n summary: await options.store.putFrames(writeOptions(retainedFrames)),\n };\n }\n /**\n * Builds the one or two frames a live append writes: the newest frame held\n * open, plus the previously held frame closed where the newest begins.\n */\n function createLiveFrameWrite(frame) {\n const openFrame = {\n ...frame,\n endTime: Math.max(frame.endTime ?? frame.mediaTime, frame.mediaTime + liveHoldSeconds),\n };\n const heldFrame = heldLiveFrame;\n // Stale results are rejected before this point, so a held frame that is\n // neither the same frame nor already closed always ends strictly later.\n if (!heldFrame ||\n haveSameDetectionFrameIdentity(heldFrame, openFrame) ||\n getFrameEndTime(heldFrame) <= frame.mediaTime) {\n return [openFrame];\n }\n return [{ ...heldFrame, endTime: frame.mediaTime }, openFrame];\n }\n function recordLatestFrame(frames) {\n for (const frame of frames) {\n if (!latestFrame ||\n compareDetectionFrames(frame, latestFrame) > 0 ||\n // A write that shares the tracked frame's identity replaces its stored\n // record. Keeping the superseded contents here would let a later\n // `finalizeCoverage` write them back over the revision.\n haveSameDetectionFrameIdentity(frame, latestFrame)) {\n latestFrame = frame;\n }\n }\n }\n /**\n * Coverage a write takes away from a frame the source already wrote.\n *\n * The journal describes a write by the interval it covers, which is enough\n * while writes only add. Rewriting a frame with a shorter interval, or at a\n * different time, instead stops covering times the new interval never\n * mentions, and a consumer parked in one of them would keep showing a frame\n * the source no longer selects.\n *\n * A frame without an `endTime` was selected with no upper bound, so what it\n * gives up is genuinely unbounded and is journaled that way. The journal\n * stays range-scoped either way: a consumer asking about times the write\n * never touched still hears that nothing changed.\n */\n function getInvalidatedCoverage(frames, coveredRanges) {\n const invalidatedRanges = [];\n for (const frame of frames) {\n for (const trackedFrame of [latestFrame, heldLiveFrame]) {\n if (!trackedFrame ||\n !haveSameDetectionFrameIdentity(trackedFrame, frame)) {\n continue;\n }\n const previousRange = {\n endTime: trackedFrame.endTime ?? Number.POSITIVE_INFINITY,\n startTime: trackedFrame.mediaTime,\n };\n const nextEndTime = frame.endTime ?? Number.POSITIVE_INFINITY;\n if (trackedFrame.mediaTime >= frame.mediaTime &&\n previousRange.endTime <= nextEndTime) {\n continue;\n }\n // A live append closes the held frame and reopens it further out in the\n // same write, so the write's own range already describes the tail. Only\n // coverage nothing else reported is worth a second journal entry.\n if (isRangeCovered(previousRange, coveredRanges)) {\n continue;\n }\n invalidatedRanges.push(previousRange);\n }\n }\n return { ranges: mergeRanges(invalidatedRanges) };\n }\n function forgetFramesBefore(startTime) {\n if (latestFrame && getFrameEndTime(latestFrame) < startTime) {\n latestFrame = null;\n }\n if (heldLiveFrame && getFrameEndTime(heldLiveFrame) < startTime) {\n heldLiveFrame = null;\n }\n }\n function resolveCoveredWaiters() {\n for (let index = waiters.length - 1; index >= 0; index -= 1) {\n const waiter = waiters[index];\n if (!waiter || !isRangeCovered(waiter.range, availableRanges)) {\n continue;\n }\n waiters.splice(index, 1);\n waiter.resolve();\n }\n }\n function compactChangedRangeJournal() {\n const overflow = changedRanges.length - MAX_CHANGED_RANGE_JOURNAL_LENGTH;\n if (overflow <= 0) {\n return;\n }\n const removedRanges = changedRanges.splice(0, overflow);\n journalFloorVersion = Math.max(journalFloorVersion, removedRanges.at(-1)?.version ?? 0);\n }\n}\nfunction createDestroyedError() {\n return new Error(\"Detection frame source has been destroyed.\");\n}\nfunction resolveLiveHoldSeconds(holdSeconds) {\n if (holdSeconds === undefined) {\n return DEFAULT_LIVE_HOLD_SECONDS;\n }\n if (!Number.isFinite(holdSeconds) || holdSeconds <= 0) {\n throw new RangeError(\"live.holdSeconds must be greater than 0.\");\n }\n return holdSeconds;\n}\nfunction getFrameEndTime(frame) {\n return frame.endTime ?? frame.mediaTime;\n}\nfunction haveSameDetectionFrameIdentity(left, right) {\n return left.frameIndex === undefined || right.frameIndex === undefined\n ? left.frameIndex === right.frameIndex && left.mediaTime === right.mediaTime\n : left.frameIndex === right.frameIndex;\n}\nfunction compareDetectionFrames(left, right) {\n if (left.mediaTime !== right.mediaTime) {\n return left.mediaTime - right.mediaTime;\n }\n return (left.frameIndex ?? 0) - (right.frameIndex ?? 0);\n}\nfunction dropAvailableRangesBefore(startTime, availableRanges) {\n let writeIndex = 0;\n for (const range of availableRanges) {\n if (range.endTime <= startTime) {\n continue;\n }\n availableRanges[writeIndex] = {\n endTime: range.endTime,\n startTime: Math.max(range.startTime, startTime),\n };\n writeIndex += 1;\n }\n availableRanges.length = writeIndex;\n}\nfunction shouldApplyWindowRetention(retention) {\n return (retention?.mode === DetectionFrameRetentionMode.PersistWindow ||\n retention?.mode === DetectionFrameRetentionMode.MemoryOnly);\n}\nfunction getDetectionFrameRanges(frames) {\n return mergeRanges(frames.map((frame) => ({\n endTime: frame.endTime ?? frame.mediaTime,\n startTime: frame.mediaTime,\n })));\n}\nfunction rangesOverlap(left, right) {\n return left.startTime <= right.endTime && right.startTime <= left.endTime;\n}\nfunction mergeRanges(ranges) {\n const sortedRanges = ranges\n .map((range) => ({ ...range }))\n .sort((left, right) => left.startTime - right.startTime);\n const mergedRanges = [];\n for (const range of sortedRanges) {\n const previousRange = mergedRanges.at(-1);\n if (previousRange &&\n range.startTime <= previousRange.endTime + RANGE_EPSILON_SECONDS) {\n previousRange.endTime = Math.max(previousRange.endTime, range.endTime);\n continue;\n }\n mergedRanges.push({ ...range });\n }\n return mergedRanges;\n}\nfunction recordAvailableRange(range, availableRanges) {\n if (range.endTime < range.startTime) {\n return;\n }\n availableRanges.push({ ...range });\n availableRanges.sort((left, right) => left.startTime - right.startTime);\n let writeIndex = 0;\n for (const nextRange of availableRanges) {\n const currentRange = availableRanges[writeIndex - 1];\n if (currentRange &&\n nextRange.startTime <= currentRange.endTime + RANGE_EPSILON_SECONDS) {\n currentRange.endTime = Math.max(currentRange.endTime, nextRange.endTime);\n continue;\n }\n availableRanges[writeIndex] = { ...nextRange };\n writeIndex += 1;\n }\n availableRanges.length = writeIndex;\n}\nfunction isRangeCovered(range, availableRanges) {\n return availableRanges.some((availableRange) => availableRange.startTime <= range.startTime + RANGE_EPSILON_SECONDS &&\n availableRange.endTime + RANGE_EPSILON_SECONDS >= range.endTime);\n}\n\n/**\n * Creates one stateful SORT tracker for a single ordered media sequence.\n *\n * Defaults, lifecycle semantics, and observation-only output mirror\n * roboflow/trackers SORT. Motion predictions remain internal to association.\n */\nfunction createSortTracker$1(options = {}) {\n const lostTrackBuffer = normalizeNonNegativeInteger$1(options.lostTrackBuffer ?? 30, \"lostTrackBuffer\");\n const frameRate = options.frameRate ?? 30;\n const trackActivationThreshold = options.trackActivationThreshold ?? 0.25;\n const minimumConsecutiveFrames = normalizePositiveInteger$1(options.minimumConsecutiveFrames ?? 3, \"minimumConsecutiveFrames\");\n const minimumIouThreshold = options.minimumIouThreshold ?? 0.3;\n if (!Number.isFinite(frameRate) || frameRate <= 0) {\n throw new Error(\"frameRate must be a finite positive value.\");\n }\n normalizeUnitInterval$1(trackActivationThreshold, \"trackActivationThreshold\");\n normalizeUnitInterval$1(minimumIouThreshold, \"minimumIouThreshold\");\n const scaledLostTrackBuffer = (frameRate / 30) * lostTrackBuffer;\n if (!Number.isFinite(scaledLostTrackBuffer)) {\n throw new Error(\"Scaled lostTrackBuffer overflows: frameRate / 30 * lostTrackBuffer must be finite.\");\n }\n const maximumFramesWithoutUpdate = lostTrackBuffer === 0 ? 0 : Math.max(1, Math.ceil(scaledLostTrackBuffer));\n let tracks = [];\n let nextTrackerId = 0;\n let previousFrameIndex;\n return {\n reset() {\n tracks = [];\n nextTrackerId = 0;\n previousFrameIndex = undefined;\n },\n update(detections, frameIndex) {\n const frameStep = resolveFrameStep(frameIndex, previousFrameIndex);\n previousFrameIndex = frameIndex ?? previousFrameIndex;\n const predicted = tracks.map((track) => track.predict(frameStep, frameRate));\n const { matches, unmatchedDetections } = associateDetectionsToTracks(detections, predicted, minimumIouThreshold);\n const trackerIds = new Map();\n for (const match of matches) {\n const track = tracks[match.trackIndex];\n const detection = detections[match.detectionIndex];\n track.update(detection);\n if (track.trackerId === undefined &&\n track.successfulUpdates >= minimumConsecutiveFrames) {\n track.trackerId = nextTrackerId;\n nextTrackerId += 1;\n }\n if (track.trackerId !== undefined) {\n trackerIds.set(detection.detectionIndex, track.trackerId);\n }\n }\n for (const detectionIndex of unmatchedDetections) {\n const detection = detections[detectionIndex];\n if ((detection.confidence ?? 1) >= trackActivationThreshold) {\n tracks.push(new KalmanBoxTrack(detection));\n }\n }\n tracks = tracks.filter((track) => track.timeSinceUpdate <= maximumFramesWithoutUpdate &&\n (track.successfulUpdates >= minimumConsecutiveFrames ||\n track.timeSinceUpdate === 0));\n return {\n activeTrackCount: tracks.length,\n assignments: detections.flatMap((detection) => {\n const trackerId = trackerIds.get(detection.detectionIndex);\n return trackerId === undefined\n ? []\n : [{ detectionIndex: detection.detectionIndex, trackerId }];\n }),\n confirmedTrackCount: tracks.filter((track) => track.trackerId !== undefined).length,\n };\n },\n };\n}\nclass KalmanBoxTrack {\n consecutiveUpdates;\n trackerId;\n successfulUpdates = 1;\n timeSinceUpdate = 0;\n state;\n covariance = identity(8);\n constructor(detection, consecutiveUpdates = false) {\n this.consecutiveUpdates = consecutiveUpdates;\n this.state = [...rectToXyxy(detection.rect), 0, 0, 0, 0].map((value) => [\n value,\n ]);\n }\n predict(frameStep, frameRate) {\n if (this.consecutiveUpdates && this.timeSinceUpdate > 0) {\n this.successfulUpdates = 0;\n }\n const transition = identity(8);\n for (let index = 0; index < 4; index += 1) {\n transition[index][index + 4] = frameStep;\n }\n this.state = multiply(transition, this.state);\n this.covariance = add(multiply(multiply(transition, this.covariance), transpose(transition)), createProcessNoise(frameStep, frameRate));\n // Python SORT counts update calls for the fixed-rate lost-track budget.\n this.timeSinceUpdate += 1;\n return stateToRect(this.state);\n }\n update(detection) {\n const measurement = rectToXyxy(detection.rect).map((value) => [value]);\n const observation = [\n [1, 0, 0, 0, 0, 0, 0, 0],\n [0, 1, 0, 0, 0, 0, 0, 0],\n [0, 0, 1, 0, 0, 0, 0, 0],\n [0, 0, 0, 1, 0, 0, 0, 0],\n ];\n const measurementNoise = scale(identity(4), 0.1);\n const innovation = subtract(measurement, multiply(observation, this.state));\n const covarianceObservationTranspose = multiply(this.covariance, transpose(observation));\n const innovationCovariance = add(multiply(observation, covarianceObservationTranspose), measurementNoise);\n const gain = multiply(covarianceObservationTranspose, inverse(innovationCovariance));\n this.state = add(this.state, multiply(gain, innovation));\n const identityMinusGainObservation = subtract(identity(8), multiply(gain, observation));\n // Joseph form matches the Python implementation and is more stable.\n this.covariance = add(multiply(multiply(identityMinusGainObservation, this.covariance), transpose(identityMinusGainObservation)), multiply(multiply(gain, measurementNoise), transpose(gain)));\n this.timeSinceUpdate = 0;\n this.successfulUpdates += 1;\n }\n getStateRect() {\n return stateToRect(this.state);\n }\n}\nfunction associateDetectionsToTracks(detections, predicted, minimumIouThreshold) {\n if (predicted.length === 0 || detections.length === 0) {\n return {\n matches: [],\n unmatchedDetections: detections.map((_, index) => index),\n };\n }\n // Python SORT associates all detections class-agnostically using standard IoU.\n const scores = predicted.map((rect) => detections.map((detection) => intersectionOverUnion(rect, detection.rect)));\n const candidateMatches = maximizeAssignment(scores);\n const matches = [];\n const matchedDetections = new Set();\n for (const [trackIndex, detectionIndex] of candidateMatches) {\n if (scores[trackIndex][detectionIndex] < minimumIouThreshold)\n continue;\n matches.push({ detectionIndex, trackIndex });\n matchedDetections.add(detectionIndex);\n }\n matches.sort((left, right) => left.trackIndex - right.trackIndex);\n return {\n matches,\n unmatchedDetections: detections.flatMap((_, index) => matchedDetections.has(index) ? [] : [index]),\n };\n}\n/** Hungarian assignment for a rectangular score matrix. */\nfunction maximizeAssignment(scores) {\n if (scores.length === 0 || scores[0]?.length === 0) {\n return [];\n }\n const rowCount = scores.length;\n const columnCount = scores[0].length;\n const transposed = rowCount > columnCount;\n const costs = transposed\n ? Array.from({ length: columnCount }, (_, row) => Array.from({ length: rowCount }, (_, column) => 1 - scores[column][row]))\n : scores.map((row) => row.map((score) => 1 - score));\n const rows = costs.length;\n const columns = costs[0].length;\n const u = new Array(rows + 1).fill(0);\n const v = new Array(columns + 1).fill(0);\n const p = new Array(columns + 1).fill(0);\n const way = new Array(columns + 1).fill(0);\n for (let row = 1; row <= rows; row += 1) {\n p[0] = row;\n let column0 = 0;\n const minValue = new Array(columns + 1).fill(Number.POSITIVE_INFINITY);\n const used = new Array(columns + 1).fill(false);\n do {\n used[column0] = true;\n const row0 = p[column0];\n let delta = Number.POSITIVE_INFINITY;\n let column1 = 0;\n for (let column = 1; column <= columns; column += 1) {\n if (used[column])\n continue;\n const current = costs[row0 - 1][column - 1] - u[row0] - v[column];\n if (current < minValue[column]) {\n minValue[column] = current;\n way[column] = column0;\n }\n if (minValue[column] < delta) {\n delta = minValue[column];\n column1 = column;\n }\n }\n for (let column = 0; column <= columns; column += 1) {\n if (used[column]) {\n u[p[column]] += delta;\n v[column] -= delta;\n }\n else {\n minValue[column] -= delta;\n }\n }\n column0 = column1;\n } while (p[column0] !== 0);\n do {\n const column1 = way[column0];\n p[column0] = p[column1];\n column0 = column1;\n } while (column0 !== 0);\n }\n const assignments = [];\n for (let column = 1; column <= columns; column += 1) {\n if (p[column] === 0)\n continue;\n const row = p[column] - 1;\n assignments.push(transposed ? [column - 1, row] : [row, column - 1]);\n }\n return assignments;\n}\nfunction rectToXyxy(rect) {\n return [\n rect.x - rect.width / 2,\n rect.y - rect.height / 2,\n rect.x + rect.width / 2,\n rect.y + rect.height / 2,\n ];\n}\nfunction stateToRect(state) {\n // The Python XYXY estimator intentionally leaves corner velocities\n // unconstrained. Normalize only at the browser Rect boundary so a crossing\n // prediction cannot violate the positive-width/height storage contract.\n const x1 = Math.min(state[0][0], state[2][0]);\n const y1 = Math.min(state[1][0], state[3][0]);\n const x2 = Math.max(state[0][0], state[2][0]);\n const y2 = Math.max(state[1][0], state[3][0]);\n return {\n height: Math.max(Number.EPSILON, y2 - y1),\n width: Math.max(Number.EPSILON, x2 - x1),\n x: (x1 + x2) / 2,\n y: (y1 + y2) / 2,\n };\n}\nfunction createProcessNoise(frameStep, frameRate) {\n if (Math.abs(frameStep - 1) <= 0.004 * frameRate) {\n return scale(identity(8), 0.01);\n }\n const result = Array.from({ length: 8 }, () => new Array(8).fill(0));\n const dt2 = frameStep * frameStep;\n const dt3 = dt2 * frameStep;\n const dt4 = dt2 * dt2;\n for (let index = 0; index < 4; index += 1) {\n const velocityIndex = index + 4;\n result[index][index] = (0.01 * dt4) / 4;\n result[index][velocityIndex] = (0.01 * dt3) / 2;\n result[velocityIndex][index] = (0.01 * dt3) / 2;\n result[velocityIndex][velocityIndex] = 0.01 * dt2;\n }\n return result;\n}\nfunction intersectionOverUnion(left, right) {\n const leftX = left.x - left.width / 2;\n const leftY = left.y - left.height / 2;\n const rightX = right.x - right.width / 2;\n const rightY = right.y - right.height / 2;\n const intersectionWidth = Math.max(0, Math.min(leftX + left.width, rightX + right.width) -\n Math.max(leftX, rightX));\n const intersectionHeight = Math.max(0, Math.min(leftY + left.height, rightY + right.height) -\n Math.max(leftY, rightY));\n const intersection = intersectionWidth * intersectionHeight;\n const union = left.width * left.height + right.width * right.height - intersection;\n return union <= 0 ? 0 : intersection / union;\n}\nfunction resolveFrameStep(current, previous) {\n if (current === undefined || previous === undefined)\n return 1;\n return Math.max(1, current - previous);\n}\nfunction normalizePositiveInteger$1(value, label) {\n if (!Number.isInteger(value) || value < 1) {\n throw new Error(`${label} must be a positive integer.`);\n }\n return value;\n}\nfunction normalizeNonNegativeInteger$1(value, label) {\n if (!Number.isInteger(value) || value < 0) {\n throw new Error(`${label} must be a non-negative integer.`);\n }\n return value;\n}\nfunction normalizeUnitInterval$1(value, label) {\n if (!Number.isFinite(value) || value < 0 || value > 1) {\n throw new Error(`${label} must be between 0 and 1.`);\n }\n}\nfunction identity(size) {\n return Array.from({ length: size }, (_, row) => Array.from({ length: size }, (_, column) => (row === column ? 1 : 0)));\n}\nfunction scale(matrix, factor) {\n return matrix.map((row) => row.map((value) => value * factor));\n}\nfunction transpose(matrix) {\n return matrix[0].map((_, column) => matrix.map((row) => row[column]));\n}\nfunction add(left, right) {\n return left.map((row, rowIndex) => row.map((value, columnIndex) => value + right[rowIndex][columnIndex]));\n}\nfunction subtract(left, right) {\n return left.map((row, rowIndex) => row.map((value, columnIndex) => value - right[rowIndex][columnIndex]));\n}\nfunction multiply(left, right) {\n return left.map((row) => right[0].map((_, column) => row.reduce((sum, value, index) => sum + value * right[index][column], 0)));\n}\nfunction inverse(matrix) {\n const size = matrix.length;\n const augmented = matrix.map((row, index) => [\n ...row,\n ...identity(size)[index],\n ]);\n for (let column = 0; column < size; column += 1) {\n let pivot = column;\n for (let row = column + 1; row < size; row += 1) {\n if (Math.abs(augmented[row][column]) >\n Math.abs(augmented[pivot][column])) {\n pivot = row;\n }\n }\n if (Math.abs(augmented[pivot][column]) < 1e-12) {\n throw new Error(\"SORT Kalman covariance is singular.\");\n }\n [augmented[column], augmented[pivot]] = [\n augmented[pivot],\n augmented[column],\n ];\n const divisor = augmented[column][column];\n augmented[column] = augmented[column].map((value) => value / divisor);\n for (let row = 0; row < size; row += 1) {\n if (row === column)\n continue;\n const factor = augmented[row][column];\n augmented[row] = augmented[row].map((value, index) => value - factor * augmented[column][index]);\n }\n }\n return augmented.map((row) => row.slice(size));\n}\n\n/**\n * Creates one stateful ByteTrack tracker for a single ordered media sequence.\n *\n * Defaults and two-stage association mirror roboflow/trackers ByteTrack at\n * source commit 60b21c8a48676784085fbee455559f16b75a7c9a.\n * Motion predictions remain internal; only observed detections receive IDs.\n */\nfunction createByteTrackTracker$1(options = {}) {\n const lostTrackBuffer = normalizeNonNegativeInteger$1(options.lostTrackBuffer ?? 30, \"lostTrackBuffer\");\n const frameRate = options.frameRate ?? 30;\n const trackActivationThreshold = options.trackActivationThreshold ?? 0.7;\n const minimumConsecutiveFrames = normalizePositiveInteger$1(options.minimumConsecutiveFrames ?? 2, \"minimumConsecutiveFrames\");\n const minimumIouThreshold = options.minimumIouThreshold ?? 0.1;\n const highConfidenceDetectionThreshold = options.highConfidenceDetectionThreshold ?? 0.6;\n if (!Number.isFinite(frameRate) || frameRate <= 0) {\n throw new Error(\"frameRate must be a finite positive value.\");\n }\n normalizeUnitInterval$1(trackActivationThreshold, \"trackActivationThreshold\");\n normalizeUnitInterval$1(minimumIouThreshold, \"minimumIouThreshold\");\n normalizeUnitInterval$1(highConfidenceDetectionThreshold, \"highConfidenceDetectionThreshold\");\n const scaledLostTrackBuffer = (frameRate / 30) * lostTrackBuffer;\n if (!Number.isFinite(scaledLostTrackBuffer)) {\n throw new Error(\"Scaled lostTrackBuffer overflows: frameRate / 30 * lostTrackBuffer must be finite.\");\n }\n const maximumFramesWithoutUpdate = lostTrackBuffer === 0 ? 0 : Math.max(1, Math.ceil(scaledLostTrackBuffer));\n let tracks = [];\n let nextTrackerId = 0;\n let previousFrameIndex;\n return {\n reset() {\n tracks = [];\n nextTrackerId = 0;\n previousFrameIndex = undefined;\n },\n update(detections, frameIndex) {\n const frameStep = resolveFrameStep(frameIndex, previousFrameIndex);\n previousFrameIndex = frameIndex ?? previousFrameIndex;\n tracks.forEach((track) => track.predict(frameStep, frameRate));\n const highDetectionIndexes = [];\n const lowDetectionIndexes = [];\n detections.forEach((detection, index) => {\n if ((detection.confidence ?? 1) >= highConfidenceDetectionThreshold) {\n highDetectionIndexes.push(index);\n }\n else {\n lowDetectionIndexes.push(index);\n }\n });\n const assignments = new Map();\n const firstStage = associate(tracks, detections, highDetectionIndexes, minimumIouThreshold);\n for (const match of firstStage.matches) {\n updateMatchedTrack(tracks[match.trackIndex], detections[match.detectionIndex], assignments);\n }\n const remainingTracks = firstStage.unmatchedTrackIndexes.map((index) => tracks[index]);\n const secondStage = associate(remainingTracks, detections, lowDetectionIndexes, minimumIouThreshold);\n for (const match of secondStage.matches) {\n updateMatchedTrack(remainingTracks[match.trackIndex], detections[match.detectionIndex], assignments);\n }\n // Only unmatched high-confidence observations can start a track.\n for (const detectionIndex of firstStage.unmatchedDetectionIndexes) {\n const detection = detections[detectionIndex];\n if ((detection.confidence ?? 1) >= trackActivationThreshold) {\n tracks.push(new KalmanBoxTrack(detection, true));\n }\n }\n // Confirmation is sticky once an ID has been allocated. An unmatched\n // unconfirmed track is discarded immediately, as in Python ByteTrack.\n tracks = tracks.filter((track) => track.timeSinceUpdate <= maximumFramesWithoutUpdate &&\n (track.trackerId !== undefined ||\n track.successfulUpdates >= minimumConsecutiveFrames ||\n track.timeSinceUpdate === 0));\n return {\n activeTrackCount: tracks.length,\n assignments: detections.flatMap((detection) => {\n const trackerId = assignments.get(detection.detectionIndex);\n return trackerId === undefined\n ? []\n : [{ detectionIndex: detection.detectionIndex, trackerId }];\n }),\n confirmedTrackCount: tracks.filter((track) => track.trackerId !== undefined).length,\n };\n },\n };\n function updateMatchedTrack(track, detection, assignments) {\n track.update(detection);\n if (track.trackerId === undefined &&\n track.successfulUpdates >= minimumConsecutiveFrames) {\n track.trackerId = nextTrackerId;\n nextTrackerId += 1;\n }\n if (track.trackerId !== undefined) {\n assignments.set(detection.detectionIndex, track.trackerId);\n }\n }\n}\nfunction associate(tracks, detections, detectionIndexes, minimumIouThreshold) {\n if (tracks.length === 0 || detectionIndexes.length === 0) {\n return {\n matches: [],\n unmatchedDetectionIndexes: [...detectionIndexes],\n unmatchedTrackIndexes: tracks.map((_, index) => index),\n };\n }\n const scores = tracks.map((track) => {\n const predictedRect = track.getStateRect();\n return detectionIndexes.map((detectionIndex) => intersectionOverUnion(predictedRect, detections[detectionIndex].rect));\n });\n const matches = [];\n const matchedTracks = new Set();\n const matchedDetections = new Set();\n for (const [trackIndex, localDetectionIndex] of maximizeAssignment(scores)) {\n if (scores[trackIndex][localDetectionIndex] < minimumIouThreshold) {\n continue;\n }\n const detectionIndex = detectionIndexes[localDetectionIndex];\n matches.push({ detectionIndex, trackIndex });\n matchedTracks.add(trackIndex);\n matchedDetections.add(detectionIndex);\n }\n matches.sort((left, right) => left.trackIndex - right.trackIndex);\n return {\n matches,\n unmatchedDetectionIndexes: detectionIndexes.filter((index) => !matchedDetections.has(index)),\n unmatchedTrackIndexes: tracks.flatMap((_, index) => matchedTracks.has(index) ? [] : [index]),\n };\n}\n\nfunction associateTrackingScores(scores, trackCount, detectionCount, minimumScore, acceptanceScores = scores) {\n const matches = [];\n const matchedTracks = new Set();\n const matchedDetections = new Set();\n for (const [trackIndex, detectionIndex] of maximizeAssignment(scores)) {\n if (acceptanceScores[trackIndex][detectionIndex] < minimumScore)\n continue;\n matches.push({ detectionIndex, trackIndex });\n matchedTracks.add(trackIndex);\n matchedDetections.add(detectionIndex);\n }\n matches.sort((left, right) => left.trackIndex - right.trackIndex);\n return {\n matches,\n unmatchedDetectionIndexes: Array.from({ length: detectionCount }, (_, index) => index).filter((index) => !matchedDetections.has(index)),\n unmatchedTrackIndexes: Array.from({ length: trackCount }, (_, index) => index).filter((index) => !matchedTracks.has(index)),\n };\n}\nfunction pairwiseIou(tracks, detections, bufferRatio = 0) {\n return tracks.map((track) => detections.map((detection) => bufferedIntersectionOverUnion(track, detection, bufferRatio)));\n}\nfunction bufferedIntersectionOverUnion(left, right, bufferRatio) {\n const leftWidth = left.width * (1 + 2 * bufferRatio);\n const leftHeight = left.height * (1 + 2 * bufferRatio);\n const rightWidth = right.width * (1 + 2 * bufferRatio);\n const rightHeight = right.height * (1 + 2 * bufferRatio);\n const leftX = left.x - leftWidth / 2;\n const leftY = left.y - leftHeight / 2;\n const rightX = right.x - rightWidth / 2;\n const rightY = right.y - rightHeight / 2;\n const intersectionWidth = Math.max(0, Math.min(leftX + leftWidth, rightX + rightWidth) - Math.max(leftX, rightX));\n const intersectionHeight = Math.max(0, Math.min(leftY + leftHeight, rightY + rightHeight) -\n Math.max(leftY, rightY));\n const intersection = intersectionWidth * intersectionHeight;\n const union = leftWidth * leftHeight + rightWidth * rightHeight - intersection;\n return union <= 0 ? 0 : intersection / union;\n}\n\n/**\n * Kalman state estimator shared by the browser C-BIoU and OC-SORT ports.\n * Its layouts, covariance update, and gap-scaled constant-velocity model\n * mirror roboflow/trackers at 60b21c8.\n */\nclass TrackingKalmanEstimator {\n representation;\n dimension;\n measurementDimension = 4;\n covariance;\n measurementNoise = identity(4);\n processNoise;\n state;\n baselineProcessNoise;\n positionIndexes;\n velocityIndexes;\n constructor(initialRect, representation) {\n this.representation = representation;\n const measurement = rectToMeasurement(initialRect, representation);\n this.dimension = representation === \"xcycsr\" ? 7 : 8;\n this.positionIndexes =\n representation === \"xcycsr\" ? [0, 1, 2] : [0, 1, 2, 3];\n this.velocityIndexes =\n representation === \"xcycsr\" ? [4, 5, 6] : [4, 5, 6, 7];\n this.state = Array.from({ length: this.dimension }, (_, index) => [\n measurement[index] ?? 0,\n ]);\n this.covariance = identity(this.dimension);\n this.processNoise = identity(this.dimension);\n this.baselineProcessNoise = identity(this.dimension);\n }\n predict(frameStep, frameRate) {\n if (this.representation === \"xcycsr\" &&\n this.state[2][0] + frameStep * this.state[6][0] <= 0) {\n this.state[6][0] = 0;\n }\n const transition = identity(this.dimension);\n this.positionIndexes.forEach((positionIndex, index) => {\n transition[positionIndex][this.velocityIndexes[index]] = frameStep;\n });\n this.processNoise = this.createProcessNoise(frameStep, frameRate);\n this.state = multiply(transition, this.state);\n this.covariance = add(multiply(multiply(transition, this.covariance), transpose(transition)), this.processNoise);\n }\n update(rect) {\n this.updateMeasurement(rectToMeasurement(rect, this.representation));\n }\n updateMeasurement(measurement) {\n const observation = Array.from({ length: 4 }, (_, row) => Array.from({ length: this.dimension }, (_, column) => row === column ? 1 : 0));\n const measurementColumn = measurement.map((value) => [value]);\n const innovation = subtract(measurementColumn, multiply(observation, this.state));\n const covarianceObservationTranspose = multiply(this.covariance, transpose(observation));\n const innovationCovariance = add(multiply(observation, covarianceObservationTranspose), this.measurementNoise);\n const gain = multiply(covarianceObservationTranspose, inverse(innovationCovariance));\n this.state = add(this.state, multiply(gain, innovation));\n const identityMinusGainObservation = subtract(identity(this.dimension), multiply(gain, observation));\n this.covariance = add(multiply(multiply(identityMinusGainObservation, this.covariance), transpose(identityMinusGainObservation)), multiply(multiply(gain, this.measurementNoise), transpose(gain)));\n }\n getRect() {\n return measurementToRect(this.state.slice(0, 4).map((row) => row[0]), this.representation);\n }\n setCovariances(options) {\n if (options.covariance)\n this.covariance = clone(options.covariance);\n if (options.measurementNoise) {\n this.measurementNoise = clone(options.measurementNoise);\n }\n if (options.processNoise) {\n this.processNoise = clone(options.processNoise);\n this.baselineProcessNoise = clone(options.processNoise);\n }\n }\n snapshot() {\n return {\n covariance: clone(this.covariance),\n measurementNoise: clone(this.measurementNoise),\n processNoise: clone(this.processNoise),\n state: clone(this.state),\n };\n }\n restore(snapshot) {\n this.covariance = clone(snapshot.covariance);\n this.measurementNoise = clone(snapshot.measurementNoise);\n this.processNoise = clone(snapshot.processNoise);\n this.state = clone(snapshot.state);\n }\n createProcessNoise(frameStep, frameRate) {\n if (Math.abs(frameStep - 1) <= 0.004 * frameRate) {\n return clone(this.baselineProcessNoise);\n }\n const result = Array.from({ length: this.dimension }, () => new Array(this.dimension).fill(0));\n const dt2 = frameStep * frameStep;\n const dt3 = dt2 * frameStep;\n const dt4 = dt2 * dt2;\n const kinematicIndexes = new Set([\n ...this.positionIndexes,\n ...this.velocityIndexes,\n ]);\n this.positionIndexes.forEach((positionIndex, index) => {\n const velocityIndex = this.velocityIndexes[index];\n const accelerationVariance = this.baselineProcessNoise[velocityIndex][velocityIndex];\n result[positionIndex][positionIndex] = (accelerationVariance * dt4) / 4;\n result[positionIndex][velocityIndex] = (accelerationVariance * dt3) / 2;\n result[velocityIndex][positionIndex] = (accelerationVariance * dt3) / 2;\n result[velocityIndex][velocityIndex] = accelerationVariance * dt2;\n });\n for (let index = 0; index < this.dimension; index += 1) {\n if (!kinematicIndexes.has(index)) {\n result[index][index] = this.baselineProcessNoise[index][index];\n }\n }\n return result;\n }\n}\nfunction rectToMeasurement(rect, representation) {\n if (representation === \"xcycwh\") {\n return [rect.x, rect.y, rect.width, rect.height];\n }\n return [\n rect.x,\n rect.y,\n rect.width * rect.height,\n rect.width / (rect.height + 1e-6),\n ];\n}\nfunction measurementToRect(measurement, representation) {\n const [x, y, third, fourth] = measurement;\n if (representation === \"xcycwh\") {\n return {\n height: Math.max(1e-3, fourth),\n width: Math.max(1e-3, third),\n x,\n y,\n };\n }\n const width = Math.sqrt(third * fourth);\n const height = width === 0 ? 0 : third / width;\n return {\n height: Math.max(Number.EPSILON, height),\n width: Math.max(Number.EPSILON, width),\n x,\n y,\n };\n}\nfunction diagonal(values) {\n return values.map((value, row) => values.map((_, column) => (row === column ? value : 0)));\n}\nfunction scaleMatrix(matrix, factor) {\n return scale(matrix, factor);\n}\nfunction clone(matrix) {\n return matrix.map((row) => [...row]);\n}\n\nconst MINIMUM_DETECTION_CONFIDENCE = 0.1;\nclass CBIoUTrack {\n trackerId;\n successfulUpdates = 1;\n timeSinceUpdate = 0;\n estimator;\n constructor(initial) {\n this.estimator = new TrackingKalmanEstimator(initial.rect, \"xcycwh\");\n this.setInitialNoise(initial.rect.width, initial.rect.height);\n }\n predict(frameStep, frameRate) {\n const current = this.estimator.getRect();\n this.estimator.setCovariances({\n processNoise: this.buildProcessNoise(Math.max(current.width, 1e-3), Math.max(current.height, 1e-3)),\n });\n this.estimator.predict(frameStep, frameRate);\n this.clampState();\n this.timeSinceUpdate += 1;\n }\n update(detection) {\n const current = this.estimator.getRect();\n this.estimator.setCovariances({\n measurementNoise: this.buildMeasurementNoise(Math.max(current.width, 1e-3), Math.max(current.height, 1e-3)),\n });\n this.estimator.update(detection.rect);\n this.clampState();\n this.timeSinceUpdate = 0;\n this.successfulUpdates += 1;\n }\n getRect() {\n return this.estimator.getRect();\n }\n setInitialNoise(width, height) {\n const sigmaPosition = 0.05;\n const sigmaVelocity = 0.00625;\n this.estimator.setCovariances({\n covariance: diagonal([\n (2 * sigmaPosition * width) ** 2,\n (2 * sigmaPosition * height) ** 2,\n (2 * sigmaPosition * width) ** 2,\n (2 * sigmaPosition * height) ** 2,\n (10 * sigmaVelocity * width) ** 2,\n (10 * sigmaVelocity * height) ** 2,\n (10 * sigmaVelocity * width) ** 2,\n (10 * sigmaVelocity * height) ** 2,\n ]),\n measurementNoise: this.buildMeasurementNoise(width, height),\n processNoise: this.buildProcessNoise(width, height),\n });\n }\n buildProcessNoise(width, height) {\n const sigmaPosition = 0.05;\n const sigmaVelocity = 0.00625;\n return diagonal([\n (sigmaPosition * width) ** 2,\n (sigmaPosition * height) ** 2,\n (sigmaPosition * width) ** 2,\n (sigmaPosition * height) ** 2,\n (sigmaVelocity * width) ** 2,\n (sigmaVelocity * height) ** 2,\n (sigmaVelocity * width) ** 2,\n (sigmaVelocity * height) ** 2,\n ]);\n }\n buildMeasurementNoise(width, height) {\n const sigmaMeasurement = 0.05;\n return diagonal([\n (sigmaMeasurement * width) ** 2,\n (sigmaMeasurement * height) ** 2,\n (sigmaMeasurement * width) ** 2,\n (sigmaMeasurement * height) ** 2,\n ]);\n }\n clampState() {\n this.estimator.state[2][0] = Math.max(this.estimator.state[2][0], 1e-3);\n this.estimator.state[3][0] = Math.max(this.estimator.state[3][0], 1e-3);\n }\n}\n/** Creates the detection-only C-BIoU implementation from roboflow/trackers. */\nfunction createCBIoUTracker$1(options = {}) {\n const lostTrackBuffer = normalizeNonNegativeInteger$1(options.lostTrackBuffer ?? 30, \"lostTrackBuffer\");\n const frameRate = options.frameRate ?? 30;\n const trackActivationThreshold = options.trackActivationThreshold ?? 0.7;\n const minimumConsecutiveFrames = normalizePositiveInteger$1(options.minimumConsecutiveFrames ?? 2, \"minimumConsecutiveFrames\");\n const minimumIouThresholdFirstAssociation = options.minimumIouThresholdFirstAssociation ?? 0.2;\n const minimumIouThresholdSecondAssociation = options.minimumIouThresholdSecondAssociation ?? 0.5;\n const minimumIouThresholdUnconfirmedAssociation = options.minimumIouThresholdUnconfirmedAssociation ?? 0.3;\n const highConfidenceDetectionThreshold = options.highConfidenceDetectionThreshold ?? 0.6;\n const instantFirstFrameActivation = options.instantFirstFrameActivation ?? true;\n const bufferRatioFirst = options.bufferRatioFirst ?? 0.3;\n const bufferRatioSecond = options.bufferRatioSecond ?? 0.5;\n if (!Number.isFinite(frameRate) || frameRate <= 0) {\n throw new Error(\"frameRate must be a finite positive value.\");\n }\n normalizeUnitInterval$1(trackActivationThreshold, \"trackActivationThreshold\");\n normalizeUnitInterval$1(minimumIouThresholdFirstAssociation, \"minimumIouThresholdFirstAssociation\");\n normalizeUnitInterval$1(minimumIouThresholdSecondAssociation, \"minimumIouThresholdSecondAssociation\");\n normalizeUnitInterval$1(minimumIouThresholdUnconfirmedAssociation, \"minimumIouThresholdUnconfirmedAssociation\");\n normalizeUnitInterval$1(highConfidenceDetectionThreshold, \"highConfidenceDetectionThreshold\");\n if (!Number.isFinite(bufferRatioFirst) || bufferRatioFirst < 0) {\n throw new Error(\"bufferRatioFirst must be a finite non-negative value.\");\n }\n if (!Number.isFinite(bufferRatioSecond) || bufferRatioSecond < 0) {\n throw new Error(\"bufferRatioSecond must be a finite non-negative value.\");\n }\n const scaledLostTrackBuffer = (frameRate / 30) * lostTrackBuffer;\n if (!Number.isFinite(scaledLostTrackBuffer)) {\n throw new Error(\"Scaled lostTrackBuffer overflows: frameRate / 30 * lostTrackBuffer must be finite.\");\n }\n const maximumFramesWithoutUpdate = lostTrackBuffer === 0 ? 0 : Math.max(1, Math.ceil(scaledLostTrackBuffer));\n let tracks = [];\n let nextTrackerId = 0;\n let frameId = 0;\n let previousFrameIndex;\n return {\n reset() {\n tracks = [];\n nextTrackerId = 0;\n frameId = 0;\n previousFrameIndex = undefined;\n },\n update(detections, frameIndex) {\n const frameStep = resolveFrameStep(frameIndex, previousFrameIndex);\n previousFrameIndex = frameIndex ?? previousFrameIndex;\n frameId += 1;\n tracks.forEach((track) => track.predict(frameStep, frameRate));\n const highDetectionIndexes = [];\n const lowDetectionIndexes = [];\n detections.forEach((detection, index) => {\n const confidence = detection.confidence ?? 1;\n if (confidence >= highConfidenceDetectionThreshold) {\n highDetectionIndexes.push(index);\n }\n else if (confidence > MINIMUM_DETECTION_CONFIDENCE) {\n lowDetectionIndexes.push(index);\n }\n });\n const confirmed = [];\n const unconfirmed = [];\n const lost = [];\n for (const track of tracks) {\n if (track.timeSinceUpdate > 1)\n lost.push(track);\n else if (track.trackerId !== undefined ||\n track.successfulUpdates >= minimumConsecutiveFrames) {\n confirmed.push(track);\n }\n else\n unconfirmed.push(track);\n }\n const assignments = new Map();\n const pool = [...confirmed, ...lost];\n const highDetections = highDetectionIndexes.map((index) => detections[index]);\n const firstScores = pairwiseIou(pool.map((track) => track.getRect()), highDetections.map((detection) => detection.rect), bufferRatioFirst).map((row) => row.map((score, index) => score * (highDetections[index].confidence ?? 1)));\n const firstAssociation = associateTrackingScores(firstScores, pool.length, highDetections.length, minimumIouThresholdFirstAssociation);\n for (const match of firstAssociation.matches) {\n updateMatchedTrack(pool[match.trackIndex], highDetections[match.detectionIndex], assignments);\n }\n const remainingTracked = firstAssociation.unmatchedTrackIndexes\n .map((index) => pool[index])\n .filter((track) => track.timeSinceUpdate === 1);\n const lowDetections = lowDetectionIndexes.map((index) => detections[index]);\n const secondScores = pairwiseIou(remainingTracked.map((track) => track.getRect()), lowDetections.map((detection) => detection.rect), bufferRatioSecond);\n const secondAssociation = associateTrackingScores(secondScores, remainingTracked.length, lowDetections.length, minimumIouThresholdSecondAssociation);\n for (const match of secondAssociation.matches) {\n updateMatchedTrack(remainingTracked[match.trackIndex], lowDetections[match.detectionIndex], assignments);\n }\n let unmatchedHighLocal = [...firstAssociation.unmatchedDetectionIndexes];\n let unmatchedUnconfirmed = unconfirmed.map((_, index) => index);\n if (unconfirmed.length > 0 && unmatchedHighLocal.length > 0) {\n const remainingHigh = unmatchedHighLocal.map((index) => highDetections[index]);\n const unconfirmedScores = pairwiseIou(unconfirmed.map((track) => track.getRect()), remainingHigh.map((detection) => detection.rect), bufferRatioFirst).map((row) => row.map((score, index) => score * (remainingHigh[index].confidence ?? 1)));\n const unconfirmedAssociation = associateTrackingScores(unconfirmedScores, unconfirmed.length, remainingHigh.length, minimumIouThresholdUnconfirmedAssociation);\n unmatchedUnconfirmed = unconfirmedAssociation.unmatchedTrackIndexes;\n for (const match of unconfirmedAssociation.matches) {\n updateMatchedTrack(unconfirmed[match.trackIndex], remainingHigh[match.detectionIndex], assignments);\n }\n unmatchedHighLocal =\n unconfirmedAssociation.unmatchedDetectionIndexes.map((index) => unmatchedHighLocal[index]);\n }\n const unmatchedUnconfirmedTracks = new Set(unmatchedUnconfirmed.map((index) => unconfirmed[index]));\n tracks = tracks.filter((track) => !unmatchedUnconfirmedTracks.has(track));\n for (const localIndex of unmatchedHighLocal) {\n const detection = highDetections[localIndex];\n if ((detection.confidence ?? 1) < trackActivationThreshold)\n continue;\n const track = new CBIoUTrack(detection);\n if (frameId === 1 && instantFirstFrameActivation) {\n track.trackerId = nextTrackerId++;\n assignments.set(detection.detectionIndex, track.trackerId);\n }\n tracks.push(track);\n }\n tracks = tracks.filter((track) => track.timeSinceUpdate <= maximumFramesWithoutUpdate &&\n (track.timeSinceUpdate === 0 ||\n track.trackerId !== undefined ||\n track.successfulUpdates >= minimumConsecutiveFrames));\n return createUpdate(detections, assignments, tracks);\n },\n };\n function updateMatchedTrack(track, detection, assignments) {\n track.update(detection);\n if (track.trackerId === undefined &&\n track.successfulUpdates >= minimumConsecutiveFrames) {\n track.trackerId = nextTrackerId++;\n }\n if (track.trackerId !== undefined) {\n assignments.set(detection.detectionIndex, track.trackerId);\n }\n }\n}\nfunction createUpdate(detections, assignments, tracks) {\n const resolvedAssignments = detections.flatMap((detection) => {\n const trackerId = assignments.get(detection.detectionIndex);\n return trackerId === undefined\n ? []\n : [{ detectionIndex: detection.detectionIndex, trackerId }];\n });\n return {\n activeTrackCount: tracks.length,\n assignments: resolvedAssignments,\n confirmedTrackCount: tracks.filter((track) => track.trackerId !== undefined)\n .length,\n };\n}\n\nclass OCSortTrack {\n deltaT;\n age = 0;\n lastObservation;\n trackerId;\n successfulConsecutiveUpdates = 0;\n timeSinceUpdate = 0;\n velocity;\n frozenState;\n observed = true;\n observations = new Map();\n estimator;\n constructor(initial, deltaT) {\n this.deltaT = deltaT;\n this.lastObservation = initial;\n this.estimator = new TrackingKalmanEstimator(initial.rect, \"xcycsr\");\n this.configureNoise();\n }\n predict(frameStep, frameRate) {\n if (this.observed && this.timeSinceUpdate > 0) {\n this.frozenState = this.estimator.snapshot();\n this.observed = false;\n }\n this.estimator.predict(frameStep, frameRate);\n if (this.timeSinceUpdate > 0) {\n this.successfulConsecutiveUpdates = 0;\n }\n this.timeSinceUpdate += 1;\n this.age += 1;\n }\n update(detection, frameStep, frameRate) {\n const previous = this.getPreviousObservation();\n if (previous) {\n this.velocity = computeVelocity(previous, detection);\n }\n if (!this.observed && this.frozenState) {\n this.unfreeze(detection, frameStep, frameRate);\n }\n this.estimator.update(detection.rect);\n this.observed = true;\n this.timeSinceUpdate = 0;\n this.successfulConsecutiveUpdates += 1;\n this.lastObservation = detection;\n this.observations.set(this.age, detection);\n const cutoff = this.age - this.deltaT;\n for (const age of this.observations.keys()) {\n if (age < cutoff)\n this.observations.delete(age);\n }\n }\n getRect() {\n return this.estimator.getRect();\n }\n getPreviousObservation() {\n if (this.observations.size === 0)\n return undefined;\n for (let index = 0; index < this.deltaT; index += 1) {\n const delta = this.deltaT - index;\n const observation = this.observations.get(this.age - delta);\n if (observation)\n return observation;\n }\n const latestAge = Math.max(...this.observations.keys());\n return this.observations.get(latestAge);\n }\n configureNoise() {\n const measurementNoise = identity(4);\n for (let index = 2; index < 4; index += 1) {\n measurementNoise[index][index] *= 10;\n }\n const covariance = scaleMatrix(identity(7), 10);\n for (let index = 4; index < 7; index += 1) {\n covariance[index][index] *= 1000;\n }\n const processNoise = identity(7);\n processNoise[6][6] *= 0.01;\n for (let index = 4; index < 7; index += 1) {\n processNoise[index][index] *= 0.01;\n }\n this.estimator.setCovariances({\n covariance,\n measurementNoise,\n processNoise,\n });\n }\n unfreeze(detection, frameStep, frameRate) {\n if (!this.frozenState || this.timeSinceUpdate === 0)\n return;\n this.estimator.restore(this.frozenState);\n const timeGap = this.timeSinceUpdate;\n const start = rectToMeasurement(this.lastObservation.rect, \"xcycsr\");\n const end = rectToMeasurement(detection.rect, \"xcycsr\");\n const startWidth = Math.sqrt(start[2] * start[3]);\n const startHeight = start[3] === 0 ? 0 : Math.sqrt(start[2] / start[3]);\n const endWidth = Math.sqrt(end[2] * end[3]);\n const endHeight = end[3] === 0 ? 0 : Math.sqrt(end[2] / end[3]);\n for (let index = 0; index < timeGap; index += 1) {\n const progress = (index + 1) / timeGap;\n const x = start[0] + progress * (end[0] - start[0]);\n const y = start[1] + progress * (end[1] - start[1]);\n const width = startWidth + progress * (endWidth - startWidth);\n const height = startHeight + progress * (endHeight - startHeight);\n this.estimator.updateMeasurement([x, y, width * height, width / height]);\n if (index < timeGap - 1) {\n this.estimator.predict(frameStep, frameRate);\n }\n }\n this.frozenState = undefined;\n }\n}\n/** Creates the observation-centric SORT implementation from roboflow/trackers. */\nfunction createOCSortTracker$1(options = {}) {\n const lostTrackBuffer = normalizeNonNegativeInteger$1(options.lostTrackBuffer ?? 30, \"lostTrackBuffer\");\n const frameRate = options.frameRate ?? 30;\n const minimumConsecutiveFrames = normalizePositiveInteger$1(options.minimumConsecutiveFrames ?? 3, \"minimumConsecutiveFrames\");\n const minimumIouThreshold = options.minimumIouThreshold ?? 0.3;\n const directionConsistencyWeight = options.directionConsistencyWeight ?? 0.2;\n const highConfidenceDetectionThreshold = options.highConfidenceDetectionThreshold ?? 0.6;\n const deltaT = normalizePositiveInteger$1(options.deltaT ?? 3, \"deltaT\");\n if (!Number.isFinite(frameRate) || frameRate <= 0) {\n throw new Error(\"frameRate must be a finite positive value.\");\n }\n normalizeUnitInterval$1(minimumIouThreshold, \"minimumIouThreshold\");\n normalizeUnitInterval$1(directionConsistencyWeight, \"directionConsistencyWeight\");\n normalizeUnitInterval$1(highConfidenceDetectionThreshold, \"highConfidenceDetectionThreshold\");\n const scaledLostTrackBuffer = (frameRate / 30) * lostTrackBuffer;\n if (!Number.isFinite(scaledLostTrackBuffer)) {\n throw new Error(\"Scaled lostTrackBuffer overflows: frameRate / 30 * lostTrackBuffer must be finite.\");\n }\n const maximumFramesWithoutUpdate = lostTrackBuffer === 0 ? 0 : Math.max(1, Math.ceil(scaledLostTrackBuffer));\n let tracks = [];\n let frameCount = 0;\n let nextTrackerId = 0;\n let previousFrameIndex;\n return {\n reset() {\n tracks = [];\n frameCount = 0;\n nextTrackerId = 0;\n previousFrameIndex = undefined;\n },\n update(detections, frameIndex) {\n const frameStep = resolveFrameStep(frameIndex, previousFrameIndex);\n previousFrameIndex = frameIndex ?? previousFrameIndex;\n // Match roboflow/trackers: an empty stream before any track exists is\n // not part of OC-SORT's early-sequence activation window.\n if (tracks.length === 0 && detections.length === 0) {\n return {\n activeTrackCount: 0,\n assignments: [],\n confirmedTrackCount: 0,\n };\n }\n tracks.forEach((track) => track.predict(frameStep, frameRate));\n const highDetectionIndexes = detections.flatMap((detection, index) => detection.confidence === undefined ||\n detection.confidence >= highConfidenceDetectionThreshold\n ? [index]\n : []);\n const highDetections = highDetectionIndexes.map((index) => detections[index]);\n const iouScores = pairwiseIou(tracks.map((track) => track.getRect()), highDetections.map((detection) => detection.rect));\n const combinedScores = iouScores.map((row, trackIndex) => row.map((iou, detectionIndex) => {\n if (directionConsistencyWeight === 0)\n return iou;\n return (iou +\n directionConsistencyWeight *\n directionConsistency(tracks[trackIndex], highDetections[detectionIndex]) *\n (highDetections[detectionIndex].confidence ?? 1));\n }));\n const primary = associateTrackingScores(combinedScores, tracks.length, highDetections.length, minimumIouThreshold, iouScores);\n const assignments = new Map();\n for (const match of primary.matches) {\n updateMatchedTrack(tracks[match.trackIndex], highDetections[match.detectionIndex], assignments, frameStep);\n }\n let remainingHigh = [...primary.unmatchedDetectionIndexes];\n if (primary.unmatchedTrackIndexes.length > 0 &&\n remainingHigh.length > 0) {\n const unmatchedTracks = primary.unmatchedTrackIndexes.map((index) => tracks[index]);\n const unmatchedDetections = remainingHigh.map((index) => highDetections[index]);\n const recoveryScores = pairwiseIou(unmatchedTracks.map((track) => track.lastObservation.rect), unmatchedDetections.map((detection) => detection.rect));\n const recovery = associateTrackingScores(recoveryScores, unmatchedTracks.length, unmatchedDetections.length, minimumIouThreshold);\n for (const match of recovery.matches) {\n updateMatchedTrack(unmatchedTracks[match.trackIndex], unmatchedDetections[match.detectionIndex], assignments, frameStep);\n }\n remainingHigh = recovery.unmatchedDetectionIndexes.map((index) => remainingHigh[index]);\n }\n for (const localIndex of remainingHigh) {\n tracks.push(new OCSortTrack(highDetections[localIndex], deltaT));\n }\n tracks = tracks.filter((track) => track.timeSinceUpdate <= maximumFramesWithoutUpdate);\n frameCount += 1;\n const resolvedAssignments = detections.flatMap((detection) => {\n const trackerId = assignments.get(detection.detectionIndex);\n return trackerId === undefined\n ? []\n : [{ detectionIndex: detection.detectionIndex, trackerId }];\n });\n return {\n activeTrackCount: tracks.length,\n assignments: resolvedAssignments,\n confirmedTrackCount: tracks.filter((track) => track.trackerId !== undefined).length,\n };\n },\n };\n function updateMatchedTrack(track, detection, assignments, frameStep) {\n track.update(detection, frameStep, frameRate);\n const earlySequence = frameCount <= minimumConsecutiveFrames;\n const shouldEmit = (earlySequence && track.timeSinceUpdate === 0) ||\n track.successfulConsecutiveUpdates >= minimumConsecutiveFrames;\n if (shouldEmit) {\n if (track.trackerId === undefined) {\n track.trackerId = nextTrackerId++;\n }\n assignments.set(detection.detectionIndex, track.trackerId);\n }\n }\n}\nfunction computeVelocity(previous, current) {\n const deltaX = current.rect.x - previous.rect.x;\n const deltaY = current.rect.y - previous.rect.y;\n const norm = Math.sqrt(deltaX * deltaX + deltaY * deltaY) + 1e-6;\n return { x: deltaX / norm, y: deltaY / norm };\n}\nfunction directionConsistency(track, detection) {\n if (!track.velocity)\n return 0;\n const reference = track.getPreviousObservation() ?? track.lastObservation;\n const deltaX = detection.rect.x - reference.rect.x;\n const deltaY = detection.rect.y - reference.rect.y;\n const norm = Math.sqrt(deltaX * deltaX + deltaY * deltaY) + 1e-6;\n const cosine = Math.max(-1, Math.min(1, track.velocity.x * (deltaX / norm) + track.velocity.y * (deltaY / norm)));\n const angle = Math.acos(cosine);\n return (Math.PI / 2 - Math.abs(angle)) / Math.PI;\n}\n\n/** Public core facade for the internal tracker engine workspace. */\nfunction createSortTracker(options = {}) {\n return createSortTracker$1(options);\n}\n\n/** Public core facade for the internal tracker engine workspace. */\nfunction createByteTrackTracker(options = {}) {\n return createByteTrackTracker$1(options);\n}\n\n/** Public core facade for the internal tracker engine workspace. */\nfunction createCBIoUTracker(options = {}) {\n return createCBIoUTracker$1(options);\n}\n\n/** Public core facade for the internal tracker engine workspace. */\nfunction createOCSortTracker(options = {}) {\n return createOCSortTracker$1(options);\n}\n\n/** Geometry projected into a tracker. The original detection payload is retained. */\nvar TrackingGeometry;\n(function (TrackingGeometry) {\n TrackingGeometry[\"Box\"] = \"box\";\n TrackingGeometry[\"Mask\"] = \"mask\";\n TrackingGeometry[\"Keypoints\"] = \"keypoints\";\n})(TrackingGeometry || (TrackingGeometry = {}));\n\nvar DetectionMaskPayloadFormat;\n(function (DetectionMaskPayloadFormat) {\n DetectionMaskPayloadFormat[\"RawCocoRle\"] = \"rawCocoRle\";\n DetectionMaskPayloadFormat[\"DeflatedBase64\"] = \"deflatedBase64\";\n})(DetectionMaskPayloadFormat || (DetectionMaskPayloadFormat = {}));\nfunction encodeBinaryMask(data, width, height) {\n assertMaskDimensions(data, width, height);\n const runs = [];\n let currentValue = 0;\n let runLength = 0;\n for (let x = 0; x < width; x += 1) {\n for (let y = 0; y < height; y += 1) {\n const value = data[y * width + x] ? 1 : 0;\n if (value === currentValue) {\n runLength += 1;\n }\n else {\n runs.push(runLength);\n currentValue = value;\n runLength = 1;\n }\n }\n }\n runs.push(runLength);\n return {\n counts: encodeCompressedRleCounts(runs),\n encoding: DetectionMaskEncoding.CompressedRle,\n height,\n width,\n };\n}\n/** Encodes a binary mask and derives its bounds in the same raster traversal. */\nfunction encodeBinaryMaskWithBounds(data, width, height) {\n assertMaskDimensions(data, width, height);\n const runs = [];\n let currentValue = 0;\n let runLength = 0;\n let minX = width;\n let minY = height;\n let maxX = -1;\n let maxY = -1;\n for (let x = 0; x < width; x += 1) {\n for (let y = 0; y < height; y += 1) {\n const value = data[y * width + x] ? 1 : 0;\n if (value) {\n minX = Math.min(minX, x);\n minY = Math.min(minY, y);\n maxX = Math.max(maxX, x);\n maxY = Math.max(maxY, y);\n }\n if (value === currentValue) {\n runLength += 1;\n }\n else {\n runs.push(runLength);\n currentValue = value;\n runLength = 1;\n }\n }\n }\n runs.push(runLength);\n return {\n bounds: maxX < 0\n ? null\n : {\n height: maxY - minY + 1,\n width: maxX - minX + 1,\n x: minX + (maxX - minX + 1) / 2,\n y: minY + (maxY - minY + 1) / 2,\n },\n mask: {\n counts: encodeCompressedRleCounts(runs),\n encoding: DetectionMaskEncoding.CompressedRle,\n height,\n width,\n },\n };\n}\nfunction encodeDetectionMaskPayload(mask, codec) {\n return codec ? codec.deflate(mask.counts) : mask.counts;\n}\nfunction decodeDetectionMaskPayload(payload, width, height, options = {}) {\n const format = options.format ??\n (isDeflatedBase64DetectionMaskPayload(payload)\n ? DetectionMaskPayloadFormat.DeflatedBase64\n : DetectionMaskPayloadFormat.RawCocoRle);\n if (format === DetectionMaskPayloadFormat.DeflatedBase64 && !options.codec) {\n throw new Error(\"A detection mask compression codec is required for deflated payloads.\");\n }\n return {\n counts: format === DetectionMaskPayloadFormat.DeflatedBase64\n ? options.codec.inflate(payload)\n : payload,\n encoding: DetectionMaskEncoding.CompressedRle,\n height,\n width,\n };\n}\n/** Matches the annotation editor's legacy transport-format heuristic. */\nfunction isDeflatedBase64DetectionMaskPayload(value) {\n return value.length > 100 && /^[A-Za-z0-9+/]+={0,2}$/.test(value);\n}\nfunction computeMaskBounds(data, width, height) {\n assertMaskDimensions(data, width, height);\n let minX = width;\n let minY = height;\n let maxX = -1;\n let maxY = -1;\n for (let y = 0; y < height; y += 1) {\n for (let x = 0; x < width; x += 1) {\n if (!data[y * width + x]) {\n continue;\n }\n minX = Math.min(minX, x);\n minY = Math.min(minY, y);\n maxX = Math.max(maxX, x);\n maxY = Math.max(maxY, y);\n }\n }\n if (maxX < 0) {\n return null;\n }\n const boundsWidth = maxX - minX + 1;\n const boundsHeight = maxY - minY + 1;\n return {\n height: boundsHeight,\n width: boundsWidth,\n x: minX + boundsWidth / 2,\n y: minY + boundsHeight / 2,\n };\n}\nfunction computeDetectionMaskRect(mask) {\n const decoded = decodeCompressedRleMask(mask);\n const bounds = computeMaskBounds(decoded.data, decoded.width, decoded.height);\n return bounds ?? undefined;\n}\nfunction detectMaskBorders(data, width, height) {\n assertMaskDimensions(data, width, height);\n const borders = new Uint8Array(data.length);\n for (let y = 0; y < height; y += 1) {\n for (let x = 0; x < width; x += 1) {\n const offset = y * width + x;\n if (!data[offset]) {\n continue;\n }\n if (x === 0 ||\n y === 0 ||\n x === width - 1 ||\n y === height - 1 ||\n !data[offset - 1] ||\n !data[offset + 1] ||\n !data[offset - width] ||\n !data[offset + width]) {\n borders[offset] = 1;\n }\n }\n }\n return borders;\n}\nfunction extractMaskContour(data, width, height) {\n assertMaskDimensions(data, width, height);\n const stride = Math.max(1, Math.floor(height / 100));\n const leftEdge = [];\n const rightEdge = [];\n for (let y = 0; y < height; y += stride) {\n let left = -1;\n let right = -1;\n for (let x = 0; x < width; x += 1) {\n if (data[y * width + x]) {\n left = left === -1 ? x : left;\n right = x;\n }\n }\n if (left !== -1) {\n leftEdge.push({ x: left, y });\n rightEdge.push({ x: right, y });\n }\n }\n return leftEdge.length < 2\n ? undefined\n : [...leftEdge, ...rightEdge.reverse()];\n}\nfunction extractMaskRectRuns(data, width, height) {\n assertMaskDimensions(data, width, height);\n const rects = [];\n const openRects = new Map();\n for (let y = 0; y < height; y += 1) {\n const activeSpans = new Set();\n let x = 0;\n while (x < width) {\n while (x < width && !data[y * width + x]) {\n x += 1;\n }\n if (x >= width) {\n break;\n }\n const startX = x;\n while (x < width && data[y * width + x]) {\n x += 1;\n }\n const runWidth = x - startX;\n const key = `${startX}:${runWidth}`;\n const openRect = openRects.get(key);\n activeSpans.add(key);\n if (openRect && openRect.y + openRect.height === y) {\n openRects.set(key, { ...openRect, height: openRect.height + 1 });\n }\n else {\n if (openRect) {\n rects.push(openRect);\n }\n openRects.set(key, { height: 1, width: runWidth, x: startX, y });\n }\n }\n for (const [key, openRect] of openRects) {\n if (!activeSpans.has(key)) {\n rects.push(openRect);\n openRects.delete(key);\n }\n }\n }\n rects.push(...openRects.values());\n return rects.length > 0 ? rects : undefined;\n}\nfunction assertMaskDimensions(data, width, height) {\n if (!Number.isInteger(width) || width <= 0) {\n throw new Error(\"Mask width must be a positive integer.\");\n }\n if (!Number.isInteger(height) || height <= 0) {\n throw new Error(\"Mask height must be a positive integer.\");\n }\n if (data.length !== width * height) {\n throw new Error(\"Mask data length must equal width * height.\");\n }\n}\n\nfunction centerRectToTopLeftRect(rect) {\n return {\n height: rect.height,\n width: rect.width,\n x: rect.x - rect.width / 2,\n y: rect.y - rect.height / 2,\n };\n}\nfunction topLeftRectToCenterRect(rect) {\n return {\n height: rect.height,\n width: rect.width,\n x: rect.x + rect.width / 2,\n y: rect.y + rect.height / 2,\n };\n}\nfunction getPointsRect(points) {\n if (points.length === 0) {\n return undefined;\n }\n let minX = Number.POSITIVE_INFINITY;\n let minY = Number.POSITIVE_INFINITY;\n let maxX = Number.NEGATIVE_INFINITY;\n let maxY = Number.NEGATIVE_INFINITY;\n for (const point of points) {\n minX = Math.min(minX, point.x);\n minY = Math.min(minY, point.y);\n maxX = Math.max(maxX, point.x);\n maxY = Math.max(maxY, point.y);\n }\n return {\n height: maxY - minY,\n width: maxX - minX,\n x: (minX + maxX) / 2,\n y: (minY + maxY) / 2,\n };\n}\nfunction getDetectionRect(detection) {\n return (detection.rect ??\n getPointsRect(detection.polygon?.points ?? []) ??\n getPointsRect(detection.polyline?.points ?? []) ??\n getPointsRect(detection.keypoints?.points ?? []));\n}\nfunction containsPoint(rect, point, padding = 0) {\n const halfWidth = rect.width / 2;\n const halfHeight = rect.height / 2;\n return (point.x >= rect.x - halfWidth - padding &&\n point.x <= rect.x + halfWidth + padding &&\n point.y >= rect.y - halfHeight - padding &&\n point.y <= rect.y + halfHeight + padding);\n}\nfunction pointInPolygon(point, polygon) {\n if (polygon.length < 3) {\n return false;\n }\n let inside = false;\n for (let currentIndex = 0, previousIndex = polygon.length - 1; currentIndex < polygon.length; previousIndex = currentIndex, currentIndex += 1) {\n const current = polygon[currentIndex];\n const previous = polygon[previousIndex];\n const crossesRay = current.y > point.y !== previous.y > point.y &&\n point.x <\n ((previous.x - current.x) * (point.y - current.y)) /\n (previous.y - current.y) +\n current.x;\n if (crossesRay) {\n inside = !inside;\n }\n }\n return inside;\n}\nfunction distanceToSegment(point, start, end) {\n const deltaX = end.x - start.x;\n const deltaY = end.y - start.y;\n const lengthSquared = deltaX * deltaX + deltaY * deltaY;\n if (lengthSquared === 0) {\n return Math.hypot(point.x - start.x, point.y - start.y);\n }\n const projection = Math.max(0, Math.min(1, ((point.x - start.x) * deltaX + (point.y - start.y) * deltaY) /\n lengthSquared));\n const closestX = start.x + projection * deltaX;\n const closestY = start.y + projection * deltaY;\n return Math.hypot(point.x - closestX, point.y - closestY);\n}\nfunction polygonArea(points) {\n if (points.length < 3) {\n return 0;\n }\n let twiceArea = 0;\n for (let index = 0; index < points.length; index += 1) {\n const current = points[index];\n const next = points[(index + 1) % points.length];\n twiceArea += current.x * next.y - next.x * current.y;\n }\n return Math.abs(twiceArea) / 2;\n}\nfunction rectArea(rect) {\n return rect ? Math.max(0, rect.width) * Math.max(0, rect.height) : 0;\n}\n\nconst DEFAULT_LOST_TRACK_BUFFER = 30;\nconst DEFAULT_FRAME_RATE = 30;\nconst DEFAULT_TRACK_ACTIVATION_THRESHOLD = 0.25;\nconst DEFAULT_MINIMUM_CONSECUTIVE_FRAMES = 3;\nconst DEFAULT_MINIMUM_IOU_THRESHOLD = 0.3;\nconst DEFAULT_BYTE_TRACK_ACTIVATION_THRESHOLD = 0.7;\nconst DEFAULT_BYTE_MINIMUM_CONSECUTIVE_FRAMES = 2;\nconst DEFAULT_BYTE_MINIMUM_IOU_THRESHOLD = 0.1;\nconst DEFAULT_HIGH_CONFIDENCE_DETECTION_THRESHOLD = 0.6;\nconst DEFAULT_CBIOU_FIRST_IOU_THRESHOLD = 0.2;\nconst DEFAULT_CBIOU_SECOND_IOU_THRESHOLD = 0.5;\nconst DEFAULT_CBIOU_UNCONFIRMED_IOU_THRESHOLD = 0.3;\nconst DEFAULT_CBIOU_FIRST_BUFFER_RATIO = 0.3;\nconst DEFAULT_CBIOU_SECOND_BUFFER_RATIO = 0.5;\nconst DEFAULT_OCSORT_DIRECTION_CONSISTENCY_WEIGHT = 0.2;\nconst DEFAULT_OCSORT_DELTA_T = 3;\nconst detectionPostProcessors = {\n tracking: ((options = {}) => {\n const isByteTrack = options.algorithm === \"bytetrack\";\n const isCBIoU = options.algorithm === \"cbiou\";\n const isOCSort = options.algorithm === \"ocsort\";\n const lostTrackBuffer = normalizeNonNegativeInteger(options.lostTrackBuffer, DEFAULT_LOST_TRACK_BUFFER, \"lostTrackBuffer\");\n const frameRate = options.frameRate ?? DEFAULT_FRAME_RATE;\n const trackActivationThreshold = options.trackActivationThreshold ??\n (isByteTrack || isCBIoU\n ? DEFAULT_BYTE_TRACK_ACTIVATION_THRESHOLD\n : DEFAULT_TRACK_ACTIVATION_THRESHOLD);\n const minimumConsecutiveFrames = normalizePositiveInteger(options.minimumConsecutiveFrames, isByteTrack || isCBIoU\n ? DEFAULT_BYTE_MINIMUM_CONSECUTIVE_FRAMES\n : DEFAULT_MINIMUM_CONSECUTIVE_FRAMES, \"minimumConsecutiveFrames\");\n const minimumIouThreshold = options.minimumIouThreshold ??\n (isByteTrack\n ? DEFAULT_BYTE_MINIMUM_IOU_THRESHOLD\n : DEFAULT_MINIMUM_IOU_THRESHOLD);\n if (!Number.isFinite(frameRate) || frameRate <= 0) {\n throw new Error(\"frameRate must be a finite positive value.\");\n }\n normalizeUnitInterval(trackActivationThreshold, \"trackActivationThreshold\");\n normalizeUnitInterval(minimumIouThreshold, \"minimumIouThreshold\");\n const base = {\n geometry: options.geometry ?? TrackingGeometry.Box,\n kind: \"tracking\",\n };\n if (isCBIoU) {\n const highConfidenceDetectionThreshold = options.highConfidenceDetectionThreshold ??\n DEFAULT_HIGH_CONFIDENCE_DETECTION_THRESHOLD;\n const minimumIouThresholdFirstAssociation = options.minimumIouThresholdFirstAssociation ??\n DEFAULT_CBIOU_FIRST_IOU_THRESHOLD;\n const minimumIouThresholdSecondAssociation = options.minimumIouThresholdSecondAssociation ??\n DEFAULT_CBIOU_SECOND_IOU_THRESHOLD;\n const minimumIouThresholdUnconfirmedAssociation = options.minimumIouThresholdUnconfirmedAssociation ??\n DEFAULT_CBIOU_UNCONFIRMED_IOU_THRESHOLD;\n const bufferRatioFirst = options.bufferRatioFirst ?? DEFAULT_CBIOU_FIRST_BUFFER_RATIO;\n const bufferRatioSecond = options.bufferRatioSecond ?? DEFAULT_CBIOU_SECOND_BUFFER_RATIO;\n normalizeUnitInterval(highConfidenceDetectionThreshold, \"highConfidenceDetectionThreshold\");\n normalizeUnitInterval(minimumIouThresholdFirstAssociation, \"minimumIouThresholdFirstAssociation\");\n normalizeUnitInterval(minimumIouThresholdSecondAssociation, \"minimumIouThresholdSecondAssociation\");\n normalizeUnitInterval(minimumIouThresholdUnconfirmedAssociation, \"minimumIouThresholdUnconfirmedAssociation\");\n normalizeNonNegativeFinite(bufferRatioFirst, \"bufferRatioFirst\");\n normalizeNonNegativeFinite(bufferRatioSecond, \"bufferRatioSecond\");\n return {\n ...base,\n algorithm: \"cbiou\",\n options: {\n bufferRatioFirst,\n bufferRatioSecond,\n frameRate,\n highConfidenceDetectionThreshold,\n instantFirstFrameActivation: options.instantFirstFrameActivation ?? true,\n lostTrackBuffer,\n minimumConsecutiveFrames,\n minimumIouThresholdFirstAssociation,\n minimumIouThresholdSecondAssociation,\n minimumIouThresholdUnconfirmedAssociation,\n trackActivationThreshold,\n },\n };\n }\n if (isOCSort) {\n const directionConsistencyWeight = options.directionConsistencyWeight ??\n DEFAULT_OCSORT_DIRECTION_CONSISTENCY_WEIGHT;\n const highConfidenceDetectionThreshold = options.highConfidenceDetectionThreshold ??\n DEFAULT_HIGH_CONFIDENCE_DETECTION_THRESHOLD;\n const deltaT = normalizePositiveInteger(options.deltaT, DEFAULT_OCSORT_DELTA_T, \"deltaT\");\n normalizeUnitInterval(directionConsistencyWeight, \"directionConsistencyWeight\");\n normalizeUnitInterval(highConfidenceDetectionThreshold, \"highConfidenceDetectionThreshold\");\n return {\n ...base,\n algorithm: \"ocsort\",\n options: {\n deltaT,\n directionConsistencyWeight,\n frameRate,\n highConfidenceDetectionThreshold,\n lostTrackBuffer,\n minimumConsecutiveFrames,\n minimumIouThreshold,\n },\n };\n }\n if (isByteTrack) {\n const highConfidenceDetectionThreshold = options.highConfidenceDetectionThreshold ??\n DEFAULT_HIGH_CONFIDENCE_DETECTION_THRESHOLD;\n normalizeUnitInterval(highConfidenceDetectionThreshold, \"highConfidenceDetectionThreshold\");\n return {\n ...base,\n algorithm: \"bytetrack\",\n options: {\n frameRate,\n highConfidenceDetectionThreshold,\n lostTrackBuffer,\n minimumConsecutiveFrames,\n minimumIouThreshold,\n trackActivationThreshold,\n },\n };\n }\n return {\n ...base,\n algorithm: \"sort\",\n options: {\n frameRate,\n lostTrackBuffer,\n minimumConsecutiveFrames,\n minimumIouThreshold,\n trackActivationThreshold,\n },\n };\n }),\n};\n/**\n * Creates the lightweight projection sent to a tracker or tracking worker.\n * Masks and keypoint arrays never cross the worker boundary.\n */\nfunction projectDetectionFrameForTracking(frame, geometry) {\n return frame.detections.flatMap((detection, detectionIndex) => {\n const rect = resolveTrackingRect(detection, geometry);\n if (!rect || rect.width <= 0 || rect.height <= 0) {\n return [];\n }\n return [\n {\n ...(detection.confidence === undefined\n ? {}\n : { confidence: detection.confidence }),\n detectionIndex,\n rect,\n },\n ];\n });\n}\nfunction resolveTrackingRect(detection, geometry) {\n switch (geometry) {\n case TrackingGeometry.Box:\n return detection.rect;\n case TrackingGeometry.Mask:\n if (!detection.mask) {\n return undefined;\n }\n // Most segmentation producers already supply the exact mask bounds as a\n // rect. Decode RLE only when that inexpensive projection is absent.\n return detection.rect ?? computeDetectionMaskRect(detection.mask);\n case TrackingGeometry.Keypoints:\n return detection.keypoints\n ? getPointsRect(detection.keypoints.points)\n : undefined;\n }\n}\nfunction normalizePositiveInteger(value, fallback, label) {\n const resolved = value ?? fallback;\n if (!Number.isInteger(resolved) || resolved < 1) {\n throw new Error(`${label} must be a positive integer.`);\n }\n return resolved;\n}\nfunction normalizeNonNegativeInteger(value, fallback, label) {\n const resolved = value ?? fallback;\n if (!Number.isInteger(resolved) || resolved < 0) {\n throw new Error(`${label} must be a non-negative integer.`);\n }\n return resolved;\n}\nfunction normalizeUnitInterval(value, label) {\n if (!Number.isFinite(value) || value < 0 || value > 1) {\n throw new Error(`${label} must be between 0 and 1.`);\n }\n}\nfunction normalizeNonNegativeFinite(value, label) {\n if (!Number.isFinite(value) || value < 0) {\n throw new Error(`${label} must be a finite non-negative value.`);\n }\n}\n\nvar DetectionPickTarget;\n(function (DetectionPickTarget) {\n DetectionPickTarget[\"Box\"] = \"box\";\n DetectionPickTarget[\"Edge\"] = \"edge\";\n DetectionPickTarget[\"Keypoint\"] = \"keypoint\";\n DetectionPickTarget[\"Label\"] = \"label\";\n DetectionPickTarget[\"Mask\"] = \"mask\";\n DetectionPickTarget[\"Polygon\"] = \"polygon\";\n DetectionPickTarget[\"Polyline\"] = \"polyline\";\n})(DetectionPickTarget || (DetectionPickTarget = {}));\nvar MediaInteractionMode;\n(function (MediaInteractionMode) {\n MediaInteractionMode[\"Always\"] = \"always\";\n MediaInteractionMode[\"Disabled\"] = \"disabled\";\n MediaInteractionMode[\"PausedOnly\"] = \"pausedOnly\";\n})(MediaInteractionMode || (MediaInteractionMode = {}));\n\nconst decodedMaskCache = new WeakMap();\nfunction pickDetectionAtPoint(frame, point, options = {}) {\n if (!frame) {\n return null;\n }\n // Tolerances are screen-space sizes; a zoomed-out viewport maps more media\n // units per screen pixel, so a fixed media-space padding would shrink the\n // hit target below the drawn marker.\n const viewportScale = options.viewportScale !== undefined &&\n Number.isFinite(options.viewportScale) &&\n options.viewportScale > 0\n ? options.viewportScale\n : 1;\n const padding = Math.max(0, options.padding ?? 0) / viewportScale;\n const polylinePadding = Math.max(0, options.polylinePadding ?? 6) / viewportScale;\n const keypointPadding = Math.max(0, options.keypointPadding ?? 10) / viewportScale;\n const edgePadding = Math.max(0, options.edgePadding ?? 8) / viewportScale;\n const candidates = [];\n for (let detectionIndex = 0; detectionIndex < frame.detections.length; detectionIndex += 1) {\n const detection = frame.detections[detectionIndex];\n if (!detection ||\n (detection.locked && options.includeLocked === false) ||\n options.filter?.(detection, detectionIndex) === false) {\n continue;\n }\n const detectionArea = Math.max(1, rectArea(getDetectionRect(detection)));\n const pushCandidate = (target, area = detectionArea, geometryIndex) => {\n candidates.push({\n area,\n priority: getPickTargetPriority(target),\n result: {\n detection,\n detectionIndex,\n frame,\n geometryIndex,\n mediaTime: frame.mediaTime,\n point,\n target,\n },\n zIndex: detection.zIndex ?? detectionIndex,\n });\n };\n if (detection.rect && containsPoint(detection.rect, point, padding)) {\n pushCandidate(DetectionPickTarget.Box, rectArea(detection.rect));\n }\n if (detection.polygon && pointInPolygon(point, detection.polygon.points)) {\n pushCandidate(DetectionPickTarget.Polygon, Math.max(1, polygonArea(detection.polygon.points)));\n }\n if (detection.polyline) {\n const segmentIndex = findHitSegment(point, detection.polyline.points, polylinePadding);\n if (segmentIndex !== -1) {\n pushCandidate(DetectionPickTarget.Polyline, detectionArea, segmentIndex);\n }\n }\n if (detection.keypoints) {\n // Clustered keypoints share the pick tolerance; take the nearest so the\n // hover label, the menu, and the handle drag all name the same point.\n // Later points draw on top, so they win exact ties.\n let keypointIndex = -1;\n let keypointDistance = Number.POSITIVE_INFINITY;\n detection.keypoints.points.forEach((keypoint, index) => {\n if (detection.keypoints?.visibility?.[index] ===\n KeypointVisibility.NotLabeled) {\n return;\n }\n const distance = Math.hypot(point.x - keypoint.x, point.y - keypoint.y);\n if (distance <= keypointPadding && distance <= keypointDistance) {\n keypointIndex = index;\n keypointDistance = distance;\n }\n });\n if (keypointIndex !== -1) {\n pushCandidate(DetectionPickTarget.Keypoint, Math.PI * keypointPadding * keypointPadding, keypointIndex);\n }\n else {\n const edgeIndex = detection.keypoints.edges.findIndex(([fromIndex, toIndex]) => {\n const from = detection.keypoints?.points[fromIndex];\n const to = detection.keypoints?.points[toIndex];\n return Boolean(from &&\n to &&\n detection.keypoints?.visibility?.[fromIndex] !==\n KeypointVisibility.NotLabeled &&\n detection.keypoints?.visibility?.[toIndex] !==\n KeypointVisibility.NotLabeled &&\n distanceToSegment(point, from, to) <= edgePadding);\n });\n if (edgeIndex !== -1) {\n const [fromIndex, toIndex] = detection.keypoints.edges[edgeIndex];\n const from = detection.keypoints.points[fromIndex];\n const to = detection.keypoints.points[toIndex];\n const edgeArea = Math.max(1, Math.hypot(to.x - from.x, to.y - from.y) * edgePadding * 2);\n pushCandidate(DetectionPickTarget.Edge, edgeArea, edgeIndex);\n }\n }\n }\n if (detection.mask && options.includeMasks !== false) {\n const decoded = getDecodedMask(detection.mask);\n const mediaDimensions = options.maskMediaDimensions;\n const x = Math.floor(mediaDimensions && mediaDimensions.width > 0\n ? (point.x / mediaDimensions.width) * decoded.width\n : point.x);\n const y = Math.floor(mediaDimensions && mediaDimensions.height > 0\n ? (point.y / mediaDimensions.height) * decoded.height\n : point.y);\n if (x >= 0 &&\n y >= 0 &&\n x < decoded.width &&\n y < decoded.height &&\n decoded.data[y * decoded.width + x]) {\n pushCandidate(DetectionPickTarget.Mask, getMaskMediaArea(decoded, mediaDimensions));\n }\n }\n }\n candidates.sort((left, right) => {\n const priorityDifference = left.priority - right.priority;\n if (priorityDifference !== 0) {\n return priorityDifference;\n }\n const areaDifference = left.area - right.area;\n if (areaDifference !== 0) {\n return areaDifference;\n }\n const zIndexDifference = right.zIndex - left.zIndex;\n return zIndexDifference === 0\n ? right.result.detectionIndex - left.result.detectionIndex\n : zIndexDifference;\n });\n return candidates[0]?.result ?? null;\n}\nfunction getPickTargetPriority(target) {\n switch (target) {\n case DetectionPickTarget.Keypoint:\n return 0;\n case DetectionPickTarget.Edge:\n case DetectionPickTarget.Polyline:\n return 1;\n default:\n return 2;\n }\n}\nfunction getDecodedMask(mask) {\n const cached = decodedMaskCache.get(mask);\n if (cached)\n return cached;\n const decoded = decodeCompressedRleMask(mask);\n const result = {\n ...decoded,\n pixelArea: countMaskPixels(decoded.data),\n };\n decodedMaskCache.set(mask, result);\n return result;\n}\nfunction getMaskMediaArea(mask, mediaDimensions) {\n if (!mediaDimensions ||\n mediaDimensions.width <= 0 ||\n mediaDimensions.height <= 0) {\n return mask.pixelArea;\n }\n return (mask.pixelArea *\n (mediaDimensions.width / mask.width) *\n (mediaDimensions.height / mask.height));\n}\nfunction createDetectionPickKey(pick) {\n if (!pick) {\n return null;\n }\n const frameKey = [pick.frame.frameIndex ?? \"time\", pick.frame.mediaTime];\n const detectionKey = detectionPickKey(pick);\n return [\n ...frameKey,\n ...detectionKey,\n pick.target,\n pick.geometryIndex ?? \"geometry\",\n ].join(\":\");\n}\n/**\n * Compares the frame-independent membership of two ordered selections.\n *\n * Detection ids retain their runtime type, so numeric `1` and string `\"1\"`\n * remain distinct identities. Anonymous detections keep their frame-local\n * index identity.\n */\nfunction haveSameDetectionPickIdentities(current, next) {\n if (current.length !== next.length) {\n return false;\n }\n return current.every((pick, index) => {\n const nextPick = next[index];\n if (!nextPick) {\n return false;\n }\n const id = pick.detection.id;\n const nextId = nextPick.detection.id;\n return id === undefined || nextId === undefined\n ? id === nextId && pick.detectionIndex === nextPick.detectionIndex\n : typeof id === typeof nextId && id === nextId;\n });\n}\nfunction rebaseDetectionPickToFrame(pick, frame) {\n if (!pick || !frame) {\n return null;\n }\n const detectionIndex = pick.detection.id === undefined ||\n !hasUniqueDetectionId(pick.frame, pick.detection.id)\n ? pick.detectionIndex\n : frame.detections.findIndex((detection) => detection.id === pick.detection.id);\n const detection = frame.detections[detectionIndex];\n if (!detection) {\n return null;\n }\n const rebasedPick = {\n detection,\n detectionIndex,\n frame,\n geometryIndex: pick.geometryIndex,\n mediaTime: frame.mediaTime,\n point: pick.point,\n target: pick.target,\n };\n return createDetectionPickKey(rebasedPick) === createDetectionPickKey(pick)\n ? rebasedPick\n : null;\n}\n/**\n * Carries a pick forward onto a different frame of the same timeline.\n *\n * Same-frame rebases behave exactly like `rebaseDetectionPickToFrame`. Across\n * frames, the pick follows its detection only when the detection id is defined\n * and unique in both frames, so anonymous and duplicate-id detections keep the\n * per-frame lifetime they have today. Returns `null` when the detection has no\n * followable identity or has left the frame.\n */\nfunction followDetectionPickAcrossFrames(pick, frame) {\n const rebasedPick = rebaseDetectionPickToFrame(pick, frame);\n if (rebasedPick || !pick || !frame) {\n return rebasedPick;\n }\n const id = pick.detection.id;\n if (id === undefined ||\n !hasUniqueDetectionId(pick.frame, id) ||\n !hasUniqueDetectionId(frame, id)) {\n return null;\n }\n const detectionIndex = frame.detections.findIndex((detection) => detection.id === id);\n const detection = frame.detections[detectionIndex];\n if (!detection) {\n return null;\n }\n return {\n detection,\n detectionIndex,\n frame,\n geometryIndex: pick.geometryIndex,\n mediaTime: frame.mediaTime,\n point: pick.point,\n target: pick.target,\n };\n}\nfunction detectionPickKey(pick) {\n if (pick.detection.id === undefined) {\n return [\"anonymous\", pick.detectionIndex];\n }\n const idType = typeof pick.detection.id;\n return hasUniqueDetectionId(pick.frame, pick.detection.id)\n ? [\"id\", idType, String(pick.detection.id)]\n : [\n \"duplicate-id\",\n idType,\n String(pick.detection.id),\n \"index\",\n pick.detectionIndex,\n ];\n}\nfunction hasUniqueDetectionId(frame, id) {\n let count = 0;\n for (const detection of frame.detections) {\n if (detection.id !== id)\n continue;\n count += 1;\n if (count > 1)\n return false;\n }\n return count === 1;\n}\nfunction pickDetectionByMaskId(frame, maskId, point) {\n if (!frame || maskId <= 0 || !Number.isInteger(maskId)) {\n return null;\n }\n const detectionIndex = maskId - 1;\n const detection = frame.detections[detectionIndex];\n if (!detection) {\n return null;\n }\n return {\n detection,\n detectionIndex,\n frame,\n mediaTime: frame.mediaTime,\n point,\n target: DetectionPickTarget.Mask,\n };\n}\nfunction findHitSegment(point, points, padding, closed) {\n const segmentCount = points.length - 1;\n for (let index = 0; index < segmentCount; index += 1) {\n const start = points[index];\n const end = points[(index + 1) % points.length];\n if (start && end && distanceToSegment(point, start, end) <= padding) {\n return index;\n }\n }\n return -1;\n}\nfunction countMaskPixels(data) {\n let count = 0;\n for (const value of data) {\n count += value ? 1 : 0;\n }\n return Math.max(1, count);\n}\n\nconst MIN_SCALE = 0.1;\nconst MAX_SCALE = 10;\nconst MAX_WHEEL_DELTA = 50;\nfunction createViewportController(initial = {}) {\n let transform = freeze({\n locked: initial.locked ?? false,\n scale: clampScale(initial.scale ?? 0.8),\n x: initial.x ?? 0,\n y: initial.y ?? 0,\n });\n const listeners = new Set();\n const update = (next) => {\n if (next.scale === transform.scale &&\n next.x === transform.x &&\n next.y === transform.y &&\n next.locked === transform.locked)\n return;\n transform = freeze(next);\n for (const listener of listeners)\n listener(transform);\n };\n const controller = {\n getTransform: () => transform,\n screenToMedia: (point) => screenToMedia(point, transform),\n mediaToScreen: (point) => mediaToScreen(point, transform),\n setTransform(next) {\n if (transform.locked)\n return;\n update({\n ...transform,\n ...(next.scale === undefined ? {} : { scale: clampScale(next.scale) }),\n ...(next.x === undefined ? {} : { x: next.x }),\n ...(next.y === undefined ? {} : { y: next.y }),\n });\n },\n setLocked(locked) {\n update({ ...transform, locked });\n },\n panBy(dx, dy) {\n if (transform.locked)\n return;\n update({ ...transform, x: transform.x + dx, y: transform.y + dy });\n },\n zoomAt(point, factor) {\n if (transform.locked || !Number.isFinite(factor) || factor <= 0)\n return;\n const mediaPoint = screenToMedia(point, transform);\n const scale = clampScale(transform.scale * factor);\n update({\n ...transform,\n scale,\n x: point.x - mediaPoint.x * scale,\n y: point.y - mediaPoint.y * scale,\n });\n },\n zoomFromWheel(point, deltaY) {\n const delta = Math.max(-MAX_WHEEL_DELTA, Math.min(MAX_WHEEL_DELTA, deltaY));\n controller.zoomAt(point, Math.exp(-delta * 0.01));\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n };\n return controller;\n}\nfunction screenToMedia(point, transform) {\n return {\n x: (point.x - transform.x) / transform.scale,\n y: (point.y - transform.y) / transform.scale,\n };\n}\nfunction mediaToScreen(point, transform) {\n return {\n x: point.x * transform.scale + transform.x,\n y: point.y * transform.scale + transform.y,\n };\n}\nfunction clampScale(scale) {\n return Math.max(MIN_SCALE, Math.min(MAX_SCALE, Number.isFinite(scale) ? scale : 1));\n}\nfunction freeze(transform) {\n return Object.freeze(transform);\n}\n\nvar AnnotationGeometryKind;\n(function (AnnotationGeometryKind) {\n AnnotationGeometryKind[\"Box\"] = \"box\";\n AnnotationGeometryKind[\"Polygon\"] = \"polygon\";\n AnnotationGeometryKind[\"Polyline\"] = \"polyline\";\n AnnotationGeometryKind[\"Keypoints\"] = \"keypoints\";\n AnnotationGeometryKind[\"Mask\"] = \"mask\";\n})(AnnotationGeometryKind || (AnnotationGeometryKind = {}));\nvar AnnotationGestureStateKind;\n(function (AnnotationGestureStateKind) {\n AnnotationGestureStateKind[\"Idle\"] = \"idle\";\n AnnotationGestureStateKind[\"Creating\"] = \"creating\";\n AnnotationGestureStateKind[\"Moving\"] = \"moving\";\n AnnotationGestureStateKind[\"Resizing\"] = \"resizing\";\n AnnotationGestureStateKind[\"DragSelecting\"] = \"dragSelecting\";\n})(AnnotationGestureStateKind || (AnnotationGestureStateKind = {}));\nvar AnnotationHandleKind;\n(function (AnnotationHandleKind) {\n AnnotationHandleKind[\"Resize\"] = \"resize\";\n AnnotationHandleKind[\"Vertex\"] = \"vertex\";\n AnnotationHandleKind[\"AddVertex\"] = \"addVertex\";\n AnnotationHandleKind[\"Keypoint\"] = \"keypoint\";\n})(AnnotationHandleKind || (AnnotationHandleKind = {}));\n\nconst HANDLE_RADIUS = 6;\nconst ADD_HANDLE_RADIUS = 3.6;\nconst HANDLE_HIT_SIZE = 16;\nfunction getAnnotationHandles(detection, viewportScale = 1) {\n if (detection.locked || detection.mask)\n return [];\n const radius = HANDLE_RADIUS / viewportScale;\n const hitSize = HANDLE_HIT_SIZE / viewportScale;\n if (detection.polygon)\n return getPathHandles(detection.polygon.points, true, radius, hitSize);\n if (detection.polyline)\n return getPathHandles(detection.polyline.points, false, radius, hitSize);\n if (detection.keypoints) {\n const keypointHandles = detection.keypoints.points.flatMap((point, geometryIndex) => detection.keypoints?.visibility?.[geometryIndex] ===\n KeypointVisibility.NotLabeled\n ? []\n : [\n {\n cursor: \"pointer\",\n geometryIndex,\n hitSize,\n id: `kp-${geometryIndex}`,\n kind: AnnotationHandleKind.Keypoint,\n point,\n radius,\n },\n ]);\n if (!detection.rect)\n return keypointHandles;\n // The skeleton's box resizes like any box; keypoints come last so a point\n // sitting on the box edge still wins the hit test.\n return [\n ...getBoxHandles(detection.rect, radius, hitSize),\n ...keypointHandles,\n ];\n }\n if (detection.rect)\n return getBoxHandles(detection.rect, radius, hitSize);\n return [];\n}\nfunction pickAnnotationHandle(handles, point) {\n let picked;\n let pickedDistance = Number.POSITIVE_INFINITY;\n for (const handle of handles) {\n const dx = Math.abs(point.x - handle.point.x);\n const dy = Math.abs(point.y - handle.point.y);\n if (dx > handle.hitSize / 2 || dy > handle.hitSize / 2)\n continue;\n // Clustered handles (face keypoints, a tiny box's corners) share hit\n // areas; the nearest one is what the pointer is on. Later handles draw on\n // top, so they win exact ties.\n const distance = Math.hypot(dx, dy);\n if (distance <= pickedDistance) {\n picked = handle;\n pickedDistance = distance;\n }\n }\n return picked;\n}\nfunction applyAnnotationHandleDrag(detection, handle, point) {\n if (detection.rect && handle.kind === AnnotationHandleKind.Resize) {\n const rect = resizeRect(detection.rect, handle.id, point);\n const boxRelative = detection.keypoints?.boxRelative;\n if (!detection.keypoints || !boxRelative?.some(Boolean)) {\n return { ...detection, rect };\n }\n // Box-relative keypoints (unplaced template points) follow the rect;\n // points the user positioned keep their coordinates.\n const from = detection.rect;\n const scaleX = from.width > 0 ? rect.width / from.width : 1;\n const scaleY = from.height > 0 ? rect.height / from.height : 1;\n return {\n ...detection,\n keypoints: {\n ...detection.keypoints,\n points: detection.keypoints.points.map((keypoint, index) => boxRelative[index]\n ? {\n x: rect.x + (keypoint.x - from.x) * scaleX,\n y: rect.y + (keypoint.y - from.y) * scaleY,\n }\n : keypoint),\n },\n rect,\n };\n }\n if (handle.kind === AnnotationHandleKind.AddVertex &&\n handle.edgeIndex !== undefined) {\n const key = detection.polygon ? \"polygon\" : \"polyline\";\n const geometry = detection[key];\n if (!geometry)\n return detection;\n const points = [...geometry.points];\n points.splice(handle.edgeIndex + 1, 0, point);\n return { ...detection, [key]: { points } };\n }\n if (handle.geometryIndex === undefined)\n return detection;\n if (detection.polygon)\n return replacePathPoint(detection, \"polygon\", handle.geometryIndex, point);\n if (detection.polyline)\n return replacePathPoint(detection, \"polyline\", handle.geometryIndex, point);\n if (detection.keypoints) {\n const points = [...detection.keypoints.points];\n points[handle.geometryIndex] = point;\n // A positioned point no longer follows the box.\n const boxRelative = detection.keypoints.boxRelative?.map((relative, index) => relative && index !== handle.geometryIndex);\n return {\n ...detection,\n keypoints: {\n ...detection.keypoints,\n points,\n ...(boxRelative ? { boxRelative } : {}),\n },\n };\n }\n return detection;\n}\nfunction deleteAnnotationVertex(detection, vertexIndex) {\n const geometry = detection.polygon ?? detection.polyline;\n const minimum = detection.polygon ? 3 : 2;\n if (!geometry || geometry.points.length <= minimum)\n return null;\n const points = geometry.points.filter((_, index) => index !== vertexIndex);\n return detection.polygon\n ? { ...detection, polygon: { points } }\n : { ...detection, polyline: { points } };\n}\nfunction offsetDetection(detection, dx, dy) {\n const offsetPoint = ({ x, y }) => ({ x: x + dx, y: y + dy });\n return {\n ...detection,\n ...(detection.rect\n ? {\n rect: {\n ...detection.rect,\n x: detection.rect.x + dx,\n y: detection.rect.y + dy,\n },\n }\n : {}),\n ...(detection.polygon\n ? { polygon: { points: detection.polygon.points.map(offsetPoint) } }\n : {}),\n ...(detection.polyline\n ? { polyline: { points: detection.polyline.points.map(offsetPoint) } }\n : {}),\n ...(detection.keypoints\n ? {\n keypoints: {\n ...detection.keypoints,\n points: detection.keypoints.points.map(offsetPoint),\n },\n }\n : {}),\n };\n}\nfunction getBoxHandles(rect, radius, hitSize) {\n const left = rect.x - rect.width / 2;\n const right = rect.x + rect.width / 2;\n const top = rect.y - rect.height / 2;\n const bottom = rect.y + rect.height / 2;\n const definitions = [\n [\"nw\", { x: left, y: top }, \"nwse-resize\"],\n [\"n\", { x: rect.x, y: top }, \"ns-resize\"],\n [\"ne\", { x: right, y: top }, \"nesw-resize\"],\n [\"e\", { x: right, y: rect.y }, \"ew-resize\"],\n [\"se\", { x: right, y: bottom }, \"nwse-resize\"],\n [\"s\", { x: rect.x, y: bottom }, \"ns-resize\"],\n [\"sw\", { x: left, y: bottom }, \"nesw-resize\"],\n [\"w\", { x: left, y: rect.y }, \"ew-resize\"],\n ];\n return definitions.map(([id, point, cursor]) => ({\n cursor,\n hitSize,\n id,\n kind: AnnotationHandleKind.Resize,\n point,\n radius,\n }));\n}\nfunction getPathHandles(points, closed, radius, hitSize) {\n const handles = points.map((point, geometryIndex) => ({\n cursor: \"move\",\n geometryIndex,\n hitSize,\n id: `vertex-${geometryIndex}`,\n kind: AnnotationHandleKind.Vertex,\n point,\n radius,\n }));\n const edgeCount = closed ? points.length : Math.max(0, points.length - 1);\n for (let edgeIndex = 0; edgeIndex < edgeCount; edgeIndex += 1) {\n const from = points[edgeIndex];\n const to = points[(edgeIndex + 1) % points.length];\n handles.push({\n cursor: \"copy\",\n edgeIndex,\n hitSize,\n id: `add-${edgeIndex}`,\n kind: AnnotationHandleKind.AddVertex,\n point: { x: (from.x + to.x) / 2, y: (from.y + to.y) / 2 },\n radius: ADD_HANDLE_RADIUS / (HANDLE_RADIUS / radius),\n });\n }\n return handles;\n}\nfunction resizeRect(rect, handle, point) {\n let left = rect.x - rect.width / 2;\n let right = rect.x + rect.width / 2;\n let top = rect.y - rect.height / 2;\n let bottom = rect.y + rect.height / 2;\n if (handle.includes(\"w\"))\n left = Math.min(point.x, right - 5);\n if (handle.includes(\"e\"))\n right = Math.max(point.x, left + 5);\n if (handle.includes(\"n\"))\n top = Math.min(point.y, bottom - 5);\n if (handle.includes(\"s\"))\n bottom = Math.max(point.y, top + 5);\n return {\n x: (left + right) / 2,\n y: (top + bottom) / 2,\n width: right - left,\n height: bottom - top,\n };\n}\nfunction replacePathPoint(detection, key, index, point) {\n const geometry = detection[key];\n const points = [...geometry.points];\n points[index] = point;\n return { ...detection, [key]: { points } };\n}\n/** Finds the closest path segment, useful for contextual vertex insertion. */\nfunction findClosestAnnotationSegment(points, point, closed) {\n const count = closed ? points.length : points.length - 1;\n let best;\n for (let index = 0; index < count; index += 1) {\n const distance = distanceToSegment(point, points[index], points[(index + 1) % points.length]);\n if (!best || distance < best.distance)\n best = { index, distance };\n }\n return best;\n}\n\nconst MOVE_THRESHOLD = 4;\nconst CLICK_CANCEL_MS = 250;\nconst CLICK_CANCEL_DIAGONAL = 25;\nconst CLOSE_ZONE_SCREEN_PX = 12;\nfunction createAnnotationEditingEngine(options = {}) {\n let tool = null;\n let gesture = null;\n let state = idleState();\n const stateListeners = new Set();\n const fastTranslateListeners = new Set();\n return {\n getState: () => state,\n setCreationTool(nextTool) {\n if (gesture)\n cancel();\n tool = nextTool;\n },\n pointerDown(input, pick = null) {\n if (input.button !== undefined && input.button !== 0)\n return;\n if (tool) {\n beginCreation(input);\n }\n else if (pick && !pick.detection.locked && !pick.detection.mask) {\n beginMove(pick, input);\n }\n },\n pointerMove(input) {\n if (!gesture)\n return;\n switch (gesture.kind) {\n case \"box\":\n gesture = { ...gesture, current: input };\n setPreview(resolveBoxPreview(gesture));\n break;\n case \"move\": {\n const dx = input.point.x - gesture.start.point.x;\n const dy = input.point.y - gesture.start.point.y;\n const moved = gesture.moved || exceedsMoveThreshold(gesture.start, input);\n gesture = { ...gesture, current: input, moved };\n if (moved) {\n const preview = offsetDetection(gesture.detection, dx, dy);\n setPreview(preview);\n if (gesture.detection.id !== undefined) {\n options.onFastTranslate?.(gesture.detection.id, dx, dy);\n for (const listener of fastTranslateListeners) {\n listener(gesture.detection.id, dx, dy);\n }\n }\n }\n break;\n }\n case \"resize\": {\n // A click on a handle is not a drag: a keypoint tapped to open its\n // menu must not snap to the pointer, so nothing previews or commits\n // until the pointer has travelled like a move gesture.\n const moved = gesture.moved || exceedsMoveThreshold(gesture.start, input);\n gesture = { ...gesture, current: input, moved };\n if (moved) {\n setPreview(applyAnnotationHandleDrag(gesture.detection, gesture.handle, input.point));\n }\n break;\n }\n case \"path\":\n setPreview(resolvePathPreview([...gesture.points, input.point]));\n break;\n case \"freehand\": {\n const previous = gesture.points.at(-1);\n if (!previous ||\n previous.x !== input.point.x ||\n previous.y !== input.point.y) {\n gesture.points.push(input.point);\n }\n gesture = { ...gesture, current: input };\n setPreview(resolvePathPreview(gesture.points));\n break;\n }\n }\n },\n pointerUp(input) {\n if (!gesture)\n return;\n if (gesture.kind === \"path\")\n return;\n const active = gesture;\n gesture = null;\n release(active.start.pointerId);\n if (active.kind === \"box\") {\n const preview = resolveBoxPreview({ ...active, current: input });\n const duration = input.timestamp - active.start.timestamp;\n const diagonal = Math.hypot(input.point.x - active.start.point.x, input.point.y - active.start.point.y) * scale();\n const rect = preview.rect;\n if (!rect ||\n rect.width < 1 ||\n rect.height < 1 ||\n (duration < CLICK_CANCEL_MS && diagonal <= CLICK_CANCEL_DIAGONAL)) {\n cancel();\n return;\n }\n if (tool?.shouldCommit?.(rect) === false) {\n cancel();\n return;\n }\n commit(preview, null);\n return;\n }\n if (active.kind === \"freehand\") {\n const points = [...active.points, input.point];\n if (points.length < 2 || tool?.shouldCommit?.(points) === false) {\n cancel();\n return;\n }\n commit(tool.createDetection(points), null);\n return;\n }\n if (active.kind === \"move\") {\n if (!active.moved) {\n setState(idleState());\n return;\n }\n const preview = offsetDetection(active.detection, input.point.x - active.start.point.x, input.point.y - active.start.point.y);\n commit(preview, active.detection);\n return;\n }\n if (!active.moved) {\n setState(idleState());\n return;\n }\n const preview = applyAnnotationHandleDrag(active.detection, active.handle, input.point);\n commit(preview, active.detection);\n },\n keyDown(key) {\n if (key === \"Escape\") {\n cancel();\n }\n else if (key === \"Enter\" && gesture?.kind === \"path\") {\n commitPath();\n }\n },\n beginHandleDrag(detection, handle, input) {\n if (detection.locked || detection.mask)\n return;\n capture(input.pointerId);\n gesture = {\n current: input,\n detection,\n handle,\n kind: \"resize\",\n moved: false,\n start: input,\n };\n setState({\n activeDetectionId: detection.id ?? null,\n activeHandleId: handle.id,\n kind: AnnotationGestureStateKind.Resizing,\n pointerId: input.pointerId ?? null,\n preview: null,\n });\n },\n deleteVertex(detection, vertexIndex) {\n const next = deleteAnnotationVertex(detection, vertexIndex);\n if (next)\n commit(next, detection);\n return next;\n },\n cancel,\n hasCreationTool: () => tool !== null,\n subscribe(listener) {\n stateListeners.add(listener);\n return () => stateListeners.delete(listener);\n },\n subscribeFastTranslate(listener) {\n fastTranslateListeners.add(listener);\n return () => fastTranslateListeners.delete(listener);\n },\n };\n function beginCreation(input) {\n if (!tool)\n return;\n const mode = tool.mode ??\n (tool.geometry === AnnotationGeometryKind.Box\n ? \"drag\"\n : tool.geometry === AnnotationGeometryKind.Mask\n ? \"freehand\"\n : \"multiClick\");\n if (mode === \"drag\") {\n capture(input.pointerId);\n gesture = { current: input, kind: \"box\", start: input };\n const preview = resolveBoxPreview(gesture);\n options.onPreview?.(preview);\n setState({\n activeDetectionId: null,\n activeHandleId: null,\n kind: AnnotationGestureStateKind.Creating,\n pointerId: input.pointerId ?? null,\n preview,\n });\n return;\n }\n if (mode === \"freehand\") {\n capture(input.pointerId);\n gesture = {\n current: input,\n kind: \"freehand\",\n points: [input.point],\n start: input,\n };\n setPreview(resolvePathPreview([input.point]));\n return;\n }\n if (tool.geometry !== AnnotationGeometryKind.Polygon &&\n tool.geometry !== AnnotationGeometryKind.Polyline &&\n tool.geometry !== AnnotationGeometryKind.Keypoints)\n return;\n if (gesture?.kind !== \"path\") {\n capture(input.pointerId);\n gesture = {\n kind: \"path\",\n lastTimestamp: input.timestamp,\n pointerId: input.pointerId ?? null,\n points: [input.point],\n };\n setPreview(resolvePathPreview([input.point, input.point]));\n return;\n }\n const minimum = tool.minVertices ??\n (tool.geometry === AnnotationGeometryKind.Polygon ? 3 : 2);\n const closeDistance = Math.hypot(input.point.x - gesture.points[0].x, input.point.y - gesture.points[0].y) * scale();\n const doubleClick = input.detail !== undefined\n ? input.detail >= 2\n : input.timestamp - gesture.lastTimestamp < CLICK_CANCEL_MS;\n if (gesture.points.length >= minimum &&\n ((tool.geometry === AnnotationGeometryKind.Polygon &&\n closeDistance <= CLOSE_ZONE_SCREEN_PX) ||\n doubleClick)) {\n commitPath();\n return;\n }\n gesture.points.push(input.point);\n gesture.lastTimestamp = input.timestamp;\n setPreview(resolvePathPreview([...gesture.points, input.point]));\n }\n function beginMove(pick, input) {\n capture(input.pointerId);\n gesture = {\n current: input,\n detection: pick.detection,\n kind: \"move\",\n moved: false,\n start: input,\n };\n setState({\n activeDetectionId: pick.detection.id ?? null,\n activeHandleId: null,\n kind: AnnotationGestureStateKind.Moving,\n pointerId: input.pointerId ?? null,\n preview: null,\n });\n }\n function resolveBoxPreview(active) {\n const left = Math.min(active.start.point.x, active.current.point.x);\n const right = Math.max(active.start.point.x, active.current.point.x);\n const top = Math.min(active.start.point.y, active.current.point.y);\n const bottom = Math.max(active.start.point.y, active.current.point.y);\n const rect = {\n x: (left + right) / 2,\n y: (top + bottom) / 2,\n width: right - left,\n height: bottom - top,\n };\n return tool.createDetection(rect);\n }\n function resolvePathPreview(points) {\n return tool.createDetection(points);\n }\n function commitPath() {\n if (!tool || gesture?.kind !== \"path\")\n return;\n const minimum = tool.minVertices ??\n (tool.geometry === AnnotationGeometryKind.Polygon ? 3 : 2);\n if (gesture.points.length < minimum) {\n cancel();\n return;\n }\n const preview = tool.createDetection(gesture.points);\n if (tool.shouldCommit?.(gesture.points) === false) {\n cancel();\n return;\n }\n release(gesture.pointerId ?? undefined);\n gesture = null;\n commit(preview, null);\n }\n function commit(detection, previous) {\n options.onCommit?.(detection, previous);\n options.onPreview?.(null);\n setState(idleState());\n }\n function setPreview(preview) {\n options.onPreview?.(preview);\n setState({\n activeDetectionId: state.activeDetectionId,\n activeHandleId: state.activeHandleId,\n kind: state.kind === AnnotationGestureStateKind.Idle\n ? AnnotationGestureStateKind.Creating\n : state.kind,\n pointerId: state.pointerId ??\n (gesture && \"pointerId\" in gesture\n ? (gesture.pointerId ?? null)\n : null),\n preview,\n });\n }\n function cancel() {\n const pointerId = gesture && \"start\" in gesture\n ? gesture.start.pointerId\n : gesture?.kind === \"path\"\n ? (gesture.pointerId ?? undefined)\n : undefined;\n release(pointerId);\n gesture = null;\n options.onPreview?.(null);\n options.onCancel?.();\n setState(idleState());\n }\n function setState(next) {\n state = Object.freeze(next);\n options.onStateChange?.(state);\n for (const listener of stateListeners)\n listener(state);\n }\n function scale() {\n return Math.max(options.viewportScale?.() ?? 1, Number.EPSILON);\n }\n function exceedsMoveThreshold(start, input) {\n return (Math.hypot(input.point.x - start.point.x, input.point.y - start.point.y) >=\n MOVE_THRESHOLD / scale());\n }\n function capture(pointerId) {\n if (pointerId !== undefined)\n options.capturePointer?.(pointerId);\n }\n function release(pointerId) {\n if (pointerId !== undefined)\n options.releasePointer?.(pointerId);\n }\n}\nfunction idleState() {\n return Object.freeze({\n activeDetectionId: null,\n activeHandleId: null,\n kind: AnnotationGestureStateKind.Idle,\n pointerId: null,\n preview: null,\n });\n}\n\nfunction resolveStyleValue(value, detection, context) {\n return typeof value === \"function\"\n ? value(detection, context)\n : value;\n}\n\n/**\n * Resolves the renderer-neutral stroke contract without dropping optional\n * geometry controls as styles move from public configuration to draw\n * instructions.\n *\n * Keep this as the single normalization point for every built-in style. That\n * makes additions to `StrokeStyle` deliberate instead of relying on each style\n * implementation to copy a growing property list correctly.\n */\nfunction resolveStrokeStyle(stroke, defaults) {\n const alignment = stroke?.alignment ?? defaults.alignment;\n const cap = stroke?.cap ?? defaults.cap;\n const dash = stroke?.dash ?? defaults.dash;\n const join = stroke?.join ?? defaults.join;\n const miterLimit = stroke?.miterLimit ?? defaults.miterLimit;\n return {\n alpha: stroke?.alpha ?? defaults.alpha,\n color: stroke?.color ?? defaults.color,\n width: stroke?.width ?? defaults.width,\n ...(alignment === undefined ? {} : { alignment }),\n ...(cap === undefined ? {} : { cap }),\n ...(dash === undefined ? {} : { dash }),\n ...(join === undefined ? {} : { join }),\n ...(miterLimit === undefined ? {} : { miterLimit }),\n };\n}\n\n/** Alignment for strokes applied to closed rendered geometry. */\nvar StrokeAlignment;\n(function (StrokeAlignment) {\n StrokeAlignment[\"Inside\"] = \"inside\";\n StrokeAlignment[\"Center\"] = \"center\";\n StrokeAlignment[\"Outside\"] = \"outside\";\n})(StrokeAlignment || (StrokeAlignment = {}));\n\nvar BoxShape;\n(function (BoxShape) {\n BoxShape[\"Rect\"] = \"rect\";\n BoxShape[\"RoundedRect\"] = \"roundedRect\";\n})(BoxShape || (BoxShape = {}));\n\nconst DEFAULT_BOX_STROKE_ALPHA = 1;\nconst DEFAULT_BOX_STROKE_COLOR = 0x00ff66;\nconst DEFAULT_BOX_STROKE_WIDTH = 2;\n/**\n * Default configurable box style.\n *\n * This is the simplest `supervision-js` equivalent of a box annotator: it\n * converts detections with `rect` geometry into renderer-neutral box draw\n * instructions. Use options such as `shape`, `cornerRadius`, `stroke`, and\n * `fill` for static or per-detection styling.\n */\nclass BaseBoxStyle {\n options;\n constructor(options = {}) {\n this.options = options;\n }\n resolve(detection, context) {\n if (!detection.rect ||\n context.hidden ||\n this.options.shouldRender?.(detection, context) === false) {\n return undefined;\n }\n const shape = this.resolveShape(detection, context);\n const cornerRadius = this.resolveCornerRadius(detection, context, shape);\n const instruction = {\n fill: this.resolveFill(detection, context),\n rect: detection.rect,\n shape,\n stroke: this.resolveStroke(detection, context),\n };\n if (cornerRadius !== undefined) {\n return {\n ...instruction,\n cornerRadius,\n };\n }\n return instruction;\n }\n resolveShape(detection, context) {\n return (resolveStyleValue(this.options.shape, detection, context) ?? BoxShape.Rect);\n }\n resolveCornerRadius(detection, context, shape) {\n if (shape !== BoxShape.RoundedRect) {\n return undefined;\n }\n return resolveStyleValue(this.options.cornerRadius, detection, context);\n }\n resolveStroke(detection, context) {\n const stroke = resolveStyleValue(this.options.stroke, detection, context);\n if (stroke === null) {\n return undefined;\n }\n return resolveStrokeStyle(stroke, {\n alpha: DEFAULT_BOX_STROKE_ALPHA,\n color: DEFAULT_BOX_STROKE_COLOR,\n ...(context.ephemeral ? { dash: [6, 4] } : {}),\n width: DEFAULT_BOX_STROKE_WIDTH,\n });\n }\n resolveFill(detection, context) {\n const fill = resolveStyleValue(this.options.fill, detection, context);\n if (fill === null || fill === undefined) {\n return undefined;\n }\n return {\n alpha: fill.alpha ?? 1,\n color: fill.color ?? DEFAULT_BOX_STROKE_COLOR,\n };\n }\n}\n\nconst DEFAULT_CORNER_LENGTH = 20;\nconst DEFAULT_CORNER_STROKE = {\n alpha: 1,\n cap: \"round\",\n color: 0x00ff66,\n join: \"round\",\n width: 2,\n};\n/**\n * Configurable four-corner decoration derived from a detection rectangle.\n *\n * This is the BoxCornerAnnotator-style facade: it keeps the rectangle semantic\n * and draws only its four open corner segments.\n */\nclass BaseBoxCornerStyle {\n options;\n constructor(options = {}) {\n this.options = options;\n }\n resolve(detection, context) {\n if (!detection.rect ||\n context.hidden ||\n this.options.shouldRender?.(detection, context) === false) {\n return undefined;\n }\n const resolvedLength = resolveStyleValue(this.options.length, detection, context) ??\n DEFAULT_CORNER_LENGTH;\n const viewportScale = context.viewportScale && context.viewportScale > 0\n ? context.viewportScale\n : 1;\n const length = Math.min(Math.max(0, resolvedLength / viewportScale), detection.rect.width / 2, detection.rect.height / 2);\n const stroke = resolveStyleValue(this.options.stroke, detection, context);\n if (length <= 0 || stroke === null)\n return undefined;\n const { height, width, x, y } = detection.rect;\n const left = x - width / 2;\n const right = x + width / 2;\n const top = y - height / 2;\n const bottom = y + height / 2;\n const resolvedStroke = resolveStrokeStyle(stroke, DEFAULT_CORNER_STROKE);\n const openStroke = {\n alpha: resolvedStroke.alpha,\n cap: resolvedStroke.cap,\n color: resolvedStroke.color,\n dash: resolvedStroke.dash,\n join: resolvedStroke.join,\n miterLimit: resolvedStroke.miterLimit,\n width: resolvedStroke.width,\n };\n return {\n segments: [\n [\n { x: left + length, y: top },\n { x: left, y: top },\n { x: left, y: top + length },\n ],\n [\n { x: right - length, y: top },\n { x: right, y: top },\n { x: right, y: top + length },\n ],\n [\n { x: left + length, y: bottom },\n { x: left, y: bottom },\n { x: left, y: bottom - length },\n ],\n [\n { x: right - length, y: bottom },\n { x: right, y: bottom },\n { x: right, y: bottom - length },\n ],\n ],\n stroke: openStroke,\n };\n }\n}\n\nvar FocusTargetMode;\n(function (FocusTargetMode) {\n FocusTargetMode[\"Hovered\"] = \"hovered\";\n FocusTargetMode[\"Selected\"] = \"selected\";\n FocusTargetMode[\"HoveredAndSelected\"] = \"hoveredAndSelected\";\n FocusTargetMode[\"Ambient\"] = \"ambient\";\n})(FocusTargetMode || (FocusTargetMode = {}));\n\nconst DEFAULT_FOCUS_FILL_COLOR = 0x020617;\nconst DEFAULT_FOCUS_FILL_ALPHA = 0.45;\nconst DEFAULT_FOCUS_CORNER_RADIUS = 8;\n/**\n * Default configurable focus style.\n *\n * Focus styles dim the current media frame while leaving the selected or\n * hovered detections visible. Renderers may use prepared mask artifacts for\n * shape-accurate cutouts, falling back to detection rectangles when needed.\n */\nclass BaseFocusStyle {\n options;\n constructor(options = {}) {\n this.options = options;\n }\n resolve(context) {\n if (this.options.shouldRender?.(context) === false) {\n return undefined;\n }\n const targetMode = this.options.targetMode ?? FocusTargetMode.Selected;\n const targets = getFocusTargets(context, targetMode);\n const ambient = targetMode === FocusTargetMode.Ambient &&\n !context.selectedPick &&\n !context.hoveredPick;\n const fill = resolveFocusStyleValue(this.options.fill, context);\n if (targets.length === 0 || fill === null) {\n return undefined;\n }\n return {\n fallback: this.resolveFallback(context),\n fill: {\n alpha: fill?.alpha ?? DEFAULT_FOCUS_FILL_ALPHA,\n color: fill?.color ?? DEFAULT_FOCUS_FILL_COLOR,\n },\n targetMode,\n targets,\n ...(ambient ? { ambient: true } : {}),\n };\n }\n resolveFallback(context) {\n const shape = resolveFocusStyleValue(this.options.shape, context) ??\n BoxShape.RoundedRect;\n const cornerRadius = shape === BoxShape.RoundedRect\n ? (resolveFocusStyleValue(this.options.cornerRadius, context) ??\n DEFAULT_FOCUS_CORNER_RADIUS)\n : undefined;\n if (cornerRadius === undefined) {\n return { shape };\n }\n return {\n cornerRadius,\n shape,\n };\n }\n}\nfunction getFocusTargets(context, targetMode) {\n const targets = [];\n if (targetMode === FocusTargetMode.Ambient) {\n if (context.selectedPick)\n return [context.selectedPick];\n if (context.hoveredPick)\n return [context.hoveredPick];\n return context.frame.detections.map((detection, detectionIndex) => ({\n detection,\n detectionIndex,\n frame: context.frame,\n mediaTime: context.mediaTime,\n point: detection.rect\n ? { x: detection.rect.x, y: detection.rect.y }\n : { x: 0, y: 0 },\n target: detection.mask\n ? DetectionPickTarget.Mask\n : detection.polygon\n ? DetectionPickTarget.Polygon\n : DetectionPickTarget.Box,\n }));\n }\n if ((targetMode === FocusTargetMode.Selected ||\n targetMode === FocusTargetMode.HoveredAndSelected) &&\n context.selectedPick) {\n targets.push(context.selectedPick);\n }\n if ((targetMode === FocusTargetMode.Hovered ||\n targetMode === FocusTargetMode.HoveredAndSelected) &&\n context.hoveredPick) {\n targets.push(context.hoveredPick);\n }\n return dedupeTargetsForFrame(targets, context.frame);\n}\nfunction dedupeTargetsForFrame(targets, frame) {\n const keys = new Set();\n const dedupedTargets = [];\n for (const target of targets) {\n if (target.frame !== frame) {\n continue;\n }\n const key = `${target.detectionIndex}:${target.target}`;\n if (keys.has(key)) {\n continue;\n }\n keys.add(key);\n dedupedTargets.push(target);\n }\n return dedupedTargets;\n}\nfunction resolveFocusStyleValue(value, context) {\n if (typeof value === \"function\") {\n return value(context);\n }\n return value;\n}\n\nvar DetectionInteractionState;\n(function (DetectionInteractionState) {\n DetectionInteractionState[\"Hovered\"] = \"hovered\";\n DetectionInteractionState[\"Selected\"] = \"selected\";\n})(DetectionInteractionState || (DetectionInteractionState = {}));\n\nconst DEFAULT_HOVER_FILL_COLOR = 0x67e8f9;\nconst DEFAULT_HOVER_FILL_ALPHA = 0.1;\nconst DEFAULT_HOVER_STROKE_COLOR = 0x67e8f9;\nconst DEFAULT_HOVER_STROKE_ALPHA = 0.95;\nconst DEFAULT_HOVER_STROKE_WIDTH = 3;\nconst DEFAULT_SELECTED_FILL_COLOR = 0xfde047;\nconst DEFAULT_SELECTED_FILL_ALPHA = 0.18;\nconst DEFAULT_SELECTED_STROKE_COLOR = 0xfde047;\nconst DEFAULT_SELECTED_STROKE_ALPHA = 1;\nconst DEFAULT_SELECTED_STROKE_WIDTH = 4;\n/**\n * Default configurable interaction style.\n *\n * It resolves hover and selected picks into state-specific presentations using\n * the same style contracts as normal boxes, masks, and labels. The older\n * rectangle options are kept as compatibility sugar and resolve to a box style.\n */\nclass BaseInteractionStyle {\n options;\n constructor(options = {}) {\n this.options = options;\n }\n resolve(detection, context) {\n if (this.options.shouldRender?.(detection, context) === false) {\n return undefined;\n }\n const statePresentation = context.state === DetectionInteractionState.Selected\n ? this.options.selected\n : this.options.hovered;\n if (statePresentation !== undefined) {\n return statePresentation ?? undefined;\n }\n const rect = detection.rect;\n if (!rect) {\n return undefined;\n }\n return {\n boxStyle: createResolvedBoxStyle(this.resolveBoxInstruction(detection, context, rect)),\n };\n }\n resolveBoxInstruction(detection, context, rect) {\n const shape = this.resolveShape(detection, context);\n const cornerRadius = this.resolveCornerRadius(detection, context, shape);\n const instruction = {\n fill: this.resolveFill(detection, context),\n rect,\n shape,\n stroke: this.resolveStroke(detection, context),\n };\n if (cornerRadius !== undefined) {\n return {\n ...instruction,\n cornerRadius,\n };\n }\n return instruction;\n }\n resolveShape(detection, context) {\n return (resolveStyleValue(this.options.shape, detection, context) ?? BoxShape.Rect);\n }\n resolveCornerRadius(detection, context, shape) {\n if (shape !== BoxShape.RoundedRect) {\n return undefined;\n }\n return resolveStyleValue(this.options.cornerRadius, detection, context);\n }\n resolveStroke(detection, context) {\n const stroke = resolveStyleValue(this.options.stroke, detection, context);\n if (stroke === null) {\n return undefined;\n }\n const defaults = getStateDefaults(context.state);\n return resolveStrokeStyle(stroke, {\n alpha: defaults.strokeAlpha,\n color: defaults.strokeColor,\n width: defaults.strokeWidth,\n });\n }\n resolveFill(detection, context) {\n const fill = resolveStyleValue(this.options.fill, detection, context);\n if (fill === null) {\n return undefined;\n }\n const defaults = getStateDefaults(context.state);\n return {\n alpha: fill?.alpha ?? defaults.fillAlpha,\n color: fill?.color ?? defaults.fillColor,\n };\n }\n}\nfunction createResolvedBoxStyle(instruction) {\n return {\n resolve() {\n return instruction;\n },\n };\n}\nfunction getStateDefaults(state) {\n if (state === DetectionInteractionState.Selected) {\n return {\n fillAlpha: DEFAULT_SELECTED_FILL_ALPHA,\n fillColor: DEFAULT_SELECTED_FILL_COLOR,\n strokeAlpha: DEFAULT_SELECTED_STROKE_ALPHA,\n strokeColor: DEFAULT_SELECTED_STROKE_COLOR,\n strokeWidth: DEFAULT_SELECTED_STROKE_WIDTH,\n };\n }\n return {\n fillAlpha: DEFAULT_HOVER_FILL_ALPHA,\n fillColor: DEFAULT_HOVER_FILL_COLOR,\n strokeAlpha: DEFAULT_HOVER_STROKE_ALPHA,\n strokeColor: DEFAULT_HOVER_STROKE_COLOR,\n strokeWidth: DEFAULT_HOVER_STROKE_WIDTH,\n };\n}\n\nvar LabelPlacement;\n(function (LabelPlacement) {\n LabelPlacement[\"Top\"] = \"top\";\n LabelPlacement[\"Bottom\"] = \"bottom\";\n LabelPlacement[\"InsideTop\"] = \"insideTop\";\n LabelPlacement[\"InsideBottom\"] = \"insideBottom\";\n LabelPlacement[\"Center\"] = \"center\";\n})(LabelPlacement || (LabelPlacement = {}));\nvar LabelVisibilityMode;\n(function (LabelVisibilityMode) {\n LabelVisibilityMode[\"Always\"] = \"always\";\n LabelVisibilityMode[\"HoveredOnly\"] = \"hoveredOnly\";\n})(LabelVisibilityMode || (LabelVisibilityMode = {}));\n\n/** Returns black or white for readable text over an RGB background. */\nfunction resolveContrastTextColor(color) {\n \"worklet\";\n const red = (color >> 16) & 0xff;\n const green = (color >> 8) & 0xff;\n const blue = color & 0xff;\n const luminance = (red * 299 + green * 587 + blue * 114) / 1000;\n return luminance >= 150 ? 0x111111 : 0xffffff;\n}\nfunction lightenColor(color, amount = 0.14) {\n \"worklet\";\n const mix = (value) => Math.round(value + (255 - value) * amount);\n return ((mix((color >> 16) & 0xff) << 16) |\n (mix((color >> 8) & 0xff) << 8) |\n mix(color & 0xff));\n}\n\n/**\n * Default label style.\n *\n * Resolves labels from `className`, `metadata.label`, or a custom text\n * resolver, optionally including confidence.\n */\nclass BaseLabelStyle {\n includeConfidence;\n offsetY;\n options;\n constructor(options = {}) {\n this.options = options;\n this.includeConfidence = options.includeConfidence ?? false;\n this.offsetY = options.offsetY ?? 0;\n }\n resolve(detection, context) {\n if (!getDetectionRect(detection) ||\n context.hidden ||\n (this.options.visibilityMode === LabelVisibilityMode.HoveredOnly &&\n !context.hovered) ||\n this.options.shouldRender?.(detection, context) === false) {\n return undefined;\n }\n const text = resolveStyleValue(this.options.text, detection, context) ??\n formatDetectionLabel(detection, this.includeConfidence);\n if (!text) {\n return undefined;\n }\n const background = this.resolveBackground(detection, context);\n return {\n background,\n ...this.resolveOffset(detection, context),\n placement: this.resolvePlacement(detection, context),\n rect: getDetectionRect(detection),\n text,\n textStyle: this.resolveTextStyle(detection, context, background),\n };\n }\n resolveBackground(detection, context) {\n const background = resolveStyleValue(this.options.background, detection, context);\n return {\n alpha: background?.alpha ?? 0.72,\n color: context.hovered\n ? lightenColor(background?.color ?? 0x111827)\n : (background?.color ?? 0x111827),\n cornerRadius: background?.cornerRadius ?? 4,\n paddingX: background?.paddingX ?? 6,\n paddingY: background?.paddingY ?? 3,\n ...(background?.topCornersOnly === undefined\n ? {}\n : { topCornersOnly: background.topCornersOnly }),\n };\n }\n resolveTextStyle(detection, context, background) {\n const textStyle = resolveStyleValue(this.options.textStyle, detection, context);\n return {\n alpha: textStyle?.alpha ?? 1,\n color: textStyle?.color ?? resolveContrastTextColor(background.color),\n fontFamily: textStyle?.fontFamily ?? \"Inter, sans-serif\",\n fontSize: textStyle?.fontSize ?? 13,\n fontWeight: textStyle?.fontWeight ?? \"600\",\n };\n }\n resolveOffset(detection, context) {\n const offset = resolveStyleValue(this.options.offset, detection, context);\n if (offset === null) {\n return {};\n }\n return {\n ...(offset?.x === undefined ? {} : { offsetX: offset.x }),\n offsetY: offset?.y ?? this.offsetY,\n };\n }\n resolvePlacement(detection, context) {\n return (resolveStyleValue(this.options.placement, detection, context) ??\n LabelPlacement.Top);\n }\n}\nfunction formatDetectionLabel(detection, includeConfidence) {\n const label = detection.className ??\n (typeof detection.metadata?.label === \"string\"\n ? detection.metadata.label\n : undefined);\n if (!label) {\n return undefined;\n }\n if (!includeConfidence || detection.confidence === undefined) {\n return label;\n }\n return `${label} ${Math.round(detection.confidence * 100)}%`;\n}\n\nvar MaskRenderMode;\n(function (MaskRenderMode) {\n MaskRenderMode[\"FillAndStroke\"] = \"fillAndStroke\";\n MaskRenderMode[\"FillOnly\"] = \"fillOnly\";\n MaskRenderMode[\"StrokeOnly\"] = \"strokeOnly\";\n})(MaskRenderMode || (MaskRenderMode = {}));\n\n/**\n * Default compressed-RLE mask style.\n *\n * Static color/stroke options can reuse prepared mask artifacts by key. Dynamic\n * color or stroke resolvers intentionally do not expose an artifact key, so new\n * style objects rebuild prepared palettes instead of reusing stale ones.\n */\nclass BaseMaskStyle {\n artifactKey;\n opacity;\n options;\n constructor(options = {}) {\n this.options = options;\n this.opacity = clampOpacity(options.opacity ?? options.alpha ?? 0.35);\n this.artifactKey = createArtifactKey(options);\n }\n resolve(detection, context) {\n if (!detection.mask ||\n context.hidden ||\n this.options.shouldRender?.(detection, context) === false) {\n return undefined;\n }\n const color = resolveStyleValue(this.options.color, detection, context) ??\n DEFAULT_MASK_COLOR;\n const mode = resolveStyleValue(this.options.mode, detection, context) ??\n MaskRenderMode.FillAndStroke;\n const stroke = resolveStroke(resolveStyleValue(this.options.stroke, detection, context), color, mode);\n return {\n alpha: mode === MaskRenderMode.StrokeOnly\n ? 0\n : clampOpacity(resolveStyleValue(this.options.fillAlpha, detection, context) ??\n 1),\n color,\n mask: detection.mask,\n stroke: mode === MaskRenderMode.FillOnly ? undefined : stroke,\n };\n }\n}\nconst DEFAULT_MASK_COLOR = 0x00ff66;\nconst DEFAULT_MASK_STROKE_ALPHA = 1;\nconst DEFAULT_MASK_STROKE_WIDTH = 1;\nfunction clampOpacity(opacity) {\n return Number.isFinite(opacity) ? Math.max(0, Math.min(opacity, 1)) : 1;\n}\nfunction serializeStroke(stroke) {\n if (!stroke) {\n return \"none\";\n }\n return `${stroke.color}:${stroke.alpha}:${stroke.width}`;\n}\nfunction normalizeStroke(stroke, fallbackColor) {\n if (!stroke) {\n return undefined;\n }\n return {\n alpha: stroke.alpha ?? DEFAULT_MASK_STROKE_ALPHA,\n color: stroke.color ?? fallbackColor,\n width: stroke.width ?? DEFAULT_MASK_STROKE_WIDTH,\n };\n}\nfunction resolveStroke(stroke, fallbackColor, mode) {\n if (mode === MaskRenderMode.FillOnly) {\n return undefined;\n }\n if (mode === MaskRenderMode.StrokeOnly && stroke === undefined) {\n return normalizeStroke({}, fallbackColor);\n }\n return normalizeStroke(stroke, fallbackColor);\n}\nfunction createArtifactKey(options) {\n if (typeof options.color === \"function\" ||\n typeof options.fillAlpha === \"function\" ||\n typeof options.mode === \"function\" ||\n typeof options.stroke === \"function\") {\n return undefined;\n }\n const color = options.color ?? DEFAULT_MASK_COLOR;\n const fillAlpha = options.fillAlpha ?? 1;\n const mode = options.mode ?? MaskRenderMode.FillAndStroke;\n return `base:${color}:${fillAlpha}:${mode}:${serializeStroke(resolveStroke(options.stroke, color, mode))}`;\n}\n\n/**\n * Discriminator for renderer-neutral shape draw instructions.\n */\nvar ShapeInstructionKind;\n(function (ShapeInstructionKind) {\n ShapeInstructionKind[\"Ellipse\"] = \"ellipse\";\n ShapeInstructionKind[\"Marker\"] = \"marker\";\n ShapeInstructionKind[\"Path\"] = \"path\";\n})(ShapeInstructionKind || (ShapeInstructionKind = {}));\nvar MarkerShape;\n(function (MarkerShape) {\n MarkerShape[\"Circle\"] = \"circle\";\n MarkerShape[\"Cross\"] = \"cross\";\n MarkerShape[\"Square\"] = \"square\";\n MarkerShape[\"Triangle\"] = \"triangle\";\n})(MarkerShape || (MarkerShape = {}));\n/**\n * Coordinate space for marker sizing. Media sizes scale with the viewport;\n * screen sizes stay constant on screen, matching stroke-width semantics.\n */\nvar MarkerSizeSpace;\n(function (MarkerSizeSpace) {\n MarkerSizeSpace[\"Media\"] = \"media\";\n MarkerSizeSpace[\"Screen\"] = \"screen\";\n})(MarkerSizeSpace || (MarkerSizeSpace = {}));\n\nconst DEFAULT_FILL = { alpha: 1, color: 0x00ff66 };\nconst DEFAULT_STROKE = { alpha: 1, color: 0xffffff, width: 1 };\nconst DEFAULT_SIZE = 12;\n/**\n * Configurable marker renderer style anchored to semantic detection geometry.\n *\n * Use `center` for any domain-specific anchor (keypoint, centroid, or custom\n * metadata). Without it, the marker lands at the detection rectangle center.\n */\nclass BaseMarkerStyle {\n options;\n constructor(options = {}) {\n this.options = options;\n }\n resolve(detection, context) {\n if (context.hidden ||\n this.options.shouldRender?.(detection, context) === false) {\n return undefined;\n }\n const center = resolveStyleValue(this.options.center, detection, context) ??\n (detection.rect\n ? { x: detection.rect.x, y: detection.rect.y }\n : undefined);\n const shape = resolveStyleValue(this.options.shape, detection, context) ??\n MarkerShape.Circle;\n const size = resolveStyleValue(this.options.size, detection, context) ?? DEFAULT_SIZE;\n const sizeSpace = resolveStyleValue(this.options.sizeSpace, detection, context) ??\n MarkerSizeSpace.Screen;\n const rotation = resolveStyleValue(this.options.rotation, detection, context);\n const stroke = resolveStyleValue(this.options.stroke, detection, context);\n if (!center || !Number.isFinite(size) || size <= 0) {\n return undefined;\n }\n if (shape === MarkerShape.Cross) {\n if (stroke === null) {\n return undefined;\n }\n const resolvedStroke = resolveStrokeStyle(stroke, DEFAULT_STROKE);\n const openStroke = {\n alpha: resolvedStroke.alpha,\n cap: resolvedStroke.cap,\n color: resolvedStroke.color,\n dash: resolvedStroke.dash,\n join: resolvedStroke.join,\n miterLimit: resolvedStroke.miterLimit,\n width: resolvedStroke.width,\n };\n return {\n center,\n shape,\n size,\n sizeSpace,\n ...(rotation === undefined ? {} : { rotation }),\n stroke: openStroke,\n };\n }\n const fill = resolveStyleValue(this.options.fill, detection, context);\n if (fill === null && stroke === null) {\n return undefined;\n }\n return {\n center,\n shape,\n size,\n sizeSpace,\n ...(rotation === undefined ? {} : { rotation }),\n ...(fill === null\n ? {}\n : {\n fill: {\n alpha: fill?.alpha ?? DEFAULT_FILL.alpha,\n color: fill?.color ?? DEFAULT_FILL.color,\n },\n }),\n ...(stroke === null\n ? {}\n : stroke === undefined\n ? { stroke: DEFAULT_STROKE }\n : { stroke: resolveStrokeStyle(stroke, DEFAULT_STROKE) }),\n };\n }\n}\n\nclass BasePolygonStyle {\n options;\n constructor(options = {}) {\n this.options = options;\n }\n resolve(detection, context) {\n if (!detection.polygon ||\n context.hidden ||\n detection.polygon.points.length < 3 ||\n this.options.shouldRender?.(detection, context) === false) {\n return undefined;\n }\n const fill = resolveStyleValue(this.options.fill, detection, context);\n const stroke = resolveStyleValue(this.options.stroke, detection, context);\n return {\n points: detection.polygon.points,\n ...(fill === null\n ? {}\n : {\n fill: {\n alpha: fill?.alpha ?? 0.16,\n color: fill?.color ?? 0x00ff66,\n },\n }),\n ...(stroke === null\n ? {}\n : {\n stroke: resolveStrokeStyle(stroke, {\n alpha: 1,\n color: 0x00ff66,\n width: 2,\n }),\n }),\n };\n }\n}\n\nclass BasePolylineStyle {\n options;\n constructor(options = {}) {\n this.options = options;\n }\n resolve(detection, context) {\n if (!detection.polyline ||\n context.hidden ||\n detection.polyline.points.length < 2 ||\n this.options.shouldRender?.(detection, context) === false) {\n return undefined;\n }\n const stroke = resolveStyleValue(this.options.stroke, detection, context);\n if (stroke === null) {\n return undefined;\n }\n return {\n points: detection.polyline.points,\n stroke: resolveStrokeStyle(stroke, {\n alpha: 1,\n color: 0x00ff66,\n width: 2,\n }),\n };\n }\n}\n\nvar KeypointMarkerShape;\n(function (KeypointMarkerShape) {\n KeypointMarkerShape[\"Circle\"] = \"circle\";\n KeypointMarkerShape[\"Cross\"] = \"cross\";\n})(KeypointMarkerShape || (KeypointMarkerShape = {}));\n\nclass BaseKeypointStyle {\n options;\n constructor(options = {}) {\n this.options = options;\n }\n resolve(detection, context) {\n const geometry = detection.keypoints;\n if (!geometry ||\n context.hidden ||\n this.options.shouldRender?.(detection, context) === false) {\n return undefined;\n }\n const definition = detection.className\n ? this.options.definitions?.[detection.className]\n : undefined;\n const markerFill = resolveStyleValue(this.options.markerFill, detection, context);\n const markerStroke = resolveStyleValue(this.options.markerStroke, detection, context);\n const edgeStroke = resolveStyleValue(this.options.edgeStroke, detection, context);\n const shadowStroke = resolveStyleValue(this.options.edgeShadowStroke, detection, context);\n const radius = resolveStyleValue(this.options.radius, detection, context) ?? 6;\n const edges = geometry.edges.map(([fromIndex, toIndex], edgeIndex) => ({\n from: geometry.points[fromIndex],\n to: geometry.points[toIndex],\n stroke: resolveStrokeStyle(definition?.edges[edgeIndex]?.color === undefined\n ? edgeStroke\n : { ...edgeStroke, color: definition.edges[edgeIndex].color }, {\n alpha: 1,\n color: 0x00ff66,\n width: 2,\n }),\n ...(shadowStroke === null\n ? {}\n : {\n shadowStroke: resolveStrokeStyle(shadowStroke, {\n alpha: 0.65,\n color: 0x000000,\n width: 4,\n }),\n }),\n }));\n const markers = geometry.points.flatMap((point, index) => {\n const visibility = geometry.visibility?.[index] ?? KeypointVisibility.Visible;\n if (visibility === KeypointVisibility.NotLabeled) {\n return [];\n }\n return [\n {\n fill: {\n alpha: markerFill?.alpha ?? 1,\n color: definition?.vertices[index]?.color ??\n markerFill?.color ??\n 0x00ff66,\n },\n index,\n point,\n radius,\n shape: visibility === KeypointVisibility.Occluded\n ? KeypointMarkerShape.Cross\n : KeypointMarkerShape.Circle,\n stroke: resolveStrokeStyle(markerStroke, {\n alpha: 1,\n color: 0xffffff,\n width: 2,\n }),\n },\n ];\n });\n return { edges, markers };\n }\n}\n\nconst SUPERVISION_ROBOFLOW_COLOR = 0xa351fb;\nconst DEFAULT_DETECTION_COLOR_SEQUENCE = [\n createClassColorStyle(0x38bdf8, 0x164e63, 0xecfeff, 0x7dd3fc),\n createClassColorStyle(0x22c55e, 0x14532d, 0xf0fdf4, 0x86efac),\n createClassColorStyle(0xa78bfa, 0x4c1d95, 0xf5f3ff, 0xc4b5fd),\n createClassColorStyle(0xfacc15, 0x713f12, 0xfffbeb, 0xfde047),\n createClassColorStyle(0xf97316, 0x7c2d12, 0xfff7ed, 0xffa23a),\n createClassColorStyle(0xf472b6, 0x831843, 0xfdf2f8, 0xf9a8d4),\n createClassColorStyle(0x60a5fa, 0x1e3a8a, 0xeff6ff, 0x93c5fd),\n createClassColorStyle(0xfb7185, 0x881337, 0xfff1f2, 0xfda4af),\n createClassColorStyle(0x34d399, 0x064e3b, 0xecfdf5, 0x6ee7b7),\n createClassColorStyle(0xe879f9, 0x701a75, 0xfdf4ff, 0xf0abfc),\n];\nconst DEFAULT_DETECTION_CLASS_STYLES = {\n basketball: DEFAULT_DETECTION_COLOR_SEQUENCE[4],\n bed: DEFAULT_DETECTION_COLOR_SEQUENCE[5],\n bottle: DEFAULT_DETECTION_COLOR_SEQUENCE[4],\n \"cell phone\": DEFAULT_DETECTION_COLOR_SEQUENCE[8],\n cow: DEFAULT_DETECTION_COLOR_SEQUENCE[2],\n cup: DEFAULT_DETECTION_COLOR_SEQUENCE[3],\n horse: DEFAULT_DETECTION_COLOR_SEQUENCE[0],\n keyboard: DEFAULT_DETECTION_COLOR_SEQUENCE[1],\n knife: DEFAULT_DETECTION_COLOR_SEQUENCE[7],\n laptop: DEFAULT_DETECTION_COLOR_SEQUENCE[6],\n person: DEFAULT_DETECTION_COLOR_SEQUENCE[1],\n \"potted plant\": DEFAULT_DETECTION_COLOR_SEQUENCE[8],\n \"sports ball\": DEFAULT_DETECTION_COLOR_SEQUENCE[4],\n tv: DEFAULT_DETECTION_COLOR_SEQUENCE[2],\n \"white team player\": createClassColorStyle(0xf8fafc, 0x334155, 0xffffff, 0xffffff),\n \"yellow team player\": DEFAULT_DETECTION_COLOR_SEQUENCE[3],\n};\n// The \"worklet\" directives below are inert everywhere except React Native:\n// browsers and Node see a no-op string, while React Native's worklets Babel\n// plugin makes these functions callable inside frame worklets, so every\n// platform resolves detection colors through this one implementation.\n// Worklet constraint on ordering: the plugin turns marked declarations into\n// non-hoisted assignments that capture each other at module-init time, so\n// helpers must be defined before the functions that call them.\nfunction normalizeDetectionClassName(className) {\n \"worklet\";\n return (className ?? \"\")\n .trim()\n .toLowerCase()\n .replace(/[_-]/g, \" \")\n .replace(/\\s+/g, \" \");\n}\nfunction hashClassName(className) {\n \"worklet\";\n let hash = 0;\n for (let index = 0; index < className.length; index += 1) {\n hash = (hash * 31 + className.charCodeAt(index)) >>> 0;\n }\n return hash;\n}\nfunction resolveDetectionClassColorStyle(className) {\n \"worklet\";\n const normalizedClassName = normalizeDetectionClassName(className);\n const knownStyle = DEFAULT_DETECTION_CLASS_STYLES[normalizedClassName];\n if (knownStyle) {\n return knownStyle;\n }\n return DEFAULT_DETECTION_COLOR_SEQUENCE[hashClassName(normalizedClassName) % DEFAULT_DETECTION_COLOR_SEQUENCE.length];\n}\nfunction createClassColorStyle(fill, labelBackground, labelText, stroke) {\n return {\n fill,\n labelBackground,\n labelText,\n stroke,\n };\n}\n\nconst DEFAULT_BOX_CORNER_RADIUS = 1;\nconst DEFAULT_ELLIPSE_AXIS_RATIO = 0.35;\nconst DEFAULT_ELLIPSE_START_ANGLE = (-45 * Math.PI) / 180;\nconst DEFAULT_ELLIPSE_END_ANGLE = (235 * Math.PI) / 180;\nconst DEFAULT_OUTLINE_WIDTH = 2;\nconst DEFAULT_FILL_ALPHA = 0.08;\nconst DEFAULT_MASK_FILL_ALPHA = 0.45;\nconst DEFAULT_KEYPOINT_EDGE_WIDTH = 1.5;\nconst DEFAULT_KEYPOINT_SHADOW_WIDTH = 3;\nconst DEFAULT_KEYPOINT_SHADOW_ALPHA = 0.25;\nconst DEFAULT_KEYPOINT_RADIUS = 3.5;\nconst DEFAULT_LABEL_CORNER_RADIUS = 4;\nconst DEFAULT_LABEL_PADDING_X = 6;\nconst DEFAULT_LABEL_PADDING_Y = 3;\nconst DEFAULT_LABEL_FONT_SIZE = 12;\nconst DEFAULT_LABEL_FONT_FAMILY = \"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace\";\n/**\n * Builds the canonical, unselected annotation presentation used by Roboflow's\n * Core annotation editor. It deliberately excludes host-specific interaction,\n * editing, focus, and theme behaviour so consumers can compose those layers.\n */\nfunction createDefaultAnnotationPresentation(options = {}) {\n return {\n boxStyle: createDefaultBoxStyle(options),\n keypointStyle: createDefaultKeypointStyle(options),\n labelStyle: createDefaultLabelStyle(options),\n maskStyle: createDefaultMaskStyle(options),\n polygonStyle: createDefaultPolygonStyle(options),\n polylineStyle: createDefaultPolylineStyle(options),\n };\n}\n/**\n * The canonical default style of one built-in layer.\n *\n * These exist so a renderer kind can build only the style it needs instead of\n * the whole default presentation. Keep them equivalent to the matching field of\n * {@link createDefaultAnnotationPresentation}.\n */\nfunction createDefaultBoxStyle(options = {}) {\n const getClassColor = createClassColorResolver(options);\n return new BaseBoxStyle({\n cornerRadius: DEFAULT_BOX_CORNER_RADIUS,\n fill: (detection) => ({\n alpha: DEFAULT_FILL_ALPHA,\n color: getClassColor(detection),\n }),\n shape: BoxShape.RoundedRect,\n shouldRender: (detection) => !(detection.mask ||\n detection.polygon ||\n detection.polyline ||\n detection.keypoints),\n stroke: (detection) => ({\n alpha: 1,\n color: getClassColor(detection),\n width: DEFAULT_OUTLINE_WIDTH,\n }),\n });\n}\n/** Canonical opt-in BoxCornerAnnotator-style presentation. */\nfunction createDefaultBoxCornerStyle(options = {}) {\n const getClassColor = createClassColorResolver(options);\n return new BaseBoxCornerStyle({\n stroke: (detection) => ({ color: getClassColor(detection) }),\n });\n}\n/**\n * Matches the Python Supervision EllipseAnnotator: an elliptical footprint\n * arc under the detection box, swept from -45deg to 235deg, in the class\n * color.\n */\nfunction createDefaultEllipseStyle(options = {}) {\n const getClassColor = createClassColorResolver(options);\n return {\n resolve(detection, context) {\n if (!detection.rect || context.hidden) {\n return undefined;\n }\n const radiusX = detection.rect.width / 2;\n const radiusY = radiusX * DEFAULT_ELLIPSE_AXIS_RATIO;\n return {\n center: {\n x: detection.rect.x,\n // Bottom-tangent so the arc hugs the detection instead of dipping\n // below its feet.\n y: detection.rect.y + detection.rect.height / 2 - radiusY,\n },\n endAngle: DEFAULT_ELLIPSE_END_ANGLE,\n radiusX,\n radiusY,\n startAngle: DEFAULT_ELLIPSE_START_ANGLE,\n stroke: {\n alpha: 1,\n color: getClassColor(detection),\n width: DEFAULT_OUTLINE_WIDTH,\n },\n };\n },\n };\n}\nfunction createDefaultKeypointStyle(options = {}) {\n const getClassColor = createClassColorResolver(options);\n return new BaseKeypointStyle({\n ...(options.skeletonDefinitions === undefined\n ? {}\n : { definitions: options.skeletonDefinitions }),\n edgeShadowStroke: {\n alpha: DEFAULT_KEYPOINT_SHADOW_ALPHA,\n color: 0x000000,\n width: DEFAULT_KEYPOINT_SHADOW_WIDTH,\n },\n edgeStroke: (detection) => ({\n alpha: 1,\n color: getClassColor(detection),\n width: DEFAULT_KEYPOINT_EDGE_WIDTH,\n }),\n markerFill: (detection) => ({\n alpha: 1,\n color: getClassColor(detection),\n }),\n markerStroke: { alpha: 1, color: 0xffffff, width: 1 },\n radius: DEFAULT_KEYPOINT_RADIUS,\n });\n}\nfunction createDefaultLabelStyle(options = {}) {\n const getClassColor = createClassColorResolver(options);\n return new BaseLabelStyle({\n background: (detection) => ({\n alpha: 1,\n color: getClassColor(detection),\n cornerRadius: DEFAULT_LABEL_CORNER_RADIUS,\n paddingX: DEFAULT_LABEL_PADDING_X,\n paddingY: DEFAULT_LABEL_PADDING_Y,\n topCornersOnly: true,\n }),\n includeConfidence: options.includeConfidence,\n placement: LabelPlacement.Top,\n textStyle: {\n fontFamily: DEFAULT_LABEL_FONT_FAMILY,\n fontSize: DEFAULT_LABEL_FONT_SIZE,\n fontWeight: \"600\",\n },\n });\n}\nfunction createDefaultMaskStyle(options = {}) {\n const getClassColor = createClassColorResolver(options);\n return new BaseMaskStyle({\n color: (detection) => getClassColor(detection),\n fillAlpha: DEFAULT_MASK_FILL_ALPHA,\n mode: MaskRenderMode.FillAndStroke,\n opacity: 1,\n stroke: (detection) => ({\n alpha: 1,\n color: getClassColor(detection),\n width: DEFAULT_OUTLINE_WIDTH,\n }),\n });\n}\n/** Canonical opt-in marker presentation. */\nfunction createDefaultMarkerStyle() {\n return new BaseMarkerStyle();\n}\nconst DEFAULT_MASK_HALO_ALPHA = 0.6;\nconst DEFAULT_MASK_HALO_SPREAD = 12;\n/**\n * Canonical mask halo: a class-colored glow that follows the mask silhouette.\n * Used when the `maskHalo` renderer is listed without an explicit style.\n */\nfunction createDefaultMaskHaloStyle(options = {}) {\n const getClassColor = createClassColorResolver(options);\n return {\n resolve(detection, context) {\n if (!detection.mask || context.hidden) {\n return undefined;\n }\n return {\n alpha: DEFAULT_MASK_HALO_ALPHA,\n color: getClassColor(detection),\n spread: DEFAULT_MASK_HALO_SPREAD,\n };\n },\n };\n}\nfunction createDefaultPolygonStyle(options = {}) {\n const getClassColor = createClassColorResolver(options);\n return new BasePolygonStyle({\n fill: (detection) => ({\n alpha: DEFAULT_FILL_ALPHA,\n color: getClassColor(detection),\n }),\n stroke: (detection) => ({\n alpha: 1,\n color: getClassColor(detection),\n width: DEFAULT_OUTLINE_WIDTH,\n }),\n });\n}\nfunction createDefaultPolylineStyle(options = {}) {\n const getClassColor = createClassColorResolver(options);\n return new BasePolylineStyle({\n stroke: (detection) => ({\n alpha: 1,\n color: getClassColor(detection),\n width: DEFAULT_OUTLINE_WIDTH,\n }),\n });\n}\nfunction createClassColorResolver(options) {\n return (detection) => resolveDefaultClassColor(detection, options.getClassColor);\n}\nfunction resolveDefaultClassColor(detection, getClassColor) {\n const color = getClassColor?.(detection.className);\n return typeof color === \"number\" && Number.isFinite(color)\n ? color\n : resolveDetectionClassColorStyle(detection.className).fill;\n}\n\n/**\n * The built-in annotation renderer vocabulary.\n *\n * These stay plain string literals so `{ kind: \"box\" }` remains assignable to\n * the public descriptor union from JavaScript and TypeScript alike.\n */\nconst annotationRendererKinds = [\n \"box\",\n \"box-corners\",\n \"ellipse\",\n \"keypoints\",\n \"label\",\n \"mask\",\n \"maskHalo\",\n \"marker\",\n \"polygon\",\n \"polyline\",\n \"region\",\n];\n/** Sources currently supported by a region renderer. */\nconst RegionRendererSourceKind = {\n Asset: \"asset\",\n};\n/** Media-space regions currently supported by a region renderer. */\nconst RegionRendererRegionKind = {\n Bounds: \"bounds\",\n KeypointAnchor: \"keypoint-anchor\",\n};\n/** Composition modes currently supported by a region renderer. */\nconst RegionRendererComposeMode = {\n Over: \"over\",\n};\n/**\n * Creates built-in annotation renderer descriptors for\n * `MediaRendererPresentation.renderers`.\n *\n * The currently supported renderers retain their established scene ordering:\n * masks, polygons, vectors, labels. A later renderer kind may add a new\n * composition capability without changing that ordering for existing scenes.\n */\nconst annotationRenderers = {\n box: (options) => createAnnotationRenderer(\"box\", options),\n boxCorners: (options) => createAnnotationRenderer(\"box-corners\", options),\n ellipse: (options) => createAnnotationRenderer(\"ellipse\", options),\n keypoints: (options) => createAnnotationRenderer(\"keypoints\", options),\n label: (options) => createAnnotationRenderer(\"label\", options),\n mask: (options) => createAnnotationRenderer(\"mask\", options),\n maskHalo: (options) => createAnnotationRenderer(\"maskHalo\", options),\n marker: (options) => createAnnotationRenderer(\"marker\", options),\n polygon: (options) => createAnnotationRenderer(\"polygon\", options),\n polyline: (options) => createAnnotationRenderer(\"polyline\", options),\n region: (options) => ({ kind: \"region\", ...options }),\n};\nfunction createAnnotationRenderer(kind, options) {\n // Every built-in renderer keeps its kind as its stable id. TypeScript cannot\n // narrow the descriptor union through the generic kind parameter, so the\n // descriptor shape is asserted here and covered by a focused test.\n return { id: kind, kind, ...options };\n}\n\nconst annotationRendererRegistry = {\n box: {\n cardinality: \"singleton\",\n createCanonicalStyle: createDefaultBoxStyle,\n styleField: \"boxStyle\",\n },\n \"box-corners\": {\n cardinality: \"singleton\",\n createCanonicalStyle: createDefaultBoxCornerStyle,\n styleField: \"boxCornerStyle\",\n },\n ellipse: {\n cardinality: \"singleton\",\n createCanonicalStyle: createDefaultEllipseStyle,\n styleField: \"ellipseStyle\",\n },\n keypoints: {\n cardinality: \"singleton\",\n createCanonicalStyle: createDefaultKeypointStyle,\n styleField: \"keypointStyle\",\n },\n label: {\n cardinality: \"singleton\",\n createCanonicalStyle: createDefaultLabelStyle,\n styleField: \"labelStyle\",\n },\n mask: {\n cardinality: \"singleton\",\n createCanonicalStyle: createDefaultMaskStyle,\n styleField: \"maskStyle\",\n },\n maskHalo: {\n cardinality: \"singleton\",\n createCanonicalStyle: () => createDefaultMaskHaloStyle(),\n styleField: \"maskHaloStyle\",\n },\n marker: {\n cardinality: \"singleton\",\n createCanonicalStyle: createDefaultMarkerStyle,\n styleField: \"markerStyle\",\n },\n polygon: {\n cardinality: \"singleton\",\n createCanonicalStyle: createDefaultPolygonStyle,\n styleField: \"polygonStyle\",\n },\n polyline: {\n cardinality: \"singleton\",\n createCanonicalStyle: createDefaultPolylineStyle,\n styleField: \"polylineStyle\",\n },\n region: { cardinality: \"multiple\" },\n};\nfunction isStyleBackedAnnotationRendererKind(kind) {\n return annotationRendererRegistry[kind].cardinality === \"singleton\";\n}\n/** Presentation fields owned by the given renderer kinds, in the same order. */\nfunction resolveAnnotationRendererStyleFields(kinds) {\n return kinds.flatMap((kind) => {\n const metadata = annotationRendererRegistry[kind];\n return \"styleField\" in metadata ? [metadata.styleField] : [];\n });\n}\n\n/**\n * Resolves built-in renderer descriptors into the existing specialized style\n * fields. The browser backend deliberately keeps ownership of its box, mask,\n * label, polygon, polyline, and keypoint pipelines; this normalizer only\n * supplies those pipelines with their configured style.\n */\nfunction resolveAnnotationRendererPresentation(presentation) {\n const renderers = presentation.renderers;\n if (renderers === undefined) {\n return presentation;\n }\n const resolved = {\n boxStyle: null,\n boxCornerStyle: null,\n ellipseStyle: null,\n keypointStyle: null,\n labelStyle: null,\n maskHaloStyle: null,\n maskStyle: null,\n markerStyle: null,\n polygonStyle: null,\n polylineStyle: null,\n };\n const rendererIds = new Set();\n const rendererKinds = new Set();\n for (const renderer of renderers) {\n if (rendererIds.has(renderer.id)) {\n throw new RangeError(`MediaRendererPresentation.renderers contains duplicate renderer id \"${renderer.id}\".`);\n }\n rendererIds.add(renderer.id);\n if (annotationRendererRegistry[renderer.kind].cardinality === \"singleton\" &&\n rendererKinds.has(renderer.kind)) {\n throw new RangeError(`MediaRendererPresentation.renderers contains duplicate renderer kind \"${renderer.kind}\".`);\n }\n rendererKinds.add(renderer.kind);\n if (isStyleBackedAnnotationRenderer(renderer)) {\n applyRendererStyle(resolved, presentation, renderer);\n }\n }\n return {\n ...presentation,\n ...resolved,\n };\n}\nfunction isStyleBackedAnnotationRenderer(renderer) {\n return isStyleBackedAnnotationRendererKind(renderer.kind);\n}\nfunction applyRendererStyle(resolved, configured, renderer) {\n const { createCanonicalStyle, styleField } = annotationRendererRegistry[renderer.kind];\n const configuredStyle = configured[styleField];\n const style = renderer.style !== undefined\n ? renderer.style\n : configuredStyle !== undefined\n ? configuredStyle\n : createCanonicalStyle();\n // The registry pairs each kind with the presentation field holding the same\n // style contract, but TypeScript cannot correlate that pairing across a\n // lookup on a union, so the write is asserted once here.\n resolved[styleField] = style;\n}\n\nfunction createSourceAwarePresentation(globalPresentation = {}, sources, options = {}) {\n if (!sources.some((source) => source.presentation !== undefined)) {\n return globalPresentation;\n }\n const sourcePresentations = new Map(sources.map((source) => [source.id, source.presentation]));\n const enabledStyleFields = options.enabledRendererKinds === undefined\n ? undefined\n : new Set(resolveAnnotationRendererStyleFields(options.enabledRendererKinds));\n const shouldApplySourceStyle = (style) => hasSourceStyle(sources, style) &&\n (enabledStyleFields === undefined || enabledStyleFields.has(style));\n return {\n ...globalPresentation,\n boxStyle: shouldApplySourceStyle(\"boxStyle\")\n ? new SourceAwareBoxStyle(normalizeGlobalBoxStyle(globalPresentation.boxStyle), sourcePresentations)\n : globalPresentation.boxStyle,\n boxCornerStyle: shouldApplySourceStyle(\"boxCornerStyle\")\n ? new SourceAwareBoxCornerStyle(globalPresentation.boxCornerStyle ?? null, sourcePresentations)\n : globalPresentation.boxCornerStyle,\n ellipseStyle: shouldApplySourceStyle(\"ellipseStyle\")\n ? new SourceAwareEllipseStyle(globalPresentation.ellipseStyle ?? null, sourcePresentations)\n : globalPresentation.ellipseStyle,\n labelStyle: shouldApplySourceStyle(\"labelStyle\")\n ? new SourceAwareLabelStyle(normalizeGlobalLabelStyle(globalPresentation.labelStyle), sourcePresentations)\n : globalPresentation.labelStyle,\n maskStyle: shouldApplySourceStyle(\"maskStyle\")\n ? new SourceAwareMaskStyle(normalizeGlobalMaskStyle(globalPresentation.maskStyle), sourcePresentations)\n : globalPresentation.maskStyle,\n markerStyle: shouldApplySourceStyle(\"markerStyle\")\n ? new SourceAwareMarkerStyle(normalizeGlobalMarkerStyle(globalPresentation.markerStyle), sourcePresentations)\n : globalPresentation.markerStyle,\n maskHaloStyle: shouldApplySourceStyle(\"maskHaloStyle\")\n ? new SourceAwareMaskHaloStyle(normalizeGlobalMaskHaloStyle(globalPresentation.maskHaloStyle), sourcePresentations)\n : globalPresentation.maskHaloStyle,\n polygonStyle: shouldApplySourceStyle(\"polygonStyle\")\n ? new SourceAwarePolygonStyle(normalizeGlobalPolygonStyle(globalPresentation.polygonStyle), sourcePresentations)\n : globalPresentation.polygonStyle,\n polylineStyle: shouldApplySourceStyle(\"polylineStyle\")\n ? new SourceAwarePolylineStyle(normalizeGlobalPolylineStyle(globalPresentation.polylineStyle), sourcePresentations)\n : globalPresentation.polylineStyle,\n keypointStyle: shouldApplySourceStyle(\"keypointStyle\")\n ? new SourceAwareKeypointStyle(normalizeGlobalKeypointStyle(globalPresentation.keypointStyle), sourcePresentations)\n : globalPresentation.keypointStyle,\n };\n}\nclass SourceAwareBoxCornerStyle {\n globalStyle;\n sourcePresentations;\n constructor(globalStyle, sourcePresentations) {\n this.globalStyle = globalStyle;\n this.sourcePresentations = sourcePresentations;\n }\n resolve(detection, context) {\n return resolveSourceStyle(detection, this.globalStyle, this.sourcePresentations, \"boxCornerStyle\")?.resolve(detection, context);\n }\n}\nclass SourceAwareBoxStyle {\n globalStyle;\n sourcePresentations;\n constructor(globalStyle, sourcePresentations) {\n this.globalStyle = globalStyle;\n this.sourcePresentations = sourcePresentations;\n }\n resolve(detection, context) {\n const style = resolveSourceStyle(detection, this.globalStyle, this.sourcePresentations, \"boxStyle\");\n return style?.resolve(detection, context);\n }\n}\nclass SourceAwareEllipseStyle {\n globalStyle;\n sourcePresentations;\n constructor(globalStyle, sourcePresentations) {\n this.globalStyle = globalStyle;\n this.sourcePresentations = sourcePresentations;\n }\n resolve(detection, context) {\n const style = resolveSourceStyle(detection, this.globalStyle, this.sourcePresentations, \"ellipseStyle\");\n return style?.resolve(detection, context);\n }\n}\nclass SourceAwareLabelStyle {\n globalStyle;\n sourcePresentations;\n constructor(globalStyle, sourcePresentations) {\n this.globalStyle = globalStyle;\n this.sourcePresentations = sourcePresentations;\n }\n resolve(detection, context) {\n const style = resolveSourceStyle(detection, this.globalStyle, this.sourcePresentations, \"labelStyle\");\n return style?.resolve(detection, context);\n }\n}\nclass SourceAwareMaskStyle {\n globalStyle;\n sourcePresentations;\n artifactKey = undefined;\n opacity;\n constructor(globalStyle, sourcePresentations) {\n this.globalStyle = globalStyle;\n this.sourcePresentations = sourcePresentations;\n this.opacity = globalStyle?.opacity;\n }\n resolve(detection, context) {\n const style = resolveSourceStyle(detection, this.globalStyle, this.sourcePresentations, \"maskStyle\");\n return style?.resolve(detection, context);\n }\n}\nclass SourceAwareMaskHaloStyle {\n globalStyle;\n sourcePresentations;\n constructor(globalStyle, sourcePresentations) {\n this.globalStyle = globalStyle;\n this.sourcePresentations = sourcePresentations;\n }\n resolve(detection, context) {\n const style = resolveSourceStyle(detection, this.globalStyle, this.sourcePresentations, \"maskHaloStyle\");\n return style?.resolve(detection, context);\n }\n}\nclass SourceAwareMarkerStyle {\n globalStyle;\n sourcePresentations;\n constructor(globalStyle, sourcePresentations) {\n this.globalStyle = globalStyle;\n this.sourcePresentations = sourcePresentations;\n }\n resolve(detection, context) {\n const style = resolveSourceStyle(detection, this.globalStyle, this.sourcePresentations, \"markerStyle\");\n return style?.resolve(detection, context);\n }\n}\nclass SourceAwarePolygonStyle {\n globalStyle;\n sourcePresentations;\n constructor(globalStyle, sourcePresentations) {\n this.globalStyle = globalStyle;\n this.sourcePresentations = sourcePresentations;\n }\n resolve(detection, context) {\n const style = resolveSourceStyle(detection, this.globalStyle, this.sourcePresentations, \"polygonStyle\");\n return style?.resolve(detection, context);\n }\n}\nclass SourceAwarePolylineStyle {\n globalStyle;\n sourcePresentations;\n constructor(globalStyle, sourcePresentations) {\n this.globalStyle = globalStyle;\n this.sourcePresentations = sourcePresentations;\n }\n resolve(detection, context) {\n const style = resolveSourceStyle(detection, this.globalStyle, this.sourcePresentations, \"polylineStyle\");\n return style?.resolve(detection, context);\n }\n}\nclass SourceAwareKeypointStyle {\n globalStyle;\n sourcePresentations;\n constructor(globalStyle, sourcePresentations) {\n this.globalStyle = globalStyle;\n this.sourcePresentations = sourcePresentations;\n }\n resolve(detection, context) {\n const style = resolveSourceStyle(detection, this.globalStyle, this.sourcePresentations, \"keypointStyle\");\n return style?.resolve(detection, context);\n }\n}\nfunction hasSourceStyle(sources, key) {\n return sources.some((source) => source.presentation?.[key] !== undefined);\n}\nfunction resolveSourceStyle(detection, globalStyle, sourcePresentations, key) {\n const sourcePresentation = detection.sourceId\n ? sourcePresentations.get(detection.sourceId)\n : undefined;\n const sourceStyle = sourcePresentation?.[key];\n return sourceStyle === undefined ? globalStyle : sourceStyle;\n}\nfunction normalizeGlobalBoxStyle(style) {\n return style === undefined ? new BaseBoxStyle() : style;\n}\nfunction normalizeGlobalLabelStyle(style) {\n return style === undefined ? new BaseLabelStyle() : style;\n}\nfunction normalizeGlobalMaskStyle(style) {\n return style === undefined ? new BaseMaskStyle() : style;\n}\nfunction normalizeGlobalMaskHaloStyle(style) {\n return style === undefined ? createDefaultMaskHaloStyle() : style;\n}\nfunction normalizeGlobalMarkerStyle(style) {\n return style === undefined ? new BaseMarkerStyle() : style;\n}\nfunction normalizeGlobalPolygonStyle(style) {\n return style === undefined ? new BasePolygonStyle() : style;\n}\nfunction normalizeGlobalPolylineStyle(style) {\n return style === undefined ? new BasePolylineStyle() : style;\n}\nfunction normalizeGlobalKeypointStyle(style) {\n return style === undefined ? new BaseKeypointStyle() : style;\n}\n\nfunction rectToPolygon(rect) {\n const halfWidth = rect.width / 2;\n const halfHeight = rect.height / 2;\n return {\n points: [\n { x: rect.x - halfWidth, y: rect.y - halfHeight },\n { x: rect.x + halfWidth, y: rect.y - halfHeight },\n { x: rect.x + halfWidth, y: rect.y + halfHeight },\n { x: rect.x - halfWidth, y: rect.y + halfHeight },\n ],\n };\n}\nfunction polygonToRect(polygon) {\n return getPointsRect(polygon.points);\n}\nfunction rasterizeRectToMask(rect, dimensions) {\n const data = createEmptyMask(dimensions);\n const topLeft = centerRectToTopLeftRect(rect);\n const left = Math.max(0, Math.round(topLeft.x));\n const top = Math.max(0, Math.round(topLeft.y));\n const right = Math.min(dimensions.width - 1, Math.round(rect.x + rect.width / 2));\n const bottom = Math.min(dimensions.height - 1, Math.round(rect.y + rect.height / 2));\n for (let y = top; y <= bottom; y += 1) {\n for (let x = left; x <= right; x += 1) {\n data[y * dimensions.width + x] = 1;\n }\n }\n return data;\n}\nfunction rasterizePolygonToMask(points, dimensions) {\n const data = createEmptyMask(dimensions);\n if (points.length < 3) {\n return data;\n }\n const bounds = centerRectToTopLeftRect(getPointsRect(points));\n const startY = Math.max(0, Math.floor(bounds.y));\n const endY = Math.min(dimensions.height - 1, Math.ceil(bounds.y + bounds.height));\n for (let y = startY; y <= endY; y += 1) {\n const scanY = y + 0.5;\n const intersections = [];\n for (let index = 0; index < points.length; index += 1) {\n const current = points[index];\n const next = points[(index + 1) % points.length];\n if ((current.y <= scanY && next.y > scanY) ||\n (next.y <= scanY && current.y > scanY)) {\n const ratio = (scanY - current.y) / (next.y - current.y);\n intersections.push(current.x + ratio * (next.x - current.x));\n }\n }\n intersections.sort((left, right) => left - right);\n for (let index = 0; index < intersections.length - 1; index += 2) {\n const left = Math.max(0, Math.ceil(intersections[index]));\n const right = Math.min(dimensions.width - 1, Math.floor(intersections[index + 1]));\n for (let x = left; x <= right; x += 1) {\n data[y * dimensions.width + x] = 1;\n }\n }\n }\n return data;\n}\nfunction convertDetectionBoxToPolygon(detection) {\n return detection.rect\n ? replaceGeometry(detection, { polygon: rectToPolygon(detection.rect) })\n : detection;\n}\nfunction convertDetectionPolygonToBox(detection) {\n const rect = detection.polygon ? polygonToRect(detection.polygon) : undefined;\n return rect ? replaceGeometry(detection, { rect }) : detection;\n}\nfunction convertDetectionMaskToBox(detection) {\n const rect = detection.mask\n ? computeDetectionMaskRect(detection.mask)\n : undefined;\n return rect ? replaceGeometry(detection, { rect }) : detection;\n}\nfunction convertDetectionMaskToPolygon(detection) {\n if (!detection.mask) {\n return detection;\n }\n const decoded = decodeCompressedRleMask(detection.mask);\n const contour = extractMaskContour(decoded.data, decoded.width, decoded.height);\n const fallbackRect = computeDetectionMaskRect(detection.mask);\n const polygon = contour && contour.length >= 3\n ? { points: contour }\n : fallbackRect\n ? rectToPolygon(fallbackRect)\n : undefined;\n return polygon ? replaceGeometry(detection, { polygon }) : detection;\n}\nfunction convertDetectionBoxToMask(detection, dimensions) {\n if (!detection.rect) {\n return detection;\n }\n const mask = encodeBinaryMask(rasterizeRectToMask(detection.rect, dimensions), dimensions.width, dimensions.height);\n return replaceGeometry(detection, { mask });\n}\nfunction convertDetectionPolygonToMask(detection, dimensions) {\n if (!detection.polygon) {\n return detection;\n }\n const mask = encodeBinaryMask(rasterizePolygonToMask(detection.polygon.points, dimensions), dimensions.width, dimensions.height);\n return replaceGeometry(detection, { mask });\n}\nfunction mergeDetectionMasks(detections) {\n if (detections.length < 2 || detections.some(({ mask }) => !mask)) {\n return null;\n }\n const first = detections[0];\n if (detections.some((detection) => detection.className !== first.className)) {\n return null;\n }\n const masks = detections.map(({ mask }) => decodeCompressedRleMask(mask));\n const width = masks[0].width;\n const height = masks[0].height;\n if (masks.some((mask) => mask.width !== width || mask.height !== height)) {\n throw new Error(\"Merged detection masks must have matching dimensions.\");\n }\n const data = new Uint8Array(width * height);\n for (const mask of masks) {\n for (let index = 0; index < data.length; index += 1) {\n data[index] ||= mask.data[index] ?? 0;\n }\n }\n return replaceGeometry(first, {\n mask: encodeBinaryMask(data, width, height),\n });\n}\n/**\n * Groups polygon detections by class and returns one union bounding-box\n * detection per class. This intentionally converts the output to rectangles.\n */\nfunction mergeDetectionPolygonsByClass(detections, options = {}) {\n const groups = new Map();\n for (const detection of detections) {\n if (!detection.polygon) {\n continue;\n }\n const group = groups.get(detection.className) ?? [];\n group.push(detection);\n groups.set(detection.className, group);\n }\n return [...groups.entries()].flatMap(([className, group], groupIndex) => {\n const points = group.flatMap((detection) => detection.polygon.points);\n const rect = getPointsRect(points);\n if (!rect) {\n return [];\n }\n const first = group[0];\n const id = options.createId?.(className, groupIndex) ?? first.id;\n return [\n replaceGeometry({\n ...first,\n id,\n }, { rect }),\n ];\n });\n}\nfunction createEmptyMask(dimensions) {\n if (!Number.isInteger(dimensions.width) ||\n dimensions.width <= 0 ||\n !Number.isInteger(dimensions.height) ||\n dimensions.height <= 0) {\n throw new Error(\"Media dimensions must be positive integers.\");\n }\n return new Uint8Array(dimensions.width * dimensions.height);\n}\nfunction replaceGeometry(detection, geometry) {\n return {\n ...detection,\n keypoints: undefined,\n mask: geometry.mask,\n polygon: geometry.polygon,\n polyline: undefined,\n rect: geometry.rect,\n };\n}\n\nfunction canReuseMaskStyleArtifacts(previousMaskStyle, nextMaskStyle) {\n if (previousMaskStyle === nextMaskStyle) {\n return true;\n }\n return (previousMaskStyle?.artifactKey !== undefined &&\n nextMaskStyle?.artifactKey !== undefined &&\n previousMaskStyle.artifactKey === nextMaskStyle.artifactKey);\n}\nfunction resolveMaskStyleOpacity(maskStyle) {\n const opacity = maskStyle?.opacity;\n if (opacity === undefined) {\n return 1;\n }\n return Number.isFinite(opacity) ? Math.max(0, Math.min(opacity, 1)) : 1;\n}\n\nconst MAX_ID_MASK_PALETTE_ENTRIES = 64;\nconst MAX_ID_MASK_STROKE_WIDTH = 16;\nfunction createIdMaskFrame(instructions) {\n if (instructions.length === 0) {\n return undefined;\n }\n const width = Math.max(...instructions.map(({ mask }) => mask.width));\n const height = Math.max(...instructions.map(({ mask }) => mask.height));\n const data = new Uint8Array(new ArrayBuffer(width * height));\n const fillPalette = new Float32Array(new ArrayBuffer(MAX_ID_MASK_PALETTE_ENTRIES * 4 * 4));\n const strokePalette = new Float32Array(new ArrayBuffer(MAX_ID_MASK_PALETTE_ENTRIES * 4 * 4));\n const strokeWidths = new Float32Array(new ArrayBuffer(MAX_ID_MASK_PALETTE_ENTRIES * 4));\n let hasStroke = false;\n let maxStrokeWidth = 0;\n for (const instruction of instructions) {\n const detectionMaskId = instruction.detectionIndex + 1;\n if (detectionMaskId <= 0 ||\n detectionMaskId >= MAX_ID_MASK_PALETTE_ENTRIES) {\n return undefined;\n }\n writePaletteEntry(fillPalette, detectionMaskId, instruction.color, instruction.alpha);\n if (instruction.stroke && instruction.stroke.width > 0) {\n const strokeWidth = Math.min(Math.max(0, instruction.stroke.width), MAX_ID_MASK_STROKE_WIDTH);\n hasStroke = true;\n strokeWidths[detectionMaskId] = strokeWidth;\n maxStrokeWidth = Math.max(maxStrokeWidth, strokeWidth);\n writePaletteEntry(strokePalette, detectionMaskId, instruction.stroke.color, instruction.stroke.alpha);\n }\n const decodedMask = decodeCompressedRleMask(instruction.mask);\n for (let y = 0; y < decodedMask.height; y += 1) {\n for (let x = 0; x < decodedMask.width; x += 1) {\n const maskOffset = y * decodedMask.width + x;\n if (decodedMask.data[maskOffset]) {\n data[y * width + x] = detectionMaskId;\n }\n }\n }\n }\n return {\n data,\n fillPalette,\n hasStroke,\n height,\n maxStrokeWidth,\n strokePalette,\n strokeWidths,\n width,\n };\n}\nfunction writePaletteEntry(palette, id, color, alpha) {\n const offset = id * 4;\n palette[offset] = ((color >> 16) & 0xff) / 255;\n palette[offset + 1] = ((color >> 8) & 0xff) / 255;\n palette[offset + 2] = (color & 0xff) / 255;\n palette[offset + 3] = Math.max(0, Math.min(alpha, 1));\n}\n\nconst DEFAULT_ELLIPSE_SEGMENT_COUNT = 48;\nconst FULL_TURN = Math.PI * 2;\nconst GEOMETRY_EPSILON = 1e-9;\nconst MIN_ELLIPSE_SEGMENT_COUNT = 4;\nconst MAX_ELLIPSE_SEGMENT_COUNT = 512;\n/**\n * Samples an ellipse or elliptical arc into a deterministic polyline so every\n * backend rasterizes the same structural geometry, including dashed strokes.\n *\n * A closed ellipse returns `segmentCount` points without repeating the first\n * point; an open arc returns `segmentCount + 1` points including both\n * endpoints.\n */\nfunction sampleEllipseArc(ellipse, segmentCount = DEFAULT_ELLIPSE_SEGMENT_COUNT) {\n validateEllipseGeometry(ellipse);\n assertIntegerAtLeast(segmentCount, MIN_ELLIPSE_SEGMENT_COUNT, \"segmentCount\");\n const rotation = ellipse.rotation ?? 0;\n const startAngle = ellipse.startAngle ?? 0;\n const endAngle = ellipse.endAngle ?? startAngle + FULL_TURN;\n const range = endAngle - startAngle;\n const closed = ellipse.startAngle === undefined;\n const pointCount = closed ? segmentCount : segmentCount + 1;\n const cosRotation = Math.cos(rotation);\n const sinRotation = Math.sin(rotation);\n const points = [];\n for (let index = 0; index < pointCount; index += 1) {\n const angle = startAngle + (range * index) / segmentCount;\n const localX = Math.cos(angle) * ellipse.radiusX;\n const localY = Math.sin(angle) * ellipse.radiusY;\n points.push({\n x: ellipse.center.x + localX * cosRotation - localY * sinRotation,\n y: ellipse.center.y + localX * sinRotation + localY * cosRotation,\n });\n }\n return { closed, points };\n}\n/**\n * Chooses a bounded segment count from the ellipse's on-screen radius. The\n * result targets sub-pixel chord error without making tiny markers expensive.\n */\nfunction resolveEllipseSegmentCount(ellipse, viewportScale = 1, maxErrorPixels = 0.5) {\n validateEllipseGeometry(ellipse);\n assertPositiveFinite(viewportScale, \"viewportScale\");\n assertPositiveFinite(maxErrorPixels, \"maxErrorPixels\");\n const screenRadius = Math.max(ellipse.radiusX, ellipse.radiusY) * viewportScale;\n assertPositiveFinite(screenRadius, \"screenRadius\");\n const clampedError = Math.min(maxErrorPixels, screenRadius);\n const maxStep = clampedError === screenRadius\n ? Math.PI / 2\n : 2 * Math.acos(1 - clampedError / screenRadius);\n const startAngle = ellipse.startAngle ?? 0;\n const endAngle = ellipse.endAngle ?? startAngle + FULL_TURN;\n const arcLength = Math.abs(endAngle - startAngle);\n const segmentCount = Math.ceil(arcLength / maxStep);\n return Math.min(MAX_ELLIPSE_SEGMENT_COUNT, Math.max(MIN_ELLIPSE_SEGMENT_COUNT, segmentCount));\n}\n/**\n * Resolves a marker instruction into media-space geometry. Screen-space sizes\n * are divided by the viewport scale, matching stroke-width semantics.\n */\nfunction resolveMarkerGeometry(marker, viewportScale = 1) {\n assertFinitePoint(marker.center, \"center\");\n assertPositiveFinite(marker.size, \"size\");\n assertPositiveFinite(viewportScale, \"viewportScale\");\n if (marker.rotation !== undefined) {\n assertFinite(marker.rotation, \"rotation\");\n }\n const size = marker.sizeSpace === MarkerSizeSpace.Screen\n ? marker.size / viewportScale\n : marker.size;\n const radius = size / 2;\n const rotation = marker.rotation ?? 0;\n if (marker.shape === MarkerShape.Circle) {\n return { center: marker.center, kind: \"circle\", radius };\n }\n if (marker.shape === MarkerShape.Cross) {\n return {\n closed: false,\n kind: \"subpaths\",\n subpaths: [\n [\n rotatePoint(marker.center, -radius, -radius, rotation),\n rotatePoint(marker.center, radius, radius, rotation),\n ],\n [\n rotatePoint(marker.center, radius, -radius, rotation),\n rotatePoint(marker.center, -radius, radius, rotation),\n ],\n ],\n };\n }\n if (marker.shape === MarkerShape.Square) {\n return {\n closed: true,\n kind: \"subpaths\",\n subpaths: [\n [\n rotatePoint(marker.center, -radius, -radius, rotation),\n rotatePoint(marker.center, radius, -radius, rotation),\n rotatePoint(marker.center, radius, radius, rotation),\n rotatePoint(marker.center, -radius, radius, rotation),\n ],\n ],\n };\n }\n // Triangle: equilateral, inscribed in the marker circle, pointing toward\n // positive y at rotation 0.\n const baseOffsetY = -radius / 2;\n const baseOffsetX = radius * (Math.sqrt(3) / 2);\n return {\n closed: true,\n kind: \"subpaths\",\n subpaths: [\n [\n rotatePoint(marker.center, 0, radius, rotation),\n rotatePoint(marker.center, baseOffsetX, baseOffsetY, rotation),\n rotatePoint(marker.center, -baseOffsetX, baseOffsetY, rotation),\n ],\n ],\n };\n}\nfunction validateEllipseGeometry(ellipse) {\n assertFinitePoint(ellipse.center, \"center\");\n assertPositiveFinite(ellipse.radiusX, \"radiusX\");\n assertPositiveFinite(ellipse.radiusY, \"radiusY\");\n if (ellipse.rotation !== undefined) {\n assertFinite(ellipse.rotation, \"rotation\");\n }\n const hasStart = ellipse.startAngle !== undefined;\n const hasEnd = ellipse.endAngle !== undefined;\n if (hasStart !== hasEnd) {\n throw new RangeError(\"startAngle and endAngle must be provided together\");\n }\n if (hasStart && hasEnd) {\n assertFinite(ellipse.startAngle, \"startAngle\");\n assertFinite(ellipse.endAngle, \"endAngle\");\n const range = Math.abs(ellipse.endAngle - ellipse.startAngle);\n if (range <= GEOMETRY_EPSILON || range >= FULL_TURN - GEOMETRY_EPSILON) {\n throw new RangeError(\"ellipse arc range must be greater than 0 and less than one full turn\");\n }\n }\n}\nfunction assertFinitePoint(point, name) {\n assertFinite(point.x, `${name}.x`);\n assertFinite(point.y, `${name}.y`);\n}\nfunction assertFinite(value, name) {\n if (!Number.isFinite(value)) {\n throw new RangeError(`${name} must be finite`);\n }\n}\nfunction assertPositiveFinite(value, name) {\n assertFinite(value, name);\n if (value <= 0) {\n throw new RangeError(`${name} must be greater than 0`);\n }\n}\nfunction assertIntegerAtLeast(value, minimum, name) {\n if (!Number.isInteger(value) || value < minimum) {\n throw new RangeError(`${name} must be an integer of at least ${minimum}`);\n }\n}\nfunction rotatePoint(center, offsetX, offsetY, rotation) {\n if (rotation === 0) {\n return { x: center.x + offsetX, y: center.y + offsetY };\n }\n const cosRotation = Math.cos(rotation);\n const sinRotation = Math.sin(rotation);\n return {\n x: center.x + offsetX * cosRotation - offsetY * sinRotation,\n y: center.y + offsetX * sinRotation + offsetY * cosRotation,\n };\n}\n\nfunction includeDefined(values) {\n return Object.fromEntries(Object.entries(values).filter(([, value]) => value !== undefined));\n}\n\nfunction resolveAnnotationStyleState(detection, visibility) {\n const id = detection.id;\n return {\n hidden: visibility?.annotationsHidden === true ||\n (detection.className !== undefined &&\n includes(visibility?.hiddenClasses, detection.className)) ||\n (id !== undefined && includes(visibility?.hiddenDetectionIds, id)),\n loading: id !== undefined && includes(visibility?.loadingDetectionIds, id),\n ephemeral: id !== undefined && includes(visibility?.ephemeralDetectionIds, id),\n isCreating: id !== undefined && visibility?.creatingDetectionId === id,\n };\n}\nfunction includes(values, value) {\n if (!values)\n return false;\n return Array.isArray(values)\n ? values.includes(value)\n : values.has(value);\n}\n\nvar MediaRendererFit;\n(function (MediaRendererFit) {\n /**\n * Preserve media aspect ratio and fit the full frame inside the canvas.\n */\n MediaRendererFit[\"Contain\"] = \"contain\";\n /**\n * Preserve media aspect ratio and fill the canvas, cropping if necessary.\n */\n MediaRendererFit[\"Cover\"] = \"cover\";\n})(MediaRendererFit || (MediaRendererFit = {}));\n/**\n * Playback lifecycle state reported by a platform renderer.\n */\nvar MediaRendererPlaybackState;\n(function (MediaRendererPlaybackState) {\n MediaRendererPlaybackState[\"Loading\"] = \"loading\";\n MediaRendererPlaybackState[\"Ready\"] = \"ready\";\n MediaRendererPlaybackState[\"Playing\"] = \"playing\";\n MediaRendererPlaybackState[\"Buffering\"] = \"buffering\";\n MediaRendererPlaybackState[\"Paused\"] = \"paused\";\n MediaRendererPlaybackState[\"Error\"] = \"error\";\n MediaRendererPlaybackState[\"Destroyed\"] = \"destroyed\";\n})(MediaRendererPlaybackState || (MediaRendererPlaybackState = {}));\n/**\n * Stable classification of a media failure.\n *\n * Applications branch on these kinds instead of parsing decoder, demuxer, or\n * container error text, and still own their localized user-facing copy. New\n * kinds may be added over time, so treat unrecognized values like `Unknown`.\n */\nvar MediaErrorKind;\n(function (MediaErrorKind) {\n /** The media could not be opened or read at all. */\n MediaErrorKind[\"Unreadable\"] = \"unreadable\";\n /** The container or codec is not supported by this platform. */\n MediaErrorKind[\"UnsupportedFormat\"] = \"unsupportedFormat\";\n /** The media opened but carries no usable video track. */\n MediaErrorKind[\"NoVideoTrack\"] = \"noVideoTrack\";\n /** Decoding a sample failed after the source opened. */\n MediaErrorKind[\"Decode\"] = \"decode\";\n /** The media could not be fetched. */\n MediaErrorKind[\"Network\"] = \"network\";\n /** The host environment lacks the APIs this media source requires. */\n MediaErrorKind[\"EnvironmentUnsupported\"] = \"environmentUnsupported\";\n /** The failure could not be classified. */\n MediaErrorKind[\"Unknown\"] = \"unknown\";\n})(MediaErrorKind || (MediaErrorKind = {}));\n/**\n * Lower-level media source readiness.\n */\nvar MediaSourceStatus;\n(function (MediaSourceStatus) {\n MediaSourceStatus[\"Loading\"] = \"loading\";\n MediaSourceStatus[\"Ready\"] = \"ready\";\n MediaSourceStatus[\"Error\"] = \"error\";\n MediaSourceStatus[\"Destroyed\"] = \"destroyed\";\n})(MediaSourceStatus || (MediaSourceStatus = {}));\n\n/**\n * Media-session operating mode.\n *\n * Core owns the names because file-like and stream-like lifecycle choices are\n * platform-neutral. Platform packages decide how these modes tune media,\n * storage, and renderer defaults.\n */\nvar MediaSessionMode;\n(function (MediaSessionMode) {\n /**\n * Finite media. Defaults usually favor seek/replay and persistent detection\n * storage.\n */\n MediaSessionMode[\"File\"] = \"file\";\n /**\n * Live or append-only media. Defaults usually favor rolling windows and\n * bounded retention.\n */\n MediaSessionMode[\"Stream\"] = \"stream\";\n})(MediaSessionMode || (MediaSessionMode = {}));\n/**\n * Aggregate lifecycle state for a media session.\n */\nvar MediaSessionStatus;\n(function (MediaSessionStatus) {\n MediaSessionStatus[\"Buffering\"] = \"buffering\";\n MediaSessionStatus[\"Destroyed\"] = \"destroyed\";\n MediaSessionStatus[\"Error\"] = \"error\";\n MediaSessionStatus[\"Loading\"] = \"loading\";\n MediaSessionStatus[\"Paused\"] = \"paused\";\n MediaSessionStatus[\"Playing\"] = \"playing\";\n MediaSessionStatus[\"Processing\"] = \"processing\";\n MediaSessionStatus[\"Ready\"] = \"ready\";\n})(MediaSessionStatus || (MediaSessionStatus = {}));\n/**\n * Subsystem currently affecting session readiness or presentation.\n */\nvar MediaSessionActivityKind;\n(function (MediaSessionActivityKind) {\n MediaSessionActivityKind[\"DetectionsBuffering\"] = \"detectionsBuffering\";\n MediaSessionActivityKind[\"DetectionsLoading\"] = \"detectionsLoading\";\n MediaSessionActivityKind[\"Error\"] = \"error\";\n MediaSessionActivityKind[\"MediaNormalizing\"] = \"mediaNormalizing\";\n MediaSessionActivityKind[\"MediaOpening\"] = \"mediaOpening\";\n MediaSessionActivityKind[\"PlaybackBuffering\"] = \"playbackBuffering\";\n MediaSessionActivityKind[\"RenderPreparing\"] = \"renderPreparing\";\n})(MediaSessionActivityKind || (MediaSessionActivityKind = {}));\n/**\n * State of one session activity.\n */\nvar MediaSessionActivityStatus;\n(function (MediaSessionActivityStatus) {\n MediaSessionActivityStatus[\"Error\"] = \"error\";\n MediaSessionActivityStatus[\"Running\"] = \"running\";\n MediaSessionActivityStatus[\"Waiting\"] = \"waiting\";\n})(MediaSessionActivityStatus || (MediaSessionActivityStatus = {}));\n\nexport { AnnotationFrameMutationKind, AnnotationGeometryKind, AnnotationGestureStateKind, AnnotationHandleKind, BaseBoxCornerStyle, BaseBoxStyle, BaseFocusStyle, BaseInteractionStyle, BaseKeypointStyle, BaseLabelStyle, BaseMarkerStyle, BaseMaskStyle, BasePolygonStyle, BasePolylineStyle, BoxShape, StrokeAlignment as BoxStrokeAlignment, DEFAULT_DETECTION_CLASS_STYLES, DEFAULT_DETECTION_COLOR_SEQUENCE, DetectionBufferStatus, DetectionFrameRetentionMode, DetectionFrameSelectionMode, DetectionInteractionState, DetectionMaskEncoding, DetectionMaskPayloadFormat, DetectionPickTarget, FocusTargetMode, KeypointMarkerShape, KeypointVisibility, LabelPlacement, LabelVisibilityMode, MAX_ID_MASK_PALETTE_ENTRIES, MAX_ID_MASK_STROKE_WIDTH, MarkerShape, MarkerSizeSpace, MaskRenderMode, MediaErrorKind, MediaInteractionMode, MediaRendererFit, MediaRendererPlaybackState, MediaSessionActivityKind, MediaSessionActivityStatus, MediaSessionMode, MediaSessionStatus, MediaSourceStatus, RegionRendererComposeMode, RegionRendererRegionKind, RegionRendererSourceKind, SUPERVISION_ROBOFLOW_COLOR, ShapeInstructionKind, StrokeAlignment, TrackingGeometry, annotationRendererKinds, annotationRenderers, applyAnnotationHandleDrag, canReuseMaskStyleArtifacts, centerRectToTopLeftRect, computeDetectionMaskRect, computeMaskBounds, containsPoint, convertDetectionBoxToMask, convertDetectionBoxToPolygon, convertDetectionMaskToBox, convertDetectionMaskToPolygon, convertDetectionPolygonToBox, convertDetectionPolygonToMask, copySortedDetectionFrames, createAnnotationEditingEngine, createArrayDetectionFrameSource, createBufferedDetectionTimeline, createByteTrackTracker, createCBIoUTracker, createColdDetectionFrameSource, createCompositeDetectionFrameSource, createDefaultAnnotationPresentation, createDetectionPickKey, createEditableAnnotationFrameSession, createIdMaskFrame, createIdleDetectionBufferState, createMemoryColdDetectionFrameStore, createOCSortTracker, createProjectedDetectionFrameSource, createSortTracker, createSourceAwarePresentation, createViewportController, createWritableDetectionFrameSource, decodeCompressedRleCounts, decodeCompressedRleMask, decodeDetectionMaskPayload, deleteAnnotationVertex, detectMaskBorders, detectionFrameOverlapsRange, detectionPostProcessors, distanceToSegment, encodeBinaryMask, encodeBinaryMaskWithBounds, encodeCompressedRleCounts, encodeDetectionMaskPayload, extractMaskContour, extractMaskRectRuns, filterDetectionFramesForRange, findClosestAnnotationSegment, followDetectionPickAcrossFrames, getAnnotationHandles, getBufferedDetectionTimelineFrameSnapshot, getDetectionRect, getPointsRect, haveSameDetectionPickIdentities, includeDefined, isDeflatedBase64DetectionMaskPayload, lightenColor, mediaToScreen, mergeDetectionMasks, mergeDetectionPolygonsByClass, normalizeDetectionClassName, offsetDetection, pickAnnotationHandle, pickDetectionAtPoint, pickDetectionByMaskId, pointInPolygon, polygonArea, polygonToRect, projectDetectionFrame, projectDetectionFrameForTracking, projectDetectionFrames, rasterizePolygonToMask, rasterizeRectToMask, rebaseDetectionPickToFrame, rectArea, rectToPolygon, resolveAnnotationRendererPresentation, resolveAnnotationStyleState, resolveContrastTextColor, resolveDetectionClassColorStyle, resolveEllipseSegmentCount, resolveMarkerGeometry, resolveMaskStyleOpacity, resolveStyleValue, sampleEllipseArc, screenToMedia, selectDetectionFrame, topLeftRectToCenterRect, validateDetectionFrames };\n//# sourceMappingURL=index.js.map\n",null,null,null,null],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mask-preparation.worker.js","sources":["../../core/dist/index.js","../src/render-preparation/mask-frame-compositor.ts","../src/render-preparation/mask-frame-artifact.ts","../src/render-preparation/mask-preparation-worker-protocol.ts","../src/render-preparation/mask-preparation.worker.ts"],"sourcesContent":["/** COCO-compatible keypoint visibility values. */\nvar KeypointVisibility;\n(function (KeypointVisibility) {\n KeypointVisibility[KeypointVisibility[\"NotLabeled\"] = 0] = \"NotLabeled\";\n KeypointVisibility[KeypointVisibility[\"Occluded\"] = 1] = \"Occluded\";\n KeypointVisibility[KeypointVisibility[\"Visible\"] = 2] = \"Visible\";\n})(KeypointVisibility || (KeypointVisibility = {}));\nvar DetectionMaskEncoding;\n(function (DetectionMaskEncoding) {\n DetectionMaskEncoding[\"CompressedRle\"] = \"compressedRle\";\n})(DetectionMaskEncoding || (DetectionMaskEncoding = {}));\n\nvar DetectionBufferStatus;\n(function (DetectionBufferStatus) {\n DetectionBufferStatus[\"Idle\"] = \"idle\";\n DetectionBufferStatus[\"Loading\"] = \"loading\";\n /**\n * The playback gate is holding playback until the source produces detections\n * for the requested range. {@link DetectionBufferStatus.Loading} fetches\n * detections the source already has; this one waits on a producer.\n */\n DetectionBufferStatus[\"AwaitingCoverage\"] = \"awaitingCoverage\";\n DetectionBufferStatus[\"Ready\"] = \"ready\";\n DetectionBufferStatus[\"Error\"] = \"error\";\n DetectionBufferStatus[\"Destroyed\"] = \"destroyed\";\n})(DetectionBufferStatus || (DetectionBufferStatus = {}));\n/**\n * How the renderer selects the active detection frame for a media timestamp.\n */\nvar DetectionFrameSelectionMode;\n(function (DetectionFrameSelectionMode) {\n /**\n * Select the frame whose `[mediaTime, endTime)` interval contains the media\n * time. This is the default for interval annotations and timestamped sources.\n *\n * Frame times must come from the media itself. Times reconstructed from a\n * nominal frame rate drift against a clip whose real rate differs, and once\n * that drift passes the half-millisecond selection tolerance a playhead\n * landing on a frame boundary selects the previous frame's detections.\n *\n * A frame that carries no `endTime` stays active until the next frame starts,\n * which bridges an index the source never produced.\n * {@link DetectionFrameSelectionMode.NearestFrameIndex} bounds each frame to\n * one grid step instead, so a missing index reads as no detections.\n */\n DetectionFrameSelectionMode[\"Interval\"] = \"interval\";\n /**\n * Select from a known inference frame grid using `frameIndex` and\n * `frameRate`. This is useful when inference was run on normalized frames and\n * playback should snap detections to that grid.\n *\n * A frame speaks for one grid step starting at its own media time, and an\n * index the source never produced selects nothing rather than a neighbour.\n *\n * A frame carrying no `frameIndex` speaks for its step on the same terms,\n * reached by the time it starts at rather than by an index that names it. Its\n * `endTime` does not widen it past that step, so a source that labels only\n * part of what it writes cannot bridge the indexes it never produced.\n */\n DetectionFrameSelectionMode[\"NearestFrameIndex\"] = \"nearestFrameIndex\";\n})(DetectionFrameSelectionMode || (DetectionFrameSelectionMode = {}));\nvar DetectionFrameRetentionMode;\n(function (DetectionFrameRetentionMode) {\n /**\n * Keep writable detections only in an in-memory store. Useful for ephemeral\n * live streams where old predictions should disappear when evicted.\n */\n DetectionFrameRetentionMode[\"MemoryOnly\"] = \"memoryOnly\";\n /**\n * Persist every written detection frame. Useful for finite media where seek\n * and replay should not require recomputing detections.\n */\n DetectionFrameRetentionMode[\"PersistAll\"] = \"persistAll\";\n /**\n * Persist only the most recent retention window. Useful for long-running\n * streams where replay is bounded to a recent time horizon.\n */\n DetectionFrameRetentionMode[\"PersistWindow\"] = \"persistWindow\";\n})(DetectionFrameRetentionMode || (DetectionFrameRetentionMode = {}));\n\n/**\n * A playhead compared by time can arrive perturbed by whatever plane it crossed\n * to get here: a producer that publishes whole milliseconds reports a source\n * frame at 5.033333s as 5.033s, and even an exact producer loses a bit to a\n * seconds-to-milliseconds-and-back trip. A frame starting within that reach may\n * already be on screen while one starting past it cannot.\n *\n * Selection by identity carries none of this, which is why it lives in its own\n * function and not in a smaller number here.\n */\nconst PLAYHEAD_QUANTIZATION_TOLERANCE_SECONDS = 0.0005;\nfunction copySortedDetectionFrames(detectionFrames) {\n validateDetectionFrames(detectionFrames ?? []);\n return (detectionFrames ?? [])\n .map(copyDetectionFrame)\n .sort((left, right) => left.mediaTime - right.mediaTime);\n}\n/**\n * One frame's deep copy, for a caller that already knows which frames it is\n * keeping and only needs the ones it does not hold.\n */\nfunction copyDetectionFrame(frame) {\n return {\n detections: frame.detections.map((detection) => ({\n ...detection,\n attributes: detection.attributes ? [...detection.attributes] : undefined,\n keypoints: detection.keypoints\n ? {\n boxRelative: detection.keypoints.boxRelative\n ? [...detection.keypoints.boxRelative]\n : undefined,\n edges: detection.keypoints.edges.map((edge) => [edge[0], edge[1]]),\n points: detection.keypoints.points.map((point) => ({ ...point })),\n visibility: detection.keypoints.visibility\n ? [...detection.keypoints.visibility]\n : undefined,\n }\n : undefined,\n mask: detection.mask ? { ...detection.mask } : undefined,\n metadata: detection.metadata\n ? copyDetectionMetadata(detection.metadata)\n : undefined,\n polygon: detection.polygon\n ? {\n points: detection.polygon.points.map((point) => ({ ...point })),\n }\n : undefined,\n polyline: detection.polyline\n ? {\n points: detection.polyline.points.map((point) => ({ ...point })),\n }\n : undefined,\n rect: detection.rect ? { ...detection.rect } : undefined,\n })),\n coordinateSpace: frame.coordinateSpace\n ? { ...frame.coordinateSpace }\n : undefined,\n endTime: frame.endTime,\n frameIndex: frame.frameIndex,\n mediaTime: frame.mediaTime,\n };\n}\n/** Returned by the plain copier when it meets a value only structuredClone can\n * carry, so the caller pays for that only when the metadata needs it. */\nconst NOT_PLAIN = Symbol(\"not-plain-metadata\");\nfunction copyDetectionMetadata(metadata) {\n // Detection metadata is almost always a handful of strings and numbers, and a\n // window rebuild copies it once per detection, tens of thousands of times a\n // second while scrubbing. structuredClone serialises; walking the object does\n // not, so it is the fallback rather than the default.\n const plain = copyPlainMetadata(metadata);\n if (plain !== NOT_PLAIN) {\n return plain;\n }\n const clone = globalThis.structuredClone;\n if (clone) {\n return clone(metadata);\n }\n return copyMetadataValue(metadata);\n}\nfunction copyPlainMetadata(value) {\n if (value === null || typeof value !== \"object\") {\n return typeof value === \"function\" || typeof value === \"symbol\"\n ? NOT_PLAIN\n : value;\n }\n if (Array.isArray(value)) {\n const copied = [];\n for (const entry of value) {\n const child = copyPlainMetadata(entry);\n if (child === NOT_PLAIN) {\n return NOT_PLAIN;\n }\n copied.push(child);\n }\n return copied;\n }\n if (Object.getPrototypeOf(value) !== Object.prototype) {\n return NOT_PLAIN;\n }\n const copied = {};\n for (const [key, entry] of Object.entries(value)) {\n const child = copyPlainMetadata(entry);\n if (child === NOT_PLAIN) {\n return NOT_PLAIN;\n }\n copied[key] = child;\n }\n return copied;\n}\nfunction copyMetadataValue(value) {\n if (Array.isArray(value)) {\n return value.map(copyMetadataValue);\n }\n if (value && typeof value === \"object\") {\n return Object.fromEntries(Object.entries(value).map(([key, child]) => [\n key,\n copyMetadataValue(child),\n ]));\n }\n return value;\n}\nfunction validateDetectionFrames(detectionFrames) {\n for (let frameOffset = 0; frameOffset < detectionFrames.length; frameOffset += 1) {\n const frame = detectionFrames[frameOffset];\n const framePath = () => `frames[${frameOffset}]`;\n validateNumber(frame.mediaTime, () => `${framePath()}.mediaTime`, {\n min: 0,\n });\n if (frame.endTime !== undefined) {\n validateNumber(frame.endTime, () => `${framePath()}.endTime`, {\n exclusiveMin: frame.mediaTime,\n });\n }\n if (frame.frameIndex !== undefined) {\n validateNumber(frame.frameIndex, () => `${framePath()}.frameIndex`, {\n integer: true,\n min: 0,\n });\n }\n if (frame.coordinateSpace !== undefined) {\n validateNumber(frame.coordinateSpace.width, () => `${framePath()}.coordinateSpace.width`, { exclusiveMin: 0 });\n validateNumber(frame.coordinateSpace.height, () => `${framePath()}.coordinateSpace.height`, { exclusiveMin: 0 });\n }\n for (let detectionOffset = 0; detectionOffset < frame.detections.length; detectionOffset += 1) {\n const detection = frame.detections[detectionOffset];\n const detectionPath = () => `${framePath()}.detections[${detectionOffset}]`;\n if (detection.confidence !== undefined) {\n validateNumber(detection.confidence, () => `${detectionPath()}.confidence`, { max: 1, min: 0 });\n }\n if (detection.zIndex !== undefined) {\n validateNumber(detection.zIndex, () => `${detectionPath()}.zIndex`);\n }\n if (detection.trackerId !== undefined) {\n validateNumber(detection.trackerId, () => `${detectionPath()}.trackerId`, { integer: true, min: 0 });\n }\n if (detection.sourceId !== undefined &&\n typeof detection.sourceId !== \"string\") {\n throw new Error(`${detectionPath()}.sourceId must be a string.`);\n }\n if (detection.sourceDetectionIndex !== undefined) {\n validateNumber(detection.sourceDetectionIndex, () => `${detectionPath()}.sourceDetectionIndex`, {\n integer: true,\n min: 0,\n });\n }\n if (detection.rect) {\n validateNumber(detection.rect.x, () => `${detectionPath()}.rect.x`);\n validateNumber(detection.rect.y, () => `${detectionPath()}.rect.y`);\n validateNumber(detection.rect.width, () => `${detectionPath()}.rect.width`, { exclusiveMin: 0 });\n validateNumber(detection.rect.height, () => `${detectionPath()}.rect.height`, { exclusiveMin: 0 });\n }\n validatePoints(detection.polygon?.points, () => `${detectionPath()}.polygon`, 3);\n validatePoints(detection.polyline?.points, () => `${detectionPath()}.polyline`, 2);\n if (detection.keypoints) {\n validatePoints(detection.keypoints.points, () => `${detectionPath()}.keypoints`, 1);\n if (detection.keypoints.visibility !== undefined &&\n detection.keypoints.visibility.length !==\n detection.keypoints.points.length) {\n throw new Error(`${detectionPath()}.keypoints.visibility must match points length.`);\n }\n const edges = detection.keypoints.edges;\n for (let edgeOffset = 0; edgeOffset < edges.length; edgeOffset += 1) {\n const edge = edges[edgeOffset];\n const edgePath = () => `${detectionPath()}.keypoints.edges[${edgeOffset}]`;\n for (let endpointOffset = 0; endpointOffset < edge.length; endpointOffset += 1) {\n const endpoint = edge[endpointOffset];\n validateNumber(endpoint, () => `${edgePath()}[${endpointOffset}]`, {\n integer: true,\n min: 0,\n });\n if (endpoint >= detection.keypoints.points.length) {\n throw new Error(`${edgePath()}[${endpointOffset}] is out of range.`);\n }\n }\n }\n }\n if (detection.mask) {\n validateNumber(detection.mask.width, () => `${detectionPath()}.mask.width`, { integer: true, exclusiveMin: 0 });\n validateNumber(detection.mask.height, () => `${detectionPath()}.mask.height`, { integer: true, exclusiveMin: 0 });\n if (detection.mask.counts.length === 0) {\n throw new Error(`${detectionPath()}.mask.counts must not be empty.`);\n }\n }\n }\n }\n}\nfunction validatePoints(points, path, minimumLength) {\n if (!points) {\n return;\n }\n if (points.length < minimumLength) {\n throw new Error(`${path()}.points must contain at least ${minimumLength} points.`);\n }\n for (let pointOffset = 0; pointOffset < points.length; pointOffset += 1) {\n const point = points[pointOffset];\n validateNumber(point.x, () => `${path()}.points[${pointOffset}].x`);\n validateNumber(point.y, () => `${path()}.points[${pointOffset}].y`);\n }\n}\nconst NO_BOUNDS = {};\nfunction validateNumber(value, path, options = NO_BOUNDS) {\n if (!Number.isFinite(value)) {\n throw new Error(`${path()} must be a finite number.`);\n }\n if (options.integer && !Number.isInteger(value)) {\n throw new Error(`${path()} must be an integer.`);\n }\n if (options.min !== undefined && value < options.min) {\n throw new Error(`${path()} must be greater than or equal to ${options.min}.`);\n }\n if (options.exclusiveMin !== undefined && value <= options.exclusiveMin) {\n throw new Error(`${path()} must be greater than ${options.exclusiveMin}.`);\n }\n if (options.max !== undefined && value > options.max) {\n throw new Error(`${path()} must be less than or equal to ${options.max}.`);\n }\n}\nfunction filterDetectionFramesForRange(detectionFrames, startTime, endTime) {\n return detectionFrames.filter((frame) => detectionFrameOverlapsRange(frame, startTime, endTime));\n}\nfunction selectDetectionFrame(detectionFrames, mediaTime, options = {}) {\n if (options.selectionMode === DetectionFrameSelectionMode.NearestFrameIndex) {\n const selection = selectNearestFrameIndexDetectionFrame(detectionFrames, mediaTime, options.frameRate);\n if (selection.isApplicable) {\n return selection.frame;\n }\n }\n return selectIntervalDetectionFrame(detectionFrames, mediaTime);\n}\nfunction decodeCompressedRleMask(mask) {\n if (mask.encoding !== DetectionMaskEncoding.CompressedRle) {\n throw new Error(`Unsupported detection mask encoding: ${mask.encoding}`);\n }\n const data = new Uint8Array(mask.width * mask.height);\n const counts = decodeCompressedRleCounts(mask.counts);\n let offset = 0;\n for (let index = 0; index < counts.length; index += 1) {\n const runLength = counts[index] ?? 0;\n const isForeground = index % 2 === 1;\n if (isForeground) {\n for (let runOffset = 0; runOffset < runLength; runOffset += 1) {\n const maskOffset = offset + runOffset;\n const x = Math.floor(maskOffset / mask.height);\n const y = maskOffset % mask.height;\n const rowMajorOffset = y * mask.width + x;\n if (rowMajorOffset < data.length) {\n data[rowMajorOffset] = 1;\n }\n }\n }\n offset += runLength;\n }\n return {\n data,\n height: mask.height,\n width: mask.width,\n };\n}\nfunction selectIntervalDetectionFrame(detectionFrames, mediaTime) {\n const selectedFrame = findLastStartedDetectionFrame(detectionFrames, mediaTime);\n return selectedFrame && isDetectionFrameActive(selectedFrame, mediaTime)\n ? selectedFrame\n : undefined;\n}\n/**\n * The grid frame a playhead is standing on, or nothing.\n *\n * A frame at index `n` speaks for one step of the inference grid and no more,\n * so it is on screen from its own media time until the next index is due. An\n * index the source never produced leaves that step blank: the frames on either\n * side describe media the playhead is not on, and a source still being written\n * has a playback gate to hold the picture rather than a neighbour to borrow.\n *\n * A grid the source labelled only in part is still that source describing this\n * media, so the frame the playhead stands on is the last one to have started,\n * whether or not an index names it. The step it is held to is measured from the\n * labelled frames alone.\n */\nfunction selectNearestFrameIndexDetectionFrame(detectionFrames, mediaTime, frameRate) {\n if (!frameRate || !Number.isFinite(frameRate) || frameRate <= 0) {\n return { frame: undefined, isApplicable: false };\n }\n const firstIndexedFrame = findIndexedDetectionFrame(detectionFrames, 1);\n const lastIndexedFrame = findIndexedDetectionFrame(detectionFrames, -1);\n if (!firstIndexedFrame || !lastIndexedFrame) {\n return { frame: undefined, isApplicable: false };\n }\n const gridStep = measureGridStep$1(firstIndexedFrame, lastIndexedFrame) ?? 1 / frameRate;\n const startedFrame = findLastStartedDetectionFrame(detectionFrames, mediaTime);\n return {\n frame: startedFrame &&\n mediaTime + PLAYHEAD_QUANTIZATION_TOLERANCE_SECONDS <\n startedFrame.mediaTime + gridStep\n ? startedFrame\n : undefined,\n isApplicable: true,\n };\n}\n/**\n * A grid step read off the frames instead of taken from `frameRate`.\n *\n * `frameRate` is what a manifest or an upload target says the grid is, and a\n * clip whose real rate differs walks a step further out of position for every\n * second that plays. The buffered frames carry the times the media itself\n * reported, and measuring across the widest index span present divides the\n * producer's millisecond rounding by that span.\n */\nfunction measureGridStep$1(firstIndexedFrame, lastIndexedFrame) {\n const indexSpan = lastIndexedFrame.frameIndex - firstIndexedFrame.frameIndex;\n const step = (lastIndexedFrame.mediaTime - firstIndexedFrame.mediaTime) / indexSpan;\n return indexSpan > 0 && Number.isFinite(step) && step > 0 ? step : undefined;\n}\nfunction findIndexedDetectionFrame(detectionFrames, direction) {\n const startOffset = direction === 1 ? 0 : detectionFrames.length - 1;\n for (let offset = startOffset; offset >= 0 && offset < detectionFrames.length; offset += direction) {\n const frame = detectionFrames[offset];\n if (isIndexedDetectionFrame(frame)) {\n return frame;\n }\n }\n return undefined;\n}\nfunction findLastStartedDetectionFrame(detectionFrames, mediaTime) {\n let selectedFrame;\n let low = 0;\n let high = detectionFrames.length - 1;\n while (low <= high) {\n const middle = Math.floor((low + high) / 2);\n const frame = detectionFrames[middle];\n if (hasDetectionFrameStarted(frame, mediaTime)) {\n selectedFrame = frame;\n low = middle + 1;\n }\n else {\n high = middle - 1;\n }\n }\n return selectedFrame;\n}\nfunction isIndexedDetectionFrame(frame) {\n return frame.frameIndex !== undefined;\n}\nfunction hasDetectionFrameStarted(frame, mediaTime) {\n return frame.mediaTime - PLAYHEAD_QUANTIZATION_TOLERANCE_SECONDS <= mediaTime;\n}\nfunction isDetectionFrameActive(frame, mediaTime) {\n return (hasDetectionFrameStarted(frame, mediaTime) &&\n (frame.endTime === undefined || mediaTime < frame.endTime));\n}\nfunction detectionFrameOverlapsRange(frame, startTime, endTime) {\n if (frame.endTime === undefined) {\n return frame.mediaTime >= startTime && frame.mediaTime <= endTime;\n }\n return frame.mediaTime <= endTime && frame.endTime > startTime;\n}\nfunction decodeCompressedRleCounts(counts) {\n const decoded = [];\n let index = 0;\n while (index < counts.length) {\n let value = 0;\n let shift = 0;\n let charCode;\n do {\n charCode = counts.charCodeAt(index) - 48;\n index += 1;\n value |= (charCode & 0x1f) << shift;\n shift += 5;\n } while (charCode & 0x20);\n if (charCode & 0x10) {\n value |= -1 << shift;\n }\n if (decoded.length > 2) {\n value += decoded[decoded.length - 2] ?? 0;\n }\n decoded.push(value);\n }\n return decoded;\n}\nfunction encodeCompressedRleCounts(counts) {\n return counts\n .map((count, index) => {\n let value = index > 2 ? count - counts[index - 2] : count;\n let encoded = \"\";\n let more = true;\n while (more) {\n let charCode = value & 0x1f;\n value >>= 5;\n more = !((value === 0 && (charCode & 0x10) === 0) ||\n (value === -1 && (charCode & 0x10) !== 0));\n if (more) {\n charCode |= 0x20;\n }\n encoded += String.fromCharCode(charCode + 48);\n }\n return encoded;\n })\n .join(\"\");\n}\n\nfunction createArrayDetectionFrameSource(frames) {\n const sortedFrames = copySortedDetectionFrames(frames);\n return {\n async loadFrames(startTime, endTime) {\n return copySortedDetectionFrames(filterDetectionFramesForRange(sortedFrames, startTime, endTime));\n },\n };\n}\n\n/**\n * Slack allowed when comparing two range ends.\n *\n * Range bounds are computed from frame times and durations, so an end that\n * should meet another exactly can miss it by a float's last bits.\n */\nconst RANGE_EPSILON_SECONDS = 1e-6;\n/**\n * Whether the available ranges together span the whole of `range`.\n *\n * Detections arrive a batch at a time, and a source that has not coalesced its\n * ranges holds several that abut. Asking each range on its own whether it spans\n * the window reports a window that is entirely covered as uncovered, so the\n * walk below spends ranges in order and asks how far their union reaches.\n */\nfunction isRangeCovered(range, availableRanges) {\n let reach = null;\n for (const availableRange of inStartOrder(availableRanges)) {\n if (reach === null) {\n if (availableRange.startTime > range.startTime + RANGE_EPSILON_SECONDS) {\n return false;\n }\n if (availableRange.endTime + RANGE_EPSILON_SECONDS < range.startTime) {\n continue;\n }\n reach = availableRange.endTime;\n }\n else {\n if (availableRange.startTime > reach + RANGE_EPSILON_SECONDS) {\n return false;\n }\n reach = Math.max(reach, availableRange.endTime);\n }\n if (reach + RANGE_EPSILON_SECONDS >= range.endTime) {\n return true;\n }\n }\n return false;\n}\nfunction inStartOrder(ranges) {\n for (let index = 1; index < ranges.length; index += 1) {\n if (ranges[index].startTime < ranges[index - 1].startTime) {\n return [...ranges].sort((left, right) => left.startTime - right.startTime);\n }\n }\n return ranges;\n}\n\nfunction startWaitBound(timeoutMs) {\n let handle;\n const expired = new Promise((resolve) => {\n handle = setTimeout(() => resolve(false), timeoutMs);\n });\n return { cancel: () => clearTimeout(handle), expired };\n}\n\nconst DEFAULT_BUFFER_AHEAD_SECONDS = 5;\nconst DEFAULT_BUFFER_BEHIND_SECONDS = 0.5;\n/**\n * Share of the ahead window that must still lead the playhead when the next\n * window is fetched.\n *\n * A window fetched only once the playhead reaches its end leaves the playhead\n * uncovered for exactly as long as the fetch takes, every time, so annotations\n * blink out once per window at a steady rate. Fetching while the current window\n * still answers means the load lands behind a picture that never lost them.\n */\nconst REFILL_LEAD_FRACTION = 0.5;\nconst MIN_REFILL_LEAD_SECONDS = 1;\n/**\n * How long a playback gate holds before it gives up on coverage.\n *\n * The gate waits on a producer, and a producer that has failed, stalled, or\n * fallen far behind is indistinguishable from one that is about to answer. Past\n * this a frozen picture is the worse of the two outcomes, so the frame is\n * presented with whatever detections exist.\n */\nconst DEFAULT_PLAYBACK_GATE_MAX_WAIT_SECONDS = 10;\nconst bufferedFrameSnapshots = new WeakMap();\n/**\n * Returns the timeline-owned hot-buffer snapshot without copying it.\n *\n * This is an internal platform-adapter fast path. Public callers should use\n * `getBufferedFrames()`, which preserves the existing defensive-copy contract.\n */\nfunction getBufferedDetectionTimelineFrameSnapshot(timeline) {\n return (bufferedFrameSnapshots.get(timeline)?.() ?? timeline.getBufferedFrames());\n}\nfunction createBufferedDetectionTimeline(options) {\n const bufferAheadSeconds = options.bufferAheadSeconds ?? DEFAULT_BUFFER_AHEAD_SECONDS;\n const bufferBehindSeconds = options.bufferBehindSeconds ?? DEFAULT_BUFFER_BEHIND_SECONDS;\n const refreshIntervalSeconds = options.refreshIntervalSeconds === undefined\n ? null\n : Math.max(0, options.refreshIntervalSeconds);\n const playbackGate = options.playbackGate;\n const refillLeadSeconds = bufferAheadSeconds <= 0\n ? 0\n : Math.min(bufferAheadSeconds, Math.max(MIN_REFILL_LEAD_SECONDS, bufferAheadSeconds * REFILL_LEAD_FRACTION));\n let buffer = [];\n let state = createIdleDetectionBufferState();\n let destroyed = false;\n let loadId = 0;\n let bufferedSourceVersion = null;\n let bufferedVersionRange = null;\n let timelineContext = {\n duration: null,\n loop: false,\n };\n let inFlight;\n let incrementalRefresh;\n /** Source version the gate last gave up on, or null while it still waits. */\n let abandonedGateSourceVersion = null;\n let pendingPrefetch;\n let prefetchPump;\n const listeners = new Set();\n const notifyBufferChanged = () => {\n for (const listener of listeners) {\n listener();\n }\n };\n const getSourceVersion = (ranges) => {\n if (!ranges) {\n return options.source.getVersion?.() ?? 0;\n }\n return ranges.reduce((version, range) => Math.max(version, options.source.getVersion?.(range) ?? 0), 0);\n };\n const isLoadingEnabled = () => typeof options.enabled === \"function\"\n ? options.enabled()\n : options.enabled !== false;\n const isBufferFresh = () => bufferedVersionRange !== null &&\n bufferedSourceVersion === getSourceVersion(getBufferedSourceRanges());\n const getLoadRange = (mediaTime, lead = \"full\") => {\n const comparableMediaTime = getComparableMediaTime(mediaTime);\n const startTime = comparableMediaTime - bufferBehindSeconds;\n const endTime = comparableMediaTime + (lead === \"full\" ? bufferAheadSeconds : 0);\n return createLoadPlan(startTime, endTime);\n };\n const loadWindow = (mediaTime, lead = \"full\") => {\n const comparableMediaTime = getComparableMediaTime(mediaTime);\n const { endTime, sourceRanges, startTime } = getLoadRange(mediaTime, lead);\n const versionRange = { endTime, startTime };\n const sourceVersion = getSourceVersion(sourceRanges);\n if (inFlight && inFlight.sourceVersion === sourceVersion) {\n if (rangeContains(inFlight.startTime, inFlight.endTime, startTime, endTime)) {\n return inFlight.promise;\n }\n // A load already in flight that still covers where the playhead is going\n // answers the same question a fresh one would. Playback moves the anchor\n // every frame, so a window superseded on anchor equality alone is\n // superseded on every frame of its own flight: the fetch is thrown away\n // and the wait restarts, which is how a gap grows instead of closing.\n if (refillLeadSeconds > 0 &&\n comparableMediaTime >= inFlight.mediaTime &&\n comparableMediaTime + refillLeadSeconds <=\n inFlight.mediaTime + bufferAheadSeconds) {\n return inFlight.promise;\n }\n }\n const currentLoadId = loadId + 1;\n loadId = currentLoadId;\n state = {\n ...state,\n errorMessage: null,\n requestedEndTime: endTime,\n requestedStartTime: startTime,\n status: DetectionBufferStatus.Loading,\n };\n const promise = Promise.all(sourceRanges.map((range) => options.source.loadFrames(range.startTime, range.endTime)))\n .then((frameRanges) => {\n if (destroyed || currentLoadId !== loadId) {\n return;\n }\n const committedSourceVersion = getSourceVersion(sourceRanges);\n const loadedFrames = frameRanges.flat();\n buffer =\n bufferedSourceVersion !== null &&\n bufferedSourceVersion === committedSourceVersion\n ? reuseBufferedFrameSnapshots(buffer, loadedFrames)\n : copySortedDetectionFrames(loadedFrames);\n bufferedVersionRange = versionRange;\n bufferedSourceVersion = committedSourceVersion;\n state = {\n bufferEndTime: endTime,\n bufferStartTime: startTime,\n detectionCount: countDetections(buffer),\n errorMessage: null,\n frameCount: buffer.length,\n requestedEndTime: endTime,\n requestedStartTime: startTime,\n status: DetectionBufferStatus.Ready,\n };\n notifyBufferChanged();\n })\n .catch((error) => {\n if (!destroyed && currentLoadId === loadId) {\n state = {\n ...state,\n errorMessage: getErrorMessage(error),\n status: DetectionBufferStatus.Error,\n };\n }\n throw error;\n })\n .finally(() => {\n if (inFlight?.id === currentLoadId) {\n inFlight = undefined;\n }\n });\n if (lead === \"covering\") {\n // Chained past the clearing of `inFlight`, which would otherwise be\n // handed back to the widening load as a window that already answers.\n void promise\n .then(() => {\n if (!destroyed && currentLoadId === loadId) {\n void loadWindow(mediaTime).catch(() => undefined);\n }\n })\n .catch(() => undefined);\n }\n inFlight = {\n endTime,\n id: currentLoadId,\n mediaTime: comparableMediaTime,\n promise,\n sourceVersion,\n startTime,\n };\n return promise;\n };\n /**\n * Restates the window in the lap the playhead is on.\n *\n * A window planned near the end of a looping source runs past the loop\n * point, so once playback wraps, a host comparing the window against the\n * media clock reads a range that starts after the time it is holding. The\n * offset removed here is a whole number of laps, which is what\n * `getComparableMediaTime` already treats as the same position, so\n * membership and the frames on screen are untouched.\n */\n const anchorWindowToPlayhead = (mediaTime) => {\n const duration = timelineContext.duration;\n if (!isLoopingTimeline() ||\n duration === null ||\n state.bufferStartTime === null ||\n state.bufferEndTime === null) {\n return;\n }\n const laps = Math.round((getComparableMediaTime(mediaTime) - mediaTime) / duration);\n if (laps === 0) {\n return;\n }\n state = {\n ...state,\n bufferEndTime: state.bufferEndTime - laps * duration,\n bufferStartTime: state.bufferStartTime - laps * duration,\n };\n notifyBufferChanged();\n };\n const isBuffered = (mediaTime) => {\n const comparableMediaTime = getComparableMediaTime(mediaTime);\n return (isBufferFresh() &&\n state.bufferStartTime !== null &&\n state.bufferEndTime !== null &&\n comparableMediaTime >= state.bufferStartTime &&\n comparableMediaTime <= state.bufferEndTime);\n };\n const isInsideBufferedRange = (mediaTime) => {\n const comparableMediaTime = getComparableMediaTime(mediaTime);\n return (bufferedVersionRange !== null &&\n state.bufferStartTime !== null &&\n state.bufferEndTime !== null &&\n comparableMediaTime >= state.bufferStartTime &&\n comparableMediaTime <= state.bufferEndTime);\n };\n const refreshBuffer = async (mediaTime, lead = \"full\") => {\n if (isBuffered(mediaTime)) {\n return;\n }\n if (isInsideBufferedRange(mediaTime) &&\n bufferedSourceVersion !== null &&\n options.source.getChangesSince) {\n if (!incrementalRefresh) {\n incrementalRefresh = applyIncrementalChanges().finally(() => {\n incrementalRefresh = undefined;\n });\n }\n await incrementalRefresh;\n if (isBuffered(mediaTime)) {\n return;\n }\n return refreshBuffer(mediaTime, lead);\n }\n await loadWindow(mediaTime, lead);\n };\n /**\n * Whether the next window is worth fetching while the current one still\n * answers, so the load lands behind annotations that never went away.\n */\n const shouldPrefetch = (mediaTime) => {\n if (!isLoadingEnabled()) {\n return false;\n }\n if (!isBuffered(mediaTime)) {\n return true;\n }\n if (shouldRefreshRollingWindow(mediaTime)) {\n return true;\n }\n if (state.bufferEndTime === null || bufferAheadSeconds <= 0) {\n return false;\n }\n if (getComparableMediaTime(mediaTime) + bufferAheadSeconds / 2 <\n state.bufferEndTime) {\n return false;\n }\n const { endTime, startTime } = getLoadRange(mediaTime);\n // A window already spanning everything the source can offer has nowhere to\n // advance to, so its lead only shrinks from here. Refetching it would\n // repeat for every remaining frame of playback and buy no coverage.\n return (startTime !== state.bufferStartTime || endTime !== state.bufferEndTime);\n };\n const timeline = {\n async prepare(mediaTime, prepareOptions) {\n if (destroyed || !isLoadingEnabled()) {\n return;\n }\n if (shouldWaitForPlaybackGate(prepareOptions)) {\n await waitForPlaybackGate(mediaTime, prepareOptions);\n if (destroyed) {\n return;\n }\n }\n await refreshBuffer(mediaTime);\n anchorWindowToPlayhead(mediaTime);\n },\n needsPlaybackGateWait(mediaTime, prepareOptions) {\n if (destroyed ||\n !isLoadingEnabled() ||\n !playbackGate?.enabled ||\n !options.source.waitForRange ||\n !options.source.getAvailableRanges ||\n hasAbandonedGate()) {\n return false;\n }\n const availableRanges = options.source.getAvailableRanges();\n return !createPlaybackGateCoveragePlan(mediaTime, prepareOptions).sourceRanges.every((range) => isRangeCovered(range, availableRanges));\n },\n prefetch(mediaTime) {\n if (destroyed || !isLoadingEnabled()) {\n return;\n }\n anchorWindowToPlayhead(mediaTime);\n if (!shouldPrefetch(mediaTime)) {\n return;\n }\n pendingPrefetch = { loadId, mediaTime };\n pumpPrefetchQueue();\n },\n selectFrame(mediaTime) {\n if (!isBuffered(mediaTime)) {\n return undefined;\n }\n return selectDetectionFrame(buffer, getSourceMediaTime(mediaTime), options);\n },\n setTimelineContext(context) {\n timelineContext = context;\n bufferedSourceVersion = null;\n bufferedVersionRange = null;\n },\n getBufferedFrames() {\n return copySortedDetectionFrames(buffer);\n },\n getState() {\n return { ...state };\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n destroy() {\n if (destroyed) {\n return;\n }\n destroyed = true;\n listeners.clear();\n pendingPrefetch = undefined;\n buffer = [];\n bufferedSourceVersion = null;\n bufferedVersionRange = null;\n state = {\n ...state,\n bufferEndTime: null,\n bufferStartTime: null,\n detectionCount: 0,\n frameCount: 0,\n status: DetectionBufferStatus.Destroyed,\n };\n options.source.destroy?.();\n },\n };\n bufferedFrameSnapshots.set(timeline, () => buffer);\n return timeline;\n function pumpPrefetchQueue() {\n if (destroyed || prefetchPump) {\n return;\n }\n prefetchPump = drainPrefetchQueue().finally(() => {\n prefetchPump = undefined;\n if (!destroyed && pendingPrefetch) {\n pumpPrefetchQueue();\n }\n });\n }\n async function drainPrefetchQueue() {\n while (!destroyed && pendingPrefetch) {\n if (inFlight) {\n await inFlight.promise.catch(() => undefined);\n continue;\n }\n const request = pendingPrefetch;\n pendingPrefetch = undefined;\n if (request.loadId !== loadId) {\n continue;\n }\n const { mediaTime } = request;\n if (!shouldPrefetch(mediaTime)) {\n continue;\n }\n // A playhead the window does not reach is a jump rather than the window\n // rolling forward, and the frame under it is the one thing the picture\n // cannot draw without. A window that still spans it, however stale, is\n // missing no such frame.\n const lead = isInsideBufferedRange(mediaTime)\n ? \"full\"\n : \"covering\";\n await (isBuffered(mediaTime) || shouldRefreshRollingWindow(mediaTime)\n ? loadWindow(mediaTime, lead)\n : refreshBuffer(mediaTime, lead)).catch(() => undefined);\n }\n }\n async function applyIncrementalChanges() {\n if (destroyed ||\n bufferedSourceVersion === null ||\n !options.source.getChangesSince) {\n return;\n }\n const sourceRanges = getBufferedSourceRanges();\n const incrementalLoadId = loadId;\n const incrementalVersionRange = bufferedVersionRange;\n const changes = options.source.getChangesSince(bufferedSourceVersion, sourceRanges);\n if (changes.requiresReload) {\n bufferedSourceVersion = null;\n return;\n }\n if (changes.ranges.length === 0) {\n bufferedSourceVersion = changes.version;\n return;\n }\n const changedRanges = getOverlappingRanges(changes.ranges, sourceRanges);\n if (changedRanges.length === 0) {\n bufferedSourceVersion = changes.version;\n return;\n }\n try {\n const changedFrameRanges = await Promise.all(changedRanges.map((range) => options.source.loadFrames(range.startTime, range.endTime)));\n if (destroyed ||\n loadId !== incrementalLoadId ||\n bufferedVersionRange !== incrementalVersionRange) {\n return;\n }\n buffer = mergeIncrementalFrames(buffer, changedFrameRanges.flat(), changedRanges);\n bufferedSourceVersion = changes.version;\n state = {\n ...state,\n detectionCount: countDetections(buffer),\n errorMessage: null,\n frameCount: buffer.length,\n status: DetectionBufferStatus.Ready,\n };\n notifyBufferChanged();\n }\n catch (error) {\n if (!destroyed) {\n state = {\n ...state,\n errorMessage: getErrorMessage(error),\n status: DetectionBufferStatus.Error,\n };\n }\n throw error;\n }\n }\n function shouldWaitForPlaybackGate(prepareOptions) {\n return (prepareOptions?.gatePlayback === true &&\n playbackGate?.enabled === true &&\n Boolean(options.source.waitForRange));\n }\n function createPlaybackGateCoveragePlan(mediaTime, prepareOptions) {\n const requiredAheadSeconds = Math.max(0, playbackGate?.requiredAheadSeconds ?? 0);\n const comparableMediaTime = getComparableMediaTime(mediaTime);\n const endTime = getRequiredCoverageEndTime({\n // A looping window counts past the end of media and wraps into the\n // replay, so clamping it to duration would ask for less than it needs.\n duration: isLoopingTimeline() ? null : prepareOptions?.duration,\n firstTimestamp: prepareOptions?.firstTimestamp,\n mediaTime: comparableMediaTime,\n requiredAheadSeconds,\n });\n // A lead clamped away at the end of media, or asked for as zero, still\n // leaves the frame under the playhead to wait for.\n return createLoadPlan(comparableMediaTime, Math.max(comparableMediaTime, endTime));\n }\n function hasAbandonedGate() {\n return (abandonedGateSourceVersion !== null &&\n getSourceVersion() <= abandonedGateSourceVersion);\n }\n async function waitForPlaybackGate(mediaTime, prepareOptions) {\n if (!playbackGate?.enabled || !options.source.waitForRange) {\n return;\n }\n if (hasAbandonedGate()) {\n return;\n }\n const coveragePlan = createPlaybackGateCoveragePlan(mediaTime, prepareOptions);\n state = {\n ...state,\n errorMessage: null,\n requestedEndTime: coveragePlan.endTime,\n requestedStartTime: coveragePlan.startTime,\n status: DetectionBufferStatus.AwaitingCoverage,\n };\n try {\n const covered = await waitForSourceCoverage(coveragePlan.sourceRanges, Math.max(0, playbackGate.maxWaitSeconds ?? DEFAULT_PLAYBACK_GATE_MAX_WAIT_SECONDS));\n abandonedGateSourceVersion =\n covered || !options.source.getVersion ? null : getSourceVersion();\n }\n catch (error) {\n if (!destroyed) {\n state = {\n ...state,\n errorMessage: getErrorMessage(error),\n status: DetectionBufferStatus.Error,\n };\n }\n throw error;\n }\n }\n /**\n * Resolves true once the source covers every range, false once the wait has\n * run longer than `maxWaitSeconds`.\n */\n async function waitForSourceCoverage(sourceRanges, maxWaitSeconds) {\n const covered = whenRangesCovered(sourceRanges);\n if (!Number.isFinite(maxWaitSeconds)) {\n return covered;\n }\n const bound = startWaitBound(maxWaitSeconds * 1000);\n try {\n const result = await Promise.race([covered, bound.expired]);\n if (!result) {\n // The abandoned wait outlives this call, and a rejection it reaches\n // afterwards has nobody left to hand it to.\n void covered.catch(() => undefined);\n }\n return result;\n }\n finally {\n bound.cancel();\n }\n }\n async function whenRangesCovered(sourceRanges) {\n await Promise.all(sourceRanges.map((range) => options.source.waitForRange?.(range)));\n return true;\n }\n function shouldRefreshRollingWindow(mediaTime) {\n if (refreshIntervalSeconds === null ||\n refreshIntervalSeconds <= 0 ||\n state.bufferStartTime === null ||\n state.bufferEndTime === null) {\n return false;\n }\n const { endTime, startTime } = getLoadRange(mediaTime);\n return (getWindowDrift(startTime, state.bufferStartTime) >=\n refreshIntervalSeconds ||\n getWindowDrift(endTime, state.bufferEndTime) >= refreshIntervalSeconds);\n }\n /**\n * How far a planned edge sits from the window's, counted the short way around\n * a looping timeline. A plan is stated on the first lap and the window on the\n * playhead's, so across the loop point the two state the same edge a whole lap\n * apart; measured straight, that reads as a window a full lap stale and\n * rebuilds it on every frame until the plan stops reaching behind zero.\n */\n function getWindowDrift(planTime, bufferTime) {\n const drift = Math.abs(planTime - bufferTime);\n if (!isLoopingTimeline() || timelineContext.duration === null) {\n return drift;\n }\n const wrappedDrift = modulo(drift, timelineContext.duration);\n return Math.min(wrappedDrift, timelineContext.duration - wrappedDrift);\n }\n function createLoadPlan(requestedStartTime, requestedEndTime) {\n const startTime = Math.min(requestedStartTime, requestedEndTime);\n const endTime = Math.max(startTime, requestedEndTime);\n if (!isLoopingTimeline()) {\n const clampedStartTime = Math.max(0, startTime);\n const clampedEndTime = Math.max(clampedStartTime, endTime);\n return {\n endTime: clampedEndTime,\n sourceRanges: [\n {\n endTime: clampedEndTime,\n startTime: clampedStartTime,\n },\n ],\n startTime: clampedStartTime,\n };\n }\n const duration = timelineContext.duration ?? 0;\n if (endTime - startTime >= duration) {\n return {\n endTime: duration,\n sourceRanges: [{ endTime: duration, startTime: 0 }],\n startTime: 0,\n };\n }\n // Both ends move by the same whole number of laps, so the window keeps its\n // span and its source ranges while its start reads on the media clock: a\n // window counted in the laps playback accumulated is one no host can hold\n // against a current time.\n const laps = Math.floor(startTime / duration);\n return {\n endTime: endTime - laps * duration,\n sourceRanges: getLoopingSourceRanges(startTime, endTime, duration),\n startTime: startTime - laps * duration,\n };\n }\n function getBufferedSourceRanges() {\n if (!bufferedVersionRange) {\n return [];\n }\n return createLoadPlan(bufferedVersionRange.startTime, bufferedVersionRange.endTime).sourceRanges;\n }\n function isLoopingTimeline() {\n return (timelineContext.loop &&\n timelineContext.duration !== null &&\n timelineContext.duration > 0);\n }\n function getComparableMediaTime(mediaTime) {\n if (!isLoopingTimeline() ||\n state.bufferStartTime === null ||\n state.bufferEndTime === null ||\n timelineContext.duration === null) {\n return mediaTime;\n }\n const duration = timelineContext.duration;\n // The representative of mediaTime (mod duration) anchored at the window\n // start. The mapping must depend only on where the window sits, never on\n // how many laps playback has accumulated, or membership drifts away from\n // what the buffer actually holds. A time less than one lap past the\n // anchor never wraps, so ordinary forward playback cannot ratchet.\n return (state.bufferStartTime +\n modulo(mediaTime - state.bufferStartTime, duration));\n }\n function getSourceMediaTime(mediaTime) {\n if (!isLoopingTimeline() || timelineContext.duration === null) {\n return mediaTime;\n }\n if (mediaTime >= 0 && mediaTime <= timelineContext.duration) {\n return mediaTime;\n }\n return modulo(mediaTime, timelineContext.duration);\n }\n}\nfunction createIdleDetectionBufferState() {\n return {\n bufferEndTime: null,\n bufferStartTime: null,\n detectionCount: 0,\n errorMessage: null,\n frameCount: 0,\n requestedEndTime: null,\n requestedStartTime: null,\n status: DetectionBufferStatus.Idle,\n };\n}\nfunction rangeContains(outerStart, outerEnd, innerStart, innerEnd) {\n return outerStart <= innerStart && innerEnd <= outerEnd;\n}\nfunction countDetections(frames) {\n return frames.reduce((total, frame) => total + frame.detections.length, 0);\n}\nfunction getErrorMessage(error) {\n return error instanceof Error\n ? error.message\n : \"Detection buffer load failed.\";\n}\nfunction getRequiredCoverageEndTime(options) {\n const requestedEndTime = options.mediaTime + options.requiredAheadSeconds;\n if (options.duration === null || options.duration === undefined) {\n return requestedEndTime;\n }\n return Math.min(requestedEndTime, (options.firstTimestamp ?? 0) + Math.max(options.duration, 0));\n}\nfunction getLoopingSourceRanges(startTime, endTime, duration) {\n const normalizedStartTime = modulo(startTime, duration);\n const normalizedEndTime = modulo(endTime, duration);\n const startCycle = Math.floor(startTime / duration);\n const endCycle = Math.floor(endTime / duration);\n if (startCycle === endCycle) {\n return [{ endTime: normalizedEndTime, startTime: normalizedStartTime }];\n }\n const ranges = [];\n if (normalizedStartTime < duration) {\n ranges.push({ endTime: duration, startTime: normalizedStartTime });\n }\n if (normalizedEndTime > 0) {\n ranges.push({ endTime: normalizedEndTime, startTime: 0 });\n }\n return ranges;\n}\nfunction modulo(value, modulus) {\n return ((value % modulus) + modulus) % modulus;\n}\nfunction getOverlappingRanges(changedRanges, bufferedRanges) {\n const intersections = [];\n for (const changedRange of changedRanges) {\n for (const bufferedRange of bufferedRanges) {\n const startTime = Math.max(changedRange.startTime, bufferedRange.startTime);\n const endTime = Math.min(changedRange.endTime, bufferedRange.endTime);\n if (startTime <= endTime) {\n intersections.push({ endTime, startTime });\n }\n }\n }\n return intersections;\n}\nfunction mergeIncrementalFrames(currentFrames, changedFrames, changedRanges) {\n const framesByIdentity = new Map();\n for (const frame of currentFrames) {\n if (changedRanges.some((range) => detectionFrameOverlapsRange(frame, range.startTime, range.endTime))) {\n continue;\n }\n framesByIdentity.set(getDetectionFrameIdentity(frame), frame);\n }\n for (const frame of changedFrames) {\n framesByIdentity.set(getDetectionFrameIdentity(frame), frame);\n }\n return Array.from(framesByIdentity.values()).sort(compareDetectionFrames$1);\n}\n/**\n * Keeps the frame already held wherever the source returned one this buffer\n * knows, and copies only what is new. Copying everything first and then\n * discarding it is the same result for a great deal more work: a window rebuilt\n * while a gesture moves inside it re-derives hundreds of frames it already has.\n */\nfunction reuseBufferedFrameSnapshots(currentFrames, loadedFrames) {\n validateDetectionFrames(loadedFrames);\n const currentFramesByIdentity = new Map(currentFrames.map((frame) => [getDetectionFrameIdentity(frame), frame]));\n return loadedFrames\n .map((frame) => currentFramesByIdentity.get(getDetectionFrameIdentity(frame)) ??\n copyDetectionFrame(frame))\n .sort(compareDetectionFrames$1);\n}\nfunction getDetectionFrameIdentity(frame) {\n return frame.frameIndex === undefined\n ? `time:${frame.mediaTime}`\n : `index:${frame.frameIndex}`;\n}\nfunction compareDetectionFrames$1(left, right) {\n if (left.mediaTime !== right.mediaTime) {\n return left.mediaTime - right.mediaTime;\n }\n return (left.frameIndex ?? 0) - (right.frameIndex ?? 0);\n}\n\nfunction createColdDetectionFrameSource(options) {\n return {\n loadFrames(startTime, endTime) {\n return options.store.loadFrames({\n datasetId: options.datasetId,\n endTime,\n startTime,\n });\n },\n };\n}\n\n/**\n * Projects one detection frame from its declared source coordinate space into\n * `target`.\n *\n * Rectangles, polygons, polylines, and keypoints are scaled. Masks are left\n * untouched because a `DetectionMask` already carries the mask-pixel\n * dimensions its `counts` are encoded against; scaling them here would apply\n * the ratio twice. Frames without `coordinateSpace`, with a degenerate space,\n * or already in the target space are returned unchanged so mixed producers can\n * share one source.\n */\nfunction projectDetectionFrame(frame, target) {\n const space = frame.coordinateSpace;\n if (!space ||\n !isUsableCoordinateSpace(space) ||\n !isUsableCoordinateSpace(target) ||\n (space.width === target.width && space.height === target.height)) {\n return frame;\n }\n const scaleX = target.width / space.width;\n const scaleY = target.height / space.height;\n return {\n ...frame,\n coordinateSpace: { height: target.height, width: target.width },\n detections: frame.detections.map((detection) => projectDetection(detection, scaleX, scaleY)),\n };\n}\n/**\n * Projects every frame that declares a source coordinate space into `target`.\n *\n * Frames without coordinate metadata pass through by reference.\n */\nfunction projectDetectionFrames(frames, target) {\n let didProject = false;\n const projectedFrames = frames.map((frame) => {\n const projectedFrame = projectDetectionFrame(frame, target);\n didProject ||= projectedFrame !== frame;\n return projectedFrame;\n });\n return didProject ? projectedFrames : frames;\n}\nfunction projectDetection(detection, scaleX, scaleY) {\n return {\n ...detection,\n ...(detection.rect\n ? { rect: scaleRect(detection.rect, scaleX, scaleY) }\n : {}),\n ...(detection.polygon\n ? { polygon: scalePolygon(detection.polygon, scaleX, scaleY) }\n : {}),\n ...(detection.polyline\n ? { polyline: scalePolyline(detection.polyline, scaleX, scaleY) }\n : {}),\n ...(detection.keypoints\n ? { keypoints: scaleKeypoints(detection.keypoints, scaleX, scaleY) }\n : {}),\n };\n}\nfunction scaleRect(rect, scaleX, scaleY) {\n return {\n height: rect.height * scaleY,\n width: rect.width * scaleX,\n x: rect.x * scaleX,\n y: rect.y * scaleY,\n };\n}\nfunction scalePolygon(polygon, scaleX, scaleY) {\n return { points: scalePoints(polygon.points, scaleX, scaleY) };\n}\nfunction scalePolyline(polyline, scaleX, scaleY) {\n return { points: scalePoints(polyline.points, scaleX, scaleY) };\n}\nfunction scaleKeypoints(keypoints, scaleX, scaleY) {\n return {\n ...keypoints,\n points: scalePoints(keypoints.points, scaleX, scaleY),\n };\n}\nfunction scalePoints(points, scaleX, scaleY) {\n return points.map((point) => ({ x: point.x * scaleX, y: point.y * scaleY }));\n}\nfunction isUsableCoordinateSpace(space) {\n return (Number.isFinite(space.width) &&\n Number.isFinite(space.height) &&\n space.width > 0 &&\n space.height > 0);\n}\n\nfunction createCompositeDetectionFrameSource(options) {\n const sources = normalizeCompositeSources(options.sources);\n return {\n async loadFrames(startTime, endTime, loadOptions) {\n const target = loadOptions?.coordinateSpace;\n const loadedSources = await Promise.all(sources.map(async (source) => {\n const frames = copySortedDetectionFrames(await source.source.loadFrames(startTime, endTime, loadOptions));\n return {\n ...source,\n // Composition flattens child detections into one frame, which can\n // only carry one coordinate space. Each child is projected here,\n // while its own `coordinateSpace` is still attached to its own\n // detections, so children inferred at different sizes compose\n // correctly. Masks keep their intrinsic dimensions.\n frames: target ? projectDetectionFrames(frames, target) : frames,\n };\n }));\n if (options.selectionMode === DetectionFrameSelectionMode.NearestFrameIndex) {\n const nearestFrames = composeNearestFrameIndexFrames(loadedSources, startTime, endTime, options, target);\n if (nearestFrames) {\n return nearestFrames;\n }\n }\n return composeIntervalFrames(loadedSources, startTime, endTime, options, target);\n },\n async waitForRange(range) {\n await Promise.all(sources\n .filter((source) => source.requiredForCoverage)\n .map((source) => source.source.waitForRange?.(range)));\n },\n getAvailableRanges() {\n return mergeRanges$1(sources.flatMap((source) => source.source.getAvailableRanges?.() ?? []));\n },\n getVersion(range) {\n return sources.reduce((version, source) => version + (source.source.getVersion?.(range) ?? 0), 0);\n },\n destroy() {\n for (const source of sources) {\n source.source.destroy?.();\n }\n },\n };\n}\nfunction normalizeCompositeSources(entries) {\n const sourceIds = new Set();\n return entries\n .map((entry, declarationIndex) => {\n if (sourceIds.has(entry.id)) {\n throw new Error(`Duplicate detection source id: ${entry.id}.`);\n }\n sourceIds.add(entry.id);\n const inputCount = [\n entry.frames !== undefined,\n entry.source !== undefined,\n ].filter(Boolean).length;\n if (inputCount !== 1) {\n throw new Error(`Detection source ${entry.id} must provide exactly one input: frames or source.`);\n }\n return {\n declarationIndex,\n id: entry.id,\n order: entry.order ?? 0,\n requiredForCoverage: entry.requiredForCoverage !== false,\n source: entry.source ?? createArrayDetectionFrameSource(entry.frames ?? []),\n sync: entry.sync,\n };\n })\n .sort((left, right) => left.order - right.order ||\n left.declarationIndex - right.declarationIndex);\n}\nfunction composeIntervalFrames(sources, startTime, endTime, options, coordinateSpace) {\n const boundaryTimes = new Set([startTime]);\n for (const source of sources) {\n for (const frame of source.frames) {\n if (frame.mediaTime >= startTime && frame.mediaTime < endTime) {\n boundaryTimes.add(frame.mediaTime);\n }\n if (frame.endTime !== undefined &&\n frame.endTime > startTime &&\n frame.endTime < endTime) {\n boundaryTimes.add(frame.endTime);\n }\n }\n }\n const sortedBoundaryTimes = [...boundaryTimes].sort((left, right) => left - right);\n const frames = [];\n for (const [boundaryIndex, mediaTime] of sortedBoundaryTimes.entries()) {\n if (mediaTime < startTime || mediaTime >= endTime) {\n continue;\n }\n const nextBoundaryTime = sortedBoundaryTimes[boundaryIndex + 1] ?? endTime;\n const endTimeForFrame = Math.min(nextBoundaryTime, endTime);\n const frame = composeFrameAtTime(sources, mediaTime, endTimeForFrame, { ...options, selectionMode: DetectionFrameSelectionMode.Interval }, coordinateSpace);\n if (frame) {\n frames.push(frame);\n }\n }\n return frames;\n}\n/**\n * One composed frame per inference grid index the children wrote, and one per\n * child frame that carries no index.\n *\n * Children are paired on the index their producer labelled, and the composed\n * frame keeps the media time they reported for it. `frameRate` states the grid\n * a producer was asked for, not the one the clip plays at, and a slower clip\n * answers consecutive indexes with the same decoded sample: a timeline placed\n * by that rate then asks for a time two indexes share, which can only ever\n * reach one of them and leaves the other's detections stranded. Pairing on the\n * index reaches every one and keeps each frame's detections on the frame they\n * were computed for.\n *\n * A frame carrying no index has no grid position to be paired on, so it\n * composes at its own media time, and every child answers there with whatever\n * it has standing. That is how a child sitting on a grid index keeps its\n * detections on screen while an unindexed frame from another child overlaps it.\n * The composed frame carries no index of its own, because only part of what it\n * holds sits on the grid.\n */\nfunction composeNearestFrameIndexFrames(sources, startTime, endTime, options, coordinateSpace) {\n const frameRate = options.frameRate;\n if (!frameRate || !Number.isFinite(frameRate) || frameRate <= 0) {\n return null;\n }\n const gridSources = sources.map((source) => ({\n ...source,\n framesByIndex: indexFramesByFrameIndex(source, options),\n }));\n const indexedSlots = mergeGridSlots(gridSources);\n if (indexedSlots.length === 0) {\n return null;\n }\n const gridStep = measureGridStep(indexedSlots) ?? 1 / frameRate;\n const slots = [\n ...indexedSlots,\n ...mergeUnindexedGridSlots(gridSources, indexedSlots),\n ];\n const frames = [];\n for (const slot of slots) {\n if (slot.mediaTime < startTime || slot.mediaTime >= endTime) {\n continue;\n }\n const frame = composeFrameAtGridSlot(gridSources, slot, Math.min(slot.mediaTime + gridStep, endTime), {\n ...options,\n selectionMode: DetectionFrameSelectionMode.NearestFrameIndex,\n }, coordinateSpace);\n if (frame) {\n frames.push(frame);\n }\n }\n return frames.sort((left, right) => left.mediaTime - right.mediaTime);\n}\nfunction indexFramesByFrameIndex(source, options) {\n const framesByIndex = new Map();\n const selectionMode = source.sync?.selectionMode ?? options.selectionMode;\n if (selectionMode !== DetectionFrameSelectionMode.NearestFrameIndex) {\n return framesByIndex;\n }\n for (const frame of source.frames) {\n if (frame.frameIndex !== undefined) {\n framesByIndex.set(frame.frameIndex, frame);\n }\n }\n return framesByIndex;\n}\n/**\n * Where each grid index sits in the media, taken as the earliest time any child\n * reported for it. Children on one grid watched the same source frame, and the\n * earliest of their answers is where that frame starts.\n */\nfunction mergeGridSlots(sources) {\n const mediaTimes = new Map();\n for (const source of sources) {\n for (const [frameIndex, frame] of source.framesByIndex) {\n const mediaTime = mediaTimes.get(frameIndex);\n if (mediaTime === undefined || frame.mediaTime < mediaTime) {\n mediaTimes.set(frameIndex, frame.mediaTime);\n }\n }\n }\n return [...mediaTimes]\n .map(([frameIndex, mediaTime]) => ({ frameIndex, mediaTime }))\n .sort((left, right) => left.frameIndex - right.frameIndex);\n}\n/**\n * Where a frame no producer labelled sits: at the media time it reported, which\n * is the only handle it has.\n */\nfunction mergeUnindexedGridSlots(sources, indexedSlots) {\n const indexedSlotTimes = new Set(indexedSlots.map((slot) => slot.mediaTime));\n const mediaTimes = new Set();\n for (const source of sources) {\n for (const frame of source.frames) {\n if (frame.frameIndex === undefined &&\n !indexedSlotTimes.has(frame.mediaTime)) {\n mediaTimes.add(frame.mediaTime);\n }\n }\n }\n return [...mediaTimes]\n .sort((left, right) => left - right)\n .map((mediaTime) => ({ mediaTime }));\n}\n/**\n * How long one grid index stands, read off the slots across the widest index\n * span they cover. A clip whose real rate differs from `frameRate` would end\n * every composed frame a little short or a little long of where the next index\n * is due, and that error accumulates for as long as the clip plays.\n */\nfunction measureGridStep(gridSlots) {\n const firstSlot = gridSlots[0];\n const lastSlot = gridSlots[gridSlots.length - 1];\n const indexSpan = lastSlot.frameIndex - firstSlot.frameIndex;\n const step = (lastSlot.mediaTime - firstSlot.mediaTime) / indexSpan;\n return indexSpan > 0 && Number.isFinite(step) && step > 0 ? step : undefined;\n}\nfunction composeFrameAtGridSlot(sources, slot, endTime, options, coordinateSpace) {\n const detections = [];\n for (const source of sources) {\n const activeFrame = selectGridSlotFrame(source, slot, options);\n if (!activeFrame) {\n continue;\n }\n activeFrame.detections.forEach((detection, sourceDetectionIndex) => {\n detections.push(copyDetectionWithSource(detection, source.id, sourceDetectionIndex));\n });\n }\n if (detections.length === 0) {\n return undefined;\n }\n return {\n detections,\n endTime,\n frameIndex: slot.frameIndex,\n mediaTime: slot.mediaTime,\n // Children were projected before composition, so a composed frame is\n // already in the coordinate space the renderer presents.\n ...(coordinateSpace ? { coordinateSpace } : {}),\n };\n}\n/**\n * What one child has to say at a slot.\n *\n * A child that wrote this index answers with the frame it wrote for it, however\n * far its own media time sits from the slot's. A child on the grid that never\n * wrote this index answers only with a frame of its own that carries no index\n * and is standing here: its neighbouring indexes describe media the slot is not\n * on, and lending one of those would put those detections on a frame they were\n * not computed for.\n */\nfunction selectGridSlotFrame(source, slot, options) {\n const pairedFrame = slot.frameIndex === undefined\n ? undefined\n : source.framesByIndex.get(slot.frameIndex);\n if (pairedFrame) {\n return pairedFrame;\n }\n const selectedFrame = selectDetectionFrame(source.frames, slot.mediaTime, {\n ...options,\n ...source.sync,\n });\n const isGridChildAtIndexedSlot = source.framesByIndex.size > 0 && slot.frameIndex !== undefined;\n return isGridChildAtIndexedSlot && selectedFrame?.frameIndex !== undefined\n ? undefined\n : selectedFrame;\n}\nfunction composeFrameAtTime(sources, mediaTime, endTime, options, coordinateSpace) {\n const detections = [];\n const activeFrameIndexes = [];\n for (const source of sources) {\n const activeFrame = selectDetectionFrame(source.frames, mediaTime, {\n ...options,\n ...source.sync,\n });\n if (!activeFrame) {\n continue;\n }\n if (activeFrame.frameIndex !== undefined) {\n activeFrameIndexes.push(activeFrame.frameIndex);\n }\n activeFrame.detections.forEach((detection, sourceDetectionIndex) => {\n detections.push(copyDetectionWithSource(detection, source.id, sourceDetectionIndex));\n });\n }\n if (detections.length === 0) {\n return undefined;\n }\n return {\n detections,\n endTime,\n frameIndex: resolveComposedFrameIndex(activeFrameIndexes) ?? undefined,\n mediaTime,\n // Children were projected before composition, so a composed frame is\n // already in the coordinate space the renderer presents.\n ...(coordinateSpace ? { coordinateSpace } : {}),\n };\n}\nfunction copyDetectionWithSource(detection, sourceId, sourceDetectionIndex) {\n return {\n ...detection,\n mask: detection.mask ? { ...detection.mask } : undefined,\n metadata: detection.metadata ? { ...detection.metadata } : undefined,\n rect: detection.rect ? { ...detection.rect } : undefined,\n sourceDetectionIndex,\n sourceId,\n };\n}\nfunction resolveComposedFrameIndex(frameIndexes) {\n if (frameIndexes.length === 0) {\n return undefined;\n }\n const firstFrameIndex = frameIndexes[0];\n return frameIndexes.every((frameIndex) => frameIndex === firstFrameIndex)\n ? firstFrameIndex\n : undefined;\n}\nfunction mergeRanges$1(ranges) {\n const sortedRanges = [...ranges].sort((left, right) => left.startTime - right.startTime);\n const mergedRanges = [];\n for (const range of sortedRanges) {\n const lastRange = mergedRanges.at(-1);\n if (!lastRange || range.startTime > lastRange.endTime) {\n mergedRanges.push({ ...range });\n continue;\n }\n mergedRanges[mergedRanges.length - 1] = {\n startTime: lastRange.startTime,\n endTime: Math.max(lastRange.endTime, range.endTime),\n };\n }\n return mergedRanges;\n}\n\nvar AnnotationFrameMutationKind;\n(function (AnnotationFrameMutationKind) {\n AnnotationFrameMutationKind[\"Add\"] = \"add\";\n AnnotationFrameMutationKind[\"Remove\"] = \"remove\";\n AnnotationFrameMutationKind[\"Replace\"] = \"replace\";\n AnnotationFrameMutationKind[\"Transact\"] = \"transact\";\n AnnotationFrameMutationKind[\"Update\"] = \"update\";\n})(AnnotationFrameMutationKind || (AnnotationFrameMutationKind = {}));\nfunction createEditableAnnotationFrameSession(initialFrame) {\n let snapshot = createSnapshot(initialFrame);\n let destroyed = false;\n const listeners = new Set();\n assertStableIds(snapshot);\n const commit = (frame, kind, detectionIds) => {\n assertActive();\n const previous = snapshot;\n const current = createSnapshot(frame);\n assertStableIds(current);\n snapshot = current;\n const mutation = Object.freeze({\n current,\n detectionIds: Object.freeze([...detectionIds]),\n kind,\n previous,\n });\n for (const listener of listeners) {\n listener(mutation);\n }\n return current;\n };\n const session = {\n getSnapshot() {\n assertActive();\n return snapshot;\n },\n add(detection, index = snapshot.detections.length) {\n assertActive();\n const id = requireDetectionId(detection);\n if (findDetectionIndex(snapshot, id) !== -1) {\n throw new Error(`Detection id ${String(id)} already exists.`);\n }\n const detections = [...snapshot.detections];\n const insertionIndex = Math.max(0, Math.min(index, detections.length));\n detections.splice(insertionIndex, 0, detection);\n return commit({ ...snapshot, detections }, AnnotationFrameMutationKind.Add, [id]);\n },\n update(id, update) {\n assertActive();\n const detectionIndex = findDetectionIndex(snapshot, id);\n if (detectionIndex === -1) {\n throw new Error(`Detection id ${String(id)} was not found.`);\n }\n const detections = [...snapshot.detections];\n const current = detections[detectionIndex];\n const next = typeof update === \"function\"\n ? update(current)\n : { ...current, ...update };\n if (next.id !== id) {\n throw new Error(\"Detection updates must preserve the stable id.\");\n }\n detections[detectionIndex] = next;\n return commit({ ...snapshot, detections }, AnnotationFrameMutationKind.Update, [id]);\n },\n remove(id) {\n assertActive();\n const detectionIndex = findDetectionIndex(snapshot, id);\n if (detectionIndex === -1) {\n return snapshot;\n }\n const detections = [...snapshot.detections];\n detections.splice(detectionIndex, 1);\n return commit({ ...snapshot, detections }, AnnotationFrameMutationKind.Remove, [id]);\n },\n replace(frame) {\n return commit(frame, AnnotationFrameMutationKind.Replace, frame.detections.map(requireDetectionId));\n },\n transact(mutate, detectionIds = []) {\n assertActive();\n const detections = copySortedDetectionFrames([snapshot])[0]\n .detections;\n mutate(detections);\n return commit({ ...snapshot, detections }, AnnotationFrameMutationKind.Transact, detectionIds);\n },\n subscribe(listener) {\n assertActive();\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n destroy() {\n destroyed = true;\n listeners.clear();\n },\n };\n return session;\n function assertActive() {\n if (destroyed) {\n throw new Error(\"Editable annotation frame session has been destroyed.\");\n }\n }\n}\nfunction findDetectionIndex(frame, id) {\n return frame.detections.findIndex((detection) => detection.id === id);\n}\nfunction requireDetectionId(detection) {\n if (detection.id === undefined) {\n throw new Error(\"Editable annotation detections require a stable id.\");\n }\n return detection.id;\n}\nfunction assertStableIds(frame) {\n const ids = new Set();\n for (const detection of frame.detections) {\n const id = requireDetectionId(detection);\n if (ids.has(id)) {\n throw new Error(`Detection id ${String(id)} is duplicated.`);\n }\n ids.add(id);\n }\n}\nfunction createSnapshot(frame) {\n const snapshot = copySortedDetectionFrames([frame])[0];\n return deepFreeze(snapshot);\n}\nfunction deepFreeze(value) {\n if (value && typeof value === \"object\" && !Object.isFrozen(value)) {\n Object.freeze(value);\n for (const nested of Object.values(value)) {\n deepFreeze(nested);\n }\n }\n return value;\n}\n\nconst DEFAULT_CHUNK_DURATION_SECONDS = 1;\nfunction createMemoryColdDetectionFrameStore() {\n const datasets = new Map();\n let destroyed = false;\n return {\n async putFrames(options) {\n assertActive();\n const dataset = createDataset(resolveChunkDurationSeconds(options));\n upsertFrames(dataset, copySortedDetectionFrames(options.frames));\n datasets.set(options.datasetId, dataset);\n return createWriteSummary(options.datasetId, dataset);\n },\n async appendFrames(options) {\n assertActive();\n const existingDataset = datasets.get(options.datasetId);\n const chunkDurationSeconds = resolveChunkDurationSeconds(options, existingDataset);\n const dataset = existingDataset ?? createDataset(chunkDurationSeconds);\n upsertFrames(dataset, copySortedDetectionFrames(options.frames));\n datasets.set(options.datasetId, dataset);\n return createWriteSummary(options.datasetId, dataset);\n },\n async loadFrames(options) {\n assertActive();\n const dataset = datasets.get(options.datasetId);\n if (!dataset) {\n return [];\n }\n const startTime = Math.max(0, options.startTime);\n const endTime = Math.max(startTime, options.endTime);\n const frameKeys = new Set();\n const startChunkIndex = getChunkIndex(startTime, dataset.chunkDurationSeconds);\n const endChunkIndex = getChunkIndex(endTime, dataset.chunkDurationSeconds);\n for (let chunkIndex = startChunkIndex; chunkIndex <= endChunkIndex; chunkIndex += 1) {\n for (const frameKey of dataset.frameKeysByChunk.get(chunkIndex) ?? []) {\n frameKeys.add(frameKey);\n }\n }\n const frames = Array.from(frameKeys)\n .map((frameKey) => dataset.framesByKey.get(frameKey)?.frame)\n .filter((frame) => frame !== undefined &&\n detectionFrameOverlapsRange(frame, startTime, endTime));\n return copySortedDetectionFrames(frames);\n },\n async pruneFrames(options) {\n assertActive();\n assertRetentionFloor(options.startTime);\n const dataset = datasets.get(options.datasetId);\n if (!dataset) {\n return createWriteSummary(options.datasetId, createDataset(DEFAULT_CHUNK_DURATION_SECONDS));\n }\n pruneDatasetBefore(dataset, options.startTime);\n return createWriteSummary(options.datasetId, dataset);\n },\n async clearDataset(datasetId) {\n assertActive();\n datasets.delete(datasetId);\n },\n destroy() {\n destroyed = true;\n datasets.clear();\n },\n };\n function assertActive() {\n if (destroyed) {\n throw new Error(\"Memory cold detection frame store has been destroyed.\");\n }\n }\n}\nfunction createDataset(chunkDurationSeconds) {\n return {\n chunkDurationSeconds,\n detectionCount: 0,\n endTime: null,\n frameKeysByChunk: new Map(),\n framesByKey: new Map(),\n startTime: null,\n };\n}\nfunction upsertFrames(dataset, frames) {\n let shouldRecalculateBounds = false;\n for (const frame of frames) {\n const frameKey = getDetectionFrameDedupeKey(frame);\n const existingFrame = dataset.framesByKey.get(frameKey);\n if (existingFrame) {\n removeStoredFrame(dataset, frameKey, existingFrame);\n shouldRecalculateBounds ||= replacementCanShrinkBounds(dataset, existingFrame.frame, frame);\n }\n const chunkIndexes = getFrameChunkIndexes(frame, dataset.chunkDurationSeconds);\n dataset.framesByKey.set(frameKey, { chunkIndexes, frame });\n dataset.detectionCount += frame.detections.length;\n dataset.startTime = Math.min(dataset.startTime ?? frame.mediaTime, frame.mediaTime);\n dataset.endTime = Math.max(dataset.endTime ?? getFrameEndTime$1(frame), getFrameEndTime$1(frame));\n for (const chunkIndex of chunkIndexes) {\n const frameKeys = dataset.frameKeysByChunk.get(chunkIndex) ?? new Set();\n frameKeys.add(frameKey);\n dataset.frameKeysByChunk.set(chunkIndex, frameKeys);\n }\n }\n if (shouldRecalculateBounds) {\n recalculateBounds(dataset);\n }\n}\nfunction removeStoredFrame(dataset, frameKey, storedFrame) {\n dataset.framesByKey.delete(frameKey);\n dataset.detectionCount -= storedFrame.frame.detections.length;\n for (const chunkIndex of storedFrame.chunkIndexes) {\n const frameKeys = dataset.frameKeysByChunk.get(chunkIndex);\n frameKeys?.delete(frameKey);\n if (frameKeys?.size === 0) {\n dataset.frameKeysByChunk.delete(chunkIndex);\n }\n }\n}\n/**\n * Drops frames that end before `startTime`.\n *\n * Only chunks at or below the retention boundary are visited, and the new\n * start bound is read from the lowest surviving chunk, so eviction costs work\n * proportional to the evicted range rather than to retained history.\n */\nfunction pruneDatasetBefore(dataset, startTime) {\n const boundaryChunkIndex = getChunkIndex(Math.max(0, startTime), dataset.chunkDurationSeconds);\n let didRemoveFrame = false;\n for (const chunkIndex of Array.from(dataset.frameKeysByChunk.keys())) {\n if (chunkIndex > boundaryChunkIndex) {\n continue;\n }\n for (const frameKey of Array.from(dataset.frameKeysByChunk.get(chunkIndex) ?? [])) {\n const storedFrame = dataset.framesByKey.get(frameKey);\n if (!storedFrame || isFrameRetainedFrom(storedFrame.frame, startTime)) {\n continue;\n }\n removeStoredFrame(dataset, frameKey, storedFrame);\n didRemoveFrame = true;\n }\n }\n if (!didRemoveFrame) {\n return;\n }\n if (dataset.framesByKey.size === 0) {\n dataset.startTime = null;\n dataset.endTime = null;\n return;\n }\n // Every frame is registered in the chunk its `mediaTime` falls in, so the\n // earliest retained frame lives in the lowest remaining chunk. Removing\n // frames can only raise the start bound; `endTime` is unchanged because only\n // frames ending before `startTime` were dropped.\n dataset.startTime = getEarliestChunkStartTime(dataset);\n}\n/**\n * Mirrors `detectionFrameOverlapsRange` so pruning keeps exactly the frames a\n * load from `startTime` would return.\n */\nfunction isFrameRetainedFrom(frame, startTime) {\n return frame.endTime === undefined\n ? frame.mediaTime >= startTime\n : frame.endTime > startTime;\n}\nfunction getEarliestChunkStartTime(dataset) {\n let earliestChunkIndex = null;\n for (const chunkIndex of dataset.frameKeysByChunk.keys()) {\n if (earliestChunkIndex === null || chunkIndex < earliestChunkIndex) {\n earliestChunkIndex = chunkIndex;\n }\n }\n let startTime = null;\n for (const frameKey of dataset.frameKeysByChunk.get(earliestChunkIndex ?? 0) ?? []) {\n const storedFrame = dataset.framesByKey.get(frameKey);\n if (!storedFrame) {\n continue;\n }\n startTime = Math.min(startTime ?? storedFrame.frame.mediaTime, storedFrame.frame.mediaTime);\n }\n return startTime;\n}\nfunction replacementCanShrinkBounds(dataset, previousFrame, nextFrame) {\n return ((dataset.startTime === previousFrame.mediaTime &&\n nextFrame.mediaTime > previousFrame.mediaTime) ||\n (dataset.endTime === getFrameEndTime$1(previousFrame) &&\n getFrameEndTime$1(nextFrame) < getFrameEndTime$1(previousFrame)));\n}\nfunction recalculateBounds(dataset) {\n let startTime = null;\n let endTime = null;\n for (const { frame } of dataset.framesByKey.values()) {\n startTime = Math.min(startTime ?? frame.mediaTime, frame.mediaTime);\n endTime = Math.max(endTime ?? getFrameEndTime$1(frame), getFrameEndTime$1(frame));\n }\n dataset.startTime = startTime;\n dataset.endTime = endTime;\n}\nfunction resolveChunkDurationSeconds(options, existingDataset) {\n const chunkDurationSeconds = options.chunkDurationSeconds ??\n existingDataset?.chunkDurationSeconds ??\n DEFAULT_CHUNK_DURATION_SECONDS;\n if (chunkDurationSeconds <= 0) {\n throw new Error(\"chunkDurationSeconds must be greater than 0.\");\n }\n if (existingDataset &&\n options.chunkDurationSeconds !== undefined &&\n options.chunkDurationSeconds !== existingDataset.chunkDurationSeconds) {\n throw new Error(\"chunkDurationSeconds must match the existing detection dataset.\");\n }\n return chunkDurationSeconds;\n}\nfunction createWriteSummary(datasetId, dataset) {\n return {\n chunkCount: dataset.frameKeysByChunk.size,\n chunkDurationSeconds: dataset.chunkDurationSeconds,\n datasetId,\n detectionCount: dataset.detectionCount,\n endTime: dataset.endTime,\n frameCount: dataset.framesByKey.size,\n startTime: dataset.startTime,\n };\n}\nfunction getFrameChunkIndexes(frame, chunkDurationSeconds) {\n const startIndex = getChunkIndex(frame.mediaTime, chunkDurationSeconds);\n const endIndex = Math.max(startIndex, Math.ceil(getFrameEndTime$1(frame) / chunkDurationSeconds) - 1);\n return Array.from({ length: endIndex - startIndex + 1 }, (_, offset) => startIndex + offset);\n}\nfunction getFrameEndTime$1(frame) {\n return frame.endTime ?? frame.mediaTime;\n}\nfunction getChunkIndex(mediaTime, chunkDurationSeconds) {\n return Math.floor(mediaTime / chunkDurationSeconds);\n}\nfunction getDetectionFrameDedupeKey(frame) {\n return frame.frameIndex === undefined\n ? `time:${frame.mediaTime}`\n : `index:${frame.frameIndex}`;\n}\n/**\n * Rejects a retention floor that could not describe a real media time.\n *\n * Pruning mutates retained history in place, and `NaN` compares false against\n * every retained frame, so an unvalidated floor would silently erase a dataset\n * instead of evicting a bounded range.\n */\nfunction assertRetentionFloor(startTime) {\n if (!Number.isFinite(startTime) || startTime < 0) {\n throw new RangeError(\"pruneFrames requires a finite, non-negative startTime.\");\n }\n}\n\n/**\n * Wraps a detection source so every loaded frame that declares a source\n * coordinate space is projected into the target space.\n *\n * The target is resolved per load because media dimensions are only known once\n * the media has opened, while sources are composed before that. Frames without\n * `coordinateSpace`, and loads made before a target exists, pass through by\n * reference, so a producer already emitting media-pixel geometry pays nothing.\n *\n * The target is also handed to the wrapped source through\n * `DetectionFrameLoadOptions`. A source that flattens child frames, such as a\n * composite source, needs it to project each child while that child's own\n * `coordinateSpace` is still attached; projecting the flattened result again\n * here is then a no-op.\n *\n * Optional source hooks are forwarded only when the wrapped source implements\n * them, so capability detection keeps working through the wrapper.\n */\nfunction createProjectedDetectionFrameSource(source, resolveTarget) {\n const { getAvailableRanges, getChangesSince, getVersion, waitForRange } = source;\n return {\n async loadFrames(startTime, endTime, options) {\n const target = resolveTarget();\n const frames = await source.loadFrames(startTime, endTime, {\n ...options,\n ...(target ? { coordinateSpace: target } : {}),\n });\n return target ? projectDetectionFrames(frames, target) : frames;\n },\n ...(waitForRange\n ? {\n waitForRange: (range) => waitForRange.call(source, range),\n }\n : {}),\n ...(getAvailableRanges\n ? { getAvailableRanges: () => getAvailableRanges.call(source) }\n : {}),\n ...(getVersion\n ? {\n getVersion: (range) => getVersion.call(source, range),\n }\n : {}),\n ...(getChangesSince\n ? {\n getChangesSince: (version, ranges) => getChangesSince.call(source, version, ranges),\n }\n : {}),\n ...(source.destroy ? { destroy: () => source.destroy?.() } : {}),\n };\n}\n\nconst MAX_CHANGED_RANGE_JOURNAL_LENGTH = 512;\nconst DEFAULT_LIVE_HOLD_SECONDS = 60;\nconst NO_COVERAGE_INVALIDATION = { ranges: [] };\nfunction createWritableDetectionFrameSource(options) {\n const liveHoldSeconds = resolveLiveHoldSeconds(options.live?.holdSeconds);\n let summary = null;\n let latestFrame = null;\n let heldLiveFrame = null;\n // Newest live `mediaTime` accepted so far. It survives retention so a late\n // result cannot reopen coverage the source already moved past.\n let liveFrontierTime = null;\n // Newest media time actually covered by producer-declared data. Live frames\n // are written with a synthetic hold end, so the summary end time cannot be\n // used to place the retention window without pushing it into the future.\n let coverageFrontierTime = null;\n let writeQueue = Promise.resolve();\n let version = 0;\n let allRangeVersion = 0;\n let journalFloorVersion = 0;\n let destroyed = false;\n const changedRanges = [];\n const availableRanges = [];\n const waiters = [];\n const writeOptions = (frames) => ({\n chunkDurationSeconds: options.chunkDurationSeconds,\n datasetId: options.datasetId,\n frames,\n });\n const recordRangeWrite = (nextSummary, changedSourceRanges, invalidation, prunedRange) => {\n summary = nextSummary;\n version += 1;\n for (const changedRange of changedSourceRanges) {\n changedRanges.push({ ...changedRange, version });\n recordAvailableRange(changedRange, availableRanges);\n }\n for (const invalidatedRange of invalidation.ranges) {\n // Coverage a write took away is journaled but never made available: a\n // consumer parked there has to redraw, and nothing new can be loaded.\n changedRanges.push({ ...invalidatedRange, version });\n }\n if (prunedRange) {\n // Evicting old history is a change to that range only. Recording it\n // alongside the append keeps a buffered timeline patching two small\n // ranges instead of invalidating and rebuilding everything it retains.\n changedRanges.push({ ...prunedRange, version });\n dropAvailableRangesBefore(prunedRange.endTime, availableRanges);\n }\n compactChangedRangeJournal();\n resolveCoveredWaiters();\n return nextSummary;\n };\n const recordAllRangesWrite = (nextSummary) => {\n summary = nextSummary;\n version += 1;\n allRangeVersion = version;\n journalFloorVersion = version;\n changedRanges.length = 0;\n availableRanges.length = 0;\n if (nextSummary.startTime !== null && nextSummary.endTime !== null) {\n recordAvailableRange({\n endTime: nextSummary.endTime,\n startTime: nextSummary.startTime,\n }, availableRanges);\n }\n resolveCoveredWaiters();\n return nextSummary;\n };\n return {\n datasetId: options.datasetId,\n async appendFrames(frames) {\n assertActive();\n return enqueueWrite(async () => {\n const nextSummary = await options.store.appendFrames(writeOptions(frames));\n assertActive();\n const changedSourceRanges = getDetectionFrameRanges(frames);\n const invalidation = getInvalidatedCoverage(frames, changedSourceRanges);\n recordLatestFrame(frames);\n recordCoverageFrontierFromFrames(frames);\n return retainAndRecord(nextSummary, changedSourceRanges, invalidation);\n });\n },\n async appendLiveFrame(frame) {\n assertActive();\n return enqueueWrite(async () => {\n if (isStaleLiveFrame(frame)) {\n // A result older than the live frontier no longer describes what is\n // on screen. Writing it would reopen coverage the source already\n // closed, so the newest causal frame simply wins.\n return summary ? { ...summary } : createEmptyWriteSummary();\n }\n const framesToWrite = createLiveFrameWrite(frame);\n const nextSummary = await options.store.appendFrames(writeOptions(framesToWrite));\n assertActive();\n const changedSourceRanges = getDetectionFrameRanges(framesToWrite);\n const invalidation = getInvalidatedCoverage(framesToWrite, changedSourceRanges);\n heldLiveFrame = framesToWrite.at(-1) ?? null;\n liveFrontierTime = Math.max(liveFrontierTime ?? frame.mediaTime, frame.mediaTime);\n recordLatestFrame(framesToWrite);\n // The hold end is a placeholder for \"still current\", not covered data.\n recordCoverageFrontier(getFrameEndTime(frame));\n return retainAndRecord(nextSummary, changedSourceRanges, invalidation);\n });\n },\n async finalizeCoverage(endTime) {\n assertActive();\n if (!Number.isFinite(endTime)) {\n throw new RangeError(\"finalizeCoverage requires a finite endTime.\");\n }\n return enqueueWrite(async () => {\n const frameToFinalize = latestFrame;\n if (!frameToFinalize ||\n // A frame that starts at or after the end of media cannot describe a\n // non-empty terminal interval.\n frameToFinalize.mediaTime >= endTime ||\n getFrameEndTime(frameToFinalize) === endTime) {\n return summary ? { ...summary } : null;\n }\n const previousEndTime = getFrameEndTime(frameToFinalize);\n const finalizedFrame = { ...frameToFinalize, endTime };\n const nextSummary = await options.store.appendFrames(writeOptions([finalizedFrame]));\n assertActive();\n const changedSourceRanges = [\n {\n endTime: Math.max(endTime, previousEndTime),\n startTime: frameToFinalize.mediaTime,\n },\n ];\n const invalidation = getInvalidatedCoverage([finalizedFrame], changedSourceRanges);\n latestFrame = finalizedFrame;\n if (heldLiveFrame &&\n haveSameDetectionFrameIdentity(heldLiveFrame, finalizedFrame)) {\n heldLiveFrame = finalizedFrame;\n }\n coverageFrontierTime = endTime;\n const finalSummary = await retainAndRecord(nextSummary, changedSourceRanges, \n // Finalizing usually closes at the end of media, where the tail an\n // open-ended frame gives up is unreachable. The caller may finalize\n // earlier, though, so report it rather than assume.\n invalidation);\n // Closing a held live frame shortens coverage. Reported availability\n // has to shrink with it, or `waitForRange` and the buffered window\n // would still believe the source covers time past the end of media.\n clipAvailableRangesAfter(endTime);\n return finalSummary;\n });\n },\n async replaceFrames(frames) {\n assertActive();\n return enqueueWrite(async () => {\n const nextSummary = await options.store.putFrames(writeOptions(frames));\n assertActive();\n latestFrame = null;\n heldLiveFrame = null;\n liveFrontierTime = null;\n coverageFrontierTime = null;\n recordLatestFrame(frames);\n recordCoverageFrontierFromFrames(frames);\n const retention = await applyRetention(nextSummary);\n assertActive();\n return recordAllRangesWrite(retention.kind === \"unchanged\" ? nextSummary : retention.summary);\n });\n },\n async clear() {\n assertActive();\n await enqueueWrite(async () => {\n await options.store.clearDataset(options.datasetId);\n assertActive();\n summary = null;\n latestFrame = null;\n heldLiveFrame = null;\n liveFrontierTime = null;\n coverageFrontierTime = null;\n version += 1;\n allRangeVersion = version;\n journalFloorVersion = version;\n changedRanges.length = 0;\n availableRanges.length = 0;\n });\n },\n async loadFrames(startTime, endTime) {\n assertActive();\n const loadedFrames = await options.store.loadFrames({\n datasetId: options.datasetId,\n endTime,\n startTime,\n });\n assertActive();\n return loadedFrames;\n },\n getSummary() {\n return summary ? { ...summary } : null;\n },\n getAvailableRanges() {\n return availableRanges.map((range) => ({ ...range }));\n },\n waitForRange(range) {\n if (destroyed) {\n return Promise.reject(createDestroyedError());\n }\n if (isRangeCovered(range, availableRanges)) {\n return Promise.resolve();\n }\n return new Promise((resolve, reject) => {\n waiters.push({ range, reject, resolve });\n });\n },\n getVersion(range) {\n if (!range) {\n return version;\n }\n return getRangeVersion(range);\n },\n getChangesSince(previousVersion, ranges) {\n const relevantVersion = ranges.reduce((rangeVersion, range) => Math.max(rangeVersion, getRangeVersion(range)), getReloadFloorVersion());\n if (relevantVersion <= previousVersion) {\n return {\n ranges: [],\n requiresReload: false,\n version: relevantVersion,\n };\n }\n if (previousVersion < getReloadFloorVersion()) {\n return {\n ranges: [],\n requiresReload: true,\n version: relevantVersion,\n };\n }\n // Clipped to the requested ranges: a consumer gets back exactly the part\n // of the change it asked about, and an unbounded invalidation stays a\n // bounded, loadable answer.\n const changedSourceRanges = changedRanges\n .filter((changedRange) => changedRange.version > previousVersion)\n .flatMap((changedRange) => ranges\n .filter((range) => rangesOverlap(range, changedRange))\n .map((range) => {\n const startTime = Math.max(range.startTime, changedRange.startTime);\n const endTime = Math.min(range.endTime, changedRange.endTime);\n return {\n // An unbounded ask against an unbounded invalidation would\n // otherwise hand back an infinite range, and a consumer would\n // take it straight to `loadFrames`. Nothing past the coverage\n // the source knows about can be loaded.\n endTime: Number.isFinite(endTime)\n ? endTime\n : Math.max(startTime, getKnownEndTime()),\n startTime,\n };\n }));\n return {\n ranges: mergeRanges(changedSourceRanges),\n requiresReload: false,\n version: relevantVersion,\n };\n },\n destroy() {\n if (destroyed) {\n return;\n }\n destroyed = true;\n for (const waiter of waiters) {\n waiter.reject(new Error(\"Detection frame source has been destroyed.\"));\n }\n waiters.length = 0;\n options.store.destroy?.();\n },\n };\n function assertActive() {\n if (destroyed) {\n throw createDestroyedError();\n }\n }\n /**\n * Oldest version the journal can still describe incrementally. Anything\n * older has to reload, because replacement, clearing, or journal compaction\n * dropped the intervening changes.\n */\n function getReloadFloorVersion() {\n return Math.max(allRangeVersion, journalFloorVersion);\n }\n /**\n * Latest media time the source can actually serve frames for.\n */\n function getKnownEndTime() {\n return Math.max(coverageFrontierTime ?? 0, summary?.endTime ?? 0);\n }\n function getRangeVersion(range) {\n return changedRanges.reduce((rangeVersion, changedRange) => rangesOverlap(range, changedRange)\n ? Math.max(rangeVersion, changedRange.version)\n : rangeVersion, getReloadFloorVersion());\n }\n /**\n * Runs mutating writes one at a time.\n *\n * Every write reads the held live frame, the latest frame, and the coverage\n * frontier before it awaits storage. Without serialization two concurrent\n * live appends would both observe the pre-write state and both stay open,\n * which is exactly the stale overlay live semantics exist to prevent.\n */\n function enqueueWrite(run) {\n const result = writeQueue.then(() => {\n assertActive();\n return run();\n });\n writeQueue = result.then(() => undefined, () => undefined);\n return result;\n }\n function isStaleLiveFrame(frame) {\n if (liveFrontierTime === null || frame.mediaTime > liveFrontierTime) {\n return false;\n }\n // A frame at the frontier that shares the held frame's identity is a\n // revision of the current result, not a late duplicate of an older one.\n return !(heldLiveFrame && haveSameDetectionFrameIdentity(heldLiveFrame, frame));\n }\n function recordCoverageFrontier(endTime) {\n coverageFrontierTime =\n coverageFrontierTime === null\n ? endTime\n : Math.max(coverageFrontierTime, endTime);\n }\n function recordCoverageFrontierFromFrames(frames) {\n for (const frame of frames) {\n recordCoverageFrontier(getFrameEndTime(frame));\n }\n }\n function clipAvailableRangesAfter(endTime) {\n let writeIndex = 0;\n for (const range of availableRanges) {\n if (range.startTime >= endTime) {\n continue;\n }\n availableRanges[writeIndex] = {\n endTime: Math.min(range.endTime, endTime),\n startTime: range.startTime,\n };\n writeIndex += 1;\n }\n availableRanges.length = writeIndex;\n }\n function createEmptyWriteSummary() {\n return {\n chunkCount: 0,\n chunkDurationSeconds: options.chunkDurationSeconds ?? 1,\n datasetId: options.datasetId,\n detectionCount: 0,\n endTime: null,\n frameCount: 0,\n startTime: null,\n };\n }\n /**\n * Applies retention and records the result as an incremental change when the\n * store can prune in place, or as a full rewrite when it cannot.\n */\n async function retainAndRecord(nextSummary, changedSourceRanges, invalidation = NO_COVERAGE_INVALIDATION) {\n const retention = await applyRetention(nextSummary);\n assertActive();\n if (retention.kind === \"rewritten\") {\n return recordAllRangesWrite(retention.summary);\n }\n if (retention.kind === \"unchanged\") {\n if (changedSourceRanges.length === 0 &&\n invalidation.ranges.length === 0) {\n summary = nextSummary;\n return nextSummary;\n }\n return recordRangeWrite(nextSummary, changedSourceRanges, invalidation);\n }\n forgetFramesBefore(retention.prunedRange.endTime);\n return recordRangeWrite(retention.summary, changedSourceRanges, invalidation, retention.prunedRange);\n }\n async function applyRetention(nextSummary) {\n const retention = options.retention;\n if (retention === undefined ||\n !shouldApplyWindowRetention(retention) ||\n nextSummary.endTime === null) {\n return { kind: \"unchanged\" };\n }\n const retentionWindowSeconds = retention.windowSeconds;\n if (retentionWindowSeconds === undefined ||\n !Number.isFinite(retentionWindowSeconds) ||\n retentionWindowSeconds <= 0) {\n throw new Error(\"retention.windowSeconds must be greater than 0.\");\n }\n // Place the window against real producer coverage. A live frame held open\n // for `holdSeconds` reports a summary end far in the future, and anchoring\n // eviction there would evict everything the producer just wrote.\n const retentionStartTime = Math.max(0, Math.min(coverageFrontierTime ?? nextSummary.endTime, nextSummary.endTime) - retentionWindowSeconds);\n if (nextSummary.startTime !== null &&\n nextSummary.startTime + RANGE_EPSILON_SECONDS >= retentionStartTime) {\n return { kind: \"unchanged\" };\n }\n if (options.store.pruneFrames) {\n return {\n kind: \"pruned\",\n prunedRange: {\n endTime: retentionStartTime,\n startTime: nextSummary.startTime ?? 0,\n },\n summary: await options.store.pruneFrames({\n datasetId: options.datasetId,\n startTime: retentionStartTime,\n }),\n };\n }\n // Stores without in-place pruning still get a correct retention window,\n // at the cost of reloading and rewriting everything they keep.\n const retainedFrames = await options.store.loadFrames({\n datasetId: options.datasetId,\n endTime: nextSummary.endTime,\n startTime: retentionStartTime,\n });\n return {\n kind: \"rewritten\",\n summary: await options.store.putFrames(writeOptions(retainedFrames)),\n };\n }\n /**\n * Builds the one or two frames a live append writes: the newest frame held\n * open, plus the previously held frame closed where the newest begins.\n */\n function createLiveFrameWrite(frame) {\n const openFrame = {\n ...frame,\n endTime: Math.max(frame.endTime ?? frame.mediaTime, frame.mediaTime + liveHoldSeconds),\n };\n const heldFrame = heldLiveFrame;\n // Stale results are rejected before this point, so a held frame that is\n // neither the same frame nor already closed always ends strictly later.\n if (!heldFrame ||\n haveSameDetectionFrameIdentity(heldFrame, openFrame) ||\n getFrameEndTime(heldFrame) <= frame.mediaTime) {\n return [openFrame];\n }\n return [{ ...heldFrame, endTime: frame.mediaTime }, openFrame];\n }\n function recordLatestFrame(frames) {\n for (const frame of frames) {\n if (!latestFrame ||\n compareDetectionFrames(frame, latestFrame) > 0 ||\n // A write that shares the tracked frame's identity replaces its stored\n // record. Keeping the superseded contents here would let a later\n // `finalizeCoverage` write them back over the revision.\n haveSameDetectionFrameIdentity(frame, latestFrame)) {\n latestFrame = frame;\n }\n }\n }\n /**\n * Coverage a write takes away from a frame the source already wrote.\n *\n * The journal describes a write by the interval it covers, which is enough\n * while writes only add. Rewriting a frame with a shorter interval, or at a\n * different time, instead stops covering times the new interval never\n * mentions, and a consumer parked in one of them would keep showing a frame\n * the source no longer selects.\n *\n * A frame without an `endTime` was selected with no upper bound, so what it\n * gives up is genuinely unbounded and is journaled that way. The journal\n * stays range-scoped either way: a consumer asking about times the write\n * never touched still hears that nothing changed.\n */\n function getInvalidatedCoverage(frames, coveredRanges) {\n const invalidatedRanges = [];\n for (const frame of frames) {\n for (const trackedFrame of [latestFrame, heldLiveFrame]) {\n if (!trackedFrame ||\n !haveSameDetectionFrameIdentity(trackedFrame, frame)) {\n continue;\n }\n const previousRange = {\n endTime: trackedFrame.endTime ?? Number.POSITIVE_INFINITY,\n startTime: trackedFrame.mediaTime,\n };\n const nextEndTime = frame.endTime ?? Number.POSITIVE_INFINITY;\n if (trackedFrame.mediaTime >= frame.mediaTime &&\n previousRange.endTime <= nextEndTime) {\n continue;\n }\n // A live append closes the held frame and reopens it further out in the\n // same write, so the write's own range already describes the tail. Only\n // coverage nothing else reported is worth a second journal entry.\n if (isRangeCovered(previousRange, coveredRanges)) {\n continue;\n }\n invalidatedRanges.push(previousRange);\n }\n }\n return { ranges: mergeRanges(invalidatedRanges) };\n }\n function forgetFramesBefore(startTime) {\n if (latestFrame && getFrameEndTime(latestFrame) < startTime) {\n latestFrame = null;\n }\n if (heldLiveFrame && getFrameEndTime(heldLiveFrame) < startTime) {\n heldLiveFrame = null;\n }\n }\n function resolveCoveredWaiters() {\n for (let index = waiters.length - 1; index >= 0; index -= 1) {\n const waiter = waiters[index];\n if (!waiter || !isRangeCovered(waiter.range, availableRanges)) {\n continue;\n }\n waiters.splice(index, 1);\n waiter.resolve();\n }\n }\n function compactChangedRangeJournal() {\n const overflow = changedRanges.length - MAX_CHANGED_RANGE_JOURNAL_LENGTH;\n if (overflow <= 0) {\n return;\n }\n const removedRanges = changedRanges.splice(0, overflow);\n journalFloorVersion = Math.max(journalFloorVersion, removedRanges.at(-1)?.version ?? 0);\n }\n}\nfunction createDestroyedError() {\n return new Error(\"Detection frame source has been destroyed.\");\n}\nfunction resolveLiveHoldSeconds(holdSeconds) {\n if (holdSeconds === undefined) {\n return DEFAULT_LIVE_HOLD_SECONDS;\n }\n if (!Number.isFinite(holdSeconds) || holdSeconds <= 0) {\n throw new RangeError(\"live.holdSeconds must be greater than 0.\");\n }\n return holdSeconds;\n}\nfunction getFrameEndTime(frame) {\n return frame.endTime ?? frame.mediaTime;\n}\nfunction haveSameDetectionFrameIdentity(left, right) {\n return left.frameIndex === undefined || right.frameIndex === undefined\n ? left.frameIndex === right.frameIndex && left.mediaTime === right.mediaTime\n : left.frameIndex === right.frameIndex;\n}\nfunction compareDetectionFrames(left, right) {\n if (left.mediaTime !== right.mediaTime) {\n return left.mediaTime - right.mediaTime;\n }\n return (left.frameIndex ?? 0) - (right.frameIndex ?? 0);\n}\nfunction dropAvailableRangesBefore(startTime, availableRanges) {\n let writeIndex = 0;\n for (const range of availableRanges) {\n if (range.endTime <= startTime) {\n continue;\n }\n availableRanges[writeIndex] = {\n endTime: range.endTime,\n startTime: Math.max(range.startTime, startTime),\n };\n writeIndex += 1;\n }\n availableRanges.length = writeIndex;\n}\nfunction shouldApplyWindowRetention(retention) {\n return (retention?.mode === DetectionFrameRetentionMode.PersistWindow ||\n retention?.mode === DetectionFrameRetentionMode.MemoryOnly);\n}\nfunction getDetectionFrameRanges(frames) {\n return mergeRanges(frames.map((frame) => ({\n endTime: frame.endTime ?? frame.mediaTime,\n startTime: frame.mediaTime,\n })));\n}\nfunction rangesOverlap(left, right) {\n return left.startTime <= right.endTime && right.startTime <= left.endTime;\n}\nfunction mergeRanges(ranges) {\n const sortedRanges = ranges\n .map((range) => ({ ...range }))\n .sort((left, right) => left.startTime - right.startTime);\n const mergedRanges = [];\n for (const range of sortedRanges) {\n const previousRange = mergedRanges.at(-1);\n if (previousRange &&\n range.startTime <= previousRange.endTime + RANGE_EPSILON_SECONDS) {\n previousRange.endTime = Math.max(previousRange.endTime, range.endTime);\n continue;\n }\n mergedRanges.push({ ...range });\n }\n return mergedRanges;\n}\nfunction recordAvailableRange(range, availableRanges) {\n if (range.endTime < range.startTime) {\n return;\n }\n availableRanges.push({ ...range });\n availableRanges.sort((left, right) => left.startTime - right.startTime);\n let writeIndex = 0;\n for (const nextRange of availableRanges) {\n const currentRange = availableRanges[writeIndex - 1];\n if (currentRange &&\n nextRange.startTime <= currentRange.endTime + RANGE_EPSILON_SECONDS) {\n currentRange.endTime = Math.max(currentRange.endTime, nextRange.endTime);\n continue;\n }\n availableRanges[writeIndex] = { ...nextRange };\n writeIndex += 1;\n }\n availableRanges.length = writeIndex;\n}\n\n/**\n * Creates one stateful SORT tracker for a single ordered media sequence.\n *\n * Defaults, lifecycle semantics, and observation-only output mirror\n * roboflow/trackers SORT. Motion predictions remain internal to association.\n */\nfunction createSortTracker$1(options = {}) {\n const lostTrackBuffer = normalizeNonNegativeInteger$1(options.lostTrackBuffer ?? 30, \"lostTrackBuffer\");\n const frameRate = options.frameRate ?? 30;\n const trackActivationThreshold = options.trackActivationThreshold ?? 0.25;\n const minimumConsecutiveFrames = normalizePositiveInteger$1(options.minimumConsecutiveFrames ?? 3, \"minimumConsecutiveFrames\");\n const minimumIouThreshold = options.minimumIouThreshold ?? 0.3;\n if (!Number.isFinite(frameRate) || frameRate <= 0) {\n throw new Error(\"frameRate must be a finite positive value.\");\n }\n normalizeUnitInterval$1(trackActivationThreshold, \"trackActivationThreshold\");\n normalizeUnitInterval$1(minimumIouThreshold, \"minimumIouThreshold\");\n const scaledLostTrackBuffer = (frameRate / 30) * lostTrackBuffer;\n if (!Number.isFinite(scaledLostTrackBuffer)) {\n throw new Error(\"Scaled lostTrackBuffer overflows: frameRate / 30 * lostTrackBuffer must be finite.\");\n }\n const maximumFramesWithoutUpdate = lostTrackBuffer === 0 ? 0 : Math.max(1, Math.ceil(scaledLostTrackBuffer));\n let tracks = [];\n let nextTrackerId = 0;\n let previousFrameIndex;\n return {\n reset() {\n tracks = [];\n nextTrackerId = 0;\n previousFrameIndex = undefined;\n },\n update(detections, frameIndex) {\n const frameStep = resolveFrameStep(frameIndex, previousFrameIndex);\n previousFrameIndex = frameIndex ?? previousFrameIndex;\n const predicted = tracks.map((track) => track.predict(frameStep, frameRate));\n const { matches, unmatchedDetections } = associateDetectionsToTracks(detections, predicted, minimumIouThreshold);\n const trackerIds = new Map();\n for (const match of matches) {\n const track = tracks[match.trackIndex];\n const detection = detections[match.detectionIndex];\n track.update(detection);\n if (track.trackerId === undefined &&\n track.successfulUpdates >= minimumConsecutiveFrames) {\n track.trackerId = nextTrackerId;\n nextTrackerId += 1;\n }\n if (track.trackerId !== undefined) {\n trackerIds.set(detection.detectionIndex, track.trackerId);\n }\n }\n for (const detectionIndex of unmatchedDetections) {\n const detection = detections[detectionIndex];\n if ((detection.confidence ?? 1) >= trackActivationThreshold) {\n tracks.push(new KalmanBoxTrack(detection));\n }\n }\n tracks = tracks.filter((track) => track.timeSinceUpdate <= maximumFramesWithoutUpdate &&\n (track.successfulUpdates >= minimumConsecutiveFrames ||\n track.timeSinceUpdate === 0));\n return {\n activeTrackCount: tracks.length,\n assignments: detections.flatMap((detection) => {\n const trackerId = trackerIds.get(detection.detectionIndex);\n return trackerId === undefined\n ? []\n : [{ detectionIndex: detection.detectionIndex, trackerId }];\n }),\n confirmedTrackCount: tracks.filter((track) => track.trackerId !== undefined).length,\n };\n },\n };\n}\nclass KalmanBoxTrack {\n consecutiveUpdates;\n trackerId;\n successfulUpdates = 1;\n timeSinceUpdate = 0;\n state;\n covariance = identity(8);\n constructor(detection, consecutiveUpdates = false) {\n this.consecutiveUpdates = consecutiveUpdates;\n this.state = [...rectToXyxy(detection.rect), 0, 0, 0, 0].map((value) => [\n value,\n ]);\n }\n predict(frameStep, frameRate) {\n if (this.consecutiveUpdates && this.timeSinceUpdate > 0) {\n this.successfulUpdates = 0;\n }\n const transition = identity(8);\n for (let index = 0; index < 4; index += 1) {\n transition[index][index + 4] = frameStep;\n }\n this.state = multiply(transition, this.state);\n this.covariance = add(multiply(multiply(transition, this.covariance), transpose(transition)), createProcessNoise(frameStep, frameRate));\n // Python SORT counts update calls for the fixed-rate lost-track budget.\n this.timeSinceUpdate += 1;\n return stateToRect(this.state);\n }\n update(detection) {\n const measurement = rectToXyxy(detection.rect).map((value) => [value]);\n const observation = [\n [1, 0, 0, 0, 0, 0, 0, 0],\n [0, 1, 0, 0, 0, 0, 0, 0],\n [0, 0, 1, 0, 0, 0, 0, 0],\n [0, 0, 0, 1, 0, 0, 0, 0],\n ];\n const measurementNoise = scale(identity(4), 0.1);\n const innovation = subtract(measurement, multiply(observation, this.state));\n const covarianceObservationTranspose = multiply(this.covariance, transpose(observation));\n const innovationCovariance = add(multiply(observation, covarianceObservationTranspose), measurementNoise);\n const gain = multiply(covarianceObservationTranspose, inverse(innovationCovariance));\n this.state = add(this.state, multiply(gain, innovation));\n const identityMinusGainObservation = subtract(identity(8), multiply(gain, observation));\n // Joseph form matches the Python implementation and is more stable.\n this.covariance = add(multiply(multiply(identityMinusGainObservation, this.covariance), transpose(identityMinusGainObservation)), multiply(multiply(gain, measurementNoise), transpose(gain)));\n this.timeSinceUpdate = 0;\n this.successfulUpdates += 1;\n }\n getStateRect() {\n return stateToRect(this.state);\n }\n}\nfunction associateDetectionsToTracks(detections, predicted, minimumIouThreshold) {\n if (predicted.length === 0 || detections.length === 0) {\n return {\n matches: [],\n unmatchedDetections: detections.map((_, index) => index),\n };\n }\n // Python SORT associates all detections class-agnostically using standard IoU.\n const scores = predicted.map((rect) => detections.map((detection) => intersectionOverUnion(rect, detection.rect)));\n const candidateMatches = maximizeAssignment(scores);\n const matches = [];\n const matchedDetections = new Set();\n for (const [trackIndex, detectionIndex] of candidateMatches) {\n if (scores[trackIndex][detectionIndex] < minimumIouThreshold)\n continue;\n matches.push({ detectionIndex, trackIndex });\n matchedDetections.add(detectionIndex);\n }\n matches.sort((left, right) => left.trackIndex - right.trackIndex);\n return {\n matches,\n unmatchedDetections: detections.flatMap((_, index) => matchedDetections.has(index) ? [] : [index]),\n };\n}\n/** Hungarian assignment for a rectangular score matrix. */\nfunction maximizeAssignment(scores) {\n if (scores.length === 0 || scores[0]?.length === 0) {\n return [];\n }\n const rowCount = scores.length;\n const columnCount = scores[0].length;\n const transposed = rowCount > columnCount;\n const costs = transposed\n ? Array.from({ length: columnCount }, (_, row) => Array.from({ length: rowCount }, (_, column) => 1 - scores[column][row]))\n : scores.map((row) => row.map((score) => 1 - score));\n const rows = costs.length;\n const columns = costs[0].length;\n const u = new Array(rows + 1).fill(0);\n const v = new Array(columns + 1).fill(0);\n const p = new Array(columns + 1).fill(0);\n const way = new Array(columns + 1).fill(0);\n for (let row = 1; row <= rows; row += 1) {\n p[0] = row;\n let column0 = 0;\n const minValue = new Array(columns + 1).fill(Number.POSITIVE_INFINITY);\n const used = new Array(columns + 1).fill(false);\n do {\n used[column0] = true;\n const row0 = p[column0];\n let delta = Number.POSITIVE_INFINITY;\n let column1 = 0;\n for (let column = 1; column <= columns; column += 1) {\n if (used[column])\n continue;\n const current = costs[row0 - 1][column - 1] - u[row0] - v[column];\n if (current < minValue[column]) {\n minValue[column] = current;\n way[column] = column0;\n }\n if (minValue[column] < delta) {\n delta = minValue[column];\n column1 = column;\n }\n }\n for (let column = 0; column <= columns; column += 1) {\n if (used[column]) {\n u[p[column]] += delta;\n v[column] -= delta;\n }\n else {\n minValue[column] -= delta;\n }\n }\n column0 = column1;\n } while (p[column0] !== 0);\n do {\n const column1 = way[column0];\n p[column0] = p[column1];\n column0 = column1;\n } while (column0 !== 0);\n }\n const assignments = [];\n for (let column = 1; column <= columns; column += 1) {\n if (p[column] === 0)\n continue;\n const row = p[column] - 1;\n assignments.push(transposed ? [column - 1, row] : [row, column - 1]);\n }\n return assignments;\n}\nfunction rectToXyxy(rect) {\n return [\n rect.x - rect.width / 2,\n rect.y - rect.height / 2,\n rect.x + rect.width / 2,\n rect.y + rect.height / 2,\n ];\n}\nfunction stateToRect(state) {\n // The Python XYXY estimator intentionally leaves corner velocities\n // unconstrained. Normalize only at the browser Rect boundary so a crossing\n // prediction cannot violate the positive-width/height storage contract.\n const x1 = Math.min(state[0][0], state[2][0]);\n const y1 = Math.min(state[1][0], state[3][0]);\n const x2 = Math.max(state[0][0], state[2][0]);\n const y2 = Math.max(state[1][0], state[3][0]);\n return {\n height: Math.max(Number.EPSILON, y2 - y1),\n width: Math.max(Number.EPSILON, x2 - x1),\n x: (x1 + x2) / 2,\n y: (y1 + y2) / 2,\n };\n}\nfunction createProcessNoise(frameStep, frameRate) {\n if (Math.abs(frameStep - 1) <= 0.004 * frameRate) {\n return scale(identity(8), 0.01);\n }\n const result = Array.from({ length: 8 }, () => new Array(8).fill(0));\n const dt2 = frameStep * frameStep;\n const dt3 = dt2 * frameStep;\n const dt4 = dt2 * dt2;\n for (let index = 0; index < 4; index += 1) {\n const velocityIndex = index + 4;\n result[index][index] = (0.01 * dt4) / 4;\n result[index][velocityIndex] = (0.01 * dt3) / 2;\n result[velocityIndex][index] = (0.01 * dt3) / 2;\n result[velocityIndex][velocityIndex] = 0.01 * dt2;\n }\n return result;\n}\nfunction intersectionOverUnion(left, right) {\n const leftX = left.x - left.width / 2;\n const leftY = left.y - left.height / 2;\n const rightX = right.x - right.width / 2;\n const rightY = right.y - right.height / 2;\n const intersectionWidth = Math.max(0, Math.min(leftX + left.width, rightX + right.width) -\n Math.max(leftX, rightX));\n const intersectionHeight = Math.max(0, Math.min(leftY + left.height, rightY + right.height) -\n Math.max(leftY, rightY));\n const intersection = intersectionWidth * intersectionHeight;\n const union = left.width * left.height + right.width * right.height - intersection;\n return union <= 0 ? 0 : intersection / union;\n}\nfunction resolveFrameStep(current, previous) {\n if (current === undefined || previous === undefined)\n return 1;\n return Math.max(1, current - previous);\n}\nfunction normalizePositiveInteger$1(value, label) {\n if (!Number.isInteger(value) || value < 1) {\n throw new Error(`${label} must be a positive integer.`);\n }\n return value;\n}\nfunction normalizeNonNegativeInteger$1(value, label) {\n if (!Number.isInteger(value) || value < 0) {\n throw new Error(`${label} must be a non-negative integer.`);\n }\n return value;\n}\nfunction normalizeUnitInterval$1(value, label) {\n if (!Number.isFinite(value) || value < 0 || value > 1) {\n throw new Error(`${label} must be between 0 and 1.`);\n }\n}\nfunction identity(size) {\n return Array.from({ length: size }, (_, row) => Array.from({ length: size }, (_, column) => (row === column ? 1 : 0)));\n}\nfunction scale(matrix, factor) {\n return matrix.map((row) => row.map((value) => value * factor));\n}\nfunction transpose(matrix) {\n return matrix[0].map((_, column) => matrix.map((row) => row[column]));\n}\nfunction add(left, right) {\n return left.map((row, rowIndex) => row.map((value, columnIndex) => value + right[rowIndex][columnIndex]));\n}\nfunction subtract(left, right) {\n return left.map((row, rowIndex) => row.map((value, columnIndex) => value - right[rowIndex][columnIndex]));\n}\nfunction multiply(left, right) {\n return left.map((row) => right[0].map((_, column) => row.reduce((sum, value, index) => sum + value * right[index][column], 0)));\n}\nfunction inverse(matrix) {\n const size = matrix.length;\n const augmented = matrix.map((row, index) => [\n ...row,\n ...identity(size)[index],\n ]);\n for (let column = 0; column < size; column += 1) {\n let pivot = column;\n for (let row = column + 1; row < size; row += 1) {\n if (Math.abs(augmented[row][column]) >\n Math.abs(augmented[pivot][column])) {\n pivot = row;\n }\n }\n if (Math.abs(augmented[pivot][column]) < 1e-12) {\n throw new Error(\"SORT Kalman covariance is singular.\");\n }\n [augmented[column], augmented[pivot]] = [\n augmented[pivot],\n augmented[column],\n ];\n const divisor = augmented[column][column];\n augmented[column] = augmented[column].map((value) => value / divisor);\n for (let row = 0; row < size; row += 1) {\n if (row === column)\n continue;\n const factor = augmented[row][column];\n augmented[row] = augmented[row].map((value, index) => value - factor * augmented[column][index]);\n }\n }\n return augmented.map((row) => row.slice(size));\n}\n\n/**\n * Creates one stateful ByteTrack tracker for a single ordered media sequence.\n *\n * Defaults and two-stage association mirror roboflow/trackers ByteTrack at\n * source commit 60b21c8a48676784085fbee455559f16b75a7c9a.\n * Motion predictions remain internal; only observed detections receive IDs.\n */\nfunction createByteTrackTracker$1(options = {}) {\n const lostTrackBuffer = normalizeNonNegativeInteger$1(options.lostTrackBuffer ?? 30, \"lostTrackBuffer\");\n const frameRate = options.frameRate ?? 30;\n const trackActivationThreshold = options.trackActivationThreshold ?? 0.7;\n const minimumConsecutiveFrames = normalizePositiveInteger$1(options.minimumConsecutiveFrames ?? 2, \"minimumConsecutiveFrames\");\n const minimumIouThreshold = options.minimumIouThreshold ?? 0.1;\n const highConfidenceDetectionThreshold = options.highConfidenceDetectionThreshold ?? 0.6;\n if (!Number.isFinite(frameRate) || frameRate <= 0) {\n throw new Error(\"frameRate must be a finite positive value.\");\n }\n normalizeUnitInterval$1(trackActivationThreshold, \"trackActivationThreshold\");\n normalizeUnitInterval$1(minimumIouThreshold, \"minimumIouThreshold\");\n normalizeUnitInterval$1(highConfidenceDetectionThreshold, \"highConfidenceDetectionThreshold\");\n const scaledLostTrackBuffer = (frameRate / 30) * lostTrackBuffer;\n if (!Number.isFinite(scaledLostTrackBuffer)) {\n throw new Error(\"Scaled lostTrackBuffer overflows: frameRate / 30 * lostTrackBuffer must be finite.\");\n }\n const maximumFramesWithoutUpdate = lostTrackBuffer === 0 ? 0 : Math.max(1, Math.ceil(scaledLostTrackBuffer));\n let tracks = [];\n let nextTrackerId = 0;\n let previousFrameIndex;\n return {\n reset() {\n tracks = [];\n nextTrackerId = 0;\n previousFrameIndex = undefined;\n },\n update(detections, frameIndex) {\n const frameStep = resolveFrameStep(frameIndex, previousFrameIndex);\n previousFrameIndex = frameIndex ?? previousFrameIndex;\n tracks.forEach((track) => track.predict(frameStep, frameRate));\n const highDetectionIndexes = [];\n const lowDetectionIndexes = [];\n detections.forEach((detection, index) => {\n if ((detection.confidence ?? 1) >= highConfidenceDetectionThreshold) {\n highDetectionIndexes.push(index);\n }\n else {\n lowDetectionIndexes.push(index);\n }\n });\n const assignments = new Map();\n const firstStage = associate(tracks, detections, highDetectionIndexes, minimumIouThreshold);\n for (const match of firstStage.matches) {\n updateMatchedTrack(tracks[match.trackIndex], detections[match.detectionIndex], assignments);\n }\n const remainingTracks = firstStage.unmatchedTrackIndexes.map((index) => tracks[index]);\n const secondStage = associate(remainingTracks, detections, lowDetectionIndexes, minimumIouThreshold);\n for (const match of secondStage.matches) {\n updateMatchedTrack(remainingTracks[match.trackIndex], detections[match.detectionIndex], assignments);\n }\n // Only unmatched high-confidence observations can start a track.\n for (const detectionIndex of firstStage.unmatchedDetectionIndexes) {\n const detection = detections[detectionIndex];\n if ((detection.confidence ?? 1) >= trackActivationThreshold) {\n tracks.push(new KalmanBoxTrack(detection, true));\n }\n }\n // Confirmation is sticky once an ID has been allocated. An unmatched\n // unconfirmed track is discarded immediately, as in Python ByteTrack.\n tracks = tracks.filter((track) => track.timeSinceUpdate <= maximumFramesWithoutUpdate &&\n (track.trackerId !== undefined ||\n track.successfulUpdates >= minimumConsecutiveFrames ||\n track.timeSinceUpdate === 0));\n return {\n activeTrackCount: tracks.length,\n assignments: detections.flatMap((detection) => {\n const trackerId = assignments.get(detection.detectionIndex);\n return trackerId === undefined\n ? []\n : [{ detectionIndex: detection.detectionIndex, trackerId }];\n }),\n confirmedTrackCount: tracks.filter((track) => track.trackerId !== undefined).length,\n };\n },\n };\n function updateMatchedTrack(track, detection, assignments) {\n track.update(detection);\n if (track.trackerId === undefined &&\n track.successfulUpdates >= minimumConsecutiveFrames) {\n track.trackerId = nextTrackerId;\n nextTrackerId += 1;\n }\n if (track.trackerId !== undefined) {\n assignments.set(detection.detectionIndex, track.trackerId);\n }\n }\n}\nfunction associate(tracks, detections, detectionIndexes, minimumIouThreshold) {\n if (tracks.length === 0 || detectionIndexes.length === 0) {\n return {\n matches: [],\n unmatchedDetectionIndexes: [...detectionIndexes],\n unmatchedTrackIndexes: tracks.map((_, index) => index),\n };\n }\n const scores = tracks.map((track) => {\n const predictedRect = track.getStateRect();\n return detectionIndexes.map((detectionIndex) => intersectionOverUnion(predictedRect, detections[detectionIndex].rect));\n });\n const matches = [];\n const matchedTracks = new Set();\n const matchedDetections = new Set();\n for (const [trackIndex, localDetectionIndex] of maximizeAssignment(scores)) {\n if (scores[trackIndex][localDetectionIndex] < minimumIouThreshold) {\n continue;\n }\n const detectionIndex = detectionIndexes[localDetectionIndex];\n matches.push({ detectionIndex, trackIndex });\n matchedTracks.add(trackIndex);\n matchedDetections.add(detectionIndex);\n }\n matches.sort((left, right) => left.trackIndex - right.trackIndex);\n return {\n matches,\n unmatchedDetectionIndexes: detectionIndexes.filter((index) => !matchedDetections.has(index)),\n unmatchedTrackIndexes: tracks.flatMap((_, index) => matchedTracks.has(index) ? [] : [index]),\n };\n}\n\nfunction associateTrackingScores(scores, trackCount, detectionCount, minimumScore, acceptanceScores = scores) {\n const matches = [];\n const matchedTracks = new Set();\n const matchedDetections = new Set();\n for (const [trackIndex, detectionIndex] of maximizeAssignment(scores)) {\n if (acceptanceScores[trackIndex][detectionIndex] < minimumScore)\n continue;\n matches.push({ detectionIndex, trackIndex });\n matchedTracks.add(trackIndex);\n matchedDetections.add(detectionIndex);\n }\n matches.sort((left, right) => left.trackIndex - right.trackIndex);\n return {\n matches,\n unmatchedDetectionIndexes: Array.from({ length: detectionCount }, (_, index) => index).filter((index) => !matchedDetections.has(index)),\n unmatchedTrackIndexes: Array.from({ length: trackCount }, (_, index) => index).filter((index) => !matchedTracks.has(index)),\n };\n}\nfunction pairwiseIou(tracks, detections, bufferRatio = 0) {\n return tracks.map((track) => detections.map((detection) => bufferedIntersectionOverUnion(track, detection, bufferRatio)));\n}\nfunction bufferedIntersectionOverUnion(left, right, bufferRatio) {\n const leftWidth = left.width * (1 + 2 * bufferRatio);\n const leftHeight = left.height * (1 + 2 * bufferRatio);\n const rightWidth = right.width * (1 + 2 * bufferRatio);\n const rightHeight = right.height * (1 + 2 * bufferRatio);\n const leftX = left.x - leftWidth / 2;\n const leftY = left.y - leftHeight / 2;\n const rightX = right.x - rightWidth / 2;\n const rightY = right.y - rightHeight / 2;\n const intersectionWidth = Math.max(0, Math.min(leftX + leftWidth, rightX + rightWidth) - Math.max(leftX, rightX));\n const intersectionHeight = Math.max(0, Math.min(leftY + leftHeight, rightY + rightHeight) -\n Math.max(leftY, rightY));\n const intersection = intersectionWidth * intersectionHeight;\n const union = leftWidth * leftHeight + rightWidth * rightHeight - intersection;\n return union <= 0 ? 0 : intersection / union;\n}\n\n/**\n * Kalman state estimator shared by the browser C-BIoU and OC-SORT ports.\n * Its layouts, covariance update, and gap-scaled constant-velocity model\n * mirror roboflow/trackers at 60b21c8.\n */\nclass TrackingKalmanEstimator {\n representation;\n dimension;\n measurementDimension = 4;\n covariance;\n measurementNoise = identity(4);\n processNoise;\n state;\n baselineProcessNoise;\n positionIndexes;\n velocityIndexes;\n constructor(initialRect, representation) {\n this.representation = representation;\n const measurement = rectToMeasurement(initialRect, representation);\n this.dimension = representation === \"xcycsr\" ? 7 : 8;\n this.positionIndexes =\n representation === \"xcycsr\" ? [0, 1, 2] : [0, 1, 2, 3];\n this.velocityIndexes =\n representation === \"xcycsr\" ? [4, 5, 6] : [4, 5, 6, 7];\n this.state = Array.from({ length: this.dimension }, (_, index) => [\n measurement[index] ?? 0,\n ]);\n this.covariance = identity(this.dimension);\n this.processNoise = identity(this.dimension);\n this.baselineProcessNoise = identity(this.dimension);\n }\n predict(frameStep, frameRate) {\n if (this.representation === \"xcycsr\" &&\n this.state[2][0] + frameStep * this.state[6][0] <= 0) {\n this.state[6][0] = 0;\n }\n const transition = identity(this.dimension);\n this.positionIndexes.forEach((positionIndex, index) => {\n transition[positionIndex][this.velocityIndexes[index]] = frameStep;\n });\n this.processNoise = this.createProcessNoise(frameStep, frameRate);\n this.state = multiply(transition, this.state);\n this.covariance = add(multiply(multiply(transition, this.covariance), transpose(transition)), this.processNoise);\n }\n update(rect) {\n this.updateMeasurement(rectToMeasurement(rect, this.representation));\n }\n updateMeasurement(measurement) {\n const observation = Array.from({ length: 4 }, (_, row) => Array.from({ length: this.dimension }, (_, column) => row === column ? 1 : 0));\n const measurementColumn = measurement.map((value) => [value]);\n const innovation = subtract(measurementColumn, multiply(observation, this.state));\n const covarianceObservationTranspose = multiply(this.covariance, transpose(observation));\n const innovationCovariance = add(multiply(observation, covarianceObservationTranspose), this.measurementNoise);\n const gain = multiply(covarianceObservationTranspose, inverse(innovationCovariance));\n this.state = add(this.state, multiply(gain, innovation));\n const identityMinusGainObservation = subtract(identity(this.dimension), multiply(gain, observation));\n this.covariance = add(multiply(multiply(identityMinusGainObservation, this.covariance), transpose(identityMinusGainObservation)), multiply(multiply(gain, this.measurementNoise), transpose(gain)));\n }\n getRect() {\n return measurementToRect(this.state.slice(0, 4).map((row) => row[0]), this.representation);\n }\n setCovariances(options) {\n if (options.covariance)\n this.covariance = clone(options.covariance);\n if (options.measurementNoise) {\n this.measurementNoise = clone(options.measurementNoise);\n }\n if (options.processNoise) {\n this.processNoise = clone(options.processNoise);\n this.baselineProcessNoise = clone(options.processNoise);\n }\n }\n snapshot() {\n return {\n covariance: clone(this.covariance),\n measurementNoise: clone(this.measurementNoise),\n processNoise: clone(this.processNoise),\n state: clone(this.state),\n };\n }\n restore(snapshot) {\n this.covariance = clone(snapshot.covariance);\n this.measurementNoise = clone(snapshot.measurementNoise);\n this.processNoise = clone(snapshot.processNoise);\n this.state = clone(snapshot.state);\n }\n createProcessNoise(frameStep, frameRate) {\n if (Math.abs(frameStep - 1) <= 0.004 * frameRate) {\n return clone(this.baselineProcessNoise);\n }\n const result = Array.from({ length: this.dimension }, () => new Array(this.dimension).fill(0));\n const dt2 = frameStep * frameStep;\n const dt3 = dt2 * frameStep;\n const dt4 = dt2 * dt2;\n const kinematicIndexes = new Set([\n ...this.positionIndexes,\n ...this.velocityIndexes,\n ]);\n this.positionIndexes.forEach((positionIndex, index) => {\n const velocityIndex = this.velocityIndexes[index];\n const accelerationVariance = this.baselineProcessNoise[velocityIndex][velocityIndex];\n result[positionIndex][positionIndex] = (accelerationVariance * dt4) / 4;\n result[positionIndex][velocityIndex] = (accelerationVariance * dt3) / 2;\n result[velocityIndex][positionIndex] = (accelerationVariance * dt3) / 2;\n result[velocityIndex][velocityIndex] = accelerationVariance * dt2;\n });\n for (let index = 0; index < this.dimension; index += 1) {\n if (!kinematicIndexes.has(index)) {\n result[index][index] = this.baselineProcessNoise[index][index];\n }\n }\n return result;\n }\n}\nfunction rectToMeasurement(rect, representation) {\n if (representation === \"xcycwh\") {\n return [rect.x, rect.y, rect.width, rect.height];\n }\n return [\n rect.x,\n rect.y,\n rect.width * rect.height,\n rect.width / (rect.height + 1e-6),\n ];\n}\nfunction measurementToRect(measurement, representation) {\n const [x, y, third, fourth] = measurement;\n if (representation === \"xcycwh\") {\n return {\n height: Math.max(1e-3, fourth),\n width: Math.max(1e-3, third),\n x,\n y,\n };\n }\n const width = Math.sqrt(third * fourth);\n const height = width === 0 ? 0 : third / width;\n return {\n height: Math.max(Number.EPSILON, height),\n width: Math.max(Number.EPSILON, width),\n x,\n y,\n };\n}\nfunction diagonal(values) {\n return values.map((value, row) => values.map((_, column) => (row === column ? value : 0)));\n}\nfunction scaleMatrix(matrix, factor) {\n return scale(matrix, factor);\n}\nfunction clone(matrix) {\n return matrix.map((row) => [...row]);\n}\n\nconst MINIMUM_DETECTION_CONFIDENCE = 0.1;\nclass CBIoUTrack {\n trackerId;\n successfulUpdates = 1;\n timeSinceUpdate = 0;\n estimator;\n constructor(initial) {\n this.estimator = new TrackingKalmanEstimator(initial.rect, \"xcycwh\");\n this.setInitialNoise(initial.rect.width, initial.rect.height);\n }\n predict(frameStep, frameRate) {\n const current = this.estimator.getRect();\n this.estimator.setCovariances({\n processNoise: this.buildProcessNoise(Math.max(current.width, 1e-3), Math.max(current.height, 1e-3)),\n });\n this.estimator.predict(frameStep, frameRate);\n this.clampState();\n this.timeSinceUpdate += 1;\n }\n update(detection) {\n const current = this.estimator.getRect();\n this.estimator.setCovariances({\n measurementNoise: this.buildMeasurementNoise(Math.max(current.width, 1e-3), Math.max(current.height, 1e-3)),\n });\n this.estimator.update(detection.rect);\n this.clampState();\n this.timeSinceUpdate = 0;\n this.successfulUpdates += 1;\n }\n getRect() {\n return this.estimator.getRect();\n }\n setInitialNoise(width, height) {\n const sigmaPosition = 0.05;\n const sigmaVelocity = 0.00625;\n this.estimator.setCovariances({\n covariance: diagonal([\n (2 * sigmaPosition * width) ** 2,\n (2 * sigmaPosition * height) ** 2,\n (2 * sigmaPosition * width) ** 2,\n (2 * sigmaPosition * height) ** 2,\n (10 * sigmaVelocity * width) ** 2,\n (10 * sigmaVelocity * height) ** 2,\n (10 * sigmaVelocity * width) ** 2,\n (10 * sigmaVelocity * height) ** 2,\n ]),\n measurementNoise: this.buildMeasurementNoise(width, height),\n processNoise: this.buildProcessNoise(width, height),\n });\n }\n buildProcessNoise(width, height) {\n const sigmaPosition = 0.05;\n const sigmaVelocity = 0.00625;\n return diagonal([\n (sigmaPosition * width) ** 2,\n (sigmaPosition * height) ** 2,\n (sigmaPosition * width) ** 2,\n (sigmaPosition * height) ** 2,\n (sigmaVelocity * width) ** 2,\n (sigmaVelocity * height) ** 2,\n (sigmaVelocity * width) ** 2,\n (sigmaVelocity * height) ** 2,\n ]);\n }\n buildMeasurementNoise(width, height) {\n const sigmaMeasurement = 0.05;\n return diagonal([\n (sigmaMeasurement * width) ** 2,\n (sigmaMeasurement * height) ** 2,\n (sigmaMeasurement * width) ** 2,\n (sigmaMeasurement * height) ** 2,\n ]);\n }\n clampState() {\n this.estimator.state[2][0] = Math.max(this.estimator.state[2][0], 1e-3);\n this.estimator.state[3][0] = Math.max(this.estimator.state[3][0], 1e-3);\n }\n}\n/** Creates the detection-only C-BIoU implementation from roboflow/trackers. */\nfunction createCBIoUTracker$1(options = {}) {\n const lostTrackBuffer = normalizeNonNegativeInteger$1(options.lostTrackBuffer ?? 30, \"lostTrackBuffer\");\n const frameRate = options.frameRate ?? 30;\n const trackActivationThreshold = options.trackActivationThreshold ?? 0.7;\n const minimumConsecutiveFrames = normalizePositiveInteger$1(options.minimumConsecutiveFrames ?? 2, \"minimumConsecutiveFrames\");\n const minimumIouThresholdFirstAssociation = options.minimumIouThresholdFirstAssociation ?? 0.2;\n const minimumIouThresholdSecondAssociation = options.minimumIouThresholdSecondAssociation ?? 0.5;\n const minimumIouThresholdUnconfirmedAssociation = options.minimumIouThresholdUnconfirmedAssociation ?? 0.3;\n const highConfidenceDetectionThreshold = options.highConfidenceDetectionThreshold ?? 0.6;\n const instantFirstFrameActivation = options.instantFirstFrameActivation ?? true;\n const bufferRatioFirst = options.bufferRatioFirst ?? 0.3;\n const bufferRatioSecond = options.bufferRatioSecond ?? 0.5;\n if (!Number.isFinite(frameRate) || frameRate <= 0) {\n throw new Error(\"frameRate must be a finite positive value.\");\n }\n normalizeUnitInterval$1(trackActivationThreshold, \"trackActivationThreshold\");\n normalizeUnitInterval$1(minimumIouThresholdFirstAssociation, \"minimumIouThresholdFirstAssociation\");\n normalizeUnitInterval$1(minimumIouThresholdSecondAssociation, \"minimumIouThresholdSecondAssociation\");\n normalizeUnitInterval$1(minimumIouThresholdUnconfirmedAssociation, \"minimumIouThresholdUnconfirmedAssociation\");\n normalizeUnitInterval$1(highConfidenceDetectionThreshold, \"highConfidenceDetectionThreshold\");\n if (!Number.isFinite(bufferRatioFirst) || bufferRatioFirst < 0) {\n throw new Error(\"bufferRatioFirst must be a finite non-negative value.\");\n }\n if (!Number.isFinite(bufferRatioSecond) || bufferRatioSecond < 0) {\n throw new Error(\"bufferRatioSecond must be a finite non-negative value.\");\n }\n const scaledLostTrackBuffer = (frameRate / 30) * lostTrackBuffer;\n if (!Number.isFinite(scaledLostTrackBuffer)) {\n throw new Error(\"Scaled lostTrackBuffer overflows: frameRate / 30 * lostTrackBuffer must be finite.\");\n }\n const maximumFramesWithoutUpdate = lostTrackBuffer === 0 ? 0 : Math.max(1, Math.ceil(scaledLostTrackBuffer));\n let tracks = [];\n let nextTrackerId = 0;\n let frameId = 0;\n let previousFrameIndex;\n return {\n reset() {\n tracks = [];\n nextTrackerId = 0;\n frameId = 0;\n previousFrameIndex = undefined;\n },\n update(detections, frameIndex) {\n const frameStep = resolveFrameStep(frameIndex, previousFrameIndex);\n previousFrameIndex = frameIndex ?? previousFrameIndex;\n frameId += 1;\n tracks.forEach((track) => track.predict(frameStep, frameRate));\n const highDetectionIndexes = [];\n const lowDetectionIndexes = [];\n detections.forEach((detection, index) => {\n const confidence = detection.confidence ?? 1;\n if (confidence >= highConfidenceDetectionThreshold) {\n highDetectionIndexes.push(index);\n }\n else if (confidence > MINIMUM_DETECTION_CONFIDENCE) {\n lowDetectionIndexes.push(index);\n }\n });\n const confirmed = [];\n const unconfirmed = [];\n const lost = [];\n for (const track of tracks) {\n if (track.timeSinceUpdate > 1)\n lost.push(track);\n else if (track.trackerId !== undefined ||\n track.successfulUpdates >= minimumConsecutiveFrames) {\n confirmed.push(track);\n }\n else\n unconfirmed.push(track);\n }\n const assignments = new Map();\n const pool = [...confirmed, ...lost];\n const highDetections = highDetectionIndexes.map((index) => detections[index]);\n const firstScores = pairwiseIou(pool.map((track) => track.getRect()), highDetections.map((detection) => detection.rect), bufferRatioFirst).map((row) => row.map((score, index) => score * (highDetections[index].confidence ?? 1)));\n const firstAssociation = associateTrackingScores(firstScores, pool.length, highDetections.length, minimumIouThresholdFirstAssociation);\n for (const match of firstAssociation.matches) {\n updateMatchedTrack(pool[match.trackIndex], highDetections[match.detectionIndex], assignments);\n }\n const remainingTracked = firstAssociation.unmatchedTrackIndexes\n .map((index) => pool[index])\n .filter((track) => track.timeSinceUpdate === 1);\n const lowDetections = lowDetectionIndexes.map((index) => detections[index]);\n const secondScores = pairwiseIou(remainingTracked.map((track) => track.getRect()), lowDetections.map((detection) => detection.rect), bufferRatioSecond);\n const secondAssociation = associateTrackingScores(secondScores, remainingTracked.length, lowDetections.length, minimumIouThresholdSecondAssociation);\n for (const match of secondAssociation.matches) {\n updateMatchedTrack(remainingTracked[match.trackIndex], lowDetections[match.detectionIndex], assignments);\n }\n let unmatchedHighLocal = [...firstAssociation.unmatchedDetectionIndexes];\n let unmatchedUnconfirmed = unconfirmed.map((_, index) => index);\n if (unconfirmed.length > 0 && unmatchedHighLocal.length > 0) {\n const remainingHigh = unmatchedHighLocal.map((index) => highDetections[index]);\n const unconfirmedScores = pairwiseIou(unconfirmed.map((track) => track.getRect()), remainingHigh.map((detection) => detection.rect), bufferRatioFirst).map((row) => row.map((score, index) => score * (remainingHigh[index].confidence ?? 1)));\n const unconfirmedAssociation = associateTrackingScores(unconfirmedScores, unconfirmed.length, remainingHigh.length, minimumIouThresholdUnconfirmedAssociation);\n unmatchedUnconfirmed = unconfirmedAssociation.unmatchedTrackIndexes;\n for (const match of unconfirmedAssociation.matches) {\n updateMatchedTrack(unconfirmed[match.trackIndex], remainingHigh[match.detectionIndex], assignments);\n }\n unmatchedHighLocal =\n unconfirmedAssociation.unmatchedDetectionIndexes.map((index) => unmatchedHighLocal[index]);\n }\n const unmatchedUnconfirmedTracks = new Set(unmatchedUnconfirmed.map((index) => unconfirmed[index]));\n tracks = tracks.filter((track) => !unmatchedUnconfirmedTracks.has(track));\n for (const localIndex of unmatchedHighLocal) {\n const detection = highDetections[localIndex];\n if ((detection.confidence ?? 1) < trackActivationThreshold)\n continue;\n const track = new CBIoUTrack(detection);\n if (frameId === 1 && instantFirstFrameActivation) {\n track.trackerId = nextTrackerId++;\n assignments.set(detection.detectionIndex, track.trackerId);\n }\n tracks.push(track);\n }\n tracks = tracks.filter((track) => track.timeSinceUpdate <= maximumFramesWithoutUpdate &&\n (track.timeSinceUpdate === 0 ||\n track.trackerId !== undefined ||\n track.successfulUpdates >= minimumConsecutiveFrames));\n return createUpdate(detections, assignments, tracks);\n },\n };\n function updateMatchedTrack(track, detection, assignments) {\n track.update(detection);\n if (track.trackerId === undefined &&\n track.successfulUpdates >= minimumConsecutiveFrames) {\n track.trackerId = nextTrackerId++;\n }\n if (track.trackerId !== undefined) {\n assignments.set(detection.detectionIndex, track.trackerId);\n }\n }\n}\nfunction createUpdate(detections, assignments, tracks) {\n const resolvedAssignments = detections.flatMap((detection) => {\n const trackerId = assignments.get(detection.detectionIndex);\n return trackerId === undefined\n ? []\n : [{ detectionIndex: detection.detectionIndex, trackerId }];\n });\n return {\n activeTrackCount: tracks.length,\n assignments: resolvedAssignments,\n confirmedTrackCount: tracks.filter((track) => track.trackerId !== undefined)\n .length,\n };\n}\n\nclass OCSortTrack {\n deltaT;\n age = 0;\n lastObservation;\n trackerId;\n successfulConsecutiveUpdates = 0;\n timeSinceUpdate = 0;\n velocity;\n frozenState;\n observed = true;\n observations = new Map();\n estimator;\n constructor(initial, deltaT) {\n this.deltaT = deltaT;\n this.lastObservation = initial;\n this.estimator = new TrackingKalmanEstimator(initial.rect, \"xcycsr\");\n this.configureNoise();\n }\n predict(frameStep, frameRate) {\n if (this.observed && this.timeSinceUpdate > 0) {\n this.frozenState = this.estimator.snapshot();\n this.observed = false;\n }\n this.estimator.predict(frameStep, frameRate);\n if (this.timeSinceUpdate > 0) {\n this.successfulConsecutiveUpdates = 0;\n }\n this.timeSinceUpdate += 1;\n this.age += 1;\n }\n update(detection, frameStep, frameRate) {\n const previous = this.getPreviousObservation();\n if (previous) {\n this.velocity = computeVelocity(previous, detection);\n }\n if (!this.observed && this.frozenState) {\n this.unfreeze(detection, frameStep, frameRate);\n }\n this.estimator.update(detection.rect);\n this.observed = true;\n this.timeSinceUpdate = 0;\n this.successfulConsecutiveUpdates += 1;\n this.lastObservation = detection;\n this.observations.set(this.age, detection);\n const cutoff = this.age - this.deltaT;\n for (const age of this.observations.keys()) {\n if (age < cutoff)\n this.observations.delete(age);\n }\n }\n getRect() {\n return this.estimator.getRect();\n }\n getPreviousObservation() {\n if (this.observations.size === 0)\n return undefined;\n for (let index = 0; index < this.deltaT; index += 1) {\n const delta = this.deltaT - index;\n const observation = this.observations.get(this.age - delta);\n if (observation)\n return observation;\n }\n const latestAge = Math.max(...this.observations.keys());\n return this.observations.get(latestAge);\n }\n configureNoise() {\n const measurementNoise = identity(4);\n for (let index = 2; index < 4; index += 1) {\n measurementNoise[index][index] *= 10;\n }\n const covariance = scaleMatrix(identity(7), 10);\n for (let index = 4; index < 7; index += 1) {\n covariance[index][index] *= 1000;\n }\n const processNoise = identity(7);\n processNoise[6][6] *= 0.01;\n for (let index = 4; index < 7; index += 1) {\n processNoise[index][index] *= 0.01;\n }\n this.estimator.setCovariances({\n covariance,\n measurementNoise,\n processNoise,\n });\n }\n unfreeze(detection, frameStep, frameRate) {\n if (!this.frozenState || this.timeSinceUpdate === 0)\n return;\n this.estimator.restore(this.frozenState);\n const timeGap = this.timeSinceUpdate;\n const start = rectToMeasurement(this.lastObservation.rect, \"xcycsr\");\n const end = rectToMeasurement(detection.rect, \"xcycsr\");\n const startWidth = Math.sqrt(start[2] * start[3]);\n const startHeight = start[3] === 0 ? 0 : Math.sqrt(start[2] / start[3]);\n const endWidth = Math.sqrt(end[2] * end[3]);\n const endHeight = end[3] === 0 ? 0 : Math.sqrt(end[2] / end[3]);\n for (let index = 0; index < timeGap; index += 1) {\n const progress = (index + 1) / timeGap;\n const x = start[0] + progress * (end[0] - start[0]);\n const y = start[1] + progress * (end[1] - start[1]);\n const width = startWidth + progress * (endWidth - startWidth);\n const height = startHeight + progress * (endHeight - startHeight);\n this.estimator.updateMeasurement([x, y, width * height, width / height]);\n if (index < timeGap - 1) {\n this.estimator.predict(frameStep, frameRate);\n }\n }\n this.frozenState = undefined;\n }\n}\n/** Creates the observation-centric SORT implementation from roboflow/trackers. */\nfunction createOCSortTracker$1(options = {}) {\n const lostTrackBuffer = normalizeNonNegativeInteger$1(options.lostTrackBuffer ?? 30, \"lostTrackBuffer\");\n const frameRate = options.frameRate ?? 30;\n const minimumConsecutiveFrames = normalizePositiveInteger$1(options.minimumConsecutiveFrames ?? 3, \"minimumConsecutiveFrames\");\n const minimumIouThreshold = options.minimumIouThreshold ?? 0.3;\n const directionConsistencyWeight = options.directionConsistencyWeight ?? 0.2;\n const highConfidenceDetectionThreshold = options.highConfidenceDetectionThreshold ?? 0.6;\n const deltaT = normalizePositiveInteger$1(options.deltaT ?? 3, \"deltaT\");\n if (!Number.isFinite(frameRate) || frameRate <= 0) {\n throw new Error(\"frameRate must be a finite positive value.\");\n }\n normalizeUnitInterval$1(minimumIouThreshold, \"minimumIouThreshold\");\n normalizeUnitInterval$1(directionConsistencyWeight, \"directionConsistencyWeight\");\n normalizeUnitInterval$1(highConfidenceDetectionThreshold, \"highConfidenceDetectionThreshold\");\n const scaledLostTrackBuffer = (frameRate / 30) * lostTrackBuffer;\n if (!Number.isFinite(scaledLostTrackBuffer)) {\n throw new Error(\"Scaled lostTrackBuffer overflows: frameRate / 30 * lostTrackBuffer must be finite.\");\n }\n const maximumFramesWithoutUpdate = lostTrackBuffer === 0 ? 0 : Math.max(1, Math.ceil(scaledLostTrackBuffer));\n let tracks = [];\n let frameCount = 0;\n let nextTrackerId = 0;\n let previousFrameIndex;\n return {\n reset() {\n tracks = [];\n frameCount = 0;\n nextTrackerId = 0;\n previousFrameIndex = undefined;\n },\n update(detections, frameIndex) {\n const frameStep = resolveFrameStep(frameIndex, previousFrameIndex);\n previousFrameIndex = frameIndex ?? previousFrameIndex;\n // Match roboflow/trackers: an empty stream before any track exists is\n // not part of OC-SORT's early-sequence activation window.\n if (tracks.length === 0 && detections.length === 0) {\n return {\n activeTrackCount: 0,\n assignments: [],\n confirmedTrackCount: 0,\n };\n }\n tracks.forEach((track) => track.predict(frameStep, frameRate));\n const highDetectionIndexes = detections.flatMap((detection, index) => detection.confidence === undefined ||\n detection.confidence >= highConfidenceDetectionThreshold\n ? [index]\n : []);\n const highDetections = highDetectionIndexes.map((index) => detections[index]);\n const iouScores = pairwiseIou(tracks.map((track) => track.getRect()), highDetections.map((detection) => detection.rect));\n const combinedScores = iouScores.map((row, trackIndex) => row.map((iou, detectionIndex) => {\n if (directionConsistencyWeight === 0)\n return iou;\n return (iou +\n directionConsistencyWeight *\n directionConsistency(tracks[trackIndex], highDetections[detectionIndex]) *\n (highDetections[detectionIndex].confidence ?? 1));\n }));\n const primary = associateTrackingScores(combinedScores, tracks.length, highDetections.length, minimumIouThreshold, iouScores);\n const assignments = new Map();\n for (const match of primary.matches) {\n updateMatchedTrack(tracks[match.trackIndex], highDetections[match.detectionIndex], assignments, frameStep);\n }\n let remainingHigh = [...primary.unmatchedDetectionIndexes];\n if (primary.unmatchedTrackIndexes.length > 0 &&\n remainingHigh.length > 0) {\n const unmatchedTracks = primary.unmatchedTrackIndexes.map((index) => tracks[index]);\n const unmatchedDetections = remainingHigh.map((index) => highDetections[index]);\n const recoveryScores = pairwiseIou(unmatchedTracks.map((track) => track.lastObservation.rect), unmatchedDetections.map((detection) => detection.rect));\n const recovery = associateTrackingScores(recoveryScores, unmatchedTracks.length, unmatchedDetections.length, minimumIouThreshold);\n for (const match of recovery.matches) {\n updateMatchedTrack(unmatchedTracks[match.trackIndex], unmatchedDetections[match.detectionIndex], assignments, frameStep);\n }\n remainingHigh = recovery.unmatchedDetectionIndexes.map((index) => remainingHigh[index]);\n }\n for (const localIndex of remainingHigh) {\n tracks.push(new OCSortTrack(highDetections[localIndex], deltaT));\n }\n tracks = tracks.filter((track) => track.timeSinceUpdate <= maximumFramesWithoutUpdate);\n frameCount += 1;\n const resolvedAssignments = detections.flatMap((detection) => {\n const trackerId = assignments.get(detection.detectionIndex);\n return trackerId === undefined\n ? []\n : [{ detectionIndex: detection.detectionIndex, trackerId }];\n });\n return {\n activeTrackCount: tracks.length,\n assignments: resolvedAssignments,\n confirmedTrackCount: tracks.filter((track) => track.trackerId !== undefined).length,\n };\n },\n };\n function updateMatchedTrack(track, detection, assignments, frameStep) {\n track.update(detection, frameStep, frameRate);\n const earlySequence = frameCount <= minimumConsecutiveFrames;\n const shouldEmit = (earlySequence && track.timeSinceUpdate === 0) ||\n track.successfulConsecutiveUpdates >= minimumConsecutiveFrames;\n if (shouldEmit) {\n if (track.trackerId === undefined) {\n track.trackerId = nextTrackerId++;\n }\n assignments.set(detection.detectionIndex, track.trackerId);\n }\n }\n}\nfunction computeVelocity(previous, current) {\n const deltaX = current.rect.x - previous.rect.x;\n const deltaY = current.rect.y - previous.rect.y;\n const norm = Math.sqrt(deltaX * deltaX + deltaY * deltaY) + 1e-6;\n return { x: deltaX / norm, y: deltaY / norm };\n}\nfunction directionConsistency(track, detection) {\n if (!track.velocity)\n return 0;\n const reference = track.getPreviousObservation() ?? track.lastObservation;\n const deltaX = detection.rect.x - reference.rect.x;\n const deltaY = detection.rect.y - reference.rect.y;\n const norm = Math.sqrt(deltaX * deltaX + deltaY * deltaY) + 1e-6;\n const cosine = Math.max(-1, Math.min(1, track.velocity.x * (deltaX / norm) + track.velocity.y * (deltaY / norm)));\n const angle = Math.acos(cosine);\n return (Math.PI / 2 - Math.abs(angle)) / Math.PI;\n}\n\n/** Public core facade for the internal tracker engine workspace. */\nfunction createSortTracker(options = {}) {\n return createSortTracker$1(options);\n}\n\n/** Public core facade for the internal tracker engine workspace. */\nfunction createByteTrackTracker(options = {}) {\n return createByteTrackTracker$1(options);\n}\n\n/** Public core facade for the internal tracker engine workspace. */\nfunction createCBIoUTracker(options = {}) {\n return createCBIoUTracker$1(options);\n}\n\n/** Public core facade for the internal tracker engine workspace. */\nfunction createOCSortTracker(options = {}) {\n return createOCSortTracker$1(options);\n}\n\n/** Geometry projected into a tracker. The original detection payload is retained. */\nvar TrackingGeometry;\n(function (TrackingGeometry) {\n TrackingGeometry[\"Box\"] = \"box\";\n TrackingGeometry[\"Mask\"] = \"mask\";\n TrackingGeometry[\"Keypoints\"] = \"keypoints\";\n})(TrackingGeometry || (TrackingGeometry = {}));\n\nvar DetectionMaskPayloadFormat;\n(function (DetectionMaskPayloadFormat) {\n DetectionMaskPayloadFormat[\"RawCocoRle\"] = \"rawCocoRle\";\n DetectionMaskPayloadFormat[\"DeflatedBase64\"] = \"deflatedBase64\";\n})(DetectionMaskPayloadFormat || (DetectionMaskPayloadFormat = {}));\nfunction encodeBinaryMask(data, width, height) {\n assertMaskDimensions(data, width, height);\n const runs = [];\n let currentValue = 0;\n let runLength = 0;\n for (let x = 0; x < width; x += 1) {\n for (let y = 0; y < height; y += 1) {\n const value = data[y * width + x] ? 1 : 0;\n if (value === currentValue) {\n runLength += 1;\n }\n else {\n runs.push(runLength);\n currentValue = value;\n runLength = 1;\n }\n }\n }\n runs.push(runLength);\n return {\n counts: encodeCompressedRleCounts(runs),\n encoding: DetectionMaskEncoding.CompressedRle,\n height,\n width,\n };\n}\n/** Encodes a binary mask and derives its bounds in the same raster traversal. */\nfunction encodeBinaryMaskWithBounds(data, width, height) {\n assertMaskDimensions(data, width, height);\n const runs = [];\n let currentValue = 0;\n let runLength = 0;\n let minX = width;\n let minY = height;\n let maxX = -1;\n let maxY = -1;\n for (let x = 0; x < width; x += 1) {\n for (let y = 0; y < height; y += 1) {\n const value = data[y * width + x] ? 1 : 0;\n if (value) {\n minX = Math.min(minX, x);\n minY = Math.min(minY, y);\n maxX = Math.max(maxX, x);\n maxY = Math.max(maxY, y);\n }\n if (value === currentValue) {\n runLength += 1;\n }\n else {\n runs.push(runLength);\n currentValue = value;\n runLength = 1;\n }\n }\n }\n runs.push(runLength);\n return {\n bounds: maxX < 0\n ? null\n : {\n height: maxY - minY + 1,\n width: maxX - minX + 1,\n x: minX + (maxX - minX + 1) / 2,\n y: minY + (maxY - minY + 1) / 2,\n },\n mask: {\n counts: encodeCompressedRleCounts(runs),\n encoding: DetectionMaskEncoding.CompressedRle,\n height,\n width,\n },\n };\n}\nfunction encodeDetectionMaskPayload(mask, codec) {\n return codec ? codec.deflate(mask.counts) : mask.counts;\n}\nfunction decodeDetectionMaskPayload(payload, width, height, options = {}) {\n const format = options.format ??\n (isDeflatedBase64DetectionMaskPayload(payload)\n ? DetectionMaskPayloadFormat.DeflatedBase64\n : DetectionMaskPayloadFormat.RawCocoRle);\n if (format === DetectionMaskPayloadFormat.DeflatedBase64 && !options.codec) {\n throw new Error(\"A detection mask compression codec is required for deflated payloads.\");\n }\n return {\n counts: format === DetectionMaskPayloadFormat.DeflatedBase64\n ? options.codec.inflate(payload)\n : payload,\n encoding: DetectionMaskEncoding.CompressedRle,\n height,\n width,\n };\n}\n/** Matches the annotation editor's legacy transport-format heuristic. */\nfunction isDeflatedBase64DetectionMaskPayload(value) {\n return value.length > 100 && /^[A-Za-z0-9+/]+={0,2}$/.test(value);\n}\nfunction computeMaskBounds(data, width, height) {\n assertMaskDimensions(data, width, height);\n let minX = width;\n let minY = height;\n let maxX = -1;\n let maxY = -1;\n for (let y = 0; y < height; y += 1) {\n for (let x = 0; x < width; x += 1) {\n if (!data[y * width + x]) {\n continue;\n }\n minX = Math.min(minX, x);\n minY = Math.min(minY, y);\n maxX = Math.max(maxX, x);\n maxY = Math.max(maxY, y);\n }\n }\n if (maxX < 0) {\n return null;\n }\n const boundsWidth = maxX - minX + 1;\n const boundsHeight = maxY - minY + 1;\n return {\n height: boundsHeight,\n width: boundsWidth,\n x: minX + boundsWidth / 2,\n y: minY + boundsHeight / 2,\n };\n}\nfunction computeDetectionMaskRect(mask) {\n const decoded = decodeCompressedRleMask(mask);\n const bounds = computeMaskBounds(decoded.data, decoded.width, decoded.height);\n return bounds ?? undefined;\n}\nfunction detectMaskBorders(data, width, height) {\n assertMaskDimensions(data, width, height);\n const borders = new Uint8Array(data.length);\n for (let y = 0; y < height; y += 1) {\n for (let x = 0; x < width; x += 1) {\n const offset = y * width + x;\n if (!data[offset]) {\n continue;\n }\n if (x === 0 ||\n y === 0 ||\n x === width - 1 ||\n y === height - 1 ||\n !data[offset - 1] ||\n !data[offset + 1] ||\n !data[offset - width] ||\n !data[offset + width]) {\n borders[offset] = 1;\n }\n }\n }\n return borders;\n}\nfunction extractMaskContour(data, width, height) {\n assertMaskDimensions(data, width, height);\n const stride = Math.max(1, Math.floor(height / 100));\n const leftEdge = [];\n const rightEdge = [];\n for (let y = 0; y < height; y += stride) {\n let left = -1;\n let right = -1;\n for (let x = 0; x < width; x += 1) {\n if (data[y * width + x]) {\n left = left === -1 ? x : left;\n right = x;\n }\n }\n if (left !== -1) {\n leftEdge.push({ x: left, y });\n rightEdge.push({ x: right, y });\n }\n }\n return leftEdge.length < 2\n ? undefined\n : [...leftEdge, ...rightEdge.reverse()];\n}\nfunction extractMaskRectRuns(data, width, height) {\n assertMaskDimensions(data, width, height);\n const rects = mergeRowSpansIntoRects(data, width, height, 0, 0);\n return rects.length > 0 ? rects : undefined;\n}\n/**\n * The rect runs of a compressed-RLE mask, without holding the mask's raster.\n *\n * A mask is the size of the whole frame, so reading a silhouette that covers a\n * fraction of one percent of it otherwise costs a megabytes-wide allocation and\n * a pass over every pixel. The counts carry the occupied box, so the raster and\n * the pass are that box.\n */\nfunction extractDetectionMaskRectRuns(mask) {\n if (mask.encoding !== DetectionMaskEncoding.CompressedRle) {\n throw new Error(`Unsupported detection mask encoding: ${mask.encoding}`);\n }\n const { height, width } = mask;\n assertMaskSize(width, height);\n const counts = decodeCompressedRleCounts(mask.counts);\n const bounds = computeCompressedRleBounds(counts, width, height);\n if (!bounds) {\n return undefined;\n }\n const { left, top } = bounds;\n const boundsWidth = bounds.width;\n const boundsHeight = bounds.height;\n const boundsBuffer = new Uint8Array(boundsWidth * boundsHeight);\n const pixels = width * height;\n let offset = 0;\n for (let index = 0; index < counts.length; index += 1) {\n const start = offset;\n offset += counts[index] ?? 0;\n if (!isForegroundRun(index) || start >= pixels) {\n continue;\n }\n const end = Math.min(offset, pixels);\n let cursor = start;\n while (cursor < end) {\n const column = Math.floor(cursor / height);\n const columnEnd = Math.min(end, (column + 1) * height);\n let target = (cursor - column * height - top) * boundsWidth + (column - left);\n for (let step = columnEnd - cursor; step > 0; step -= 1) {\n boundsBuffer[target] = 1;\n target += boundsWidth;\n }\n cursor = columnEnd;\n }\n }\n const rects = mergeRowSpansIntoRects(boundsBuffer, boundsWidth, boundsHeight, left, top);\n return rects.length > 0 ? rects : undefined;\n}\n/** Counts alternate background, foreground, starting on background. */\nfunction isForegroundRun(index) {\n return index % 2 === 1;\n}\n/**\n * The occupied box of a compressed-RLE mask, read from the counts alone.\n *\n * COCO runs are column-major, so a run confined to one column bounds its own\n * rows, and a run that reaches the next column has already spanned a full\n * column top to bottom.\n */\nfunction computeCompressedRleBounds(counts, width, height) {\n const pixels = width * height;\n let left = width;\n let top = height;\n let right = -1;\n let bottom = -1;\n let offset = 0;\n for (let index = 0; index < counts.length; index += 1) {\n const start = offset;\n offset += counts[index] ?? 0;\n if (!isForegroundRun(index) || start >= pixels) {\n continue;\n }\n const end = Math.min(offset, pixels) - 1;\n const startColumn = Math.floor(start / height);\n const endColumn = Math.floor(end / height);\n if (startColumn < left) {\n left = startColumn;\n }\n if (endColumn > right) {\n right = endColumn;\n }\n if (startColumn !== endColumn) {\n top = 0;\n bottom = height - 1;\n continue;\n }\n const startRow = start - startColumn * height;\n const endRow = end - endColumn * height;\n if (startRow < top) {\n top = startRow;\n }\n if (endRow > bottom) {\n bottom = endRow;\n }\n }\n return right < 0\n ? null\n : {\n height: bottom - top + 1,\n left,\n top,\n width: right - left + 1,\n };\n}\nfunction createOpenMaskSpans(capacity) {\n return {\n openedAt: new Int32Array(capacity),\n runWidth: new Int32Array(capacity),\n startX: new Int32Array(capacity),\n topY: new Int32Array(capacity),\n };\n}\n/**\n * Emits rects in the coordinates of the raster the given window was cut from,\n * not the window's own.\n *\n * A span continues the rect above it when its start and width both match, and\n * the row above holds exactly one open rect per span, so walking both in\n * ascending x pairs them without a lookup.\n */\nfunction mergeRowSpansIntoRects(data, width, height, offsetX, offsetY) {\n const rects = [];\n // Spans need a gap between them, so a row holds at most half its width.\n const capacity = (width >> 1) + 2;\n const closed = new Int32Array(capacity);\n let open = createOpenMaskSpans(capacity);\n let next = createOpenMaskSpans(capacity);\n let openCount = 0;\n let opened = 0;\n for (let y = 0; y < height; y += 1) {\n const row = y * width;\n let nextCount = 0;\n let closedCount = 0;\n let cursor = 0;\n let x = 0;\n while (x < width) {\n while (x < width && data[row + x] === 0) {\n x += 1;\n }\n if (x >= width) {\n break;\n }\n const startX = x;\n while (x < width && data[row + x] !== 0) {\n x += 1;\n }\n const runWidth = x - startX;\n while (cursor < openCount && open.startX[cursor] < startX) {\n closed[closedCount] = cursor;\n closedCount += 1;\n cursor += 1;\n }\n if (cursor < openCount &&\n open.startX[cursor] === startX &&\n open.runWidth[cursor] === runWidth) {\n next.openedAt[nextCount] = open.openedAt[cursor];\n next.topY[nextCount] = open.topY[cursor];\n cursor += 1;\n }\n else {\n if (cursor < openCount && open.startX[cursor] === startX) {\n closed[closedCount] = cursor;\n closedCount += 1;\n cursor += 1;\n }\n next.openedAt[nextCount] = opened;\n next.topY[nextCount] = y;\n opened += 1;\n }\n next.runWidth[nextCount] = runWidth;\n next.startX[nextCount] = startX;\n nextCount += 1;\n }\n while (cursor < openCount) {\n closed[closedCount] = cursor;\n closedCount += 1;\n cursor += 1;\n }\n pushClosedRects(rects, open, closed, closedCount, y, offsetX, offsetY);\n const carried = open;\n open = next;\n next = carried;\n openCount = nextCount;\n }\n for (let index = 0; index < openCount; index += 1) {\n closed[index] = index;\n }\n pushClosedRects(rects, open, closed, openCount, height, offsetX, offsetY);\n return rects;\n}\n/** Rects leave in the order they opened, which is not their order across a row. */\nfunction pushClosedRects(rects, open, closed, closedCount, bottomY, offsetX, offsetY) {\n for (let index = 1; index < closedCount; index += 1) {\n const span = closed[index];\n const openedAt = open.openedAt[span];\n let sorted = index - 1;\n while (sorted >= 0 && open.openedAt[closed[sorted]] > openedAt) {\n closed[sorted + 1] = closed[sorted];\n sorted -= 1;\n }\n closed[sorted + 1] = span;\n }\n for (let index = 0; index < closedCount; index += 1) {\n const span = closed[index];\n rects.push({\n height: bottomY - open.topY[span],\n width: open.runWidth[span],\n x: open.startX[span] + offsetX,\n y: open.topY[span] + offsetY,\n });\n }\n}\nfunction assertMaskSize(width, height) {\n if (!Number.isInteger(width) || width <= 0) {\n throw new Error(\"Mask width must be a positive integer.\");\n }\n if (!Number.isInteger(height) || height <= 0) {\n throw new Error(\"Mask height must be a positive integer.\");\n }\n}\nfunction assertMaskDimensions(data, width, height) {\n assertMaskSize(width, height);\n if (data.length !== width * height) {\n throw new Error(\"Mask data length must equal width * height.\");\n }\n}\n\nfunction centerRectToTopLeftRect(rect) {\n return {\n height: rect.height,\n width: rect.width,\n x: rect.x - rect.width / 2,\n y: rect.y - rect.height / 2,\n };\n}\nfunction topLeftRectToCenterRect(rect) {\n return {\n height: rect.height,\n width: rect.width,\n x: rect.x + rect.width / 2,\n y: rect.y + rect.height / 2,\n };\n}\nfunction getPointsRect(points) {\n if (points.length === 0) {\n return undefined;\n }\n let minX = Number.POSITIVE_INFINITY;\n let minY = Number.POSITIVE_INFINITY;\n let maxX = Number.NEGATIVE_INFINITY;\n let maxY = Number.NEGATIVE_INFINITY;\n for (const point of points) {\n minX = Math.min(minX, point.x);\n minY = Math.min(minY, point.y);\n maxX = Math.max(maxX, point.x);\n maxY = Math.max(maxY, point.y);\n }\n return {\n height: maxY - minY,\n width: maxX - minX,\n x: (minX + maxX) / 2,\n y: (minY + maxY) / 2,\n };\n}\nfunction getDetectionRect(detection) {\n return (detection.rect ??\n getPointsRect(detection.polygon?.points ?? []) ??\n getPointsRect(detection.polyline?.points ?? []) ??\n getPointsRect(detection.keypoints?.points ?? []));\n}\nfunction containsPoint(rect, point, padding = 0) {\n const halfWidth = rect.width / 2;\n const halfHeight = rect.height / 2;\n return (point.x >= rect.x - halfWidth - padding &&\n point.x <= rect.x + halfWidth + padding &&\n point.y >= rect.y - halfHeight - padding &&\n point.y <= rect.y + halfHeight + padding);\n}\nfunction pointInPolygon(point, polygon) {\n if (polygon.length < 3) {\n return false;\n }\n let inside = false;\n for (let currentIndex = 0, previousIndex = polygon.length - 1; currentIndex < polygon.length; previousIndex = currentIndex, currentIndex += 1) {\n const current = polygon[currentIndex];\n const previous = polygon[previousIndex];\n const crossesRay = current.y > point.y !== previous.y > point.y &&\n point.x <\n ((previous.x - current.x) * (point.y - current.y)) /\n (previous.y - current.y) +\n current.x;\n if (crossesRay) {\n inside = !inside;\n }\n }\n return inside;\n}\nfunction distanceToSegment(point, start, end) {\n const deltaX = end.x - start.x;\n const deltaY = end.y - start.y;\n const lengthSquared = deltaX * deltaX + deltaY * deltaY;\n if (lengthSquared === 0) {\n return Math.hypot(point.x - start.x, point.y - start.y);\n }\n const projection = Math.max(0, Math.min(1, ((point.x - start.x) * deltaX + (point.y - start.y) * deltaY) /\n lengthSquared));\n const closestX = start.x + projection * deltaX;\n const closestY = start.y + projection * deltaY;\n return Math.hypot(point.x - closestX, point.y - closestY);\n}\nfunction polygonArea(points) {\n if (points.length < 3) {\n return 0;\n }\n let twiceArea = 0;\n for (let index = 0; index < points.length; index += 1) {\n const current = points[index];\n const next = points[(index + 1) % points.length];\n twiceArea += current.x * next.y - next.x * current.y;\n }\n return Math.abs(twiceArea) / 2;\n}\nfunction rectArea(rect) {\n return rect ? Math.max(0, rect.width) * Math.max(0, rect.height) : 0;\n}\n\nconst DEFAULT_LOST_TRACK_BUFFER = 30;\nconst DEFAULT_FRAME_RATE = 30;\nconst DEFAULT_TRACK_ACTIVATION_THRESHOLD = 0.25;\nconst DEFAULT_MINIMUM_CONSECUTIVE_FRAMES = 3;\nconst DEFAULT_MINIMUM_IOU_THRESHOLD = 0.3;\nconst DEFAULT_BYTE_TRACK_ACTIVATION_THRESHOLD = 0.7;\nconst DEFAULT_BYTE_MINIMUM_CONSECUTIVE_FRAMES = 2;\nconst DEFAULT_BYTE_MINIMUM_IOU_THRESHOLD = 0.1;\nconst DEFAULT_HIGH_CONFIDENCE_DETECTION_THRESHOLD = 0.6;\nconst DEFAULT_CBIOU_FIRST_IOU_THRESHOLD = 0.2;\nconst DEFAULT_CBIOU_SECOND_IOU_THRESHOLD = 0.5;\nconst DEFAULT_CBIOU_UNCONFIRMED_IOU_THRESHOLD = 0.3;\nconst DEFAULT_CBIOU_FIRST_BUFFER_RATIO = 0.3;\nconst DEFAULT_CBIOU_SECOND_BUFFER_RATIO = 0.5;\nconst DEFAULT_OCSORT_DIRECTION_CONSISTENCY_WEIGHT = 0.2;\nconst DEFAULT_OCSORT_DELTA_T = 3;\nconst detectionPostProcessors = {\n tracking: ((options = {}) => {\n const isByteTrack = options.algorithm === \"bytetrack\";\n const isCBIoU = options.algorithm === \"cbiou\";\n const isOCSort = options.algorithm === \"ocsort\";\n const lostTrackBuffer = normalizeNonNegativeInteger(options.lostTrackBuffer, DEFAULT_LOST_TRACK_BUFFER, \"lostTrackBuffer\");\n const frameRate = options.frameRate ?? DEFAULT_FRAME_RATE;\n const trackActivationThreshold = options.trackActivationThreshold ??\n (isByteTrack || isCBIoU\n ? DEFAULT_BYTE_TRACK_ACTIVATION_THRESHOLD\n : DEFAULT_TRACK_ACTIVATION_THRESHOLD);\n const minimumConsecutiveFrames = normalizePositiveInteger(options.minimumConsecutiveFrames, isByteTrack || isCBIoU\n ? DEFAULT_BYTE_MINIMUM_CONSECUTIVE_FRAMES\n : DEFAULT_MINIMUM_CONSECUTIVE_FRAMES, \"minimumConsecutiveFrames\");\n const minimumIouThreshold = options.minimumIouThreshold ??\n (isByteTrack\n ? DEFAULT_BYTE_MINIMUM_IOU_THRESHOLD\n : DEFAULT_MINIMUM_IOU_THRESHOLD);\n if (!Number.isFinite(frameRate) || frameRate <= 0) {\n throw new Error(\"frameRate must be a finite positive value.\");\n }\n normalizeUnitInterval(trackActivationThreshold, \"trackActivationThreshold\");\n normalizeUnitInterval(minimumIouThreshold, \"minimumIouThreshold\");\n const base = {\n geometry: options.geometry ?? TrackingGeometry.Box,\n kind: \"tracking\",\n };\n if (isCBIoU) {\n const highConfidenceDetectionThreshold = options.highConfidenceDetectionThreshold ??\n DEFAULT_HIGH_CONFIDENCE_DETECTION_THRESHOLD;\n const minimumIouThresholdFirstAssociation = options.minimumIouThresholdFirstAssociation ??\n DEFAULT_CBIOU_FIRST_IOU_THRESHOLD;\n const minimumIouThresholdSecondAssociation = options.minimumIouThresholdSecondAssociation ??\n DEFAULT_CBIOU_SECOND_IOU_THRESHOLD;\n const minimumIouThresholdUnconfirmedAssociation = options.minimumIouThresholdUnconfirmedAssociation ??\n DEFAULT_CBIOU_UNCONFIRMED_IOU_THRESHOLD;\n const bufferRatioFirst = options.bufferRatioFirst ?? DEFAULT_CBIOU_FIRST_BUFFER_RATIO;\n const bufferRatioSecond = options.bufferRatioSecond ?? DEFAULT_CBIOU_SECOND_BUFFER_RATIO;\n normalizeUnitInterval(highConfidenceDetectionThreshold, \"highConfidenceDetectionThreshold\");\n normalizeUnitInterval(minimumIouThresholdFirstAssociation, \"minimumIouThresholdFirstAssociation\");\n normalizeUnitInterval(minimumIouThresholdSecondAssociation, \"minimumIouThresholdSecondAssociation\");\n normalizeUnitInterval(minimumIouThresholdUnconfirmedAssociation, \"minimumIouThresholdUnconfirmedAssociation\");\n normalizeNonNegativeFinite(bufferRatioFirst, \"bufferRatioFirst\");\n normalizeNonNegativeFinite(bufferRatioSecond, \"bufferRatioSecond\");\n return {\n ...base,\n algorithm: \"cbiou\",\n options: {\n bufferRatioFirst,\n bufferRatioSecond,\n frameRate,\n highConfidenceDetectionThreshold,\n instantFirstFrameActivation: options.instantFirstFrameActivation ?? true,\n lostTrackBuffer,\n minimumConsecutiveFrames,\n minimumIouThresholdFirstAssociation,\n minimumIouThresholdSecondAssociation,\n minimumIouThresholdUnconfirmedAssociation,\n trackActivationThreshold,\n },\n };\n }\n if (isOCSort) {\n const directionConsistencyWeight = options.directionConsistencyWeight ??\n DEFAULT_OCSORT_DIRECTION_CONSISTENCY_WEIGHT;\n const highConfidenceDetectionThreshold = options.highConfidenceDetectionThreshold ??\n DEFAULT_HIGH_CONFIDENCE_DETECTION_THRESHOLD;\n const deltaT = normalizePositiveInteger(options.deltaT, DEFAULT_OCSORT_DELTA_T, \"deltaT\");\n normalizeUnitInterval(directionConsistencyWeight, \"directionConsistencyWeight\");\n normalizeUnitInterval(highConfidenceDetectionThreshold, \"highConfidenceDetectionThreshold\");\n return {\n ...base,\n algorithm: \"ocsort\",\n options: {\n deltaT,\n directionConsistencyWeight,\n frameRate,\n highConfidenceDetectionThreshold,\n lostTrackBuffer,\n minimumConsecutiveFrames,\n minimumIouThreshold,\n },\n };\n }\n if (isByteTrack) {\n const highConfidenceDetectionThreshold = options.highConfidenceDetectionThreshold ??\n DEFAULT_HIGH_CONFIDENCE_DETECTION_THRESHOLD;\n normalizeUnitInterval(highConfidenceDetectionThreshold, \"highConfidenceDetectionThreshold\");\n return {\n ...base,\n algorithm: \"bytetrack\",\n options: {\n frameRate,\n highConfidenceDetectionThreshold,\n lostTrackBuffer,\n minimumConsecutiveFrames,\n minimumIouThreshold,\n trackActivationThreshold,\n },\n };\n }\n return {\n ...base,\n algorithm: \"sort\",\n options: {\n frameRate,\n lostTrackBuffer,\n minimumConsecutiveFrames,\n minimumIouThreshold,\n trackActivationThreshold,\n },\n };\n }),\n};\n/**\n * Creates the lightweight projection sent to a tracker or tracking worker.\n * Masks and keypoint arrays never cross the worker boundary.\n */\nfunction projectDetectionFrameForTracking(frame, geometry) {\n return frame.detections.flatMap((detection, detectionIndex) => {\n const rect = resolveTrackingRect(detection, geometry);\n if (!rect || rect.width <= 0 || rect.height <= 0) {\n return [];\n }\n return [\n {\n ...(detection.confidence === undefined\n ? {}\n : { confidence: detection.confidence }),\n detectionIndex,\n rect,\n },\n ];\n });\n}\nfunction resolveTrackingRect(detection, geometry) {\n switch (geometry) {\n case TrackingGeometry.Box:\n return detection.rect;\n case TrackingGeometry.Mask:\n if (!detection.mask) {\n return undefined;\n }\n // Most segmentation producers already supply the exact mask bounds as a\n // rect. Decode RLE only when that inexpensive projection is absent.\n return detection.rect ?? computeDetectionMaskRect(detection.mask);\n case TrackingGeometry.Keypoints:\n return detection.keypoints\n ? getPointsRect(detection.keypoints.points)\n : undefined;\n }\n}\nfunction normalizePositiveInteger(value, fallback, label) {\n const resolved = value ?? fallback;\n if (!Number.isInteger(resolved) || resolved < 1) {\n throw new Error(`${label} must be a positive integer.`);\n }\n return resolved;\n}\nfunction normalizeNonNegativeInteger(value, fallback, label) {\n const resolved = value ?? fallback;\n if (!Number.isInteger(resolved) || resolved < 0) {\n throw new Error(`${label} must be a non-negative integer.`);\n }\n return resolved;\n}\nfunction normalizeUnitInterval(value, label) {\n if (!Number.isFinite(value) || value < 0 || value > 1) {\n throw new Error(`${label} must be between 0 and 1.`);\n }\n}\nfunction normalizeNonNegativeFinite(value, label) {\n if (!Number.isFinite(value) || value < 0) {\n throw new Error(`${label} must be a finite non-negative value.`);\n }\n}\n\nvar DetectionPickTarget;\n(function (DetectionPickTarget) {\n DetectionPickTarget[\"Box\"] = \"box\";\n DetectionPickTarget[\"Edge\"] = \"edge\";\n DetectionPickTarget[\"Keypoint\"] = \"keypoint\";\n DetectionPickTarget[\"Label\"] = \"label\";\n DetectionPickTarget[\"Mask\"] = \"mask\";\n DetectionPickTarget[\"Polygon\"] = \"polygon\";\n DetectionPickTarget[\"Polyline\"] = \"polyline\";\n})(DetectionPickTarget || (DetectionPickTarget = {}));\nvar MediaInteractionMode;\n(function (MediaInteractionMode) {\n MediaInteractionMode[\"Always\"] = \"always\";\n MediaInteractionMode[\"Disabled\"] = \"disabled\";\n MediaInteractionMode[\"PausedOnly\"] = \"pausedOnly\";\n})(MediaInteractionMode || (MediaInteractionMode = {}));\n\nconst decodedMaskCache = new WeakMap();\nfunction pickDetectionAtPoint(frame, point, options = {}) {\n if (!frame) {\n return null;\n }\n // Tolerances are screen-space sizes; a zoomed-out viewport maps more media\n // units per screen pixel, so a fixed media-space padding would shrink the\n // hit target below the drawn marker.\n const viewportScale = options.viewportScale !== undefined &&\n Number.isFinite(options.viewportScale) &&\n options.viewportScale > 0\n ? options.viewportScale\n : 1;\n const padding = Math.max(0, options.padding ?? 0) / viewportScale;\n const polylinePadding = Math.max(0, options.polylinePadding ?? 6) / viewportScale;\n const keypointPadding = Math.max(0, options.keypointPadding ?? 10) / viewportScale;\n const edgePadding = Math.max(0, options.edgePadding ?? 8) / viewportScale;\n const candidates = [];\n for (let detectionIndex = 0; detectionIndex < frame.detections.length; detectionIndex += 1) {\n const detection = frame.detections[detectionIndex];\n if (!detection ||\n (detection.locked && options.includeLocked === false) ||\n options.filter?.(detection, detectionIndex) === false) {\n continue;\n }\n const detectionArea = Math.max(1, rectArea(getDetectionRect(detection)));\n const pushCandidate = (target, area = detectionArea, geometryIndex) => {\n candidates.push({\n area,\n priority: getPickTargetPriority(target),\n result: {\n detection,\n detectionIndex,\n frame,\n geometryIndex,\n mediaTime: frame.mediaTime,\n point,\n target,\n },\n zIndex: detection.zIndex ?? detectionIndex,\n });\n };\n if (detection.rect && containsPoint(detection.rect, point, padding)) {\n pushCandidate(DetectionPickTarget.Box, rectArea(detection.rect));\n }\n if (detection.polygon && pointInPolygon(point, detection.polygon.points)) {\n pushCandidate(DetectionPickTarget.Polygon, Math.max(1, polygonArea(detection.polygon.points)));\n }\n if (detection.polyline) {\n const segmentIndex = findHitSegment(point, detection.polyline.points, polylinePadding);\n if (segmentIndex !== -1) {\n pushCandidate(DetectionPickTarget.Polyline, detectionArea, segmentIndex);\n }\n }\n if (detection.keypoints) {\n // Clustered keypoints share the pick tolerance; take the nearest so the\n // hover label, the menu, and the handle drag all name the same point.\n // Later points draw on top, so they win exact ties.\n let keypointIndex = -1;\n let keypointDistance = Number.POSITIVE_INFINITY;\n detection.keypoints.points.forEach((keypoint, index) => {\n if (detection.keypoints?.visibility?.[index] ===\n KeypointVisibility.NotLabeled) {\n return;\n }\n const distance = Math.hypot(point.x - keypoint.x, point.y - keypoint.y);\n if (distance <= keypointPadding && distance <= keypointDistance) {\n keypointIndex = index;\n keypointDistance = distance;\n }\n });\n if (keypointIndex !== -1) {\n pushCandidate(DetectionPickTarget.Keypoint, Math.PI * keypointPadding * keypointPadding, keypointIndex);\n }\n else {\n const edgeIndex = detection.keypoints.edges.findIndex(([fromIndex, toIndex]) => {\n const from = detection.keypoints?.points[fromIndex];\n const to = detection.keypoints?.points[toIndex];\n return Boolean(from &&\n to &&\n detection.keypoints?.visibility?.[fromIndex] !==\n KeypointVisibility.NotLabeled &&\n detection.keypoints?.visibility?.[toIndex] !==\n KeypointVisibility.NotLabeled &&\n distanceToSegment(point, from, to) <= edgePadding);\n });\n if (edgeIndex !== -1) {\n const [fromIndex, toIndex] = detection.keypoints.edges[edgeIndex];\n const from = detection.keypoints.points[fromIndex];\n const to = detection.keypoints.points[toIndex];\n const edgeArea = Math.max(1, Math.hypot(to.x - from.x, to.y - from.y) * edgePadding * 2);\n pushCandidate(DetectionPickTarget.Edge, edgeArea, edgeIndex);\n }\n }\n }\n if (detection.mask && options.includeMasks !== false) {\n const decoded = getDecodedMask(detection.mask);\n const mediaDimensions = options.maskMediaDimensions;\n const x = Math.floor(mediaDimensions && mediaDimensions.width > 0\n ? (point.x / mediaDimensions.width) * decoded.width\n : point.x);\n const y = Math.floor(mediaDimensions && mediaDimensions.height > 0\n ? (point.y / mediaDimensions.height) * decoded.height\n : point.y);\n if (x >= 0 &&\n y >= 0 &&\n x < decoded.width &&\n y < decoded.height &&\n decoded.data[y * decoded.width + x]) {\n pushCandidate(DetectionPickTarget.Mask, getMaskMediaArea(decoded, mediaDimensions));\n }\n }\n }\n candidates.sort((left, right) => {\n const priorityDifference = left.priority - right.priority;\n if (priorityDifference !== 0) {\n return priorityDifference;\n }\n const areaDifference = left.area - right.area;\n if (areaDifference !== 0) {\n return areaDifference;\n }\n const zIndexDifference = right.zIndex - left.zIndex;\n return zIndexDifference === 0\n ? right.result.detectionIndex - left.result.detectionIndex\n : zIndexDifference;\n });\n return candidates[0]?.result ?? null;\n}\nfunction getPickTargetPriority(target) {\n switch (target) {\n case DetectionPickTarget.Keypoint:\n return 0;\n case DetectionPickTarget.Edge:\n case DetectionPickTarget.Polyline:\n return 1;\n default:\n return 2;\n }\n}\nfunction getDecodedMask(mask) {\n const cached = decodedMaskCache.get(mask);\n if (cached)\n return cached;\n const decoded = decodeCompressedRleMask(mask);\n const result = {\n ...decoded,\n pixelArea: countMaskPixels(decoded.data),\n };\n decodedMaskCache.set(mask, result);\n return result;\n}\nfunction getMaskMediaArea(mask, mediaDimensions) {\n if (!mediaDimensions ||\n mediaDimensions.width <= 0 ||\n mediaDimensions.height <= 0) {\n return mask.pixelArea;\n }\n return (mask.pixelArea *\n (mediaDimensions.width / mask.width) *\n (mediaDimensions.height / mask.height));\n}\nfunction createDetectionPickKey(pick) {\n if (!pick) {\n return null;\n }\n const frameKey = [pick.frame.frameIndex ?? \"time\", pick.frame.mediaTime];\n const detectionKey = detectionPickKey(pick);\n return [\n ...frameKey,\n ...detectionKey,\n pick.target,\n pick.geometryIndex ?? \"geometry\",\n ].join(\":\");\n}\n/**\n * Compares the frame-independent membership of two ordered selections.\n *\n * Detection ids retain their runtime type, so numeric `1` and string `\"1\"`\n * remain distinct identities. Anonymous detections keep their frame-local\n * index identity.\n */\nfunction haveSameDetectionPickIdentities(current, next) {\n if (current.length !== next.length) {\n return false;\n }\n return current.every((pick, index) => {\n const nextPick = next[index];\n if (!nextPick) {\n return false;\n }\n const id = pick.detection.id;\n const nextId = nextPick.detection.id;\n return id === undefined || nextId === undefined\n ? id === nextId && pick.detectionIndex === nextPick.detectionIndex\n : typeof id === typeof nextId && id === nextId;\n });\n}\nfunction rebaseDetectionPickToFrame(pick, frame) {\n if (!pick || !frame) {\n return null;\n }\n const detectionIndex = pick.detection.id === undefined ||\n !hasUniqueDetectionId(pick.frame, pick.detection.id)\n ? pick.detectionIndex\n : frame.detections.findIndex((detection) => detection.id === pick.detection.id);\n const detection = frame.detections[detectionIndex];\n if (!detection) {\n return null;\n }\n const rebasedPick = {\n detection,\n detectionIndex,\n frame,\n geometryIndex: pick.geometryIndex,\n mediaTime: frame.mediaTime,\n point: pick.point,\n target: pick.target,\n };\n return createDetectionPickKey(rebasedPick) === createDetectionPickKey(pick)\n ? rebasedPick\n : null;\n}\n/**\n * Carries a pick forward onto a different frame of the same timeline.\n *\n * Same-frame rebases behave exactly like `rebaseDetectionPickToFrame`. Across\n * frames, the pick follows its detection only when the detection id is defined\n * and unique in both frames, so anonymous and duplicate-id detections keep the\n * per-frame lifetime they have today. Returns `null` when the detection has no\n * followable identity or has left the frame.\n */\nfunction followDetectionPickAcrossFrames(pick, frame) {\n const rebasedPick = rebaseDetectionPickToFrame(pick, frame);\n if (rebasedPick || !pick || !frame) {\n return rebasedPick;\n }\n const id = pick.detection.id;\n if (id === undefined ||\n !hasUniqueDetectionId(pick.frame, id) ||\n !hasUniqueDetectionId(frame, id)) {\n return null;\n }\n const detectionIndex = frame.detections.findIndex((detection) => detection.id === id);\n const detection = frame.detections[detectionIndex];\n if (!detection) {\n return null;\n }\n return {\n detection,\n detectionIndex,\n frame,\n geometryIndex: pick.geometryIndex,\n mediaTime: frame.mediaTime,\n point: pick.point,\n target: pick.target,\n };\n}\nfunction detectionPickKey(pick) {\n if (pick.detection.id === undefined) {\n return [\"anonymous\", pick.detectionIndex];\n }\n const idType = typeof pick.detection.id;\n return hasUniqueDetectionId(pick.frame, pick.detection.id)\n ? [\"id\", idType, String(pick.detection.id)]\n : [\n \"duplicate-id\",\n idType,\n String(pick.detection.id),\n \"index\",\n pick.detectionIndex,\n ];\n}\nfunction hasUniqueDetectionId(frame, id) {\n let count = 0;\n for (const detection of frame.detections) {\n if (detection.id !== id)\n continue;\n count += 1;\n if (count > 1)\n return false;\n }\n return count === 1;\n}\nfunction pickDetectionByMaskId(frame, maskId, point) {\n if (!frame || maskId <= 0 || !Number.isInteger(maskId)) {\n return null;\n }\n const detectionIndex = maskId - 1;\n const detection = frame.detections[detectionIndex];\n if (!detection) {\n return null;\n }\n return {\n detection,\n detectionIndex,\n frame,\n mediaTime: frame.mediaTime,\n point,\n target: DetectionPickTarget.Mask,\n };\n}\nfunction findHitSegment(point, points, padding, closed) {\n const segmentCount = points.length - 1;\n for (let index = 0; index < segmentCount; index += 1) {\n const start = points[index];\n const end = points[(index + 1) % points.length];\n if (start && end && distanceToSegment(point, start, end) <= padding) {\n return index;\n }\n }\n return -1;\n}\nfunction countMaskPixels(data) {\n let count = 0;\n for (const value of data) {\n count += value ? 1 : 0;\n }\n return Math.max(1, count);\n}\n\nconst MIN_SCALE = 0.1;\nconst MAX_SCALE = 10;\nconst MAX_WHEEL_DELTA = 50;\nfunction createViewportController(initial = {}) {\n let transform = freeze({\n locked: initial.locked ?? false,\n scale: clampScale(initial.scale ?? 0.8),\n x: initial.x ?? 0,\n y: initial.y ?? 0,\n });\n const listeners = new Set();\n const update = (next) => {\n if (next.scale === transform.scale &&\n next.x === transform.x &&\n next.y === transform.y &&\n next.locked === transform.locked)\n return;\n transform = freeze(next);\n for (const listener of listeners)\n listener(transform);\n };\n const controller = {\n getTransform: () => transform,\n screenToMedia: (point) => screenToMedia(point, transform),\n mediaToScreen: (point) => mediaToScreen(point, transform),\n setTransform(next) {\n if (transform.locked)\n return;\n update({\n ...transform,\n ...(next.scale === undefined ? {} : { scale: clampScale(next.scale) }),\n ...(next.x === undefined ? {} : { x: next.x }),\n ...(next.y === undefined ? {} : { y: next.y }),\n });\n },\n setLocked(locked) {\n update({ ...transform, locked });\n },\n panBy(dx, dy) {\n if (transform.locked)\n return;\n update({ ...transform, x: transform.x + dx, y: transform.y + dy });\n },\n zoomAt(point, factor) {\n if (transform.locked || !Number.isFinite(factor) || factor <= 0)\n return;\n const mediaPoint = screenToMedia(point, transform);\n const scale = clampScale(transform.scale * factor);\n update({\n ...transform,\n scale,\n x: point.x - mediaPoint.x * scale,\n y: point.y - mediaPoint.y * scale,\n });\n },\n zoomFromWheel(point, deltaY) {\n const delta = Math.max(-MAX_WHEEL_DELTA, Math.min(MAX_WHEEL_DELTA, deltaY));\n controller.zoomAt(point, Math.exp(-delta * 0.01));\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n };\n return controller;\n}\nfunction screenToMedia(point, transform) {\n return {\n x: (point.x - transform.x) / transform.scale,\n y: (point.y - transform.y) / transform.scale,\n };\n}\nfunction mediaToScreen(point, transform) {\n return {\n x: point.x * transform.scale + transform.x,\n y: point.y * transform.scale + transform.y,\n };\n}\nfunction clampScale(scale) {\n return Math.max(MIN_SCALE, Math.min(MAX_SCALE, Number.isFinite(scale) ? scale : 1));\n}\nfunction freeze(transform) {\n return Object.freeze(transform);\n}\n\nvar AnnotationGeometryKind;\n(function (AnnotationGeometryKind) {\n AnnotationGeometryKind[\"Box\"] = \"box\";\n AnnotationGeometryKind[\"Polygon\"] = \"polygon\";\n AnnotationGeometryKind[\"Polyline\"] = \"polyline\";\n AnnotationGeometryKind[\"Keypoints\"] = \"keypoints\";\n AnnotationGeometryKind[\"Mask\"] = \"mask\";\n})(AnnotationGeometryKind || (AnnotationGeometryKind = {}));\nvar AnnotationGestureStateKind;\n(function (AnnotationGestureStateKind) {\n AnnotationGestureStateKind[\"Idle\"] = \"idle\";\n AnnotationGestureStateKind[\"Creating\"] = \"creating\";\n AnnotationGestureStateKind[\"Moving\"] = \"moving\";\n AnnotationGestureStateKind[\"Resizing\"] = \"resizing\";\n AnnotationGestureStateKind[\"DragSelecting\"] = \"dragSelecting\";\n})(AnnotationGestureStateKind || (AnnotationGestureStateKind = {}));\nvar AnnotationHandleKind;\n(function (AnnotationHandleKind) {\n AnnotationHandleKind[\"Resize\"] = \"resize\";\n AnnotationHandleKind[\"Vertex\"] = \"vertex\";\n AnnotationHandleKind[\"AddVertex\"] = \"addVertex\";\n AnnotationHandleKind[\"Keypoint\"] = \"keypoint\";\n})(AnnotationHandleKind || (AnnotationHandleKind = {}));\n\nconst HANDLE_RADIUS = 6;\nconst ADD_HANDLE_RADIUS = 3.6;\nconst HANDLE_HIT_SIZE = 16;\nfunction getAnnotationHandles(detection, viewportScale = 1) {\n if (detection.locked || detection.mask)\n return [];\n const radius = HANDLE_RADIUS / viewportScale;\n const hitSize = HANDLE_HIT_SIZE / viewportScale;\n if (detection.polygon)\n return getPathHandles(detection.polygon.points, true, radius, hitSize);\n if (detection.polyline)\n return getPathHandles(detection.polyline.points, false, radius, hitSize);\n if (detection.keypoints) {\n const keypointHandles = detection.keypoints.points.flatMap((point, geometryIndex) => detection.keypoints?.visibility?.[geometryIndex] ===\n KeypointVisibility.NotLabeled\n ? []\n : [\n {\n cursor: \"pointer\",\n geometryIndex,\n hitSize,\n id: `kp-${geometryIndex}`,\n kind: AnnotationHandleKind.Keypoint,\n point,\n radius,\n },\n ]);\n if (!detection.rect)\n return keypointHandles;\n // The skeleton's box resizes like any box; keypoints come last so a point\n // sitting on the box edge still wins the hit test.\n return [\n ...getBoxHandles(detection.rect, radius, hitSize),\n ...keypointHandles,\n ];\n }\n if (detection.rect)\n return getBoxHandles(detection.rect, radius, hitSize);\n return [];\n}\nfunction pickAnnotationHandle(handles, point) {\n let picked;\n let pickedDistance = Number.POSITIVE_INFINITY;\n for (const handle of handles) {\n const dx = Math.abs(point.x - handle.point.x);\n const dy = Math.abs(point.y - handle.point.y);\n if (dx > handle.hitSize / 2 || dy > handle.hitSize / 2)\n continue;\n // Clustered handles (face keypoints, a tiny box's corners) share hit\n // areas; the nearest one is what the pointer is on. Later handles draw on\n // top, so they win exact ties.\n const distance = Math.hypot(dx, dy);\n if (distance <= pickedDistance) {\n picked = handle;\n pickedDistance = distance;\n }\n }\n return picked;\n}\nfunction applyAnnotationHandleDrag(detection, handle, point) {\n if (detection.rect && handle.kind === AnnotationHandleKind.Resize) {\n const rect = resizeRect(detection.rect, handle.id, point);\n const boxRelative = detection.keypoints?.boxRelative;\n if (!detection.keypoints || !boxRelative?.some(Boolean)) {\n return { ...detection, rect };\n }\n // Box-relative keypoints (unplaced template points) follow the rect;\n // points the user positioned keep their coordinates.\n const from = detection.rect;\n const scaleX = from.width > 0 ? rect.width / from.width : 1;\n const scaleY = from.height > 0 ? rect.height / from.height : 1;\n return {\n ...detection,\n keypoints: {\n ...detection.keypoints,\n points: detection.keypoints.points.map((keypoint, index) => boxRelative[index]\n ? {\n x: rect.x + (keypoint.x - from.x) * scaleX,\n y: rect.y + (keypoint.y - from.y) * scaleY,\n }\n : keypoint),\n },\n rect,\n };\n }\n if (handle.kind === AnnotationHandleKind.AddVertex &&\n handle.edgeIndex !== undefined) {\n const key = detection.polygon ? \"polygon\" : \"polyline\";\n const geometry = detection[key];\n if (!geometry)\n return detection;\n const points = [...geometry.points];\n points.splice(handle.edgeIndex + 1, 0, point);\n return { ...detection, [key]: { points } };\n }\n if (handle.geometryIndex === undefined)\n return detection;\n if (detection.polygon)\n return replacePathPoint(detection, \"polygon\", handle.geometryIndex, point);\n if (detection.polyline)\n return replacePathPoint(detection, \"polyline\", handle.geometryIndex, point);\n if (detection.keypoints) {\n const points = [...detection.keypoints.points];\n points[handle.geometryIndex] = point;\n // A positioned point no longer follows the box.\n const boxRelative = detection.keypoints.boxRelative?.map((relative, index) => relative && index !== handle.geometryIndex);\n return {\n ...detection,\n keypoints: {\n ...detection.keypoints,\n points,\n ...(boxRelative ? { boxRelative } : {}),\n },\n };\n }\n return detection;\n}\nfunction deleteAnnotationVertex(detection, vertexIndex) {\n const geometry = detection.polygon ?? detection.polyline;\n const minimum = detection.polygon ? 3 : 2;\n if (!geometry || geometry.points.length <= minimum)\n return null;\n const points = geometry.points.filter((_, index) => index !== vertexIndex);\n return detection.polygon\n ? { ...detection, polygon: { points } }\n : { ...detection, polyline: { points } };\n}\nfunction offsetDetection(detection, dx, dy) {\n const offsetPoint = ({ x, y }) => ({ x: x + dx, y: y + dy });\n return {\n ...detection,\n ...(detection.rect\n ? {\n rect: {\n ...detection.rect,\n x: detection.rect.x + dx,\n y: detection.rect.y + dy,\n },\n }\n : {}),\n ...(detection.polygon\n ? { polygon: { points: detection.polygon.points.map(offsetPoint) } }\n : {}),\n ...(detection.polyline\n ? { polyline: { points: detection.polyline.points.map(offsetPoint) } }\n : {}),\n ...(detection.keypoints\n ? {\n keypoints: {\n ...detection.keypoints,\n points: detection.keypoints.points.map(offsetPoint),\n },\n }\n : {}),\n };\n}\nfunction getBoxHandles(rect, radius, hitSize) {\n const left = rect.x - rect.width / 2;\n const right = rect.x + rect.width / 2;\n const top = rect.y - rect.height / 2;\n const bottom = rect.y + rect.height / 2;\n const definitions = [\n [\"nw\", { x: left, y: top }, \"nwse-resize\"],\n [\"n\", { x: rect.x, y: top }, \"ns-resize\"],\n [\"ne\", { x: right, y: top }, \"nesw-resize\"],\n [\"e\", { x: right, y: rect.y }, \"ew-resize\"],\n [\"se\", { x: right, y: bottom }, \"nwse-resize\"],\n [\"s\", { x: rect.x, y: bottom }, \"ns-resize\"],\n [\"sw\", { x: left, y: bottom }, \"nesw-resize\"],\n [\"w\", { x: left, y: rect.y }, \"ew-resize\"],\n ];\n return definitions.map(([id, point, cursor]) => ({\n cursor,\n hitSize,\n id,\n kind: AnnotationHandleKind.Resize,\n point,\n radius,\n }));\n}\nfunction getPathHandles(points, closed, radius, hitSize) {\n const handles = points.map((point, geometryIndex) => ({\n cursor: \"move\",\n geometryIndex,\n hitSize,\n id: `vertex-${geometryIndex}`,\n kind: AnnotationHandleKind.Vertex,\n point,\n radius,\n }));\n const edgeCount = closed ? points.length : Math.max(0, points.length - 1);\n for (let edgeIndex = 0; edgeIndex < edgeCount; edgeIndex += 1) {\n const from = points[edgeIndex];\n const to = points[(edgeIndex + 1) % points.length];\n handles.push({\n cursor: \"copy\",\n edgeIndex,\n hitSize,\n id: `add-${edgeIndex}`,\n kind: AnnotationHandleKind.AddVertex,\n point: { x: (from.x + to.x) / 2, y: (from.y + to.y) / 2 },\n radius: ADD_HANDLE_RADIUS / (HANDLE_RADIUS / radius),\n });\n }\n return handles;\n}\nfunction resizeRect(rect, handle, point) {\n let left = rect.x - rect.width / 2;\n let right = rect.x + rect.width / 2;\n let top = rect.y - rect.height / 2;\n let bottom = rect.y + rect.height / 2;\n if (handle.includes(\"w\"))\n left = Math.min(point.x, right - 5);\n if (handle.includes(\"e\"))\n right = Math.max(point.x, left + 5);\n if (handle.includes(\"n\"))\n top = Math.min(point.y, bottom - 5);\n if (handle.includes(\"s\"))\n bottom = Math.max(point.y, top + 5);\n return {\n x: (left + right) / 2,\n y: (top + bottom) / 2,\n width: right - left,\n height: bottom - top,\n };\n}\nfunction replacePathPoint(detection, key, index, point) {\n const geometry = detection[key];\n const points = [...geometry.points];\n points[index] = point;\n return { ...detection, [key]: { points } };\n}\n/** Finds the closest path segment, useful for contextual vertex insertion. */\nfunction findClosestAnnotationSegment(points, point, closed) {\n const count = closed ? points.length : points.length - 1;\n let best;\n for (let index = 0; index < count; index += 1) {\n const distance = distanceToSegment(point, points[index], points[(index + 1) % points.length]);\n if (!best || distance < best.distance)\n best = { index, distance };\n }\n return best;\n}\n\nconst MOVE_THRESHOLD = 4;\nconst CLICK_CANCEL_MS = 250;\nconst CLICK_CANCEL_DIAGONAL = 25;\nconst CLOSE_ZONE_SCREEN_PX = 12;\nfunction createAnnotationEditingEngine(options = {}) {\n let tool = null;\n let gesture = null;\n let state = idleState();\n const stateListeners = new Set();\n const fastTranslateListeners = new Set();\n return {\n getState: () => state,\n setCreationTool(nextTool) {\n if (gesture)\n cancel();\n tool = nextTool;\n },\n pointerDown(input, pick = null) {\n if (input.button !== undefined && input.button !== 0)\n return;\n if (tool) {\n beginCreation(input);\n }\n else if (pick && !pick.detection.locked && !pick.detection.mask) {\n beginMove(pick, input);\n }\n },\n pointerMove(input) {\n if (!gesture)\n return;\n switch (gesture.kind) {\n case \"box\":\n gesture = { ...gesture, current: input };\n setPreview(resolveBoxPreview(gesture));\n break;\n case \"move\": {\n const dx = input.point.x - gesture.start.point.x;\n const dy = input.point.y - gesture.start.point.y;\n const moved = gesture.moved || exceedsMoveThreshold(gesture.start, input);\n gesture = { ...gesture, current: input, moved };\n if (moved) {\n const preview = offsetDetection(gesture.detection, dx, dy);\n setPreview(preview);\n if (gesture.detection.id !== undefined) {\n options.onFastTranslate?.(gesture.detection.id, dx, dy);\n for (const listener of fastTranslateListeners) {\n listener(gesture.detection.id, dx, dy);\n }\n }\n }\n break;\n }\n case \"resize\": {\n // A click on a handle is not a drag: a keypoint tapped to open its\n // menu must not snap to the pointer, so nothing previews or commits\n // until the pointer has travelled like a move gesture.\n const moved = gesture.moved || exceedsMoveThreshold(gesture.start, input);\n gesture = { ...gesture, current: input, moved };\n if (moved) {\n setPreview(applyAnnotationHandleDrag(gesture.detection, gesture.handle, input.point));\n }\n break;\n }\n case \"path\":\n setPreview(resolvePathPreview([...gesture.points, input.point]));\n break;\n case \"freehand\": {\n const previous = gesture.points.at(-1);\n if (!previous ||\n previous.x !== input.point.x ||\n previous.y !== input.point.y) {\n gesture.points.push(input.point);\n }\n gesture = { ...gesture, current: input };\n setPreview(resolvePathPreview(gesture.points));\n break;\n }\n }\n },\n pointerUp(input) {\n if (!gesture)\n return;\n if (gesture.kind === \"path\")\n return;\n const active = gesture;\n gesture = null;\n release(active.start.pointerId);\n if (active.kind === \"box\") {\n const preview = resolveBoxPreview({ ...active, current: input });\n const duration = input.timestamp - active.start.timestamp;\n const diagonal = Math.hypot(input.point.x - active.start.point.x, input.point.y - active.start.point.y) * scale();\n const rect = preview.rect;\n if (!rect ||\n rect.width < 1 ||\n rect.height < 1 ||\n (duration < CLICK_CANCEL_MS && diagonal <= CLICK_CANCEL_DIAGONAL)) {\n cancel();\n return;\n }\n if (tool?.shouldCommit?.(rect) === false) {\n cancel();\n return;\n }\n commit(preview, null);\n return;\n }\n if (active.kind === \"freehand\") {\n const points = [...active.points, input.point];\n if (points.length < 2 || tool?.shouldCommit?.(points) === false) {\n cancel();\n return;\n }\n commit(tool.createDetection(points), null);\n return;\n }\n if (active.kind === \"move\") {\n if (!active.moved) {\n setState(idleState());\n return;\n }\n const preview = offsetDetection(active.detection, input.point.x - active.start.point.x, input.point.y - active.start.point.y);\n commit(preview, active.detection);\n return;\n }\n if (!active.moved) {\n setState(idleState());\n return;\n }\n const preview = applyAnnotationHandleDrag(active.detection, active.handle, input.point);\n commit(preview, active.detection);\n },\n keyDown(key) {\n if (key === \"Escape\") {\n cancel();\n }\n else if (key === \"Enter\" && gesture?.kind === \"path\") {\n commitPath();\n }\n },\n beginHandleDrag(detection, handle, input) {\n if (detection.locked || detection.mask)\n return;\n capture(input.pointerId);\n gesture = {\n current: input,\n detection,\n handle,\n kind: \"resize\",\n moved: false,\n start: input,\n };\n setState({\n activeDetectionId: detection.id ?? null,\n activeHandleId: handle.id,\n kind: AnnotationGestureStateKind.Resizing,\n pointerId: input.pointerId ?? null,\n preview: null,\n });\n },\n deleteVertex(detection, vertexIndex) {\n const next = deleteAnnotationVertex(detection, vertexIndex);\n if (next)\n commit(next, detection);\n return next;\n },\n cancel,\n hasCreationTool: () => tool !== null,\n subscribe(listener) {\n stateListeners.add(listener);\n return () => stateListeners.delete(listener);\n },\n subscribeFastTranslate(listener) {\n fastTranslateListeners.add(listener);\n return () => fastTranslateListeners.delete(listener);\n },\n };\n function beginCreation(input) {\n if (!tool)\n return;\n const mode = tool.mode ??\n (tool.geometry === AnnotationGeometryKind.Box\n ? \"drag\"\n : tool.geometry === AnnotationGeometryKind.Mask\n ? \"freehand\"\n : \"multiClick\");\n if (mode === \"drag\") {\n capture(input.pointerId);\n gesture = { current: input, kind: \"box\", start: input };\n const preview = resolveBoxPreview(gesture);\n options.onPreview?.(preview);\n setState({\n activeDetectionId: null,\n activeHandleId: null,\n kind: AnnotationGestureStateKind.Creating,\n pointerId: input.pointerId ?? null,\n preview,\n });\n return;\n }\n if (mode === \"freehand\") {\n capture(input.pointerId);\n gesture = {\n current: input,\n kind: \"freehand\",\n points: [input.point],\n start: input,\n };\n setPreview(resolvePathPreview([input.point]));\n return;\n }\n if (tool.geometry !== AnnotationGeometryKind.Polygon &&\n tool.geometry !== AnnotationGeometryKind.Polyline &&\n tool.geometry !== AnnotationGeometryKind.Keypoints)\n return;\n if (gesture?.kind !== \"path\") {\n capture(input.pointerId);\n gesture = {\n kind: \"path\",\n lastTimestamp: input.timestamp,\n pointerId: input.pointerId ?? null,\n points: [input.point],\n };\n setPreview(resolvePathPreview([input.point, input.point]));\n return;\n }\n const minimum = tool.minVertices ??\n (tool.geometry === AnnotationGeometryKind.Polygon ? 3 : 2);\n const closeDistance = Math.hypot(input.point.x - gesture.points[0].x, input.point.y - gesture.points[0].y) * scale();\n const doubleClick = input.detail !== undefined\n ? input.detail >= 2\n : input.timestamp - gesture.lastTimestamp < CLICK_CANCEL_MS;\n if (gesture.points.length >= minimum &&\n ((tool.geometry === AnnotationGeometryKind.Polygon &&\n closeDistance <= CLOSE_ZONE_SCREEN_PX) ||\n doubleClick)) {\n commitPath();\n return;\n }\n gesture.points.push(input.point);\n gesture.lastTimestamp = input.timestamp;\n setPreview(resolvePathPreview([...gesture.points, input.point]));\n }\n function beginMove(pick, input) {\n capture(input.pointerId);\n gesture = {\n current: input,\n detection: pick.detection,\n kind: \"move\",\n moved: false,\n start: input,\n };\n setState({\n activeDetectionId: pick.detection.id ?? null,\n activeHandleId: null,\n kind: AnnotationGestureStateKind.Moving,\n pointerId: input.pointerId ?? null,\n preview: null,\n });\n }\n function resolveBoxPreview(active) {\n const left = Math.min(active.start.point.x, active.current.point.x);\n const right = Math.max(active.start.point.x, active.current.point.x);\n const top = Math.min(active.start.point.y, active.current.point.y);\n const bottom = Math.max(active.start.point.y, active.current.point.y);\n const rect = {\n x: (left + right) / 2,\n y: (top + bottom) / 2,\n width: right - left,\n height: bottom - top,\n };\n return tool.createDetection(rect);\n }\n function resolvePathPreview(points) {\n return tool.createDetection(points);\n }\n function commitPath() {\n if (!tool || gesture?.kind !== \"path\")\n return;\n const minimum = tool.minVertices ??\n (tool.geometry === AnnotationGeometryKind.Polygon ? 3 : 2);\n if (gesture.points.length < minimum) {\n cancel();\n return;\n }\n const preview = tool.createDetection(gesture.points);\n if (tool.shouldCommit?.(gesture.points) === false) {\n cancel();\n return;\n }\n release(gesture.pointerId ?? undefined);\n gesture = null;\n commit(preview, null);\n }\n function commit(detection, previous) {\n options.onCommit?.(detection, previous);\n options.onPreview?.(null);\n setState(idleState());\n }\n function setPreview(preview) {\n options.onPreview?.(preview);\n setState({\n activeDetectionId: state.activeDetectionId,\n activeHandleId: state.activeHandleId,\n kind: state.kind === AnnotationGestureStateKind.Idle\n ? AnnotationGestureStateKind.Creating\n : state.kind,\n pointerId: state.pointerId ??\n (gesture && \"pointerId\" in gesture\n ? (gesture.pointerId ?? null)\n : null),\n preview,\n });\n }\n function cancel() {\n const pointerId = gesture && \"start\" in gesture\n ? gesture.start.pointerId\n : gesture?.kind === \"path\"\n ? (gesture.pointerId ?? undefined)\n : undefined;\n release(pointerId);\n gesture = null;\n options.onPreview?.(null);\n options.onCancel?.();\n setState(idleState());\n }\n function setState(next) {\n state = Object.freeze(next);\n options.onStateChange?.(state);\n for (const listener of stateListeners)\n listener(state);\n }\n function scale() {\n return Math.max(options.viewportScale?.() ?? 1, Number.EPSILON);\n }\n function exceedsMoveThreshold(start, input) {\n return (Math.hypot(input.point.x - start.point.x, input.point.y - start.point.y) >=\n MOVE_THRESHOLD / scale());\n }\n function capture(pointerId) {\n if (pointerId !== undefined)\n options.capturePointer?.(pointerId);\n }\n function release(pointerId) {\n if (pointerId !== undefined)\n options.releasePointer?.(pointerId);\n }\n}\nfunction idleState() {\n return Object.freeze({\n activeDetectionId: null,\n activeHandleId: null,\n kind: AnnotationGestureStateKind.Idle,\n pointerId: null,\n preview: null,\n });\n}\n\nfunction resolveStyleValue(value, detection, context) {\n return typeof value === \"function\"\n ? value(detection, context)\n : value;\n}\n\n/**\n * Resolves the renderer-neutral stroke contract without dropping optional\n * geometry controls as styles move from public configuration to draw\n * instructions.\n *\n * Keep this as the single normalization point for every built-in style. That\n * makes additions to `StrokeStyle` deliberate instead of relying on each style\n * implementation to copy a growing property list correctly.\n */\nfunction resolveStrokeStyle(stroke, defaults) {\n const alignment = stroke?.alignment ?? defaults.alignment;\n const cap = stroke?.cap ?? defaults.cap;\n const dash = stroke?.dash ?? defaults.dash;\n const join = stroke?.join ?? defaults.join;\n const miterLimit = stroke?.miterLimit ?? defaults.miterLimit;\n return {\n alpha: stroke?.alpha ?? defaults.alpha,\n color: stroke?.color ?? defaults.color,\n width: stroke?.width ?? defaults.width,\n ...(alignment === undefined ? {} : { alignment }),\n ...(cap === undefined ? {} : { cap }),\n ...(dash === undefined ? {} : { dash }),\n ...(join === undefined ? {} : { join }),\n ...(miterLimit === undefined ? {} : { miterLimit }),\n };\n}\n\n/** Alignment for strokes applied to closed rendered geometry. */\nvar StrokeAlignment;\n(function (StrokeAlignment) {\n StrokeAlignment[\"Inside\"] = \"inside\";\n StrokeAlignment[\"Center\"] = \"center\";\n StrokeAlignment[\"Outside\"] = \"outside\";\n})(StrokeAlignment || (StrokeAlignment = {}));\n\nvar BoxShape;\n(function (BoxShape) {\n BoxShape[\"Rect\"] = \"rect\";\n BoxShape[\"RoundedRect\"] = \"roundedRect\";\n})(BoxShape || (BoxShape = {}));\n\nconst DEFAULT_BOX_STROKE_ALPHA = 1;\nconst DEFAULT_BOX_STROKE_COLOR = 0x00ff66;\nconst DEFAULT_BOX_STROKE_WIDTH = 2;\n/**\n * Default configurable box style.\n *\n * This is the simplest `supervision-js` equivalent of a box annotator: it\n * converts detections with `rect` geometry into renderer-neutral box draw\n * instructions. Use options such as `shape`, `cornerRadius`, `stroke`, and\n * `fill` for static or per-detection styling.\n */\nclass BaseBoxStyle {\n options;\n constructor(options = {}) {\n this.options = options;\n }\n resolve(detection, context) {\n if (!detection.rect ||\n context.hidden ||\n this.options.shouldRender?.(detection, context) === false) {\n return undefined;\n }\n const shape = this.resolveShape(detection, context);\n const cornerRadius = this.resolveCornerRadius(detection, context, shape);\n const instruction = {\n fill: this.resolveFill(detection, context),\n rect: detection.rect,\n shape,\n stroke: this.resolveStroke(detection, context),\n };\n if (cornerRadius !== undefined) {\n return {\n ...instruction,\n cornerRadius,\n };\n }\n return instruction;\n }\n resolveShape(detection, context) {\n return (resolveStyleValue(this.options.shape, detection, context) ?? BoxShape.Rect);\n }\n resolveCornerRadius(detection, context, shape) {\n if (shape !== BoxShape.RoundedRect) {\n return undefined;\n }\n return resolveStyleValue(this.options.cornerRadius, detection, context);\n }\n resolveStroke(detection, context) {\n const stroke = resolveStyleValue(this.options.stroke, detection, context);\n if (stroke === null) {\n return undefined;\n }\n return resolveStrokeStyle(stroke, {\n alpha: DEFAULT_BOX_STROKE_ALPHA,\n color: DEFAULT_BOX_STROKE_COLOR,\n ...(context.ephemeral ? { dash: [6, 4] } : {}),\n width: DEFAULT_BOX_STROKE_WIDTH,\n });\n }\n resolveFill(detection, context) {\n const fill = resolveStyleValue(this.options.fill, detection, context);\n if (fill === null || fill === undefined) {\n return undefined;\n }\n return {\n alpha: fill.alpha ?? 1,\n color: fill.color ?? DEFAULT_BOX_STROKE_COLOR,\n };\n }\n}\n\nconst DEFAULT_CORNER_LENGTH = 20;\nconst DEFAULT_CORNER_STROKE = {\n alpha: 1,\n cap: \"round\",\n color: 0x00ff66,\n join: \"round\",\n width: 2,\n};\n/**\n * Configurable four-corner decoration derived from a detection rectangle.\n *\n * This is the BoxCornerAnnotator-style facade: it keeps the rectangle semantic\n * and draws only its four open corner segments.\n */\nclass BaseBoxCornerStyle {\n options;\n constructor(options = {}) {\n this.options = options;\n }\n resolve(detection, context) {\n if (!detection.rect ||\n context.hidden ||\n this.options.shouldRender?.(detection, context) === false) {\n return undefined;\n }\n const resolvedLength = resolveStyleValue(this.options.length, detection, context) ??\n DEFAULT_CORNER_LENGTH;\n const viewportScale = context.viewportScale && context.viewportScale > 0\n ? context.viewportScale\n : 1;\n const length = Math.min(Math.max(0, resolvedLength / viewportScale), detection.rect.width / 2, detection.rect.height / 2);\n const stroke = resolveStyleValue(this.options.stroke, detection, context);\n if (length <= 0 || stroke === null)\n return undefined;\n const { height, width, x, y } = detection.rect;\n const left = x - width / 2;\n const right = x + width / 2;\n const top = y - height / 2;\n const bottom = y + height / 2;\n const resolvedStroke = resolveStrokeStyle(stroke, DEFAULT_CORNER_STROKE);\n const openStroke = {\n alpha: resolvedStroke.alpha,\n cap: resolvedStroke.cap,\n color: resolvedStroke.color,\n dash: resolvedStroke.dash,\n join: resolvedStroke.join,\n miterLimit: resolvedStroke.miterLimit,\n width: resolvedStroke.width,\n };\n return {\n segments: [\n [\n { x: left + length, y: top },\n { x: left, y: top },\n { x: left, y: top + length },\n ],\n [\n { x: right - length, y: top },\n { x: right, y: top },\n { x: right, y: top + length },\n ],\n [\n { x: left + length, y: bottom },\n { x: left, y: bottom },\n { x: left, y: bottom - length },\n ],\n [\n { x: right - length, y: bottom },\n { x: right, y: bottom },\n { x: right, y: bottom - length },\n ],\n ],\n stroke: openStroke,\n };\n }\n}\n\nvar FocusTargetMode;\n(function (FocusTargetMode) {\n FocusTargetMode[\"Hovered\"] = \"hovered\";\n FocusTargetMode[\"Selected\"] = \"selected\";\n FocusTargetMode[\"HoveredAndSelected\"] = \"hoveredAndSelected\";\n FocusTargetMode[\"Ambient\"] = \"ambient\";\n})(FocusTargetMode || (FocusTargetMode = {}));\n\nconst DEFAULT_FOCUS_FILL_COLOR = 0x020617;\nconst DEFAULT_FOCUS_FILL_ALPHA = 0.45;\nconst DEFAULT_FOCUS_CORNER_RADIUS = 8;\n/**\n * Default configurable focus style.\n *\n * Focus styles dim the current media frame while leaving the selected or\n * hovered detections visible. Renderers may use prepared mask artifacts for\n * shape-accurate cutouts, falling back to detection rectangles when needed.\n */\nclass BaseFocusStyle {\n options;\n constructor(options = {}) {\n this.options = options;\n }\n resolve(context) {\n if (this.options.shouldRender?.(context) === false) {\n return undefined;\n }\n const targetMode = this.options.targetMode ?? FocusTargetMode.Selected;\n const targets = getFocusTargets(context, targetMode);\n const ambient = targetMode === FocusTargetMode.Ambient &&\n !context.selectedPick &&\n !context.hoveredPick;\n const fill = resolveFocusStyleValue(this.options.fill, context);\n if (targets.length === 0 || fill === null) {\n return undefined;\n }\n return {\n fallback: this.resolveFallback(context),\n fill: {\n alpha: fill?.alpha ?? DEFAULT_FOCUS_FILL_ALPHA,\n color: fill?.color ?? DEFAULT_FOCUS_FILL_COLOR,\n },\n targetMode,\n targets,\n ...(ambient ? { ambient: true } : {}),\n };\n }\n resolveFallback(context) {\n const shape = resolveFocusStyleValue(this.options.shape, context) ??\n BoxShape.RoundedRect;\n const cornerRadius = shape === BoxShape.RoundedRect\n ? (resolveFocusStyleValue(this.options.cornerRadius, context) ??\n DEFAULT_FOCUS_CORNER_RADIUS)\n : undefined;\n if (cornerRadius === undefined) {\n return { shape };\n }\n return {\n cornerRadius,\n shape,\n };\n }\n}\nfunction getFocusTargets(context, targetMode) {\n const targets = [];\n if (targetMode === FocusTargetMode.Ambient) {\n if (context.selectedPick)\n return [context.selectedPick];\n if (context.hoveredPick)\n return [context.hoveredPick];\n return context.frame.detections.map((detection, detectionIndex) => ({\n detection,\n detectionIndex,\n frame: context.frame,\n mediaTime: context.mediaTime,\n point: detection.rect\n ? { x: detection.rect.x, y: detection.rect.y }\n : { x: 0, y: 0 },\n target: detection.mask\n ? DetectionPickTarget.Mask\n : detection.polygon\n ? DetectionPickTarget.Polygon\n : DetectionPickTarget.Box,\n }));\n }\n if ((targetMode === FocusTargetMode.Selected ||\n targetMode === FocusTargetMode.HoveredAndSelected) &&\n context.selectedPick) {\n targets.push(context.selectedPick);\n }\n if ((targetMode === FocusTargetMode.Hovered ||\n targetMode === FocusTargetMode.HoveredAndSelected) &&\n context.hoveredPick) {\n targets.push(context.hoveredPick);\n }\n return dedupeTargetsForFrame(targets, context.frame);\n}\nfunction dedupeTargetsForFrame(targets, frame) {\n const keys = new Set();\n const dedupedTargets = [];\n for (const target of targets) {\n if (target.frame !== frame) {\n continue;\n }\n const key = `${target.detectionIndex}:${target.target}`;\n if (keys.has(key)) {\n continue;\n }\n keys.add(key);\n dedupedTargets.push(target);\n }\n return dedupedTargets;\n}\nfunction resolveFocusStyleValue(value, context) {\n if (typeof value === \"function\") {\n return value(context);\n }\n return value;\n}\n\nvar DetectionInteractionState;\n(function (DetectionInteractionState) {\n DetectionInteractionState[\"Hovered\"] = \"hovered\";\n DetectionInteractionState[\"Selected\"] = \"selected\";\n})(DetectionInteractionState || (DetectionInteractionState = {}));\n\nconst DEFAULT_HOVER_FILL_COLOR = 0x67e8f9;\nconst DEFAULT_HOVER_FILL_ALPHA = 0.1;\nconst DEFAULT_HOVER_STROKE_COLOR = 0x67e8f9;\nconst DEFAULT_HOVER_STROKE_ALPHA = 0.95;\nconst DEFAULT_HOVER_STROKE_WIDTH = 3;\nconst DEFAULT_SELECTED_FILL_COLOR = 0xfde047;\nconst DEFAULT_SELECTED_FILL_ALPHA = 0.18;\nconst DEFAULT_SELECTED_STROKE_COLOR = 0xfde047;\nconst DEFAULT_SELECTED_STROKE_ALPHA = 1;\nconst DEFAULT_SELECTED_STROKE_WIDTH = 4;\n/**\n * Default configurable interaction style.\n *\n * It resolves hover and selected picks into state-specific presentations using\n * the same style contracts as normal boxes, masks, and labels. A state left\n * unconfigured falls back to a highlight rectangle over the detection.\n */\nclass BaseInteractionStyle {\n options;\n constructor(options = {}) {\n this.options = options;\n }\n resolve(detection, context) {\n if (this.options.shouldRender?.(detection, context) === false) {\n return undefined;\n }\n const statePresentation = context.state === DetectionInteractionState.Selected\n ? this.options.selected\n : this.options.hovered;\n if (statePresentation !== undefined) {\n return statePresentation ?? undefined;\n }\n const rect = detection.rect;\n if (!rect) {\n return undefined;\n }\n return {\n boxStyle: createDefaultHighlightBoxStyle(context.state, rect),\n };\n }\n}\nfunction createDefaultHighlightBoxStyle(state, rect) {\n const defaults = getStateDefaults(state);\n const instruction = {\n fill: { alpha: defaults.fillAlpha, color: defaults.fillColor },\n rect,\n shape: BoxShape.Rect,\n stroke: {\n alpha: defaults.strokeAlpha,\n color: defaults.strokeColor,\n width: defaults.strokeWidth,\n },\n };\n return {\n resolve() {\n return instruction;\n },\n };\n}\nfunction getStateDefaults(state) {\n if (state === DetectionInteractionState.Selected) {\n return {\n fillAlpha: DEFAULT_SELECTED_FILL_ALPHA,\n fillColor: DEFAULT_SELECTED_FILL_COLOR,\n strokeAlpha: DEFAULT_SELECTED_STROKE_ALPHA,\n strokeColor: DEFAULT_SELECTED_STROKE_COLOR,\n strokeWidth: DEFAULT_SELECTED_STROKE_WIDTH,\n };\n }\n return {\n fillAlpha: DEFAULT_HOVER_FILL_ALPHA,\n fillColor: DEFAULT_HOVER_FILL_COLOR,\n strokeAlpha: DEFAULT_HOVER_STROKE_ALPHA,\n strokeColor: DEFAULT_HOVER_STROKE_COLOR,\n strokeWidth: DEFAULT_HOVER_STROKE_WIDTH,\n };\n}\n\nvar LabelPlacement;\n(function (LabelPlacement) {\n LabelPlacement[\"Top\"] = \"top\";\n LabelPlacement[\"Bottom\"] = \"bottom\";\n LabelPlacement[\"InsideTop\"] = \"insideTop\";\n LabelPlacement[\"InsideBottom\"] = \"insideBottom\";\n LabelPlacement[\"Center\"] = \"center\";\n})(LabelPlacement || (LabelPlacement = {}));\nvar LabelVisibilityMode;\n(function (LabelVisibilityMode) {\n LabelVisibilityMode[\"Always\"] = \"always\";\n LabelVisibilityMode[\"HoveredOnly\"] = \"hoveredOnly\";\n})(LabelVisibilityMode || (LabelVisibilityMode = {}));\n\n/** Returns black or white for readable text over an RGB background. */\nfunction resolveContrastTextColor(color) {\n \"worklet\";\n const red = (color >> 16) & 0xff;\n const green = (color >> 8) & 0xff;\n const blue = color & 0xff;\n const luminance = (red * 299 + green * 587 + blue * 114) / 1000;\n return luminance >= 150 ? 0x111111 : 0xffffff;\n}\nfunction lightenColor(color, amount = 0.14) {\n \"worklet\";\n const mix = (value) => Math.round(value + (255 - value) * amount);\n return ((mix((color >> 16) & 0xff) << 16) |\n (mix((color >> 8) & 0xff) << 8) |\n mix(color & 0xff));\n}\n\n/**\n * Default label style.\n *\n * Resolves labels from `className`, `metadata.label`, or a custom text\n * resolver, optionally including confidence.\n */\nclass BaseLabelStyle {\n includeConfidence;\n offsetY;\n options;\n constructor(options = {}) {\n this.options = options;\n this.includeConfidence = options.includeConfidence ?? false;\n this.offsetY = options.offsetY ?? 0;\n }\n resolve(detection, context) {\n if (!getDetectionRect(detection) ||\n context.hidden ||\n (this.options.visibilityMode === LabelVisibilityMode.HoveredOnly &&\n !context.hovered) ||\n this.options.shouldRender?.(detection, context) === false) {\n return undefined;\n }\n const text = resolveStyleValue(this.options.text, detection, context) ??\n formatDetectionLabel(detection, this.includeConfidence);\n if (!text) {\n return undefined;\n }\n const background = this.resolveBackground(detection, context);\n return {\n background,\n ...this.resolveOffset(detection, context),\n placement: this.resolvePlacement(detection, context),\n rect: getDetectionRect(detection),\n text,\n textStyle: this.resolveTextStyle(detection, context, background),\n };\n }\n resolveBackground(detection, context) {\n const background = resolveStyleValue(this.options.background, detection, context);\n return {\n alpha: background?.alpha ?? 0.72,\n color: context.hovered\n ? lightenColor(background?.color ?? 0x111827)\n : (background?.color ?? 0x111827),\n cornerRadius: background?.cornerRadius ?? 4,\n paddingX: background?.paddingX ?? 6,\n paddingY: background?.paddingY ?? 3,\n ...(background?.topCornersOnly === undefined\n ? {}\n : { topCornersOnly: background.topCornersOnly }),\n };\n }\n resolveTextStyle(detection, context, background) {\n const textStyle = resolveStyleValue(this.options.textStyle, detection, context);\n return {\n alpha: textStyle?.alpha ?? 1,\n color: textStyle?.color ?? resolveContrastTextColor(background.color),\n fontFamily: textStyle?.fontFamily ?? \"Inter, sans-serif\",\n fontSize: textStyle?.fontSize ?? 13,\n fontWeight: textStyle?.fontWeight ?? \"600\",\n };\n }\n resolveOffset(detection, context) {\n const offset = resolveStyleValue(this.options.offset, detection, context);\n if (offset === null) {\n return {};\n }\n return {\n ...(offset?.x === undefined ? {} : { offsetX: offset.x }),\n offsetY: offset?.y ?? this.offsetY,\n };\n }\n resolvePlacement(detection, context) {\n return (resolveStyleValue(this.options.placement, detection, context) ??\n LabelPlacement.Top);\n }\n}\nfunction formatDetectionLabel(detection, includeConfidence) {\n const label = detection.className ??\n (typeof detection.metadata?.label === \"string\"\n ? detection.metadata.label\n : undefined);\n if (!label) {\n return undefined;\n }\n if (!includeConfidence || detection.confidence === undefined) {\n return label;\n }\n return `${label} ${Math.round(detection.confidence * 100)}%`;\n}\n\nvar MaskRenderMode;\n(function (MaskRenderMode) {\n MaskRenderMode[\"FillAndStroke\"] = \"fillAndStroke\";\n MaskRenderMode[\"FillOnly\"] = \"fillOnly\";\n MaskRenderMode[\"StrokeOnly\"] = \"strokeOnly\";\n})(MaskRenderMode || (MaskRenderMode = {}));\n\n/**\n * Default compressed-RLE mask style.\n *\n * Static color/stroke options can reuse prepared mask artifacts by key. Dynamic\n * color or stroke resolvers intentionally do not expose an artifact key, so new\n * style objects rebuild prepared palettes instead of reusing stale ones.\n */\nclass BaseMaskStyle {\n artifactKey;\n opacity;\n options;\n constructor(options = {}) {\n this.options = options;\n this.opacity = clampOpacity(options.opacity ?? options.alpha ?? 0.35);\n this.artifactKey = createArtifactKey(options);\n }\n resolve(detection, context) {\n if (!detection.mask ||\n context.hidden ||\n this.options.shouldRender?.(detection, context) === false) {\n return undefined;\n }\n const color = resolveStyleValue(this.options.color, detection, context) ??\n DEFAULT_MASK_COLOR;\n const mode = resolveStyleValue(this.options.mode, detection, context) ??\n MaskRenderMode.FillAndStroke;\n const stroke = resolveStroke(resolveStyleValue(this.options.stroke, detection, context), color, mode);\n return {\n alpha: mode === MaskRenderMode.StrokeOnly\n ? 0\n : clampOpacity(resolveStyleValue(this.options.fillAlpha, detection, context) ??\n 1),\n color,\n mask: detection.mask,\n stroke: mode === MaskRenderMode.FillOnly ? undefined : stroke,\n };\n }\n}\nconst DEFAULT_MASK_COLOR = 0x00ff66;\nconst DEFAULT_MASK_STROKE_ALPHA = 1;\nconst DEFAULT_MASK_STROKE_WIDTH = 1;\nfunction clampOpacity(opacity) {\n return Number.isFinite(opacity) ? Math.max(0, Math.min(opacity, 1)) : 1;\n}\nfunction serializeStroke(stroke) {\n if (!stroke) {\n return \"none\";\n }\n return `${stroke.color}:${stroke.alpha}:${stroke.width}`;\n}\nfunction normalizeStroke(stroke, fallbackColor) {\n if (!stroke) {\n return undefined;\n }\n return {\n alpha: stroke.alpha ?? DEFAULT_MASK_STROKE_ALPHA,\n color: stroke.color ?? fallbackColor,\n width: stroke.width ?? DEFAULT_MASK_STROKE_WIDTH,\n };\n}\nfunction resolveStroke(stroke, fallbackColor, mode) {\n if (mode === MaskRenderMode.FillOnly) {\n return undefined;\n }\n if (mode === MaskRenderMode.StrokeOnly && stroke === undefined) {\n return normalizeStroke({}, fallbackColor);\n }\n return normalizeStroke(stroke, fallbackColor);\n}\nfunction createArtifactKey(options) {\n if (typeof options.color === \"function\" ||\n typeof options.fillAlpha === \"function\" ||\n typeof options.mode === \"function\" ||\n typeof options.stroke === \"function\") {\n return undefined;\n }\n const color = options.color ?? DEFAULT_MASK_COLOR;\n const fillAlpha = options.fillAlpha ?? 1;\n const mode = options.mode ?? MaskRenderMode.FillAndStroke;\n return `base:${color}:${fillAlpha}:${mode}:${serializeStroke(resolveStroke(options.stroke, color, mode))}`;\n}\n\n/**\n * Discriminator for renderer-neutral shape draw instructions.\n */\nvar ShapeInstructionKind;\n(function (ShapeInstructionKind) {\n ShapeInstructionKind[\"Ellipse\"] = \"ellipse\";\n ShapeInstructionKind[\"Marker\"] = \"marker\";\n ShapeInstructionKind[\"Path\"] = \"path\";\n})(ShapeInstructionKind || (ShapeInstructionKind = {}));\nvar MarkerShape;\n(function (MarkerShape) {\n MarkerShape[\"Circle\"] = \"circle\";\n MarkerShape[\"Cross\"] = \"cross\";\n MarkerShape[\"Square\"] = \"square\";\n MarkerShape[\"Triangle\"] = \"triangle\";\n})(MarkerShape || (MarkerShape = {}));\n/**\n * Coordinate space for marker sizing. Media sizes scale with the viewport;\n * screen sizes stay constant on screen, matching stroke-width semantics.\n */\nvar MarkerSizeSpace;\n(function (MarkerSizeSpace) {\n MarkerSizeSpace[\"Media\"] = \"media\";\n MarkerSizeSpace[\"Screen\"] = \"screen\";\n})(MarkerSizeSpace || (MarkerSizeSpace = {}));\n\nconst DEFAULT_FILL = { alpha: 1, color: 0x00ff66 };\nconst DEFAULT_STROKE = { alpha: 1, color: 0xffffff, width: 1 };\nconst DEFAULT_SIZE = 12;\n/**\n * Configurable marker renderer style anchored to semantic detection geometry.\n *\n * Use `center` for any domain-specific anchor (keypoint, centroid, or custom\n * metadata). Without it, the marker lands at the detection rectangle center.\n */\nclass BaseMarkerStyle {\n options;\n constructor(options = {}) {\n this.options = options;\n }\n resolve(detection, context) {\n if (context.hidden ||\n this.options.shouldRender?.(detection, context) === false) {\n return undefined;\n }\n const center = resolveStyleValue(this.options.center, detection, context) ??\n (detection.rect\n ? { x: detection.rect.x, y: detection.rect.y }\n : undefined);\n const shape = resolveStyleValue(this.options.shape, detection, context) ??\n MarkerShape.Circle;\n const size = resolveStyleValue(this.options.size, detection, context) ?? DEFAULT_SIZE;\n const sizeSpace = resolveStyleValue(this.options.sizeSpace, detection, context) ??\n MarkerSizeSpace.Screen;\n const rotation = resolveStyleValue(this.options.rotation, detection, context);\n const stroke = resolveStyleValue(this.options.stroke, detection, context);\n if (!center || !Number.isFinite(size) || size <= 0) {\n return undefined;\n }\n if (shape === MarkerShape.Cross) {\n if (stroke === null) {\n return undefined;\n }\n const resolvedStroke = resolveStrokeStyle(stroke, DEFAULT_STROKE);\n const openStroke = {\n alpha: resolvedStroke.alpha,\n cap: resolvedStroke.cap,\n color: resolvedStroke.color,\n dash: resolvedStroke.dash,\n join: resolvedStroke.join,\n miterLimit: resolvedStroke.miterLimit,\n width: resolvedStroke.width,\n };\n return {\n center,\n shape,\n size,\n sizeSpace,\n ...(rotation === undefined ? {} : { rotation }),\n stroke: openStroke,\n };\n }\n const fill = resolveStyleValue(this.options.fill, detection, context);\n if (fill === null && stroke === null) {\n return undefined;\n }\n return {\n center,\n shape,\n size,\n sizeSpace,\n ...(rotation === undefined ? {} : { rotation }),\n ...(fill === null\n ? {}\n : {\n fill: {\n alpha: fill?.alpha ?? DEFAULT_FILL.alpha,\n color: fill?.color ?? DEFAULT_FILL.color,\n },\n }),\n ...(stroke === null\n ? {}\n : stroke === undefined\n ? { stroke: DEFAULT_STROKE }\n : { stroke: resolveStrokeStyle(stroke, DEFAULT_STROKE) }),\n };\n }\n}\n\nclass BasePolygonStyle {\n options;\n constructor(options = {}) {\n this.options = options;\n }\n resolve(detection, context) {\n if (!detection.polygon ||\n context.hidden ||\n detection.polygon.points.length < 3 ||\n this.options.shouldRender?.(detection, context) === false) {\n return undefined;\n }\n const fill = resolveStyleValue(this.options.fill, detection, context);\n const stroke = resolveStyleValue(this.options.stroke, detection, context);\n return {\n points: detection.polygon.points,\n ...(fill === null\n ? {}\n : {\n fill: {\n alpha: fill?.alpha ?? 0.16,\n color: fill?.color ?? 0x00ff66,\n },\n }),\n ...(stroke === null\n ? {}\n : {\n stroke: resolveStrokeStyle(stroke, {\n alpha: 1,\n color: 0x00ff66,\n width: 2,\n }),\n }),\n };\n }\n}\n\nclass BasePolylineStyle {\n options;\n constructor(options = {}) {\n this.options = options;\n }\n resolve(detection, context) {\n if (!detection.polyline ||\n context.hidden ||\n detection.polyline.points.length < 2 ||\n this.options.shouldRender?.(detection, context) === false) {\n return undefined;\n }\n const stroke = resolveStyleValue(this.options.stroke, detection, context);\n if (stroke === null) {\n return undefined;\n }\n const shadowStroke = resolveStyleValue(this.options.shadowStroke, detection, context);\n return {\n points: detection.polyline.points,\n ...(shadowStroke === undefined || shadowStroke === null\n ? {}\n : {\n shadowStroke: resolveStrokeStyle(shadowStroke, {\n alpha: 0.65,\n color: 0x000000,\n width: 4,\n }),\n }),\n stroke: resolveStrokeStyle(stroke, {\n alpha: 1,\n color: 0x00ff66,\n width: 2,\n }),\n };\n }\n}\n\nvar KeypointMarkerShape;\n(function (KeypointMarkerShape) {\n KeypointMarkerShape[\"Circle\"] = \"circle\";\n KeypointMarkerShape[\"Cross\"] = \"cross\";\n})(KeypointMarkerShape || (KeypointMarkerShape = {}));\n\nclass BaseKeypointStyle {\n options;\n constructor(options = {}) {\n this.options = options;\n }\n resolve(detection, context) {\n const geometry = detection.keypoints;\n if (!geometry ||\n context.hidden ||\n this.options.shouldRender?.(detection, context) === false) {\n return undefined;\n }\n const definition = detection.className\n ? this.options.definitions?.[detection.className]\n : undefined;\n const markerFill = resolveStyleValue(this.options.markerFill, detection, context);\n const markerStroke = resolveStyleValue(this.options.markerStroke, detection, context);\n const edgeStroke = resolveStyleValue(this.options.edgeStroke, detection, context);\n const shadowStroke = resolveStyleValue(this.options.edgeShadowStroke, detection, context);\n const radius = resolveStyleValue(this.options.radius, detection, context) ?? 6;\n const edges = geometry.edges.map(([fromIndex, toIndex], edgeIndex) => ({\n from: geometry.points[fromIndex],\n to: geometry.points[toIndex],\n stroke: resolveStrokeStyle(definition?.edges[edgeIndex]?.color === undefined\n ? edgeStroke\n : { ...edgeStroke, color: definition.edges[edgeIndex].color }, {\n alpha: 1,\n color: 0x00ff66,\n width: 2,\n }),\n ...(shadowStroke === null\n ? {}\n : {\n shadowStroke: resolveStrokeStyle(shadowStroke, {\n alpha: 0.65,\n color: 0x000000,\n width: 4,\n }),\n }),\n }));\n const markers = geometry.points.flatMap((point, index) => {\n const visibility = geometry.visibility?.[index] ?? KeypointVisibility.Visible;\n if (visibility === KeypointVisibility.NotLabeled) {\n return [];\n }\n return [\n {\n fill: {\n alpha: markerFill?.alpha ?? 1,\n color: definition?.vertices[index]?.color ??\n markerFill?.color ??\n 0x00ff66,\n },\n index,\n point,\n radius,\n shape: visibility === KeypointVisibility.Occluded\n ? KeypointMarkerShape.Cross\n : KeypointMarkerShape.Circle,\n stroke: resolveStrokeStyle(markerStroke, {\n alpha: 1,\n color: 0xffffff,\n width: 2,\n }),\n },\n ];\n });\n return { edges, markers };\n }\n}\n\nconst SUPERVISION_ROBOFLOW_COLOR = 0xa351fb;\nconst DEFAULT_DETECTION_COLOR_SEQUENCE = [\n createClassColorStyle(0x38bdf8, 0x164e63, 0xecfeff, 0x7dd3fc),\n createClassColorStyle(0x22c55e, 0x14532d, 0xf0fdf4, 0x86efac),\n createClassColorStyle(0xa78bfa, 0x4c1d95, 0xf5f3ff, 0xc4b5fd),\n createClassColorStyle(0xfacc15, 0x713f12, 0xfffbeb, 0xfde047),\n createClassColorStyle(0xf97316, 0x7c2d12, 0xfff7ed, 0xffa23a),\n createClassColorStyle(0xf472b6, 0x831843, 0xfdf2f8, 0xf9a8d4),\n createClassColorStyle(0x60a5fa, 0x1e3a8a, 0xeff6ff, 0x93c5fd),\n createClassColorStyle(0xfb7185, 0x881337, 0xfff1f2, 0xfda4af),\n createClassColorStyle(0x34d399, 0x064e3b, 0xecfdf5, 0x6ee7b7),\n createClassColorStyle(0xe879f9, 0x701a75, 0xfdf4ff, 0xf0abfc),\n];\nconst DEFAULT_DETECTION_CLASS_STYLES = {\n basketball: DEFAULT_DETECTION_COLOR_SEQUENCE[4],\n bed: DEFAULT_DETECTION_COLOR_SEQUENCE[5],\n bottle: DEFAULT_DETECTION_COLOR_SEQUENCE[4],\n \"cell phone\": DEFAULT_DETECTION_COLOR_SEQUENCE[8],\n cow: DEFAULT_DETECTION_COLOR_SEQUENCE[2],\n cup: DEFAULT_DETECTION_COLOR_SEQUENCE[3],\n horse: DEFAULT_DETECTION_COLOR_SEQUENCE[0],\n keyboard: DEFAULT_DETECTION_COLOR_SEQUENCE[1],\n knife: DEFAULT_DETECTION_COLOR_SEQUENCE[7],\n laptop: DEFAULT_DETECTION_COLOR_SEQUENCE[6],\n person: DEFAULT_DETECTION_COLOR_SEQUENCE[1],\n \"potted plant\": DEFAULT_DETECTION_COLOR_SEQUENCE[8],\n \"sports ball\": DEFAULT_DETECTION_COLOR_SEQUENCE[4],\n tv: DEFAULT_DETECTION_COLOR_SEQUENCE[2],\n \"white team player\": createClassColorStyle(0xf8fafc, 0x334155, 0xffffff, 0xffffff),\n \"yellow team player\": DEFAULT_DETECTION_COLOR_SEQUENCE[3],\n};\n// The \"worklet\" directives below are inert everywhere except React Native:\n// browsers and Node see a no-op string, while React Native's worklets Babel\n// plugin makes these functions callable inside frame worklets, so every\n// platform resolves detection colors through this one implementation.\n// Worklet constraint on ordering: the plugin turns marked declarations into\n// non-hoisted assignments that capture each other at module-init time, so\n// helpers must be defined before the functions that call them.\nfunction normalizeDetectionClassName(className) {\n \"worklet\";\n return (className ?? \"\")\n .trim()\n .toLowerCase()\n .replace(/[_-]/g, \" \")\n .replace(/\\s+/g, \" \");\n}\nfunction hashClassName(className) {\n \"worklet\";\n let hash = 0;\n for (let index = 0; index < className.length; index += 1) {\n hash = (hash * 31 + className.charCodeAt(index)) >>> 0;\n }\n return hash;\n}\nfunction resolveDetectionClassColorStyle(className) {\n \"worklet\";\n const normalizedClassName = normalizeDetectionClassName(className);\n const knownStyle = DEFAULT_DETECTION_CLASS_STYLES[normalizedClassName];\n if (knownStyle) {\n return knownStyle;\n }\n return DEFAULT_DETECTION_COLOR_SEQUENCE[hashClassName(normalizedClassName) % DEFAULT_DETECTION_COLOR_SEQUENCE.length];\n}\nfunction createClassColorStyle(fill, labelBackground, labelText, stroke) {\n return {\n fill,\n labelBackground,\n labelText,\n stroke,\n };\n}\n\nconst DEFAULT_BOX_CORNER_RADIUS = 1;\nconst DEFAULT_ELLIPSE_AXIS_RATIO = 0.35;\nconst DEFAULT_ELLIPSE_START_ANGLE = (-45 * Math.PI) / 180;\nconst DEFAULT_ELLIPSE_END_ANGLE = (235 * Math.PI) / 180;\nconst DEFAULT_OUTLINE_WIDTH = 2;\nconst DEFAULT_FILL_ALPHA = 0.08;\nconst DEFAULT_MASK_FILL_ALPHA = 0.45;\nconst DEFAULT_KEYPOINT_EDGE_WIDTH = 1.5;\nconst DEFAULT_KEYPOINT_SHADOW_WIDTH = 3;\nconst DEFAULT_KEYPOINT_SHADOW_ALPHA = 0.25;\nconst DEFAULT_KEYPOINT_RADIUS = 3.5;\nconst DEFAULT_POLYLINE_SHADOW_WIDTH = DEFAULT_OUTLINE_WIDTH + 1;\nconst DEFAULT_POLYLINE_SHADOW_ALPHA = 0.55;\nconst DEFAULT_LABEL_CORNER_RADIUS = 4;\nconst DEFAULT_LABEL_PADDING_X = 6;\nconst DEFAULT_LABEL_PADDING_Y = 3;\nconst DEFAULT_LABEL_FONT_SIZE = 12;\nconst DEFAULT_LABEL_FONT_FAMILY = \"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace\";\n/**\n * Builds the canonical, unselected annotation presentation used by Roboflow's\n * Core annotation editor. It deliberately excludes host-specific interaction,\n * editing, focus, and theme behaviour so consumers can compose those layers.\n */\nfunction createDefaultAnnotationPresentation(options = {}) {\n return {\n boxStyle: createDefaultBoxStyle(options),\n keypointStyle: createDefaultKeypointStyle(options),\n labelStyle: createDefaultLabelStyle(options),\n maskStyle: createDefaultMaskStyle(options),\n polygonStyle: createDefaultPolygonStyle(options),\n polylineStyle: createDefaultPolylineStyle(options),\n };\n}\n/**\n * The canonical default style of one built-in layer.\n *\n * These exist so a renderer kind can build only the style it needs instead of\n * the whole default presentation. Keep them equivalent to the matching field of\n * {@link createDefaultAnnotationPresentation}.\n */\nfunction createDefaultBoxStyle(options = {}) {\n const getClassColor = createClassColorResolver(options);\n return new BaseBoxStyle({\n cornerRadius: DEFAULT_BOX_CORNER_RADIUS,\n fill: (detection) => ({\n alpha: DEFAULT_FILL_ALPHA,\n color: getClassColor(detection),\n }),\n shape: BoxShape.RoundedRect,\n shouldRender: (detection) => !(detection.mask ||\n detection.polygon ||\n detection.polyline ||\n detection.keypoints),\n stroke: (detection) => ({\n alpha: 1,\n color: getClassColor(detection),\n width: DEFAULT_OUTLINE_WIDTH,\n }),\n });\n}\n/** Canonical opt-in BoxCornerAnnotator-style presentation. */\nfunction createDefaultBoxCornerStyle(options = {}) {\n const getClassColor = createClassColorResolver(options);\n return new BaseBoxCornerStyle({\n stroke: (detection) => ({ color: getClassColor(detection) }),\n });\n}\n/**\n * Matches the Python Supervision EllipseAnnotator: an elliptical footprint\n * arc under the detection box, swept from -45deg to 235deg, in the class\n * color.\n */\nfunction createDefaultEllipseStyle(options = {}) {\n const getClassColor = createClassColorResolver(options);\n return {\n resolve(detection, context) {\n if (!detection.rect || context.hidden) {\n return undefined;\n }\n const radiusX = detection.rect.width / 2;\n const radiusY = radiusX * DEFAULT_ELLIPSE_AXIS_RATIO;\n return {\n center: {\n x: detection.rect.x,\n // Bottom-tangent so the arc hugs the detection instead of dipping\n // below its feet.\n y: detection.rect.y + detection.rect.height / 2 - radiusY,\n },\n endAngle: DEFAULT_ELLIPSE_END_ANGLE,\n radiusX,\n radiusY,\n startAngle: DEFAULT_ELLIPSE_START_ANGLE,\n stroke: {\n alpha: 1,\n color: getClassColor(detection),\n width: DEFAULT_OUTLINE_WIDTH,\n },\n };\n },\n };\n}\nfunction createDefaultKeypointStyle(options = {}) {\n const getClassColor = createClassColorResolver(options);\n return new BaseKeypointStyle({\n ...(options.skeletonDefinitions === undefined\n ? {}\n : { definitions: options.skeletonDefinitions }),\n edgeShadowStroke: {\n alpha: DEFAULT_KEYPOINT_SHADOW_ALPHA,\n color: 0x000000,\n width: DEFAULT_KEYPOINT_SHADOW_WIDTH,\n },\n edgeStroke: (detection) => ({\n alpha: 1,\n color: getClassColor(detection),\n width: DEFAULT_KEYPOINT_EDGE_WIDTH,\n }),\n markerFill: (detection) => ({\n alpha: 1,\n color: getClassColor(detection),\n }),\n markerStroke: { alpha: 1, color: 0xffffff, width: 1 },\n radius: DEFAULT_KEYPOINT_RADIUS,\n });\n}\nfunction createDefaultLabelStyle(options = {}) {\n const getClassColor = createClassColorResolver(options);\n return new BaseLabelStyle({\n background: (detection) => ({\n alpha: 1,\n color: getClassColor(detection),\n cornerRadius: DEFAULT_LABEL_CORNER_RADIUS,\n paddingX: DEFAULT_LABEL_PADDING_X,\n paddingY: DEFAULT_LABEL_PADDING_Y,\n topCornersOnly: true,\n }),\n includeConfidence: options.includeConfidence,\n placement: LabelPlacement.Top,\n textStyle: {\n fontFamily: DEFAULT_LABEL_FONT_FAMILY,\n fontSize: DEFAULT_LABEL_FONT_SIZE,\n fontWeight: \"600\",\n },\n });\n}\nfunction createDefaultMaskStyle(options = {}) {\n const getClassColor = createClassColorResolver(options);\n return new BaseMaskStyle({\n color: (detection) => getClassColor(detection),\n fillAlpha: DEFAULT_MASK_FILL_ALPHA,\n mode: MaskRenderMode.FillAndStroke,\n opacity: 1,\n stroke: (detection) => ({\n alpha: 1,\n color: getClassColor(detection),\n width: DEFAULT_OUTLINE_WIDTH,\n }),\n });\n}\n/** Canonical opt-in marker presentation. */\nfunction createDefaultMarkerStyle() {\n return new BaseMarkerStyle();\n}\nconst DEFAULT_MASK_HALO_ALPHA = 0.6;\nconst DEFAULT_MASK_HALO_SPREAD = 12;\n/**\n * Canonical mask halo: a class-colored glow that follows the mask silhouette.\n * Used when the `maskHalo` renderer is listed without an explicit style.\n */\nfunction createDefaultMaskHaloStyle(options = {}) {\n const getClassColor = createClassColorResolver(options);\n return {\n resolve(detection, context) {\n if (!detection.mask || context.hidden) {\n return undefined;\n }\n return {\n alpha: DEFAULT_MASK_HALO_ALPHA,\n color: getClassColor(detection),\n spread: DEFAULT_MASK_HALO_SPREAD,\n };\n },\n };\n}\nfunction createDefaultPolygonStyle(options = {}) {\n const getClassColor = createClassColorResolver(options);\n return new BasePolygonStyle({\n fill: (detection) => ({\n alpha: DEFAULT_FILL_ALPHA,\n color: getClassColor(detection),\n }),\n stroke: (detection) => ({\n alpha: 1,\n color: getClassColor(detection),\n width: DEFAULT_OUTLINE_WIDTH,\n }),\n });\n}\nfunction createDefaultPolylineStyle(options = {}) {\n const getClassColor = createClassColorResolver(options);\n return new BasePolylineStyle({\n shadowStroke: {\n alpha: DEFAULT_POLYLINE_SHADOW_ALPHA,\n color: 0x000000,\n width: DEFAULT_POLYLINE_SHADOW_WIDTH,\n },\n stroke: (detection) => ({\n alpha: 1,\n color: getClassColor(detection),\n width: DEFAULT_OUTLINE_WIDTH,\n }),\n });\n}\nfunction createClassColorResolver(options) {\n return (detection) => resolveDefaultClassColor(detection, options.getClassColor);\n}\nfunction resolveDefaultClassColor(detection, getClassColor) {\n const color = getClassColor?.(detection.className);\n return typeof color === \"number\" && Number.isFinite(color)\n ? color\n : resolveDetectionClassColorStyle(detection.className).fill;\n}\n\n/**\n * The built-in annotation renderer vocabulary.\n *\n * These stay plain string literals so `{ kind: \"box\" }` remains assignable to\n * the public descriptor union from JavaScript and TypeScript alike.\n */\nconst annotationRendererKinds = [\n \"box\",\n \"box-corners\",\n \"ellipse\",\n \"keypoints\",\n \"label\",\n \"mask\",\n \"maskHalo\",\n \"marker\",\n \"polygon\",\n \"polyline\",\n \"region\",\n];\n/** Sources currently supported by a region renderer. */\nconst RegionRendererSourceKind = {\n Asset: \"asset\",\n Media: \"media\",\n};\n/** Media-space regions currently supported by a region renderer. */\nconst RegionRendererRegionKind = {\n Bounds: \"bounds\",\n KeypointAnchor: \"keypoint-anchor\",\n};\n/** Semantic coverage used to clip pixels sampled from the current media. */\nconst RegionRendererCoverageKind = {\n Mask: \"mask\",\n Polygon: \"polygon\",\n};\n/** Filter effects that can be applied to a current-frame media region. */\nconst RegionRendererMediaEffectKind = {\n Blur: \"blur\",\n Pixelate: \"pixelate\",\n};\n/** Coordinate space used by an explicit region-renderer size. */\nconst RegionRendererSizeSpace = {\n Media: \"media\",\n Screen: \"screen\",\n};\n/** Composition modes currently supported by a region renderer. */\nconst RegionRendererComposeMode = {\n Over: \"over\",\n};\n/**\n * Creates built-in annotation renderer descriptors for\n * `MediaRendererPresentation.renderers`.\n *\n * The currently supported renderers retain their established scene ordering:\n * masks, polygons, vectors, labels. A later renderer kind may add a new\n * composition capability without changing that ordering for existing scenes.\n */\nconst annotationRenderers = {\n box: (options) => createAnnotationRenderer(\"box\", options),\n boxCorners: (options) => createAnnotationRenderer(\"box-corners\", options),\n ellipse: (options) => createAnnotationRenderer(\"ellipse\", options),\n keypoints: (options) => createAnnotationRenderer(\"keypoints\", options),\n label: (options) => createAnnotationRenderer(\"label\", options),\n mask: (options) => createAnnotationRenderer(\"mask\", options),\n maskHalo: (options) => createAnnotationRenderer(\"maskHalo\", options),\n marker: (options) => createAnnotationRenderer(\"marker\", options),\n polygon: (options) => createAnnotationRenderer(\"polygon\", options),\n polyline: (options) => createAnnotationRenderer(\"polyline\", options),\n region: (options) => ({ kind: \"region\", ...options }),\n};\nfunction createAnnotationRenderer(kind, options) {\n // Every built-in renderer keeps its kind as its stable id. TypeScript cannot\n // narrow the descriptor union through the generic kind parameter, so the\n // descriptor shape is asserted here and covered by a focused test.\n return { id: kind, kind, ...options };\n}\n\nconst annotationRendererRegistry = {\n box: {\n cardinality: \"singleton\",\n createCanonicalStyle: createDefaultBoxStyle,\n styleField: \"boxStyle\",\n },\n \"box-corners\": {\n cardinality: \"singleton\",\n createCanonicalStyle: createDefaultBoxCornerStyle,\n styleField: \"boxCornerStyle\",\n },\n ellipse: {\n cardinality: \"singleton\",\n createCanonicalStyle: createDefaultEllipseStyle,\n styleField: \"ellipseStyle\",\n },\n keypoints: {\n cardinality: \"singleton\",\n createCanonicalStyle: createDefaultKeypointStyle,\n styleField: \"keypointStyle\",\n },\n label: {\n cardinality: \"singleton\",\n createCanonicalStyle: createDefaultLabelStyle,\n styleField: \"labelStyle\",\n },\n mask: {\n cardinality: \"singleton\",\n createCanonicalStyle: createDefaultMaskStyle,\n styleField: \"maskStyle\",\n },\n maskHalo: {\n cardinality: \"singleton\",\n createCanonicalStyle: () => createDefaultMaskHaloStyle(),\n styleField: \"maskHaloStyle\",\n },\n marker: {\n cardinality: \"singleton\",\n createCanonicalStyle: createDefaultMarkerStyle,\n styleField: \"markerStyle\",\n },\n polygon: {\n cardinality: \"singleton\",\n createCanonicalStyle: createDefaultPolygonStyle,\n styleField: \"polygonStyle\",\n },\n polyline: {\n cardinality: \"singleton\",\n createCanonicalStyle: createDefaultPolylineStyle,\n styleField: \"polylineStyle\",\n },\n region: { cardinality: \"multiple\" },\n};\nfunction isStyleBackedAnnotationRendererKind(kind) {\n return annotationRendererRegistry[kind].cardinality === \"singleton\";\n}\n/** Presentation fields owned by the given renderer kinds, in the same order. */\nfunction resolveAnnotationRendererStyleFields(kinds) {\n return kinds.flatMap((kind) => {\n const metadata = annotationRendererRegistry[kind];\n return \"styleField\" in metadata ? [metadata.styleField] : [];\n });\n}\n\n/**\n * Resolves built-in renderer descriptors into the existing specialized style\n * fields. The browser backend deliberately keeps ownership of its box, mask,\n * label, polygon, polyline, and keypoint pipelines; this normalizer only\n * supplies those pipelines with their configured style.\n */\nfunction resolveAnnotationRendererPresentation(presentation) {\n const renderers = presentation.renderers;\n if (renderers === undefined) {\n return presentation;\n }\n const resolved = {\n boxStyle: null,\n boxCornerStyle: null,\n ellipseStyle: null,\n keypointStyle: null,\n labelStyle: null,\n maskHaloStyle: null,\n maskStyle: null,\n markerStyle: null,\n polygonStyle: null,\n polylineStyle: null,\n };\n const rendererIds = new Set();\n const rendererKinds = new Set();\n for (const renderer of renderers) {\n if (rendererIds.has(renderer.id)) {\n throw new RangeError(`MediaRendererPresentation.renderers contains duplicate renderer id \"${renderer.id}\".`);\n }\n rendererIds.add(renderer.id);\n if (annotationRendererRegistry[renderer.kind].cardinality === \"singleton\" &&\n rendererKinds.has(renderer.kind)) {\n throw new RangeError(`MediaRendererPresentation.renderers contains duplicate renderer kind \"${renderer.kind}\".`);\n }\n rendererKinds.add(renderer.kind);\n if (isStyleBackedAnnotationRenderer(renderer)) {\n applyRendererStyle(resolved, presentation, renderer);\n }\n }\n return {\n ...presentation,\n ...resolved,\n };\n}\nfunction isStyleBackedAnnotationRenderer(renderer) {\n return isStyleBackedAnnotationRendererKind(renderer.kind);\n}\nfunction applyRendererStyle(resolved, configured, renderer) {\n const { createCanonicalStyle, styleField } = annotationRendererRegistry[renderer.kind];\n const configuredStyle = configured[styleField];\n const style = renderer.style !== undefined\n ? renderer.style\n : configuredStyle !== undefined\n ? configuredStyle\n : createCanonicalStyle();\n // The registry pairs each kind with the presentation field holding the same\n // style contract, but TypeScript cannot correlate that pairing across a\n // lookup on a union, so the write is asserted once here.\n resolved[styleField] = style;\n}\n\nfunction createSourceAwarePresentation(globalPresentation = {}, sources, options = {}) {\n if (!sources.some((source) => source.presentation !== undefined)) {\n return globalPresentation;\n }\n const sourcePresentations = new Map(sources.map((source) => [source.id, source.presentation]));\n const enabledStyleFields = options.enabledRendererKinds === undefined\n ? undefined\n : new Set(resolveAnnotationRendererStyleFields(options.enabledRendererKinds));\n const shouldApplySourceStyle = (style) => hasSourceStyle(sources, style) &&\n (enabledStyleFields === undefined || enabledStyleFields.has(style));\n return {\n ...globalPresentation,\n boxStyle: shouldApplySourceStyle(\"boxStyle\")\n ? new SourceAwareBoxStyle(normalizeGlobalBoxStyle(globalPresentation.boxStyle), sourcePresentations)\n : globalPresentation.boxStyle,\n boxCornerStyle: shouldApplySourceStyle(\"boxCornerStyle\")\n ? new SourceAwareBoxCornerStyle(globalPresentation.boxCornerStyle ?? null, sourcePresentations)\n : globalPresentation.boxCornerStyle,\n ellipseStyle: shouldApplySourceStyle(\"ellipseStyle\")\n ? new SourceAwareEllipseStyle(globalPresentation.ellipseStyle ?? null, sourcePresentations)\n : globalPresentation.ellipseStyle,\n labelStyle: shouldApplySourceStyle(\"labelStyle\")\n ? new SourceAwareLabelStyle(normalizeGlobalLabelStyle(globalPresentation.labelStyle), sourcePresentations)\n : globalPresentation.labelStyle,\n maskStyle: shouldApplySourceStyle(\"maskStyle\")\n ? new SourceAwareMaskStyle(normalizeGlobalMaskStyle(globalPresentation.maskStyle), sourcePresentations)\n : globalPresentation.maskStyle,\n markerStyle: shouldApplySourceStyle(\"markerStyle\")\n ? new SourceAwareMarkerStyle(normalizeGlobalMarkerStyle(globalPresentation.markerStyle), sourcePresentations)\n : globalPresentation.markerStyle,\n maskHaloStyle: shouldApplySourceStyle(\"maskHaloStyle\")\n ? new SourceAwareMaskHaloStyle(normalizeGlobalMaskHaloStyle(globalPresentation.maskHaloStyle), sourcePresentations)\n : globalPresentation.maskHaloStyle,\n polygonStyle: shouldApplySourceStyle(\"polygonStyle\")\n ? new SourceAwarePolygonStyle(normalizeGlobalPolygonStyle(globalPresentation.polygonStyle), sourcePresentations)\n : globalPresentation.polygonStyle,\n polylineStyle: shouldApplySourceStyle(\"polylineStyle\")\n ? new SourceAwarePolylineStyle(normalizeGlobalPolylineStyle(globalPresentation.polylineStyle), sourcePresentations)\n : globalPresentation.polylineStyle,\n keypointStyle: shouldApplySourceStyle(\"keypointStyle\")\n ? new SourceAwareKeypointStyle(normalizeGlobalKeypointStyle(globalPresentation.keypointStyle), sourcePresentations)\n : globalPresentation.keypointStyle,\n };\n}\nclass SourceAwareBoxCornerStyle {\n globalStyle;\n sourcePresentations;\n constructor(globalStyle, sourcePresentations) {\n this.globalStyle = globalStyle;\n this.sourcePresentations = sourcePresentations;\n }\n resolve(detection, context) {\n return resolveSourceStyle(detection, this.globalStyle, this.sourcePresentations, \"boxCornerStyle\")?.resolve(detection, context);\n }\n}\nclass SourceAwareBoxStyle {\n globalStyle;\n sourcePresentations;\n constructor(globalStyle, sourcePresentations) {\n this.globalStyle = globalStyle;\n this.sourcePresentations = sourcePresentations;\n }\n resolve(detection, context) {\n const style = resolveSourceStyle(detection, this.globalStyle, this.sourcePresentations, \"boxStyle\");\n return style?.resolve(detection, context);\n }\n}\nclass SourceAwareEllipseStyle {\n globalStyle;\n sourcePresentations;\n constructor(globalStyle, sourcePresentations) {\n this.globalStyle = globalStyle;\n this.sourcePresentations = sourcePresentations;\n }\n resolve(detection, context) {\n const style = resolveSourceStyle(detection, this.globalStyle, this.sourcePresentations, \"ellipseStyle\");\n return style?.resolve(detection, context);\n }\n}\nclass SourceAwareLabelStyle {\n globalStyle;\n sourcePresentations;\n constructor(globalStyle, sourcePresentations) {\n this.globalStyle = globalStyle;\n this.sourcePresentations = sourcePresentations;\n }\n resolve(detection, context) {\n const style = resolveSourceStyle(detection, this.globalStyle, this.sourcePresentations, \"labelStyle\");\n return style?.resolve(detection, context);\n }\n}\nclass SourceAwareMaskStyle {\n globalStyle;\n sourcePresentations;\n artifactKey = undefined;\n opacity;\n constructor(globalStyle, sourcePresentations) {\n this.globalStyle = globalStyle;\n this.sourcePresentations = sourcePresentations;\n this.opacity = globalStyle?.opacity;\n }\n resolve(detection, context) {\n const style = resolveSourceStyle(detection, this.globalStyle, this.sourcePresentations, \"maskStyle\");\n return style?.resolve(detection, context);\n }\n}\nclass SourceAwareMaskHaloStyle {\n globalStyle;\n sourcePresentations;\n constructor(globalStyle, sourcePresentations) {\n this.globalStyle = globalStyle;\n this.sourcePresentations = sourcePresentations;\n }\n resolve(detection, context) {\n const style = resolveSourceStyle(detection, this.globalStyle, this.sourcePresentations, \"maskHaloStyle\");\n return style?.resolve(detection, context);\n }\n}\nclass SourceAwareMarkerStyle {\n globalStyle;\n sourcePresentations;\n constructor(globalStyle, sourcePresentations) {\n this.globalStyle = globalStyle;\n this.sourcePresentations = sourcePresentations;\n }\n resolve(detection, context) {\n const style = resolveSourceStyle(detection, this.globalStyle, this.sourcePresentations, \"markerStyle\");\n return style?.resolve(detection, context);\n }\n}\nclass SourceAwarePolygonStyle {\n globalStyle;\n sourcePresentations;\n constructor(globalStyle, sourcePresentations) {\n this.globalStyle = globalStyle;\n this.sourcePresentations = sourcePresentations;\n }\n resolve(detection, context) {\n const style = resolveSourceStyle(detection, this.globalStyle, this.sourcePresentations, \"polygonStyle\");\n return style?.resolve(detection, context);\n }\n}\nclass SourceAwarePolylineStyle {\n globalStyle;\n sourcePresentations;\n constructor(globalStyle, sourcePresentations) {\n this.globalStyle = globalStyle;\n this.sourcePresentations = sourcePresentations;\n }\n resolve(detection, context) {\n const style = resolveSourceStyle(detection, this.globalStyle, this.sourcePresentations, \"polylineStyle\");\n return style?.resolve(detection, context);\n }\n}\nclass SourceAwareKeypointStyle {\n globalStyle;\n sourcePresentations;\n constructor(globalStyle, sourcePresentations) {\n this.globalStyle = globalStyle;\n this.sourcePresentations = sourcePresentations;\n }\n resolve(detection, context) {\n const style = resolveSourceStyle(detection, this.globalStyle, this.sourcePresentations, \"keypointStyle\");\n return style?.resolve(detection, context);\n }\n}\nfunction hasSourceStyle(sources, key) {\n return sources.some((source) => source.presentation?.[key] !== undefined);\n}\nfunction resolveSourceStyle(detection, globalStyle, sourcePresentations, key) {\n const sourcePresentation = detection.sourceId\n ? sourcePresentations.get(detection.sourceId)\n : undefined;\n const sourceStyle = sourcePresentation?.[key];\n return sourceStyle === undefined ? globalStyle : sourceStyle;\n}\nfunction normalizeGlobalBoxStyle(style) {\n return style === undefined ? new BaseBoxStyle() : style;\n}\nfunction normalizeGlobalLabelStyle(style) {\n return style === undefined ? new BaseLabelStyle() : style;\n}\nfunction normalizeGlobalMaskStyle(style) {\n return style === undefined ? new BaseMaskStyle() : style;\n}\nfunction normalizeGlobalMaskHaloStyle(style) {\n return style === undefined ? createDefaultMaskHaloStyle() : style;\n}\nfunction normalizeGlobalMarkerStyle(style) {\n return style === undefined ? new BaseMarkerStyle() : style;\n}\nfunction normalizeGlobalPolygonStyle(style) {\n return style === undefined ? new BasePolygonStyle() : style;\n}\nfunction normalizeGlobalPolylineStyle(style) {\n return style === undefined ? new BasePolylineStyle() : style;\n}\nfunction normalizeGlobalKeypointStyle(style) {\n return style === undefined ? new BaseKeypointStyle() : style;\n}\n\nfunction rectToPolygon(rect) {\n const halfWidth = rect.width / 2;\n const halfHeight = rect.height / 2;\n return {\n points: [\n { x: rect.x - halfWidth, y: rect.y - halfHeight },\n { x: rect.x + halfWidth, y: rect.y - halfHeight },\n { x: rect.x + halfWidth, y: rect.y + halfHeight },\n { x: rect.x - halfWidth, y: rect.y + halfHeight },\n ],\n };\n}\nfunction polygonToRect(polygon) {\n return getPointsRect(polygon.points);\n}\nfunction rasterizeRectToMask(rect, dimensions) {\n const data = createEmptyMask(dimensions);\n const topLeft = centerRectToTopLeftRect(rect);\n const left = Math.max(0, Math.round(topLeft.x));\n const top = Math.max(0, Math.round(topLeft.y));\n const right = Math.min(dimensions.width, Math.round(topLeft.x + rect.width));\n const bottom = Math.min(dimensions.height, Math.round(topLeft.y + rect.height));\n for (let y = top; y < bottom; y += 1) {\n for (let x = left; x < right; x += 1) {\n data[y * dimensions.width + x] = 1;\n }\n }\n return data;\n}\nfunction rasterizePolygonToMask(points, dimensions) {\n const data = createEmptyMask(dimensions);\n if (points.length < 3) {\n return data;\n }\n const bounds = centerRectToTopLeftRect(getPointsRect(points));\n const startY = Math.max(0, Math.floor(bounds.y));\n const endY = Math.min(dimensions.height - 1, Math.ceil(bounds.y + bounds.height));\n for (let y = startY; y <= endY; y += 1) {\n const scanY = y + 0.5;\n const intersections = [];\n for (let index = 0; index < points.length; index += 1) {\n const current = points[index];\n const next = points[(index + 1) % points.length];\n if ((current.y <= scanY && next.y > scanY) ||\n (next.y <= scanY && current.y > scanY)) {\n const ratio = (scanY - current.y) / (next.y - current.y);\n intersections.push(current.x + ratio * (next.x - current.x));\n }\n }\n intersections.sort((left, right) => left - right);\n for (let index = 0; index < intersections.length - 1; index += 2) {\n const left = Math.max(0, Math.round(intersections[index]));\n const right = Math.min(dimensions.width, Math.round(intersections[index + 1]));\n for (let x = left; x < right; x += 1) {\n data[y * dimensions.width + x] = 1;\n }\n }\n }\n return data;\n}\nfunction convertDetectionBoxToPolygon(detection) {\n return detection.rect\n ? replaceGeometry(detection, { polygon: rectToPolygon(detection.rect) })\n : detection;\n}\nfunction convertDetectionPolygonToBox(detection) {\n const rect = detection.polygon ? polygonToRect(detection.polygon) : undefined;\n return rect ? replaceGeometry(detection, { rect }) : detection;\n}\nfunction convertDetectionMaskToBox(detection) {\n const rect = detection.mask\n ? computeDetectionMaskRect(detection.mask)\n : undefined;\n return rect ? replaceGeometry(detection, { rect }) : detection;\n}\nfunction convertDetectionMaskToPolygon(detection) {\n if (!detection.mask) {\n return detection;\n }\n const decoded = decodeCompressedRleMask(detection.mask);\n const contour = extractMaskContour(decoded.data, decoded.width, decoded.height);\n const fallbackRect = computeDetectionMaskRect(detection.mask);\n const polygon = contour && contour.length >= 3\n ? { points: contour }\n : fallbackRect\n ? rectToPolygon(fallbackRect)\n : undefined;\n return polygon ? replaceGeometry(detection, { polygon }) : detection;\n}\nfunction convertDetectionBoxToMask(detection, dimensions) {\n if (!detection.rect) {\n return detection;\n }\n const mask = encodeBinaryMask(rasterizeRectToMask(detection.rect, dimensions), dimensions.width, dimensions.height);\n return replaceGeometry(detection, { mask });\n}\nfunction convertDetectionPolygonToMask(detection, dimensions) {\n if (!detection.polygon) {\n return detection;\n }\n const mask = encodeBinaryMask(rasterizePolygonToMask(detection.polygon.points, dimensions), dimensions.width, dimensions.height);\n return replaceGeometry(detection, { mask });\n}\nfunction mergeDetectionMasks(detections) {\n if (detections.length < 2 || detections.some(({ mask }) => !mask)) {\n return null;\n }\n const first = detections[0];\n if (detections.some((detection) => detection.className !== first.className)) {\n return null;\n }\n const masks = detections.map(({ mask }) => decodeCompressedRleMask(mask));\n const width = masks[0].width;\n const height = masks[0].height;\n if (masks.some((mask) => mask.width !== width || mask.height !== height)) {\n throw new Error(\"Merged detection masks must have matching dimensions.\");\n }\n const data = new Uint8Array(width * height);\n for (const mask of masks) {\n for (let index = 0; index < data.length; index += 1) {\n data[index] ||= mask.data[index] ?? 0;\n }\n }\n return replaceGeometry(first, {\n mask: encodeBinaryMask(data, width, height),\n });\n}\n/**\n * Groups polygon detections by class and returns one union bounding-box\n * detection per class. This intentionally converts the output to rectangles.\n */\nfunction mergeDetectionPolygonsByClass(detections, options = {}) {\n const groups = new Map();\n for (const detection of detections) {\n if (!detection.polygon) {\n continue;\n }\n const group = groups.get(detection.className) ?? [];\n group.push(detection);\n groups.set(detection.className, group);\n }\n return [...groups.entries()].flatMap(([className, group], groupIndex) => {\n const points = group.flatMap((detection) => detection.polygon.points);\n const rect = getPointsRect(points);\n if (!rect) {\n return [];\n }\n const first = group[0];\n const id = options.createId?.(className, groupIndex) ?? first.id;\n return [\n replaceGeometry({\n ...first,\n id,\n }, { rect }),\n ];\n });\n}\nfunction createEmptyMask(dimensions) {\n if (!Number.isInteger(dimensions.width) ||\n dimensions.width <= 0 ||\n !Number.isInteger(dimensions.height) ||\n dimensions.height <= 0) {\n throw new Error(\"Media dimensions must be positive integers.\");\n }\n return new Uint8Array(dimensions.width * dimensions.height);\n}\nfunction replaceGeometry(detection, geometry) {\n return {\n ...detection,\n keypoints: undefined,\n mask: geometry.mask,\n polygon: geometry.polygon,\n polyline: undefined,\n rect: geometry.rect,\n };\n}\n\nfunction canReuseMaskStyleArtifacts(previousMaskStyle, nextMaskStyle) {\n if (previousMaskStyle === nextMaskStyle) {\n return true;\n }\n return (previousMaskStyle?.artifactKey !== undefined &&\n nextMaskStyle?.artifactKey !== undefined &&\n previousMaskStyle.artifactKey === nextMaskStyle.artifactKey);\n}\nfunction resolveMaskStyleOpacity(maskStyle) {\n const opacity = maskStyle?.opacity;\n if (opacity === undefined) {\n return 1;\n }\n return Number.isFinite(opacity) ? Math.max(0, Math.min(opacity, 1)) : 1;\n}\n\n/**\n * Palette slots an id raster can name, one of them the background. A GLSL\n * fragment stage is guaranteed only 224 uniform vectors, and a palette entry\n * costs 2.25 of them, so this stays a multiple of four and well inside that\n * budget; past it a frame draws from the RGBA composite instead.\n */\nconst MAX_ID_MASK_PALETTE_ENTRIES = 80;\nconst MAX_ID_MASK_STROKE_WIDTH = 16;\nfunction createIdMaskFrame(instructions, options = {}) {\n if (instructions.length === 0) {\n return undefined;\n }\n const maskWidth = Math.max(...instructions.map(({ mask }) => mask.width));\n const maskHeight = Math.max(...instructions.map(({ mask }) => mask.height));\n const width = resolveRasterWidth(maskWidth, options.maxWidth);\n const height = width === maskWidth\n ? maskHeight\n : Math.max(1, Math.round((width * maskHeight) / maskWidth));\n const strokeScale = width / maskWidth;\n const scaledAxes = width === maskWidth\n ? undefined\n : createScaledMaskAxes({ height, maskHeight, maskWidth, width });\n const data = new Uint8Array(new ArrayBuffer(width * height));\n const fillPalette = new Float32Array(new ArrayBuffer(MAX_ID_MASK_PALETTE_ENTRIES * 4 * 4));\n const strokePalette = new Float32Array(new ArrayBuffer(MAX_ID_MASK_PALETTE_ENTRIES * 4 * 4));\n const strokeWidths = new Float32Array(new ArrayBuffer(MAX_ID_MASK_PALETTE_ENTRIES * 4));\n let hasStroke = false;\n let maxStrokeWidth = 0;\n for (const instruction of instructions) {\n const detectionMaskId = instruction.detectionIndex + 1;\n if (detectionMaskId <= 0 ||\n detectionMaskId >= MAX_ID_MASK_PALETTE_ENTRIES) {\n return undefined;\n }\n writePaletteEntry(fillPalette, detectionMaskId, instruction.color, instruction.alpha);\n if (instruction.stroke && instruction.stroke.width > 0) {\n const strokeWidth = Math.min(resolveStrokeTexels(instruction.stroke.width, strokeScale), MAX_ID_MASK_STROKE_WIDTH);\n hasStroke = true;\n strokeWidths[detectionMaskId] = strokeWidth;\n maxStrokeWidth = Math.max(maxStrokeWidth, strokeWidth);\n writePaletteEntry(strokePalette, detectionMaskId, instruction.stroke.color, instruction.stroke.alpha);\n }\n if (scaledAxes) {\n writeScaledMaskRuns(data, scaledAxes, instruction.mask, detectionMaskId);\n }\n else {\n writeMaskRuns(data, width, instruction.mask, detectionMaskId);\n }\n }\n return {\n data,\n fillPalette,\n hasStroke,\n height,\n maxStrokeWidth,\n strokePalette,\n strokeWidths,\n width,\n };\n}\n/**\n * Compressed RLE counts runs down each column in turn, so a foreground run is\n * a contiguous walk down one column that wraps into the next.\n */\nfunction writeMaskRuns(data, frameWidth, mask, detectionMaskId) {\n const counts = decodeCompressedRleCounts(mask.counts);\n const maskWidth = mask.width;\n const maskHeight = mask.height;\n let maskOffset = 0;\n for (let index = 0; index < counts.length; index += 1) {\n const runLength = counts[index] ?? 0;\n if (index % 2 === 0 || runLength <= 0) {\n maskOffset += runLength;\n continue;\n }\n let x = Math.floor(maskOffset / maskHeight);\n let y = maskOffset - x * maskHeight;\n let frameOffset = y * frameWidth + x;\n for (let step = 0; step < runLength; step += 1) {\n if (x >= maskWidth) {\n break;\n }\n data[frameOffset] = detectionMaskId;\n y += 1;\n frameOffset += frameWidth;\n if (y === maskHeight) {\n y = 0;\n x += 1;\n frameOffset = x;\n }\n }\n maskOffset += runLength;\n }\n}\n/**\n * The scaled twin of writeMaskRuns. Every source pixel marks the texel it lands\n * in, so a mask keeps every part of itself the smaller raster can hold and\n * gains up to a texel at its edges.\n */\nfunction writeScaledMaskRuns(data, axes, mask, detectionMaskId) {\n const counts = decodeCompressedRleCounts(mask.counts);\n const maskWidth = mask.width;\n const maskHeight = mask.height;\n let maskOffset = 0;\n for (let index = 0; index < counts.length; index += 1) {\n const runLength = counts[index] ?? 0;\n if (index % 2 === 0 || runLength <= 0) {\n maskOffset += runLength;\n continue;\n }\n let x = Math.floor(maskOffset / maskHeight);\n let y = maskOffset - x * maskHeight;\n let column = axes.columns[x];\n for (let step = 0; step < runLength; step += 1) {\n if (x >= maskWidth) {\n break;\n }\n data[axes.rows[y] + column] = detectionMaskId;\n y += 1;\n if (y === maskHeight) {\n y = 0;\n x += 1;\n column = axes.columns[x];\n }\n }\n maskOffset += runLength;\n }\n}\nfunction resolveRasterWidth(maskWidth, maxWidth) {\n return maxWidth !== undefined && maxWidth > 0 && maxWidth < maskWidth\n ? Math.max(1, Math.floor(maxWidth))\n : maskWidth;\n}\n/**\n * A stroke is measured in texels of the raster it is drawn on, so a coarser\n * raster measures it in coarser texels. A stroke of a texel or more keeps at\n * least one, the thinnest line the shader can draw; a narrower one keeps its\n * own width, which the shader draws as an inner boundary at any scale.\n */\nfunction resolveStrokeTexels(strokeWidth, scale) {\n return Math.max(strokeWidth * scale, Math.min(strokeWidth, 1));\n}\nfunction createScaledMaskAxes(frame) {\n return {\n columns: createMaskAxisMap(frame.maskWidth, frame.width, 1),\n rows: createMaskAxisMap(frame.maskHeight, frame.height, frame.width),\n };\n}\n/** Rows hold their destination offset, so writing a run is two lookups. */\nfunction createMaskAxisMap(sourceLength, targetLength, stride) {\n const map = new Int32Array(sourceLength);\n const scale = targetLength / sourceLength;\n for (let index = 0; index < sourceLength; index += 1) {\n map[index] = Math.min(targetLength - 1, Math.floor(index * scale)) * stride;\n }\n return map;\n}\nfunction writePaletteEntry(palette, id, color, alpha) {\n const offset = id * 4;\n palette[offset] = ((color >> 16) & 0xff) / 255;\n palette[offset + 1] = ((color >> 8) & 0xff) / 255;\n palette[offset + 2] = (color & 0xff) / 255;\n palette[offset + 3] = Math.max(0, Math.min(alpha, 1));\n}\n\nconst DEFAULT_ELLIPSE_SEGMENT_COUNT = 48;\nconst FULL_TURN = Math.PI * 2;\nconst GEOMETRY_EPSILON = 1e-9;\nconst MIN_ELLIPSE_SEGMENT_COUNT = 4;\nconst MAX_ELLIPSE_SEGMENT_COUNT = 512;\n/**\n * Samples an ellipse or elliptical arc into a deterministic polyline so every\n * backend rasterizes the same structural geometry, including dashed strokes.\n *\n * A closed ellipse returns `segmentCount` points without repeating the first\n * point; an open arc returns `segmentCount + 1` points including both\n * endpoints.\n */\nfunction sampleEllipseArc(ellipse, segmentCount = DEFAULT_ELLIPSE_SEGMENT_COUNT) {\n validateEllipseGeometry(ellipse);\n assertIntegerAtLeast(segmentCount, MIN_ELLIPSE_SEGMENT_COUNT, \"segmentCount\");\n const rotation = ellipse.rotation ?? 0;\n const startAngle = ellipse.startAngle ?? 0;\n const endAngle = ellipse.endAngle ?? startAngle + FULL_TURN;\n const range = endAngle - startAngle;\n const closed = ellipse.startAngle === undefined;\n const pointCount = closed ? segmentCount : segmentCount + 1;\n const cosRotation = Math.cos(rotation);\n const sinRotation = Math.sin(rotation);\n const points = [];\n for (let index = 0; index < pointCount; index += 1) {\n const angle = startAngle + (range * index) / segmentCount;\n const localX = Math.cos(angle) * ellipse.radiusX;\n const localY = Math.sin(angle) * ellipse.radiusY;\n points.push({\n x: ellipse.center.x + localX * cosRotation - localY * sinRotation,\n y: ellipse.center.y + localX * sinRotation + localY * cosRotation,\n });\n }\n return { closed, points };\n}\n/**\n * Chooses a bounded segment count from the ellipse's on-screen radius. The\n * result targets sub-pixel chord error without making tiny markers expensive.\n */\nfunction resolveEllipseSegmentCount(ellipse, viewportScale = 1, maxErrorPixels = 0.5) {\n validateEllipseGeometry(ellipse);\n assertPositiveFinite(viewportScale, \"viewportScale\");\n assertPositiveFinite(maxErrorPixels, \"maxErrorPixels\");\n const screenRadius = Math.max(ellipse.radiusX, ellipse.radiusY) * viewportScale;\n assertPositiveFinite(screenRadius, \"screenRadius\");\n const clampedError = Math.min(maxErrorPixels, screenRadius);\n const maxStep = clampedError === screenRadius\n ? Math.PI / 2\n : 2 * Math.acos(1 - clampedError / screenRadius);\n const startAngle = ellipse.startAngle ?? 0;\n const endAngle = ellipse.endAngle ?? startAngle + FULL_TURN;\n const arcLength = Math.abs(endAngle - startAngle);\n const segmentCount = Math.ceil(arcLength / maxStep);\n return Math.min(MAX_ELLIPSE_SEGMENT_COUNT, Math.max(MIN_ELLIPSE_SEGMENT_COUNT, segmentCount));\n}\n/**\n * Resolves a marker instruction into media-space geometry. Screen-space sizes\n * are divided by the viewport scale, matching stroke-width semantics.\n */\nfunction resolveMarkerGeometry(marker, viewportScale = 1) {\n assertFinitePoint(marker.center, \"center\");\n assertPositiveFinite(marker.size, \"size\");\n assertPositiveFinite(viewportScale, \"viewportScale\");\n if (marker.rotation !== undefined) {\n assertFinite(marker.rotation, \"rotation\");\n }\n const size = marker.sizeSpace === MarkerSizeSpace.Screen\n ? marker.size / viewportScale\n : marker.size;\n const radius = size / 2;\n const rotation = marker.rotation ?? 0;\n if (marker.shape === MarkerShape.Circle) {\n return { center: marker.center, kind: \"circle\", radius };\n }\n if (marker.shape === MarkerShape.Cross) {\n return {\n closed: false,\n kind: \"subpaths\",\n subpaths: [\n [\n rotatePoint(marker.center, -radius, -radius, rotation),\n rotatePoint(marker.center, radius, radius, rotation),\n ],\n [\n rotatePoint(marker.center, radius, -radius, rotation),\n rotatePoint(marker.center, -radius, radius, rotation),\n ],\n ],\n };\n }\n if (marker.shape === MarkerShape.Square) {\n return {\n closed: true,\n kind: \"subpaths\",\n subpaths: [\n [\n rotatePoint(marker.center, -radius, -radius, rotation),\n rotatePoint(marker.center, radius, -radius, rotation),\n rotatePoint(marker.center, radius, radius, rotation),\n rotatePoint(marker.center, -radius, radius, rotation),\n ],\n ],\n };\n }\n // Triangle: equilateral, inscribed in the marker circle, pointing toward\n // positive y at rotation 0.\n const baseOffsetY = -radius / 2;\n const baseOffsetX = radius * (Math.sqrt(3) / 2);\n return {\n closed: true,\n kind: \"subpaths\",\n subpaths: [\n [\n rotatePoint(marker.center, 0, radius, rotation),\n rotatePoint(marker.center, baseOffsetX, baseOffsetY, rotation),\n rotatePoint(marker.center, -baseOffsetX, baseOffsetY, rotation),\n ],\n ],\n };\n}\nfunction validateEllipseGeometry(ellipse) {\n assertFinitePoint(ellipse.center, \"center\");\n assertPositiveFinite(ellipse.radiusX, \"radiusX\");\n assertPositiveFinite(ellipse.radiusY, \"radiusY\");\n if (ellipse.rotation !== undefined) {\n assertFinite(ellipse.rotation, \"rotation\");\n }\n const hasStart = ellipse.startAngle !== undefined;\n const hasEnd = ellipse.endAngle !== undefined;\n if (hasStart !== hasEnd) {\n throw new RangeError(\"startAngle and endAngle must be provided together\");\n }\n if (hasStart && hasEnd) {\n assertFinite(ellipse.startAngle, \"startAngle\");\n assertFinite(ellipse.endAngle, \"endAngle\");\n const range = Math.abs(ellipse.endAngle - ellipse.startAngle);\n if (range <= GEOMETRY_EPSILON || range >= FULL_TURN - GEOMETRY_EPSILON) {\n throw new RangeError(\"ellipse arc range must be greater than 0 and less than one full turn\");\n }\n }\n}\nfunction assertFinitePoint(point, name) {\n assertFinite(point.x, `${name}.x`);\n assertFinite(point.y, `${name}.y`);\n}\nfunction assertFinite(value, name) {\n if (!Number.isFinite(value)) {\n throw new RangeError(`${name} must be finite`);\n }\n}\nfunction assertPositiveFinite(value, name) {\n assertFinite(value, name);\n if (value <= 0) {\n throw new RangeError(`${name} must be greater than 0`);\n }\n}\nfunction assertIntegerAtLeast(value, minimum, name) {\n if (!Number.isInteger(value) || value < minimum) {\n throw new RangeError(`${name} must be an integer of at least ${minimum}`);\n }\n}\nfunction rotatePoint(center, offsetX, offsetY, rotation) {\n if (rotation === 0) {\n return { x: center.x + offsetX, y: center.y + offsetY };\n }\n const cosRotation = Math.cos(rotation);\n const sinRotation = Math.sin(rotation);\n return {\n x: center.x + offsetX * cosRotation - offsetY * sinRotation,\n y: center.y + offsetX * sinRotation + offsetY * cosRotation,\n };\n}\n\nfunction includeDefined(values) {\n return Object.fromEntries(Object.entries(values).filter(([, value]) => value !== undefined));\n}\n\nfunction resolveAnnotationStyleState(detection, visibility) {\n const id = detection.id;\n return {\n hidden: visibility?.annotationsHidden === true ||\n (detection.className !== undefined &&\n includes(visibility?.hiddenClasses, detection.className)) ||\n (id !== undefined && includes(visibility?.hiddenDetectionIds, id)),\n loading: id !== undefined && includes(visibility?.loadingDetectionIds, id),\n ephemeral: id !== undefined && includes(visibility?.ephemeralDetectionIds, id),\n isCreating: id !== undefined && visibility?.creatingDetectionId === id,\n };\n}\nfunction includes(values, value) {\n if (!values)\n return false;\n return Array.isArray(values)\n ? values.includes(value)\n : values.has(value);\n}\n\nvar MediaRendererFit;\n(function (MediaRendererFit) {\n /**\n * Preserve media aspect ratio and fit the full frame inside the canvas.\n */\n MediaRendererFit[\"Contain\"] = \"contain\";\n /**\n * Preserve media aspect ratio and fill the canvas, cropping if necessary.\n */\n MediaRendererFit[\"Cover\"] = \"cover\";\n})(MediaRendererFit || (MediaRendererFit = {}));\n/**\n * Playback lifecycle state reported by a platform renderer.\n */\nvar MediaRendererPlaybackState;\n(function (MediaRendererPlaybackState) {\n MediaRendererPlaybackState[\"Loading\"] = \"loading\";\n MediaRendererPlaybackState[\"Ready\"] = \"ready\";\n MediaRendererPlaybackState[\"Playing\"] = \"playing\";\n MediaRendererPlaybackState[\"Buffering\"] = \"buffering\";\n MediaRendererPlaybackState[\"Paused\"] = \"paused\";\n MediaRendererPlaybackState[\"Error\"] = \"error\";\n MediaRendererPlaybackState[\"Destroyed\"] = \"destroyed\";\n})(MediaRendererPlaybackState || (MediaRendererPlaybackState = {}));\n/**\n * Stable classification of a media failure.\n *\n * Applications branch on these kinds instead of parsing decoder, demuxer, or\n * container error text, and still own their localized user-facing copy. New\n * kinds may be added over time, so treat unrecognized values like `Unknown`.\n */\nvar MediaErrorKind;\n(function (MediaErrorKind) {\n /** The media could not be opened or read at all. */\n MediaErrorKind[\"Unreadable\"] = \"unreadable\";\n /** The container or codec is not supported by this platform. */\n MediaErrorKind[\"UnsupportedFormat\"] = \"unsupportedFormat\";\n /** The media opened but carries no usable video track. */\n MediaErrorKind[\"NoVideoTrack\"] = \"noVideoTrack\";\n /** Decoding a sample failed after the source opened. */\n MediaErrorKind[\"Decode\"] = \"decode\";\n /** The media could not be fetched. */\n MediaErrorKind[\"Network\"] = \"network\";\n /** The host environment lacks the APIs this media source requires. */\n MediaErrorKind[\"EnvironmentUnsupported\"] = \"environmentUnsupported\";\n /** The failure could not be classified. */\n MediaErrorKind[\"Unknown\"] = \"unknown\";\n})(MediaErrorKind || (MediaErrorKind = {}));\n/**\n * Lower-level media source readiness.\n */\nvar MediaSourceStatus;\n(function (MediaSourceStatus) {\n MediaSourceStatus[\"Loading\"] = \"loading\";\n MediaSourceStatus[\"Ready\"] = \"ready\";\n MediaSourceStatus[\"Error\"] = \"error\";\n MediaSourceStatus[\"Destroyed\"] = \"destroyed\";\n})(MediaSourceStatus || (MediaSourceStatus = {}));\n/**\n * Current renderer state.\n */\n/**\n * What a playback gate holds back.\n *\n * A source the renderer pulls samples from can be held between any two frames,\n * because the renderer decides when each one is drawn. A source that presents\n * its own frames owns the playhead, so holding it means stopping the producer\n * and starting it again, which the detection gate does and the\n * render-preparation gate does not.\n */\nvar PlaybackGateReach;\n(function (PlaybackGateReach) {\n /** No gate: the picture moves and unprepared layers are absent from it. */\n PlaybackGateReach[\"Off\"] = \"off\";\n /**\n * Playback waits to begin, and stops again at any frame whose artifacts are\n * missing, until they arrive or the gate's own wait bound gives up on them.\n */\n PlaybackGateReach[\"EveryFrame\"] = \"everyFrame\";\n /** Playback waits to begin; frames after that are not held. */\n PlaybackGateReach[\"StartOfPlayback\"] = \"startOfPlayback\";\n})(PlaybackGateReach || (PlaybackGateReach = {}));\n\n/**\n * Media-session operating mode.\n *\n * Core owns the names because file-like and stream-like lifecycle choices are\n * platform-neutral. Platform packages decide how these modes tune media,\n * storage, and renderer defaults.\n */\nvar MediaSessionMode;\n(function (MediaSessionMode) {\n /**\n * Finite media. Defaults usually favor seek/replay and persistent detection\n * storage.\n */\n MediaSessionMode[\"File\"] = \"file\";\n /**\n * Live or append-only media. Defaults usually favor rolling windows and\n * bounded retention.\n */\n MediaSessionMode[\"Stream\"] = \"stream\";\n})(MediaSessionMode || (MediaSessionMode = {}));\n/**\n * Aggregate lifecycle state for a media session.\n */\nvar MediaSessionStatus;\n(function (MediaSessionStatus) {\n MediaSessionStatus[\"Buffering\"] = \"buffering\";\n MediaSessionStatus[\"Destroyed\"] = \"destroyed\";\n MediaSessionStatus[\"Error\"] = \"error\";\n MediaSessionStatus[\"Loading\"] = \"loading\";\n MediaSessionStatus[\"Paused\"] = \"paused\";\n MediaSessionStatus[\"Playing\"] = \"playing\";\n MediaSessionStatus[\"Processing\"] = \"processing\";\n MediaSessionStatus[\"Ready\"] = \"ready\";\n})(MediaSessionStatus || (MediaSessionStatus = {}));\n/**\n * Subsystem currently affecting session readiness or presentation.\n */\nvar MediaSessionActivityKind;\n(function (MediaSessionActivityKind) {\n /**\n * Playback is held waiting for a producer to emit detections for the frame\n * about to be shown, which is a wait on inference rather than on transfer or\n * decode. `DetectionsBuffering` is the transfer of detections that already\n * exist, and `PlaybackBuffering` is the wait for media bytes.\n */\n MediaSessionActivityKind[\"DetectionsAwaitingCoverage\"] = \"detectionsAwaitingCoverage\";\n MediaSessionActivityKind[\"DetectionsBuffering\"] = \"detectionsBuffering\";\n MediaSessionActivityKind[\"DetectionsLoading\"] = \"detectionsLoading\";\n MediaSessionActivityKind[\"Error\"] = \"error\";\n MediaSessionActivityKind[\"MediaNormalizing\"] = \"mediaNormalizing\";\n MediaSessionActivityKind[\"MediaOpening\"] = \"mediaOpening\";\n MediaSessionActivityKind[\"PlaybackBuffering\"] = \"playbackBuffering\";\n MediaSessionActivityKind[\"RenderPreparing\"] = \"renderPreparing\";\n})(MediaSessionActivityKind || (MediaSessionActivityKind = {}));\n/**\n * State of one session activity.\n */\nvar MediaSessionActivityStatus;\n(function (MediaSessionActivityStatus) {\n MediaSessionActivityStatus[\"Error\"] = \"error\";\n MediaSessionActivityStatus[\"Running\"] = \"running\";\n MediaSessionActivityStatus[\"Waiting\"] = \"waiting\";\n})(MediaSessionActivityStatus || (MediaSessionActivityStatus = {}));\n\nexport { AnnotationFrameMutationKind, AnnotationGeometryKind, AnnotationGestureStateKind, AnnotationHandleKind, BaseBoxCornerStyle, BaseBoxStyle, BaseFocusStyle, BaseInteractionStyle, BaseKeypointStyle, BaseLabelStyle, BaseMarkerStyle, BaseMaskStyle, BasePolygonStyle, BasePolylineStyle, BoxShape, StrokeAlignment as BoxStrokeAlignment, DEFAULT_DETECTION_CLASS_STYLES, DEFAULT_DETECTION_COLOR_SEQUENCE, DetectionBufferStatus, DetectionFrameRetentionMode, DetectionFrameSelectionMode, DetectionInteractionState, DetectionMaskEncoding, DetectionMaskPayloadFormat, DetectionPickTarget, FocusTargetMode, KeypointMarkerShape, KeypointVisibility, LabelPlacement, LabelVisibilityMode, MAX_ID_MASK_PALETTE_ENTRIES, MAX_ID_MASK_STROKE_WIDTH, MarkerShape, MarkerSizeSpace, MaskRenderMode, MediaErrorKind, MediaInteractionMode, MediaRendererFit, MediaRendererPlaybackState, MediaSessionActivityKind, MediaSessionActivityStatus, MediaSessionMode, MediaSessionStatus, MediaSourceStatus, PlaybackGateReach, RegionRendererComposeMode, RegionRendererCoverageKind, RegionRendererMediaEffectKind, RegionRendererRegionKind, RegionRendererSizeSpace, RegionRendererSourceKind, SUPERVISION_ROBOFLOW_COLOR, ShapeInstructionKind, StrokeAlignment, TrackingGeometry, annotationRendererKinds, annotationRenderers, applyAnnotationHandleDrag, canReuseMaskStyleArtifacts, centerRectToTopLeftRect, computeDetectionMaskRect, computeMaskBounds, containsPoint, convertDetectionBoxToMask, convertDetectionBoxToPolygon, convertDetectionMaskToBox, convertDetectionMaskToPolygon, convertDetectionPolygonToBox, convertDetectionPolygonToMask, copySortedDetectionFrames, createAnnotationEditingEngine, createArrayDetectionFrameSource, createBufferedDetectionTimeline, createByteTrackTracker, createCBIoUTracker, createColdDetectionFrameSource, createCompositeDetectionFrameSource, createDefaultAnnotationPresentation, createDetectionPickKey, createEditableAnnotationFrameSession, createIdMaskFrame, createIdleDetectionBufferState, createMemoryColdDetectionFrameStore, createOCSortTracker, createProjectedDetectionFrameSource, createSortTracker, createSourceAwarePresentation, createViewportController, createWritableDetectionFrameSource, decodeCompressedRleCounts, decodeCompressedRleMask, decodeDetectionMaskPayload, deleteAnnotationVertex, detectMaskBorders, detectionFrameOverlapsRange, detectionPostProcessors, distanceToSegment, encodeBinaryMask, encodeBinaryMaskWithBounds, encodeCompressedRleCounts, encodeDetectionMaskPayload, extractDetectionMaskRectRuns, extractMaskContour, extractMaskRectRuns, filterDetectionFramesForRange, findClosestAnnotationSegment, followDetectionPickAcrossFrames, getAnnotationHandles, getBufferedDetectionTimelineFrameSnapshot, getDetectionRect, getPointsRect, haveSameDetectionPickIdentities, includeDefined, isDeflatedBase64DetectionMaskPayload, lightenColor, mediaToScreen, mergeDetectionMasks, mergeDetectionPolygonsByClass, normalizeDetectionClassName, offsetDetection, pickAnnotationHandle, pickDetectionAtPoint, pickDetectionByMaskId, pointInPolygon, polygonArea, polygonToRect, projectDetectionFrame, projectDetectionFrameForTracking, projectDetectionFrames, rasterizePolygonToMask, rasterizeRectToMask, rebaseDetectionPickToFrame, rectArea, rectToPolygon, resolveAnnotationRendererPresentation, resolveAnnotationRendererStyleFields, resolveAnnotationStyleState, resolveContrastTextColor, resolveDetectionClassColorStyle, resolveEllipseSegmentCount, resolveMarkerGeometry, resolveMaskStyleOpacity, resolveStyleValue, sampleEllipseArc, screenToMedia, selectDetectionFrame, topLeftRectToCenterRect, validateDetectionFrames };\n//# sourceMappingURL=index.js.map\n",null,null,null,null],"names":[],"mappings":"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"}