@coxwave/tap-kit 2.7.0 → 3.0.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.
- package/dist/index.js.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/react.js +10 -24
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +10 -24
- package/dist/react.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/loader.ts","../src/factory/createTapKit.ts","../src/kit.ts"],"names":["DEFAULT_CDN_LOADER_URL","getLoaderURL","isLocalCoreMode","getLocalCoreURL","createSDKChecker","resolve","reject","timeoutMs","startTime","checkSDK","loadCDNLoader","loadingPromise","coreURL","script","loaderURL","existingScript","extractHandlers","options","createSSRStub","applyOptionsToElement","element","createTapKit","isMounted","isReady","isDestroyed","handlers","readyResolve","readyReject","readyPromise","pendingCalls","cleanupFns","cdnPromise","error","loadError","ensureElement","replayPendingCalls","e","setupEventCallbacks","unsubTimelineSeek","clipPlayHead","clipId","unsubAlarmFadeIn","messageInfo","call","queueCall","target","method","args","eventsProxy","params","callback","handler","videoProxy","config","container","el","course","cleanup","_loading","_config","_loadError","_eventsProxy","_videoProxy","TapKit","__publicField","__privateAdd","__privateSet","__privateGet","err","unsubscribe","cancelled"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * CDN loader for TapKit\n * Dynamically loads the TapKit SDK from CDN\n *\n * For local testing, you can override the loader URL:\n * window.__TAP_KIT_LOADER_URL__ = '/tap-kit-core/loader.js';\n *\n * For local development (bypass loader, load IIFE directly):\n * window.__TAP_KIT_CORE_URL__ = '/packages/tap-kit-core/dist/index.global.js';\n */\n\n// Build-time constant injected by tsup define\ndeclare const __DEFAULT_CDN_LOADER_URL__: string;\n\nconst DEFAULT_CDN_LOADER_URL =\n typeof __DEFAULT_CDN_LOADER_URL__ !== \"undefined\"\n ? __DEFAULT_CDN_LOADER_URL__\n : \"https://files.edutap.ai/tap-sdk/loader.js\";\nconst DEFAULT_TIMEOUT_MS = 4000; // 4 seconds total timeout\nconst IDLE_CALLBACK_TIMEOUT_MS = 500; // 500ms for requestIdleCallback\n\n/**\n * Get the loader URL from window override or default CDN\n */\nfunction getLoaderURL(): string {\n return window?.__TAP_KIT_LOADER_URL__\n ? window.__TAP_KIT_LOADER_URL__\n : DEFAULT_CDN_LOADER_URL;\n}\n\n/**\n * Check if local core mode is enabled\n * When __TAP_KIT_CORE_URL__ is set, directly load the IIFE bundle\n * This bypasses the loader.js and loads tap-kit-core directly\n */\nfunction isLocalCoreMode(): boolean {\n return typeof window !== \"undefined\" && !!window.__TAP_KIT_CORE_URL__;\n}\n\n/**\n * Get the local core URL\n */\nfunction getLocalCoreURL(): string {\n return window.__TAP_KIT_CORE_URL__ || \"\";\n}\n\n/**\n * Creates a SDK checker function with timeout and retry logic\n * Uses requestIdleCallback to avoid blocking browser rendering\n * @param resolve - Promise resolve function\n * @param reject - Promise reject function\n * @param timeoutMs - Maximum time to wait for SDK to load (milliseconds)\n * @returns Checker function to be called repeatedly\n */\nfunction createSDKChecker(\n resolve: (value: void | PromiseLike<void>) => void,\n reject: (reason?: unknown) => void,\n timeoutMs: number,\n): () => void {\n const startTime = Date.now();\n\n const checkSDK = (): void => {\n // Check if real TapKit is loaded (not just stub)\n // Stub has TapKitLoaded flag set to true by loader.js after real SDK loads\n if (window.TapKit && window.TapKitLoaded === true) {\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n return;\n }\n\n const elapsed = Date.now() - startTime;\n\n // Check if exceeded timeout\n if (elapsed > timeoutMs) {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(\n new Error(\n `TapKit loader timeout: SDK not available after ${timeoutMs}ms`,\n ),\n );\n return;\n }\n\n // Use requestIdleCallback for better performance\n // Falls back to setTimeout if not available\n if (typeof requestIdleCallback !== \"undefined\") {\n requestIdleCallback(checkSDK, { timeout: IDLE_CALLBACK_TIMEOUT_MS });\n } else {\n setTimeout(checkSDK, IDLE_CALLBACK_TIMEOUT_MS);\n }\n };\n\n return checkSDK;\n}\n\n/**\n * Loads the CDN loader script\n * The loader will then fetch versions.json and load the appropriate SDK version\n *\n * If __TAP_KIT_CORE_URL__ is set, bypasses loader and loads IIFE directly\n *\n * @param timeoutMs - Maximum time to wait for SDK to load (default: 4000ms)\n * @returns Promise that resolves when SDK is loaded\n * @throws {Error} If loader fails to load or times out\n */\nexport function loadCDNLoader(\n timeoutMs: number = DEFAULT_TIMEOUT_MS,\n): Promise<void> {\n // If already loaded, return immediately\n if (window.__TAP_KIT_LOADER_LOADED__ && window.TapKit) {\n return Promise.resolve();\n }\n\n // If currently loading, return the existing promise\n if (window.__TAP_KIT_LOADER_LOADING__) {\n return window.__TAP_KIT_LOADER_LOADING__;\n }\n\n // Create loading promise\n const loadingPromise = new Promise<void>((resolve, reject) => {\n if (typeof document === \"undefined\") {\n reject(\n new Error(\n \"TapKit requires browser environment (document is undefined)\",\n ),\n );\n return;\n }\n\n // Local core mode: Load IIFE directly\n if (isLocalCoreMode()) {\n // Check if TapKit is already loaded (from other pages in playground)\n if (window.TapKit && window.TapKitLoaded === true) {\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n return;\n }\n\n const coreURL = getLocalCoreURL();\n\n const script = document.createElement(\"script\");\n script.src = coreURL;\n script.async = true;\n\n script.onload = () => {\n // IIFE directly sets window.TapKit\n // Set the loaded flag manually since we bypass loader.js\n if (window.TapKit) {\n window.TapKitLoaded = true;\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n } else {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(\"TapKit not available after loading local core\"));\n }\n };\n\n script.onerror = () => {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`Failed to load local TapKit core: ${coreURL}`));\n };\n\n document.head.appendChild(script);\n return;\n }\n\n // CDN mode: Load loader.js\n const loaderURL = getLoaderURL();\n const script = document.createElement(\"script\");\n script.src = loaderURL;\n script.async = true;\n\n script.onload = () => {\n // The loader script will load the actual SDK\n // We need to wait a bit for the loader to fetch and load the SDK\n const checkSDK = createSDKChecker(resolve, reject, timeoutMs);\n checkSDK();\n };\n\n script.onerror = () => {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`));\n };\n\n // Check if script already exists\n const existingScript = document.querySelector(`script[src=\"${loaderURL}\"]`);\n\n if (existingScript) {\n // Script already added but not yet loaded\n existingScript.addEventListener(\"load\", () => {\n const checkSDK = createSDKChecker(resolve, reject, timeoutMs);\n checkSDK();\n });\n existingScript.addEventListener(\"error\", () =>\n reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`)),\n );\n } else {\n document.head.appendChild(script);\n }\n });\n\n window.__TAP_KIT_LOADER_LOADING__ = loadingPromise;\n return loadingPromise;\n}\n","/**\n * createTapKit - Factory API for TapKit\n *\n * Non-React, framework-agnostic API with full configuration support.\n * Wraps the <tap-kit> Web Component with imperative control.\n *\n * @example\n * ```typescript\n * import { createTapKit } from '@coxwave/tap-kit';\n *\n * const tapkit = createTapKit({\n * apiKey: 'your-key',\n * userId: 'user-123',\n * courseId: 'course-456',\n * clipId: 'clip-789',\n * apiUrl: 'https://tapapistaging.coxwave.link',\n * onReady: () => console.log('Ready!'),\n * });\n *\n * tapkit.mount();\n * await tapkit.ready;\n * tapkit.show();\n * ```\n *\n * @see useTapKit for React projects (recommended)\n */\n\nimport type {\n Course,\n EventManager,\n TapKitElement,\n VideoController,\n} from \"@coxwave/tap-kit-types\";\nimport { loadCDNLoader } from \"../loader\";\nimport type {\n CreateTapKitOptions,\n FactoryEventHandlers,\n TapKitFactoryControl,\n} from \"./types\";\n\n/**\n * Extract event handlers from options\n */\nfunction extractHandlers(options: CreateTapKitOptions): FactoryEventHandlers {\n return {\n onReady: options.onReady,\n onError: options.onError,\n onTimelineSeek: options.onTimelineSeek,\n onAlarmFadeIn: options.onAlarmFadeIn,\n };\n}\n\n/**\n * Create a noop stub for SSR environments\n */\nfunction createSSRStub(): TapKitFactoryControl {\n const noopPromise = Promise.resolve();\n\n return {\n ready: noopPromise,\n isReady: false,\n isOpen: false,\n isMounted: false,\n mount: () => {},\n unmount: () => {},\n show: () => {},\n hide: () => {},\n setCourse: () => {},\n events: {\n seekTimeline: () => {},\n onTimelineSeek: () => () => {},\n onAlarmFadeIn: () => () => {},\n },\n video: {\n bind: () => {},\n unbind: () => {},\n },\n element: null,\n destroy: () => {},\n };\n}\n\n/**\n * Apply configuration options to TapKit element\n */\nfunction applyOptionsToElement(\n element: TapKitElement,\n options: CreateTapKitOptions,\n): void {\n // Required\n element.apiKey = options.apiKey;\n\n // Course info\n if (options.userId !== undefined) element.userId = options.userId;\n if (options.courseId !== undefined) element.courseId = options.courseId;\n if (options.clipId !== undefined) element.clipId = options.clipId;\n if (options.clipPlayHead !== undefined)\n element.clipPlayHead = options.clipPlayHead;\n\n // Display options\n if (options.mode !== undefined) element.mode = options.mode;\n if (options.language !== undefined) element.language = options.language;\n if (options.allowLayoutToggle !== undefined)\n element.allowLayoutToggle = options.allowLayoutToggle;\n if (options.buttonId !== undefined) element.buttonId = options.buttonId;\n\n // Environment options\n if (options.apiUrl !== undefined) element.apiUrl = options.apiUrl;\n if (options.tapUrl !== undefined) element.tapUrl = options.tapUrl;\n if (options.environment !== undefined)\n element.environment = options.environment;\n\n // Video target\n if (options.videoTarget !== undefined)\n element.videoTarget = options.videoTarget;\n\n // Debug\n if (options.debug !== undefined) element.debug = options.debug;\n}\n\n/**\n * Create TapKit factory instance\n *\n * Creates a <tap-kit> Web Component programmatically and returns\n * a control object for imperative management.\n *\n * **Important for React users:**\n * This API is designed for non-React environments. For React projects,\n * use `useTapKit` hook instead for proper lifecycle management.\n *\n * @param options - Configuration options including apiKey, course info, and event handlers\n * @returns Control object for managing TapKit instance\n *\n * @example\n * ```typescript\n * // Vanilla JavaScript\n * const tapkit = createTapKit({\n * apiKey: 'your-key',\n * userId: 'user-123',\n * courseId: 'course-456',\n * clipId: 'clip-789',\n * mode: 'floating',\n * onTimelineSeek: (time, clipId) => {\n * videoElement.currentTime = time;\n * },\n * });\n *\n * // Mount to DOM (required)\n * tapkit.mount();\n *\n * // Wait for ready and show\n * await tapkit.ready;\n * tapkit.show();\n *\n * // Later: cleanup\n * tapkit.destroy();\n * ```\n */\nexport function createTapKit(\n options: CreateTapKitOptions,\n): TapKitFactoryControl {\n // SSR safety: return noop stub in non-browser environment\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return createSSRStub();\n }\n\n // State (closure-based privacy)\n let element: TapKitElement | null = null;\n let isMounted = false;\n let isReady = false;\n let isDestroyed = false;\n\n // Store handlers in mutable reference for access in callbacks\n const handlers = extractHandlers(options);\n\n // Ready promise with resolver\n let readyResolve: () => void;\n let readyReject: (error: Error) => void;\n const readyPromise = new Promise<void>((resolve, reject) => {\n readyResolve = resolve;\n readyReject = reject;\n });\n\n // Queue for calls made before ready\n const pendingCalls: Array<{\n target: \"element\" | \"events\" | \"video\";\n method: string;\n args: unknown[];\n }> = [];\n\n // Cleanup functions for event subscriptions\n const cleanupFns: Array<() => void> = [];\n\n // CDN loading promise\n const cdnPromise = loadCDNLoader().catch((error) => {\n const loadError =\n error instanceof Error ? error : new Error(\"Failed to load TapKit CDN\");\n readyReject(loadError);\n handlers.onError?.(loadError);\n throw loadError;\n });\n\n /**\n * Create element after CDN is loaded\n */\n async function ensureElement(): Promise<TapKitElement> {\n if (isDestroyed) {\n throw new Error(\"[createTapKit] Instance has been destroyed\");\n }\n\n if (element) {\n return element;\n }\n\n await cdnPromise;\n\n // Create element\n element = document.createElement(\"tap-kit\");\n\n // Apply options\n applyOptionsToElement(element, options);\n\n // Set up event listeners\n element.addEventListener(\"tap-kit:ready\", () => {\n isReady = true;\n readyResolve();\n handlers.onReady?.();\n\n // Replay pending calls\n replayPendingCalls();\n });\n\n element.addEventListener(\"tap-kit:error\", (e) => {\n const customEvent = e as CustomEvent<{ error: Error }>;\n const error = customEvent.detail?.error ?? new Error(\"Unknown error\");\n readyReject(error);\n handlers.onError?.(error);\n });\n\n // Set up EventManager callbacks\n setupEventCallbacks();\n\n return element;\n }\n\n /**\n * Set up EventManager callbacks after element is ready\n */\n function setupEventCallbacks(): void {\n if (!element) return;\n\n // Use element.ready to ensure events API is available\n element.ready\n .then(() => {\n if (!element?.events) return;\n\n // Register stable wrappers and store cleanup functions\n const unsubTimelineSeek = element.events.onTimelineSeek(\n (clipPlayHead, clipId) => {\n handlers.onTimelineSeek?.(clipPlayHead, clipId);\n },\n );\n cleanupFns.push(unsubTimelineSeek);\n\n const unsubAlarmFadeIn = element.events.onAlarmFadeIn((messageInfo) => {\n handlers.onAlarmFadeIn?.(messageInfo);\n });\n cleanupFns.push(unsubAlarmFadeIn);\n })\n .catch(() => {\n // Ignore - error already handled by tap-kit:error event\n });\n }\n\n /**\n * Replay calls that were made before ready\n */\n function replayPendingCalls(): void {\n if (!element || !isReady) return;\n\n for (const call of pendingCalls) {\n try {\n if (call.target === \"element\") {\n (element as unknown as Record<string, (...args: unknown[]) => void>)[\n call.method\n ]?.(...call.args);\n } else if (call.target === \"events\" && element.events) {\n (\n element.events as unknown as Record<\n string,\n (...args: unknown[]) => void\n >\n )[call.method]?.(...call.args);\n } else if (call.target === \"video\" && element.video) {\n (\n element.video as unknown as Record<\n string,\n (...args: unknown[]) => void\n >\n )[call.method]?.(...call.args);\n }\n } catch (e) {\n console.warn(`[createTapKit] Failed to replay ${call.method}:`, e);\n }\n }\n\n // Clear queue\n pendingCalls.length = 0;\n }\n\n /**\n * Queue a call to be executed when ready\n */\n function queueCall(\n target: \"element\" | \"events\" | \"video\",\n method: string,\n args: unknown[],\n ): void {\n if (isReady && element) {\n // Execute immediately if ready\n try {\n if (target === \"element\") {\n (element as unknown as Record<string, (...args: unknown[]) => void>)[\n method\n ]?.(...args);\n } else if (target === \"events\" && element.events) {\n (\n element.events as unknown as Record<\n string,\n (...args: unknown[]) => void\n >\n )[method]?.(...args);\n } else if (target === \"video\" && element.video) {\n (\n element.video as unknown as Record<\n string,\n (...args: unknown[]) => void\n >\n )[method]?.(...args);\n }\n } catch (e) {\n console.warn(`[createTapKit] Failed to call ${method}:`, e);\n }\n } else {\n // Queue for later\n pendingCalls.push({ target, method, args });\n }\n }\n\n // Create events proxy\n const eventsProxy: EventManager = {\n seekTimeline: (params) => queueCall(\"events\", \"seekTimeline\", [params]),\n onTimelineSeek: (callback) => {\n if (isReady && element?.events) {\n return element.events.onTimelineSeek(callback);\n }\n // Return noop unsubscribe for queued calls\n queueCall(\"events\", \"onTimelineSeek\", [callback]);\n return () => {};\n },\n onAlarmFadeIn: (handler) => {\n if (isReady && element?.events) {\n return element.events.onAlarmFadeIn(handler);\n }\n // Return noop unsubscribe for queued calls\n queueCall(\"events\", \"onAlarmFadeIn\", [handler]);\n return () => {};\n },\n };\n\n // Create video proxy\n const videoProxy: VideoController = {\n bind: (config, clipId) => queueCall(\"video\", \"bind\", [config, clipId]),\n unbind: () => queueCall(\"video\", \"unbind\", []),\n };\n\n // Start element creation (non-blocking)\n ensureElement().catch(() => {\n // Error already handled in ensureElement\n });\n\n // Return control object\n const control: TapKitFactoryControl = {\n get ready() {\n return readyPromise;\n },\n\n get isReady() {\n return isReady;\n },\n\n get isOpen() {\n return element?.isOpen ?? false;\n },\n\n get isMounted() {\n return isMounted;\n },\n\n mount(container: HTMLElement = document.body) {\n if (isDestroyed) {\n console.warn(\"[createTapKit] Cannot mount: instance destroyed\");\n return;\n }\n\n // Defensive: already mounted to this container\n if (element && element.parentNode === container) {\n console.warn(\"[createTapKit] Already mounted to this container\");\n return;\n }\n\n // If mounted elsewhere, move to new container\n if (element?.parentNode) {\n element.parentNode.removeChild(element);\n }\n\n // Wait for element creation then mount\n ensureElement()\n .then((el) => {\n if (!isDestroyed) {\n container.appendChild(el);\n isMounted = true;\n }\n })\n .catch((error) => {\n console.error(\"[createTapKit] Failed to mount:\", error);\n });\n },\n\n unmount() {\n if (element?.parentNode) {\n element.parentNode.removeChild(element);\n isMounted = false;\n }\n },\n\n show() {\n if (isDestroyed) {\n console.warn(\"[createTapKit] Cannot show: instance destroyed\");\n return;\n }\n queueCall(\"element\", \"show\", []);\n },\n\n hide() {\n if (isDestroyed) {\n console.warn(\"[createTapKit] Cannot hide: instance destroyed\");\n return;\n }\n queueCall(\"element\", \"hide\", []);\n },\n\n setCourse(course: Partial<Course> & { courseId: string; clipId: string }) {\n if (isDestroyed) {\n console.warn(\"[createTapKit] Cannot setCourse: instance destroyed\");\n return;\n }\n queueCall(\"element\", \"setCourse\", [course]);\n },\n\n get events() {\n return eventsProxy;\n },\n\n get video() {\n return videoProxy;\n },\n\n get element() {\n return element;\n },\n\n destroy() {\n if (isDestroyed) return;\n\n isDestroyed = true;\n\n // Run cleanup functions (unsubscribe event handlers)\n for (const cleanup of cleanupFns) {\n try {\n cleanup();\n } catch {\n // Ignore cleanup errors\n }\n }\n cleanupFns.length = 0;\n\n // Unmount if mounted\n if (element?.parentNode) {\n element.parentNode.removeChild(element);\n }\n\n // Clear state\n element = null;\n isMounted = false;\n isReady = false;\n pendingCalls.length = 0;\n },\n };\n\n return control;\n}\n","import type { TapKitConfig, TapKitInitParams, TapKitInstance } from \"@coxwave/tap-kit-types\";\nimport { loadCDNLoader } from \"./loader\";\n\n/**\n * TapKit - Official TapKit Web SDK\n */\nexport class TapKit implements TapKitInstance {\n private instance: TapKitInstance | null = null;\n #loading: Promise<void>;\n #config: TapKitConfig;\n #loadError: Error | null = null;\n #eventsProxy?: TapKitInstance[\"events\"]; // Cached proxy for events API\n #videoProxy?: TapKitInstance[\"video\"]; // Cached proxy for video API\n\n constructor(config: TapKitConfig) {\n this.#config = config;\n this.#loading = this.load();\n }\n\n private async load(): Promise<void> {\n try {\n await loadCDNLoader();\n\n if (!window.TapKit) {\n throw new Error(\"TapKit not available after loading CDN loader\");\n }\n\n this.instance = new window.TapKit(this.#config);\n } catch (err) {\n this.#loadError = err instanceof Error ? err : new Error(String(err));\n throw this.#loadError;\n }\n }\n\n /**\n * Promise that resolves when CDN is loaded and TapKit instance is available\n * (ready to call init())\n *\n * @example\n * ```typescript\n * const kit = new TapKit({ apiKey });\n * await kit.loaded; // CDN downloaded, window.TapKit available\n * await kit.init({...}); // Now safe to call init()\n * ```\n *\n * @see ready - For waiting until AFTER init() completes (iframe initialized)\n */\n get loaded(): Promise<void> {\n return this.#loading.then(() => {\n if (this.#loadError) {\n throw this.#loadError;\n }\n if (!this.instance) {\n throw new Error(\n `TapKit instance not initialized after loading. This may indicate a CDN loading failure for apiKey: ${\n this.#config.apiKey\n }`\n );\n }\n });\n }\n\n /**\n * Promise that resolves when SDK is fully initialized\n * (after init() is called and iframe is ready)\n *\n * @example\n * ```typescript\n * const kit = new TapKit({ apiKey });\n * await kit.init({...});\n * await kit.ready; // iframe is fully initialized\n * ```\n *\n * @see loaded - For waiting until CDN load completes (before init)\n */\n get ready(): Promise<void> {\n return this.#loading.then(() => {\n if (this.#loadError) {\n throw this.#loadError;\n }\n if (!this.instance) {\n throw new Error(\n `TapKit instance not initialized after loading. This may indicate a CDN loading failure for apiKey: ${\n this.#config.apiKey\n }`\n );\n }\n return this.instance.ready;\n });\n }\n\n /**\n * Create a proxy for video API that waits for ready before delegating\n */\n private createVideoProxy(): TapKitInstance[\"video\"] {\n return {\n bind: (config, clipId) => {\n // Queue the bind call - will execute after ready\n this.ready.then(() => {\n this.instance?.video.bind(config, clipId);\n });\n },\n unbind: () => {\n // Unbind can be called immediately if instance exists\n this.instance?.video?.unbind();\n },\n };\n }\n\n /**\n * Create events API that returns unsubscribe functions synchronously\n * Queues the actual subscription until ready, but returns unsubscribe immediately\n */\n private createEventsProxy(): TapKitInstance[\"events\"] {\n return {\n seekTimeline: (params) => {\n this.ready.then(() => {\n this.instance?.events.seekTimeline(params);\n });\n },\n onTimelineSeek: (callback) => {\n let unsubscribe: (() => void) | undefined;\n let cancelled = false;\n\n // Queue subscription\n this.ready.then(() => {\n if (cancelled) return;\n unsubscribe = this.instance?.events.onTimelineSeek(callback);\n });\n\n // Return synchronous unsubscribe function\n return () => {\n cancelled = true;\n unsubscribe?.();\n };\n },\n onAlarmFadeIn: (handler) => {\n let unsubscribe: (() => void) | undefined;\n let cancelled = false;\n\n this.ready.then(() => {\n if (cancelled) return;\n unsubscribe = this.instance?.events.onAlarmFadeIn(handler);\n });\n\n return () => {\n cancelled = true;\n unsubscribe?.();\n };\n },\n };\n }\n\n get events() {\n // Cache proxy to avoid creating new object on every access\n if (!this.#eventsProxy) {\n this.#eventsProxy = this.createEventsProxy();\n }\n return this.#eventsProxy;\n }\n\n get isOpen(): boolean {\n return this.instance?.isOpen ?? false;\n }\n\n get isInitialized(): boolean {\n return this.instance?.isInitialized ?? false;\n }\n\n get video() {\n // Cache proxy to avoid creating new object on every access\n if (!this.#videoProxy) {\n this.#videoProxy = this.createVideoProxy();\n }\n return this.#videoProxy;\n }\n\n async init(params: TapKitInitParams): Promise<() => void> {\n await this.#loading;\n if (!this.instance) {\n throw new Error(\"TapKit instance not available after loading\");\n }\n return await this.instance.init(params);\n }\n\n destroy(): void {\n if (this.instance) {\n this.instance.destroy();\n this.instance = null;\n }\n // Clear proxy references to prevent memory leaks\n this.#eventsProxy = undefined;\n this.#videoProxy = undefined;\n }\n\n show(): void {\n if (this.instance) {\n this.instance.show();\n }\n }\n\n hide(): void {\n if (this.instance) {\n this.instance.hide();\n }\n }\n\n setCourse(course: Parameters<TapKitInstance[\"setCourse\"]>[0]): void {\n if (this.instance) {\n this.instance.setCourse(course);\n }\n }\n\n get [Symbol.toStringTag]() {\n return \"TapKit\";\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/loader.ts","../src/factory/createTapKit.ts","../src/kit.ts"],"names":["DEFAULT_CDN_LOADER_URL","getLoaderURL","isLocalCoreMode","getLocalCoreURL","createSDKChecker","resolve","reject","timeoutMs","startTime","checkSDK","loadCDNLoader","loadingPromise","coreURL","script","loaderURL","existingScript","extractHandlers","options","createSSRStub","applyOptionsToElement","element","createTapKit","isMounted","isReady","isDestroyed","handlers","readyResolve","readyReject","readyPromise","pendingCalls","cleanupFns","cdnPromise","error","loadError","ensureElement","replayPendingCalls","e","setupEventCallbacks","unsubTimelineSeek","clipPlayHead","clipId","unsubAlarmFadeIn","messageInfo","call","queueCall","target","method","args","eventsProxy","params","callback","handler","videoProxy","config","container","el","course","cleanup","_loading","_config","_loadError","_eventsProxy","_videoProxy","TapKit","__publicField","__privateAdd","__privateSet","__privateGet","err","unsubscribe","cancelled"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * CDN loader for TapKit\n * Dynamically loads the TapKit SDK from CDN\n *\n * For local testing, you can override the loader URL:\n * window.__TAP_KIT_LOADER_URL__ = '/tap-kit-core/loader.js';\n *\n * For local development (bypass loader, load IIFE directly):\n * window.__TAP_KIT_CORE_URL__ = '/packages/tap-kit-core/dist/index.global.js';\n */\n\n// Build-time constant injected by tsup define\ndeclare const __DEFAULT_CDN_LOADER_URL__: string;\n\nconst DEFAULT_CDN_LOADER_URL =\n typeof __DEFAULT_CDN_LOADER_URL__ !== \"undefined\" ? __DEFAULT_CDN_LOADER_URL__ : \"https://files.edutap.ai/tap-sdk/loader.js\";\nconst DEFAULT_TIMEOUT_MS = 4000; // 4 seconds total timeout\nconst IDLE_CALLBACK_TIMEOUT_MS = 500; // 500ms for requestIdleCallback\n\n/**\n * Get the loader URL from window override or default CDN\n */\nfunction getLoaderURL(): string {\n return window?.__TAP_KIT_LOADER_URL__ ? window.__TAP_KIT_LOADER_URL__ : DEFAULT_CDN_LOADER_URL;\n}\n\n/**\n * Check if local core mode is enabled\n * When __TAP_KIT_CORE_URL__ is set, directly load the IIFE bundle\n * This bypasses the loader.js and loads tap-kit-core directly\n */\nfunction isLocalCoreMode(): boolean {\n return typeof window !== \"undefined\" && !!window.__TAP_KIT_CORE_URL__;\n}\n\n/**\n * Get the local core URL\n */\nfunction getLocalCoreURL(): string {\n return window.__TAP_KIT_CORE_URL__ || \"\";\n}\n\n/**\n * Creates a SDK checker function with timeout and retry logic\n * Uses requestIdleCallback to avoid blocking browser rendering\n * @param resolve - Promise resolve function\n * @param reject - Promise reject function\n * @param timeoutMs - Maximum time to wait for SDK to load (milliseconds)\n * @returns Checker function to be called repeatedly\n */\nfunction createSDKChecker(\n resolve: (value: void | PromiseLike<void>) => void,\n reject: (reason?: unknown) => void,\n timeoutMs: number\n): () => void {\n const startTime = Date.now();\n\n const checkSDK = (): void => {\n // Check if real TapKit is loaded (not just stub)\n // Stub has TapKitLoaded flag set to true by loader.js after real SDK loads\n if (window.TapKit && window.TapKitLoaded === true) {\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n return;\n }\n\n const elapsed = Date.now() - startTime;\n\n // Check if exceeded timeout\n if (elapsed > timeoutMs) {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`TapKit loader timeout: SDK not available after ${timeoutMs}ms`));\n return;\n }\n\n // Use requestIdleCallback for better performance\n // Falls back to setTimeout if not available\n if (typeof requestIdleCallback !== \"undefined\") {\n requestIdleCallback(checkSDK, { timeout: IDLE_CALLBACK_TIMEOUT_MS });\n } else {\n setTimeout(checkSDK, IDLE_CALLBACK_TIMEOUT_MS);\n }\n };\n\n return checkSDK;\n}\n\n/**\n * Loads the CDN loader script\n * The loader will then fetch versions.json and load the appropriate SDK version\n *\n * If __TAP_KIT_CORE_URL__ is set, bypasses loader and loads IIFE directly\n *\n * @param timeoutMs - Maximum time to wait for SDK to load (default: 4000ms)\n * @returns Promise that resolves when SDK is loaded\n * @throws {Error} If loader fails to load or times out\n */\nexport function loadCDNLoader(timeoutMs: number = DEFAULT_TIMEOUT_MS): Promise<void> {\n // If already loaded, return immediately\n if (window.__TAP_KIT_LOADER_LOADED__ && window.TapKit) {\n return Promise.resolve();\n }\n\n // If currently loading, return the existing promise\n if (window.__TAP_KIT_LOADER_LOADING__) {\n return window.__TAP_KIT_LOADER_LOADING__;\n }\n\n // Create loading promise\n const loadingPromise = new Promise<void>((resolve, reject) => {\n if (typeof document === \"undefined\") {\n reject(new Error(\"TapKit requires browser environment (document is undefined)\"));\n return;\n }\n\n // Local core mode: Load IIFE directly\n if (isLocalCoreMode()) {\n // Check if TapKit is already loaded (from other pages in playground)\n if (window.TapKit && window.TapKitLoaded === true) {\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n return;\n }\n\n const coreURL = getLocalCoreURL();\n\n const script = document.createElement(\"script\");\n script.src = coreURL;\n script.async = true;\n\n script.onload = () => {\n // IIFE directly sets window.TapKit\n // Set the loaded flag manually since we bypass loader.js\n if (window.TapKit) {\n window.TapKitLoaded = true;\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n } else {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(\"TapKit not available after loading local core\"));\n }\n };\n\n script.onerror = () => {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`Failed to load local TapKit core: ${coreURL}`));\n };\n\n document.head.appendChild(script);\n return;\n }\n\n // CDN mode: Load loader.js\n const loaderURL = getLoaderURL();\n const script = document.createElement(\"script\");\n script.src = loaderURL;\n script.async = true;\n\n script.onload = () => {\n // The loader script will load the actual SDK\n // We need to wait a bit for the loader to fetch and load the SDK\n const checkSDK = createSDKChecker(resolve, reject, timeoutMs);\n checkSDK();\n };\n\n script.onerror = () => {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`));\n };\n\n // Check if script already exists\n const existingScript = document.querySelector(`script[src=\"${loaderURL}\"]`);\n\n if (existingScript) {\n // Script already added but not yet loaded\n existingScript.addEventListener(\"load\", () => {\n const checkSDK = createSDKChecker(resolve, reject, timeoutMs);\n checkSDK();\n });\n existingScript.addEventListener(\"error\", () => reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`)));\n } else {\n document.head.appendChild(script);\n }\n });\n\n window.__TAP_KIT_LOADER_LOADING__ = loadingPromise;\n return loadingPromise;\n}\n","/**\n * createTapKit - Factory API for TapKit\n *\n * Non-React, framework-agnostic API with full configuration support.\n * Wraps the <tap-kit> Web Component with imperative control.\n *\n * @example\n * ```typescript\n * import { createTapKit } from '@coxwave/tap-kit';\n *\n * const tapkit = createTapKit({\n * apiKey: 'your-key',\n * userId: 'user-123',\n * courseId: 'course-456',\n * clipId: 'clip-789',\n * apiUrl: 'https://tapapistaging.coxwave.link',\n * onReady: () => console.log('Ready!'),\n * });\n *\n * tapkit.mount();\n * await tapkit.ready;\n * tapkit.show();\n * ```\n *\n * @see useTapKit for React projects (recommended)\n */\n\nimport type { Course, EventManager, TapKitElement, VideoController } from \"@coxwave/tap-kit-types\";\nimport { loadCDNLoader } from \"../loader\";\nimport type { CreateTapKitOptions, FactoryEventHandlers, TapKitFactoryControl } from \"./types\";\n\n/**\n * Extract event handlers from options\n */\nfunction extractHandlers(options: CreateTapKitOptions): FactoryEventHandlers {\n return {\n onReady: options.onReady,\n onError: options.onError,\n onTimelineSeek: options.onTimelineSeek,\n onAlarmFadeIn: options.onAlarmFadeIn,\n };\n}\n\n/**\n * Create a noop stub for SSR environments\n */\nfunction createSSRStub(): TapKitFactoryControl {\n const noopPromise = Promise.resolve();\n\n return {\n ready: noopPromise,\n isReady: false,\n isOpen: false,\n isMounted: false,\n mount: () => {},\n unmount: () => {},\n show: () => {},\n hide: () => {},\n setCourse: () => {},\n events: {\n seekTimeline: () => {},\n onTimelineSeek: () => () => {},\n onAlarmFadeIn: () => () => {},\n },\n video: {\n bind: () => {},\n unbind: () => {},\n },\n element: null,\n destroy: () => {},\n };\n}\n\n/**\n * Apply configuration options to TapKit element\n */\nfunction applyOptionsToElement(element: TapKitElement, options: CreateTapKitOptions): void {\n // Required\n element.apiKey = options.apiKey;\n\n // Course info\n if (options.userId !== undefined) element.userId = options.userId;\n if (options.courseId !== undefined) element.courseId = options.courseId;\n if (options.clipId !== undefined) element.clipId = options.clipId;\n if (options.clipPlayHead !== undefined) element.clipPlayHead = options.clipPlayHead;\n\n // Display options\n if (options.mode !== undefined) element.mode = options.mode;\n if (options.language !== undefined) element.language = options.language;\n if (options.allowLayoutToggle !== undefined) element.allowLayoutToggle = options.allowLayoutToggle;\n if (options.buttonId !== undefined) element.buttonId = options.buttonId;\n\n // Environment options\n if (options.apiUrl !== undefined) element.apiUrl = options.apiUrl;\n if (options.tapUrl !== undefined) element.tapUrl = options.tapUrl;\n if (options.environment !== undefined) element.environment = options.environment;\n\n // Video target\n if (options.videoTarget !== undefined) element.videoTarget = options.videoTarget;\n\n // Debug\n if (options.debug !== undefined) element.debug = options.debug;\n}\n\n/**\n * Create TapKit factory instance\n *\n * Creates a <tap-kit> Web Component programmatically and returns\n * a control object for imperative management.\n *\n * **Important for React users:**\n * This API is designed for non-React environments. For React projects,\n * use `useTapKit` hook instead for proper lifecycle management.\n *\n * @param options - Configuration options including apiKey, course info, and event handlers\n * @returns Control object for managing TapKit instance\n *\n * @example\n * ```typescript\n * // Vanilla JavaScript\n * const tapkit = createTapKit({\n * apiKey: 'your-key',\n * userId: 'user-123',\n * courseId: 'course-456',\n * clipId: 'clip-789',\n * mode: 'floating',\n * onTimelineSeek: (time, clipId) => {\n * videoElement.currentTime = time;\n * },\n * });\n *\n * // Mount to DOM (required)\n * tapkit.mount();\n *\n * // Wait for ready and show\n * await tapkit.ready;\n * tapkit.show();\n *\n * // Later: cleanup\n * tapkit.destroy();\n * ```\n */\nexport function createTapKit(options: CreateTapKitOptions): TapKitFactoryControl {\n // SSR safety: return noop stub in non-browser environment\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return createSSRStub();\n }\n\n // State (closure-based privacy)\n let element: TapKitElement | null = null;\n let isMounted = false;\n let isReady = false;\n let isDestroyed = false;\n\n // Store handlers in mutable reference for access in callbacks\n const handlers = extractHandlers(options);\n\n // Ready promise with resolver\n let readyResolve: () => void;\n let readyReject: (error: Error) => void;\n const readyPromise = new Promise<void>((resolve, reject) => {\n readyResolve = resolve;\n readyReject = reject;\n });\n\n // Queue for calls made before ready\n const pendingCalls: Array<{\n target: \"element\" | \"events\" | \"video\";\n method: string;\n args: unknown[];\n }> = [];\n\n // Cleanup functions for event subscriptions\n const cleanupFns: Array<() => void> = [];\n\n // CDN loading promise\n const cdnPromise = loadCDNLoader().catch((error) => {\n const loadError = error instanceof Error ? error : new Error(\"Failed to load TapKit CDN\");\n readyReject(loadError);\n handlers.onError?.(loadError);\n throw loadError;\n });\n\n /**\n * Create element after CDN is loaded\n */\n async function ensureElement(): Promise<TapKitElement> {\n if (isDestroyed) {\n throw new Error(\"[createTapKit] Instance has been destroyed\");\n }\n\n if (element) {\n return element;\n }\n\n await cdnPromise;\n\n // Create element\n element = document.createElement(\"tap-kit\");\n\n // Apply options\n applyOptionsToElement(element, options);\n\n // Set up event listeners\n element.addEventListener(\"tap-kit:ready\", () => {\n isReady = true;\n readyResolve();\n handlers.onReady?.();\n\n // Replay pending calls\n replayPendingCalls();\n });\n\n element.addEventListener(\"tap-kit:error\", (e) => {\n const customEvent = e as CustomEvent<{ error: Error }>;\n const error = customEvent.detail?.error ?? new Error(\"Unknown error\");\n readyReject(error);\n handlers.onError?.(error);\n });\n\n // Set up EventManager callbacks\n setupEventCallbacks();\n\n return element;\n }\n\n /**\n * Set up EventManager callbacks after element is ready\n */\n function setupEventCallbacks(): void {\n if (!element) return;\n\n // Use element.ready to ensure events API is available\n element.ready\n .then(() => {\n if (!element?.events) return;\n\n // Register stable wrappers and store cleanup functions\n const unsubTimelineSeek = element.events.onTimelineSeek((clipPlayHead, clipId) => {\n handlers.onTimelineSeek?.(clipPlayHead, clipId);\n });\n cleanupFns.push(unsubTimelineSeek);\n\n const unsubAlarmFadeIn = element.events.onAlarmFadeIn((messageInfo) => {\n handlers.onAlarmFadeIn?.(messageInfo);\n });\n cleanupFns.push(unsubAlarmFadeIn);\n })\n .catch(() => {\n // Ignore - error already handled by tap-kit:error event\n });\n }\n\n /**\n * Replay calls that were made before ready\n */\n function replayPendingCalls(): void {\n if (!element || !isReady) return;\n\n for (const call of pendingCalls) {\n try {\n if (call.target === \"element\") {\n (element as unknown as Record<string, (...args: unknown[]) => void>)[call.method]?.(...call.args);\n } else if (call.target === \"events\" && element.events) {\n (element.events as unknown as Record<string, (...args: unknown[]) => void>)[call.method]?.(...call.args);\n } else if (call.target === \"video\" && element.video) {\n (element.video as unknown as Record<string, (...args: unknown[]) => void>)[call.method]?.(...call.args);\n }\n } catch (e) {\n console.warn(`[createTapKit] Failed to replay ${call.method}:`, e);\n }\n }\n\n // Clear queue\n pendingCalls.length = 0;\n }\n\n /**\n * Queue a call to be executed when ready\n */\n function queueCall(target: \"element\" | \"events\" | \"video\", method: string, args: unknown[]): void {\n if (isReady && element) {\n // Execute immediately if ready\n try {\n if (target === \"element\") {\n (element as unknown as Record<string, (...args: unknown[]) => void>)[method]?.(...args);\n } else if (target === \"events\" && element.events) {\n (element.events as unknown as Record<string, (...args: unknown[]) => void>)[method]?.(...args);\n } else if (target === \"video\" && element.video) {\n (element.video as unknown as Record<string, (...args: unknown[]) => void>)[method]?.(...args);\n }\n } catch (e) {\n console.warn(`[createTapKit] Failed to call ${method}:`, e);\n }\n } else {\n // Queue for later\n pendingCalls.push({ target, method, args });\n }\n }\n\n // Create events proxy\n const eventsProxy: EventManager = {\n seekTimeline: (params) => queueCall(\"events\", \"seekTimeline\", [params]),\n onTimelineSeek: (callback) => {\n if (isReady && element?.events) {\n return element.events.onTimelineSeek(callback);\n }\n // Return noop unsubscribe for queued calls\n queueCall(\"events\", \"onTimelineSeek\", [callback]);\n return () => {};\n },\n onAlarmFadeIn: (handler) => {\n if (isReady && element?.events) {\n return element.events.onAlarmFadeIn(handler);\n }\n // Return noop unsubscribe for queued calls\n queueCall(\"events\", \"onAlarmFadeIn\", [handler]);\n return () => {};\n },\n };\n\n // Create video proxy\n const videoProxy: VideoController = {\n bind: (config, clipId) => queueCall(\"video\", \"bind\", [config, clipId]),\n unbind: () => queueCall(\"video\", \"unbind\", []),\n };\n\n // Start element creation (non-blocking)\n ensureElement().catch(() => {\n // Error already handled in ensureElement\n });\n\n // Return control object\n const control: TapKitFactoryControl = {\n get ready() {\n return readyPromise;\n },\n\n get isReady() {\n return isReady;\n },\n\n get isOpen() {\n return element?.isOpen ?? false;\n },\n\n get isMounted() {\n return isMounted;\n },\n\n mount(container: HTMLElement = document.body) {\n if (isDestroyed) {\n console.warn(\"[createTapKit] Cannot mount: instance destroyed\");\n return;\n }\n\n // Defensive: already mounted to this container\n if (element && element.parentNode === container) {\n console.warn(\"[createTapKit] Already mounted to this container\");\n return;\n }\n\n // If mounted elsewhere, move to new container\n if (element?.parentNode) {\n element.parentNode.removeChild(element);\n }\n\n // Wait for element creation then mount\n ensureElement()\n .then((el) => {\n if (!isDestroyed) {\n container.appendChild(el);\n isMounted = true;\n }\n })\n .catch((error) => {\n console.error(\"[createTapKit] Failed to mount:\", error);\n });\n },\n\n unmount() {\n if (element?.parentNode) {\n element.parentNode.removeChild(element);\n isMounted = false;\n }\n },\n\n show() {\n if (isDestroyed) {\n console.warn(\"[createTapKit] Cannot show: instance destroyed\");\n return;\n }\n queueCall(\"element\", \"show\", []);\n },\n\n hide() {\n if (isDestroyed) {\n console.warn(\"[createTapKit] Cannot hide: instance destroyed\");\n return;\n }\n queueCall(\"element\", \"hide\", []);\n },\n\n setCourse(course: Partial<Course> & { courseId: string; clipId: string }) {\n if (isDestroyed) {\n console.warn(\"[createTapKit] Cannot setCourse: instance destroyed\");\n return;\n }\n queueCall(\"element\", \"setCourse\", [course]);\n },\n\n get events() {\n return eventsProxy;\n },\n\n get video() {\n return videoProxy;\n },\n\n get element() {\n return element;\n },\n\n destroy() {\n if (isDestroyed) return;\n\n isDestroyed = true;\n\n // Run cleanup functions (unsubscribe event handlers)\n for (const cleanup of cleanupFns) {\n try {\n cleanup();\n } catch {\n // Ignore cleanup errors\n }\n }\n cleanupFns.length = 0;\n\n // Unmount if mounted\n if (element?.parentNode) {\n element.parentNode.removeChild(element);\n }\n\n // Clear state\n element = null;\n isMounted = false;\n isReady = false;\n pendingCalls.length = 0;\n },\n };\n\n return control;\n}\n","import type { TapKitConfig, TapKitInitParams, TapKitInstance } from \"@coxwave/tap-kit-types\";\nimport { loadCDNLoader } from \"./loader\";\n\n/**\n * TapKit - Official TapKit Web SDK\n */\nexport class TapKit implements TapKitInstance {\n private instance: TapKitInstance | null = null;\n #loading: Promise<void>;\n #config: TapKitConfig;\n #loadError: Error | null = null;\n #eventsProxy?: TapKitInstance[\"events\"]; // Cached proxy for events API\n #videoProxy?: TapKitInstance[\"video\"]; // Cached proxy for video API\n\n constructor(config: TapKitConfig) {\n this.#config = config;\n this.#loading = this.load();\n }\n\n private async load(): Promise<void> {\n try {\n await loadCDNLoader();\n\n if (!window.TapKit) {\n throw new Error(\"TapKit not available after loading CDN loader\");\n }\n\n this.instance = new window.TapKit(this.#config);\n } catch (err) {\n this.#loadError = err instanceof Error ? err : new Error(String(err));\n throw this.#loadError;\n }\n }\n\n /**\n * Promise that resolves when CDN is loaded and TapKit instance is available\n * (ready to call init())\n *\n * @example\n * ```typescript\n * const kit = new TapKit({ apiKey });\n * await kit.loaded; // CDN downloaded, window.TapKit available\n * await kit.init({...}); // Now safe to call init()\n * ```\n *\n * @see ready - For waiting until AFTER init() completes (iframe initialized)\n */\n get loaded(): Promise<void> {\n return this.#loading.then(() => {\n if (this.#loadError) {\n throw this.#loadError;\n }\n if (!this.instance) {\n throw new Error(\n `TapKit instance not initialized after loading. This may indicate a CDN loading failure for apiKey: ${this.#config.apiKey}`\n );\n }\n });\n }\n\n /**\n * Promise that resolves when SDK is fully initialized\n * (after init() is called and iframe is ready)\n *\n * @example\n * ```typescript\n * const kit = new TapKit({ apiKey });\n * await kit.init({...});\n * await kit.ready; // iframe is fully initialized\n * ```\n *\n * @see loaded - For waiting until CDN load completes (before init)\n */\n get ready(): Promise<void> {\n return this.#loading.then(() => {\n if (this.#loadError) {\n throw this.#loadError;\n }\n if (!this.instance) {\n throw new Error(\n `TapKit instance not initialized after loading. This may indicate a CDN loading failure for apiKey: ${this.#config.apiKey}`\n );\n }\n return this.instance.ready;\n });\n }\n\n /**\n * Create a proxy for video API that waits for ready before delegating\n */\n private createVideoProxy(): TapKitInstance[\"video\"] {\n return {\n bind: (config, clipId) => {\n // Queue the bind call - will execute after ready\n this.ready.then(() => {\n this.instance?.video.bind(config, clipId);\n });\n },\n unbind: () => {\n // Unbind can be called immediately if instance exists\n this.instance?.video?.unbind();\n },\n };\n }\n\n /**\n * Create events API that returns unsubscribe functions synchronously\n * Queues the actual subscription until ready, but returns unsubscribe immediately\n */\n private createEventsProxy(): TapKitInstance[\"events\"] {\n return {\n seekTimeline: (params) => {\n this.ready.then(() => {\n this.instance?.events.seekTimeline(params);\n });\n },\n onTimelineSeek: (callback) => {\n let unsubscribe: (() => void) | undefined;\n let cancelled = false;\n\n // Queue subscription\n this.ready.then(() => {\n if (cancelled) return;\n unsubscribe = this.instance?.events.onTimelineSeek(callback);\n });\n\n // Return synchronous unsubscribe function\n return () => {\n cancelled = true;\n unsubscribe?.();\n };\n },\n onAlarmFadeIn: (handler) => {\n let unsubscribe: (() => void) | undefined;\n let cancelled = false;\n\n this.ready.then(() => {\n if (cancelled) return;\n unsubscribe = this.instance?.events.onAlarmFadeIn(handler);\n });\n\n return () => {\n cancelled = true;\n unsubscribe?.();\n };\n },\n };\n }\n\n get events() {\n // Cache proxy to avoid creating new object on every access\n if (!this.#eventsProxy) {\n this.#eventsProxy = this.createEventsProxy();\n }\n return this.#eventsProxy;\n }\n\n get isOpen(): boolean {\n return this.instance?.isOpen ?? false;\n }\n\n get isInitialized(): boolean {\n return this.instance?.isInitialized ?? false;\n }\n\n get video() {\n // Cache proxy to avoid creating new object on every access\n if (!this.#videoProxy) {\n this.#videoProxy = this.createVideoProxy();\n }\n return this.#videoProxy;\n }\n\n async init(params: TapKitInitParams): Promise<() => void> {\n await this.#loading;\n if (!this.instance) {\n throw new Error(\"TapKit instance not available after loading\");\n }\n return await this.instance.init(params);\n }\n\n destroy(): void {\n if (this.instance) {\n this.instance.destroy();\n this.instance = null;\n }\n // Clear proxy references to prevent memory leaks\n this.#eventsProxy = undefined;\n this.#videoProxy = undefined;\n }\n\n show(): void {\n if (this.instance) {\n this.instance.show();\n }\n }\n\n hide(): void {\n if (this.instance) {\n this.instance.hide();\n }\n }\n\n setCourse(course: Parameters<TapKitInstance[\"setCourse\"]>[0]): void {\n if (this.instance) {\n this.instance.setCourse(course);\n }\n }\n\n get [Symbol.toStringTag]() {\n return \"TapKit\";\n }\n}\n"]}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/loader.ts","../src/factory/createTapKit.ts","../src/kit.ts"],"names":["DEFAULT_CDN_LOADER_URL","getLoaderURL","isLocalCoreMode","getLocalCoreURL","createSDKChecker","resolve","reject","timeoutMs","startTime","checkSDK","loadCDNLoader","loadingPromise","coreURL","script","loaderURL","existingScript","extractHandlers","options","createSSRStub","applyOptionsToElement","element","createTapKit","isMounted","isReady","isDestroyed","handlers","readyResolve","readyReject","readyPromise","pendingCalls","cleanupFns","cdnPromise","error","loadError","ensureElement","replayPendingCalls","e","setupEventCallbacks","unsubTimelineSeek","clipPlayHead","clipId","unsubAlarmFadeIn","messageInfo","call","queueCall","target","method","args","eventsProxy","params","callback","handler","videoProxy","config","container","el","course","cleanup","_loading","_config","_loadError","_eventsProxy","_videoProxy","TapKit","__publicField","__privateAdd","__privateSet","__privateGet","err","unsubscribe","cancelled"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * CDN loader for TapKit\n * Dynamically loads the TapKit SDK from CDN\n *\n * For local testing, you can override the loader URL:\n * window.__TAP_KIT_LOADER_URL__ = '/tap-kit-core/loader.js';\n *\n * For local development (bypass loader, load IIFE directly):\n * window.__TAP_KIT_CORE_URL__ = '/packages/tap-kit-core/dist/index.global.js';\n */\n\n// Build-time constant injected by tsup define\ndeclare const __DEFAULT_CDN_LOADER_URL__: string;\n\nconst DEFAULT_CDN_LOADER_URL =\n typeof __DEFAULT_CDN_LOADER_URL__ !== \"undefined\"\n ? __DEFAULT_CDN_LOADER_URL__\n : \"https://files.edutap.ai/tap-sdk/loader.js\";\nconst DEFAULT_TIMEOUT_MS = 4000; // 4 seconds total timeout\nconst IDLE_CALLBACK_TIMEOUT_MS = 500; // 500ms for requestIdleCallback\n\n/**\n * Get the loader URL from window override or default CDN\n */\nfunction getLoaderURL(): string {\n return window?.__TAP_KIT_LOADER_URL__\n ? window.__TAP_KIT_LOADER_URL__\n : DEFAULT_CDN_LOADER_URL;\n}\n\n/**\n * Check if local core mode is enabled\n * When __TAP_KIT_CORE_URL__ is set, directly load the IIFE bundle\n * This bypasses the loader.js and loads tap-kit-core directly\n */\nfunction isLocalCoreMode(): boolean {\n return typeof window !== \"undefined\" && !!window.__TAP_KIT_CORE_URL__;\n}\n\n/**\n * Get the local core URL\n */\nfunction getLocalCoreURL(): string {\n return window.__TAP_KIT_CORE_URL__ || \"\";\n}\n\n/**\n * Creates a SDK checker function with timeout and retry logic\n * Uses requestIdleCallback to avoid blocking browser rendering\n * @param resolve - Promise resolve function\n * @param reject - Promise reject function\n * @param timeoutMs - Maximum time to wait for SDK to load (milliseconds)\n * @returns Checker function to be called repeatedly\n */\nfunction createSDKChecker(\n resolve: (value: void | PromiseLike<void>) => void,\n reject: (reason?: unknown) => void,\n timeoutMs: number,\n): () => void {\n const startTime = Date.now();\n\n const checkSDK = (): void => {\n // Check if real TapKit is loaded (not just stub)\n // Stub has TapKitLoaded flag set to true by loader.js after real SDK loads\n if (window.TapKit && window.TapKitLoaded === true) {\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n return;\n }\n\n const elapsed = Date.now() - startTime;\n\n // Check if exceeded timeout\n if (elapsed > timeoutMs) {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(\n new Error(\n `TapKit loader timeout: SDK not available after ${timeoutMs}ms`,\n ),\n );\n return;\n }\n\n // Use requestIdleCallback for better performance\n // Falls back to setTimeout if not available\n if (typeof requestIdleCallback !== \"undefined\") {\n requestIdleCallback(checkSDK, { timeout: IDLE_CALLBACK_TIMEOUT_MS });\n } else {\n setTimeout(checkSDK, IDLE_CALLBACK_TIMEOUT_MS);\n }\n };\n\n return checkSDK;\n}\n\n/**\n * Loads the CDN loader script\n * The loader will then fetch versions.json and load the appropriate SDK version\n *\n * If __TAP_KIT_CORE_URL__ is set, bypasses loader and loads IIFE directly\n *\n * @param timeoutMs - Maximum time to wait for SDK to load (default: 4000ms)\n * @returns Promise that resolves when SDK is loaded\n * @throws {Error} If loader fails to load or times out\n */\nexport function loadCDNLoader(\n timeoutMs: number = DEFAULT_TIMEOUT_MS,\n): Promise<void> {\n // If already loaded, return immediately\n if (window.__TAP_KIT_LOADER_LOADED__ && window.TapKit) {\n return Promise.resolve();\n }\n\n // If currently loading, return the existing promise\n if (window.__TAP_KIT_LOADER_LOADING__) {\n return window.__TAP_KIT_LOADER_LOADING__;\n }\n\n // Create loading promise\n const loadingPromise = new Promise<void>((resolve, reject) => {\n if (typeof document === \"undefined\") {\n reject(\n new Error(\n \"TapKit requires browser environment (document is undefined)\",\n ),\n );\n return;\n }\n\n // Local core mode: Load IIFE directly\n if (isLocalCoreMode()) {\n // Check if TapKit is already loaded (from other pages in playground)\n if (window.TapKit && window.TapKitLoaded === true) {\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n return;\n }\n\n const coreURL = getLocalCoreURL();\n\n const script = document.createElement(\"script\");\n script.src = coreURL;\n script.async = true;\n\n script.onload = () => {\n // IIFE directly sets window.TapKit\n // Set the loaded flag manually since we bypass loader.js\n if (window.TapKit) {\n window.TapKitLoaded = true;\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n } else {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(\"TapKit not available after loading local core\"));\n }\n };\n\n script.onerror = () => {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`Failed to load local TapKit core: ${coreURL}`));\n };\n\n document.head.appendChild(script);\n return;\n }\n\n // CDN mode: Load loader.js\n const loaderURL = getLoaderURL();\n const script = document.createElement(\"script\");\n script.src = loaderURL;\n script.async = true;\n\n script.onload = () => {\n // The loader script will load the actual SDK\n // We need to wait a bit for the loader to fetch and load the SDK\n const checkSDK = createSDKChecker(resolve, reject, timeoutMs);\n checkSDK();\n };\n\n script.onerror = () => {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`));\n };\n\n // Check if script already exists\n const existingScript = document.querySelector(`script[src=\"${loaderURL}\"]`);\n\n if (existingScript) {\n // Script already added but not yet loaded\n existingScript.addEventListener(\"load\", () => {\n const checkSDK = createSDKChecker(resolve, reject, timeoutMs);\n checkSDK();\n });\n existingScript.addEventListener(\"error\", () =>\n reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`)),\n );\n } else {\n document.head.appendChild(script);\n }\n });\n\n window.__TAP_KIT_LOADER_LOADING__ = loadingPromise;\n return loadingPromise;\n}\n","/**\n * createTapKit - Factory API for TapKit\n *\n * Non-React, framework-agnostic API with full configuration support.\n * Wraps the <tap-kit> Web Component with imperative control.\n *\n * @example\n * ```typescript\n * import { createTapKit } from '@coxwave/tap-kit';\n *\n * const tapkit = createTapKit({\n * apiKey: 'your-key',\n * userId: 'user-123',\n * courseId: 'course-456',\n * clipId: 'clip-789',\n * apiUrl: 'https://tapapistaging.coxwave.link',\n * onReady: () => console.log('Ready!'),\n * });\n *\n * tapkit.mount();\n * await tapkit.ready;\n * tapkit.show();\n * ```\n *\n * @see useTapKit for React projects (recommended)\n */\n\nimport type {\n Course,\n EventManager,\n TapKitElement,\n VideoController,\n} from \"@coxwave/tap-kit-types\";\nimport { loadCDNLoader } from \"../loader\";\nimport type {\n CreateTapKitOptions,\n FactoryEventHandlers,\n TapKitFactoryControl,\n} from \"./types\";\n\n/**\n * Extract event handlers from options\n */\nfunction extractHandlers(options: CreateTapKitOptions): FactoryEventHandlers {\n return {\n onReady: options.onReady,\n onError: options.onError,\n onTimelineSeek: options.onTimelineSeek,\n onAlarmFadeIn: options.onAlarmFadeIn,\n };\n}\n\n/**\n * Create a noop stub for SSR environments\n */\nfunction createSSRStub(): TapKitFactoryControl {\n const noopPromise = Promise.resolve();\n\n return {\n ready: noopPromise,\n isReady: false,\n isOpen: false,\n isMounted: false,\n mount: () => {},\n unmount: () => {},\n show: () => {},\n hide: () => {},\n setCourse: () => {},\n events: {\n seekTimeline: () => {},\n onTimelineSeek: () => () => {},\n onAlarmFadeIn: () => () => {},\n },\n video: {\n bind: () => {},\n unbind: () => {},\n },\n element: null,\n destroy: () => {},\n };\n}\n\n/**\n * Apply configuration options to TapKit element\n */\nfunction applyOptionsToElement(\n element: TapKitElement,\n options: CreateTapKitOptions,\n): void {\n // Required\n element.apiKey = options.apiKey;\n\n // Course info\n if (options.userId !== undefined) element.userId = options.userId;\n if (options.courseId !== undefined) element.courseId = options.courseId;\n if (options.clipId !== undefined) element.clipId = options.clipId;\n if (options.clipPlayHead !== undefined)\n element.clipPlayHead = options.clipPlayHead;\n\n // Display options\n if (options.mode !== undefined) element.mode = options.mode;\n if (options.language !== undefined) element.language = options.language;\n if (options.allowLayoutToggle !== undefined)\n element.allowLayoutToggle = options.allowLayoutToggle;\n if (options.buttonId !== undefined) element.buttonId = options.buttonId;\n\n // Environment options\n if (options.apiUrl !== undefined) element.apiUrl = options.apiUrl;\n if (options.tapUrl !== undefined) element.tapUrl = options.tapUrl;\n if (options.environment !== undefined)\n element.environment = options.environment;\n\n // Video target\n if (options.videoTarget !== undefined)\n element.videoTarget = options.videoTarget;\n\n // Debug\n if (options.debug !== undefined) element.debug = options.debug;\n}\n\n/**\n * Create TapKit factory instance\n *\n * Creates a <tap-kit> Web Component programmatically and returns\n * a control object for imperative management.\n *\n * **Important for React users:**\n * This API is designed for non-React environments. For React projects,\n * use `useTapKit` hook instead for proper lifecycle management.\n *\n * @param options - Configuration options including apiKey, course info, and event handlers\n * @returns Control object for managing TapKit instance\n *\n * @example\n * ```typescript\n * // Vanilla JavaScript\n * const tapkit = createTapKit({\n * apiKey: 'your-key',\n * userId: 'user-123',\n * courseId: 'course-456',\n * clipId: 'clip-789',\n * mode: 'floating',\n * onTimelineSeek: (time, clipId) => {\n * videoElement.currentTime = time;\n * },\n * });\n *\n * // Mount to DOM (required)\n * tapkit.mount();\n *\n * // Wait for ready and show\n * await tapkit.ready;\n * tapkit.show();\n *\n * // Later: cleanup\n * tapkit.destroy();\n * ```\n */\nexport function createTapKit(\n options: CreateTapKitOptions,\n): TapKitFactoryControl {\n // SSR safety: return noop stub in non-browser environment\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return createSSRStub();\n }\n\n // State (closure-based privacy)\n let element: TapKitElement | null = null;\n let isMounted = false;\n let isReady = false;\n let isDestroyed = false;\n\n // Store handlers in mutable reference for access in callbacks\n const handlers = extractHandlers(options);\n\n // Ready promise with resolver\n let readyResolve: () => void;\n let readyReject: (error: Error) => void;\n const readyPromise = new Promise<void>((resolve, reject) => {\n readyResolve = resolve;\n readyReject = reject;\n });\n\n // Queue for calls made before ready\n const pendingCalls: Array<{\n target: \"element\" | \"events\" | \"video\";\n method: string;\n args: unknown[];\n }> = [];\n\n // Cleanup functions for event subscriptions\n const cleanupFns: Array<() => void> = [];\n\n // CDN loading promise\n const cdnPromise = loadCDNLoader().catch((error) => {\n const loadError =\n error instanceof Error ? error : new Error(\"Failed to load TapKit CDN\");\n readyReject(loadError);\n handlers.onError?.(loadError);\n throw loadError;\n });\n\n /**\n * Create element after CDN is loaded\n */\n async function ensureElement(): Promise<TapKitElement> {\n if (isDestroyed) {\n throw new Error(\"[createTapKit] Instance has been destroyed\");\n }\n\n if (element) {\n return element;\n }\n\n await cdnPromise;\n\n // Create element\n element = document.createElement(\"tap-kit\");\n\n // Apply options\n applyOptionsToElement(element, options);\n\n // Set up event listeners\n element.addEventListener(\"tap-kit:ready\", () => {\n isReady = true;\n readyResolve();\n handlers.onReady?.();\n\n // Replay pending calls\n replayPendingCalls();\n });\n\n element.addEventListener(\"tap-kit:error\", (e) => {\n const customEvent = e as CustomEvent<{ error: Error }>;\n const error = customEvent.detail?.error ?? new Error(\"Unknown error\");\n readyReject(error);\n handlers.onError?.(error);\n });\n\n // Set up EventManager callbacks\n setupEventCallbacks();\n\n return element;\n }\n\n /**\n * Set up EventManager callbacks after element is ready\n */\n function setupEventCallbacks(): void {\n if (!element) return;\n\n // Use element.ready to ensure events API is available\n element.ready\n .then(() => {\n if (!element?.events) return;\n\n // Register stable wrappers and store cleanup functions\n const unsubTimelineSeek = element.events.onTimelineSeek(\n (clipPlayHead, clipId) => {\n handlers.onTimelineSeek?.(clipPlayHead, clipId);\n },\n );\n cleanupFns.push(unsubTimelineSeek);\n\n const unsubAlarmFadeIn = element.events.onAlarmFadeIn((messageInfo) => {\n handlers.onAlarmFadeIn?.(messageInfo);\n });\n cleanupFns.push(unsubAlarmFadeIn);\n })\n .catch(() => {\n // Ignore - error already handled by tap-kit:error event\n });\n }\n\n /**\n * Replay calls that were made before ready\n */\n function replayPendingCalls(): void {\n if (!element || !isReady) return;\n\n for (const call of pendingCalls) {\n try {\n if (call.target === \"element\") {\n (element as unknown as Record<string, (...args: unknown[]) => void>)[\n call.method\n ]?.(...call.args);\n } else if (call.target === \"events\" && element.events) {\n (\n element.events as unknown as Record<\n string,\n (...args: unknown[]) => void\n >\n )[call.method]?.(...call.args);\n } else if (call.target === \"video\" && element.video) {\n (\n element.video as unknown as Record<\n string,\n (...args: unknown[]) => void\n >\n )[call.method]?.(...call.args);\n }\n } catch (e) {\n console.warn(`[createTapKit] Failed to replay ${call.method}:`, e);\n }\n }\n\n // Clear queue\n pendingCalls.length = 0;\n }\n\n /**\n * Queue a call to be executed when ready\n */\n function queueCall(\n target: \"element\" | \"events\" | \"video\",\n method: string,\n args: unknown[],\n ): void {\n if (isReady && element) {\n // Execute immediately if ready\n try {\n if (target === \"element\") {\n (element as unknown as Record<string, (...args: unknown[]) => void>)[\n method\n ]?.(...args);\n } else if (target === \"events\" && element.events) {\n (\n element.events as unknown as Record<\n string,\n (...args: unknown[]) => void\n >\n )[method]?.(...args);\n } else if (target === \"video\" && element.video) {\n (\n element.video as unknown as Record<\n string,\n (...args: unknown[]) => void\n >\n )[method]?.(...args);\n }\n } catch (e) {\n console.warn(`[createTapKit] Failed to call ${method}:`, e);\n }\n } else {\n // Queue for later\n pendingCalls.push({ target, method, args });\n }\n }\n\n // Create events proxy\n const eventsProxy: EventManager = {\n seekTimeline: (params) => queueCall(\"events\", \"seekTimeline\", [params]),\n onTimelineSeek: (callback) => {\n if (isReady && element?.events) {\n return element.events.onTimelineSeek(callback);\n }\n // Return noop unsubscribe for queued calls\n queueCall(\"events\", \"onTimelineSeek\", [callback]);\n return () => {};\n },\n onAlarmFadeIn: (handler) => {\n if (isReady && element?.events) {\n return element.events.onAlarmFadeIn(handler);\n }\n // Return noop unsubscribe for queued calls\n queueCall(\"events\", \"onAlarmFadeIn\", [handler]);\n return () => {};\n },\n };\n\n // Create video proxy\n const videoProxy: VideoController = {\n bind: (config, clipId) => queueCall(\"video\", \"bind\", [config, clipId]),\n unbind: () => queueCall(\"video\", \"unbind\", []),\n };\n\n // Start element creation (non-blocking)\n ensureElement().catch(() => {\n // Error already handled in ensureElement\n });\n\n // Return control object\n const control: TapKitFactoryControl = {\n get ready() {\n return readyPromise;\n },\n\n get isReady() {\n return isReady;\n },\n\n get isOpen() {\n return element?.isOpen ?? false;\n },\n\n get isMounted() {\n return isMounted;\n },\n\n mount(container: HTMLElement = document.body) {\n if (isDestroyed) {\n console.warn(\"[createTapKit] Cannot mount: instance destroyed\");\n return;\n }\n\n // Defensive: already mounted to this container\n if (element && element.parentNode === container) {\n console.warn(\"[createTapKit] Already mounted to this container\");\n return;\n }\n\n // If mounted elsewhere, move to new container\n if (element?.parentNode) {\n element.parentNode.removeChild(element);\n }\n\n // Wait for element creation then mount\n ensureElement()\n .then((el) => {\n if (!isDestroyed) {\n container.appendChild(el);\n isMounted = true;\n }\n })\n .catch((error) => {\n console.error(\"[createTapKit] Failed to mount:\", error);\n });\n },\n\n unmount() {\n if (element?.parentNode) {\n element.parentNode.removeChild(element);\n isMounted = false;\n }\n },\n\n show() {\n if (isDestroyed) {\n console.warn(\"[createTapKit] Cannot show: instance destroyed\");\n return;\n }\n queueCall(\"element\", \"show\", []);\n },\n\n hide() {\n if (isDestroyed) {\n console.warn(\"[createTapKit] Cannot hide: instance destroyed\");\n return;\n }\n queueCall(\"element\", \"hide\", []);\n },\n\n setCourse(course: Partial<Course> & { courseId: string; clipId: string }) {\n if (isDestroyed) {\n console.warn(\"[createTapKit] Cannot setCourse: instance destroyed\");\n return;\n }\n queueCall(\"element\", \"setCourse\", [course]);\n },\n\n get events() {\n return eventsProxy;\n },\n\n get video() {\n return videoProxy;\n },\n\n get element() {\n return element;\n },\n\n destroy() {\n if (isDestroyed) return;\n\n isDestroyed = true;\n\n // Run cleanup functions (unsubscribe event handlers)\n for (const cleanup of cleanupFns) {\n try {\n cleanup();\n } catch {\n // Ignore cleanup errors\n }\n }\n cleanupFns.length = 0;\n\n // Unmount if mounted\n if (element?.parentNode) {\n element.parentNode.removeChild(element);\n }\n\n // Clear state\n element = null;\n isMounted = false;\n isReady = false;\n pendingCalls.length = 0;\n },\n };\n\n return control;\n}\n","import type { TapKitConfig, TapKitInitParams, TapKitInstance } from \"@coxwave/tap-kit-types\";\nimport { loadCDNLoader } from \"./loader\";\n\n/**\n * TapKit - Official TapKit Web SDK\n */\nexport class TapKit implements TapKitInstance {\n private instance: TapKitInstance | null = null;\n #loading: Promise<void>;\n #config: TapKitConfig;\n #loadError: Error | null = null;\n #eventsProxy?: TapKitInstance[\"events\"]; // Cached proxy for events API\n #videoProxy?: TapKitInstance[\"video\"]; // Cached proxy for video API\n\n constructor(config: TapKitConfig) {\n this.#config = config;\n this.#loading = this.load();\n }\n\n private async load(): Promise<void> {\n try {\n await loadCDNLoader();\n\n if (!window.TapKit) {\n throw new Error(\"TapKit not available after loading CDN loader\");\n }\n\n this.instance = new window.TapKit(this.#config);\n } catch (err) {\n this.#loadError = err instanceof Error ? err : new Error(String(err));\n throw this.#loadError;\n }\n }\n\n /**\n * Promise that resolves when CDN is loaded and TapKit instance is available\n * (ready to call init())\n *\n * @example\n * ```typescript\n * const kit = new TapKit({ apiKey });\n * await kit.loaded; // CDN downloaded, window.TapKit available\n * await kit.init({...}); // Now safe to call init()\n * ```\n *\n * @see ready - For waiting until AFTER init() completes (iframe initialized)\n */\n get loaded(): Promise<void> {\n return this.#loading.then(() => {\n if (this.#loadError) {\n throw this.#loadError;\n }\n if (!this.instance) {\n throw new Error(\n `TapKit instance not initialized after loading. This may indicate a CDN loading failure for apiKey: ${\n this.#config.apiKey\n }`\n );\n }\n });\n }\n\n /**\n * Promise that resolves when SDK is fully initialized\n * (after init() is called and iframe is ready)\n *\n * @example\n * ```typescript\n * const kit = new TapKit({ apiKey });\n * await kit.init({...});\n * await kit.ready; // iframe is fully initialized\n * ```\n *\n * @see loaded - For waiting until CDN load completes (before init)\n */\n get ready(): Promise<void> {\n return this.#loading.then(() => {\n if (this.#loadError) {\n throw this.#loadError;\n }\n if (!this.instance) {\n throw new Error(\n `TapKit instance not initialized after loading. This may indicate a CDN loading failure for apiKey: ${\n this.#config.apiKey\n }`\n );\n }\n return this.instance.ready;\n });\n }\n\n /**\n * Create a proxy for video API that waits for ready before delegating\n */\n private createVideoProxy(): TapKitInstance[\"video\"] {\n return {\n bind: (config, clipId) => {\n // Queue the bind call - will execute after ready\n this.ready.then(() => {\n this.instance?.video.bind(config, clipId);\n });\n },\n unbind: () => {\n // Unbind can be called immediately if instance exists\n this.instance?.video?.unbind();\n },\n };\n }\n\n /**\n * Create events API that returns unsubscribe functions synchronously\n * Queues the actual subscription until ready, but returns unsubscribe immediately\n */\n private createEventsProxy(): TapKitInstance[\"events\"] {\n return {\n seekTimeline: (params) => {\n this.ready.then(() => {\n this.instance?.events.seekTimeline(params);\n });\n },\n onTimelineSeek: (callback) => {\n let unsubscribe: (() => void) | undefined;\n let cancelled = false;\n\n // Queue subscription\n this.ready.then(() => {\n if (cancelled) return;\n unsubscribe = this.instance?.events.onTimelineSeek(callback);\n });\n\n // Return synchronous unsubscribe function\n return () => {\n cancelled = true;\n unsubscribe?.();\n };\n },\n onAlarmFadeIn: (handler) => {\n let unsubscribe: (() => void) | undefined;\n let cancelled = false;\n\n this.ready.then(() => {\n if (cancelled) return;\n unsubscribe = this.instance?.events.onAlarmFadeIn(handler);\n });\n\n return () => {\n cancelled = true;\n unsubscribe?.();\n };\n },\n };\n }\n\n get events() {\n // Cache proxy to avoid creating new object on every access\n if (!this.#eventsProxy) {\n this.#eventsProxy = this.createEventsProxy();\n }\n return this.#eventsProxy;\n }\n\n get isOpen(): boolean {\n return this.instance?.isOpen ?? false;\n }\n\n get isInitialized(): boolean {\n return this.instance?.isInitialized ?? false;\n }\n\n get video() {\n // Cache proxy to avoid creating new object on every access\n if (!this.#videoProxy) {\n this.#videoProxy = this.createVideoProxy();\n }\n return this.#videoProxy;\n }\n\n async init(params: TapKitInitParams): Promise<() => void> {\n await this.#loading;\n if (!this.instance) {\n throw new Error(\"TapKit instance not available after loading\");\n }\n return await this.instance.init(params);\n }\n\n destroy(): void {\n if (this.instance) {\n this.instance.destroy();\n this.instance = null;\n }\n // Clear proxy references to prevent memory leaks\n this.#eventsProxy = undefined;\n this.#videoProxy = undefined;\n }\n\n show(): void {\n if (this.instance) {\n this.instance.show();\n }\n }\n\n hide(): void {\n if (this.instance) {\n this.instance.hide();\n }\n }\n\n setCourse(course: Parameters<TapKitInstance[\"setCourse\"]>[0]): void {\n if (this.instance) {\n this.instance.setCourse(course);\n }\n }\n\n get [Symbol.toStringTag]() {\n return \"TapKit\";\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/loader.ts","../src/factory/createTapKit.ts","../src/kit.ts"],"names":["DEFAULT_CDN_LOADER_URL","getLoaderURL","isLocalCoreMode","getLocalCoreURL","createSDKChecker","resolve","reject","timeoutMs","startTime","checkSDK","loadCDNLoader","loadingPromise","coreURL","script","loaderURL","existingScript","extractHandlers","options","createSSRStub","applyOptionsToElement","element","createTapKit","isMounted","isReady","isDestroyed","handlers","readyResolve","readyReject","readyPromise","pendingCalls","cleanupFns","cdnPromise","error","loadError","ensureElement","replayPendingCalls","e","setupEventCallbacks","unsubTimelineSeek","clipPlayHead","clipId","unsubAlarmFadeIn","messageInfo","call","queueCall","target","method","args","eventsProxy","params","callback","handler","videoProxy","config","container","el","course","cleanup","_loading","_config","_loadError","_eventsProxy","_videoProxy","TapKit","__publicField","__privateAdd","__privateSet","__privateGet","err","unsubscribe","cancelled"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * CDN loader for TapKit\n * Dynamically loads the TapKit SDK from CDN\n *\n * For local testing, you can override the loader URL:\n * window.__TAP_KIT_LOADER_URL__ = '/tap-kit-core/loader.js';\n *\n * For local development (bypass loader, load IIFE directly):\n * window.__TAP_KIT_CORE_URL__ = '/packages/tap-kit-core/dist/index.global.js';\n */\n\n// Build-time constant injected by tsup define\ndeclare const __DEFAULT_CDN_LOADER_URL__: string;\n\nconst DEFAULT_CDN_LOADER_URL =\n typeof __DEFAULT_CDN_LOADER_URL__ !== \"undefined\" ? __DEFAULT_CDN_LOADER_URL__ : \"https://files.edutap.ai/tap-sdk/loader.js\";\nconst DEFAULT_TIMEOUT_MS = 4000; // 4 seconds total timeout\nconst IDLE_CALLBACK_TIMEOUT_MS = 500; // 500ms for requestIdleCallback\n\n/**\n * Get the loader URL from window override or default CDN\n */\nfunction getLoaderURL(): string {\n return window?.__TAP_KIT_LOADER_URL__ ? window.__TAP_KIT_LOADER_URL__ : DEFAULT_CDN_LOADER_URL;\n}\n\n/**\n * Check if local core mode is enabled\n * When __TAP_KIT_CORE_URL__ is set, directly load the IIFE bundle\n * This bypasses the loader.js and loads tap-kit-core directly\n */\nfunction isLocalCoreMode(): boolean {\n return typeof window !== \"undefined\" && !!window.__TAP_KIT_CORE_URL__;\n}\n\n/**\n * Get the local core URL\n */\nfunction getLocalCoreURL(): string {\n return window.__TAP_KIT_CORE_URL__ || \"\";\n}\n\n/**\n * Creates a SDK checker function with timeout and retry logic\n * Uses requestIdleCallback to avoid blocking browser rendering\n * @param resolve - Promise resolve function\n * @param reject - Promise reject function\n * @param timeoutMs - Maximum time to wait for SDK to load (milliseconds)\n * @returns Checker function to be called repeatedly\n */\nfunction createSDKChecker(\n resolve: (value: void | PromiseLike<void>) => void,\n reject: (reason?: unknown) => void,\n timeoutMs: number\n): () => void {\n const startTime = Date.now();\n\n const checkSDK = (): void => {\n // Check if real TapKit is loaded (not just stub)\n // Stub has TapKitLoaded flag set to true by loader.js after real SDK loads\n if (window.TapKit && window.TapKitLoaded === true) {\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n return;\n }\n\n const elapsed = Date.now() - startTime;\n\n // Check if exceeded timeout\n if (elapsed > timeoutMs) {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`TapKit loader timeout: SDK not available after ${timeoutMs}ms`));\n return;\n }\n\n // Use requestIdleCallback for better performance\n // Falls back to setTimeout if not available\n if (typeof requestIdleCallback !== \"undefined\") {\n requestIdleCallback(checkSDK, { timeout: IDLE_CALLBACK_TIMEOUT_MS });\n } else {\n setTimeout(checkSDK, IDLE_CALLBACK_TIMEOUT_MS);\n }\n };\n\n return checkSDK;\n}\n\n/**\n * Loads the CDN loader script\n * The loader will then fetch versions.json and load the appropriate SDK version\n *\n * If __TAP_KIT_CORE_URL__ is set, bypasses loader and loads IIFE directly\n *\n * @param timeoutMs - Maximum time to wait for SDK to load (default: 4000ms)\n * @returns Promise that resolves when SDK is loaded\n * @throws {Error} If loader fails to load or times out\n */\nexport function loadCDNLoader(timeoutMs: number = DEFAULT_TIMEOUT_MS): Promise<void> {\n // If already loaded, return immediately\n if (window.__TAP_KIT_LOADER_LOADED__ && window.TapKit) {\n return Promise.resolve();\n }\n\n // If currently loading, return the existing promise\n if (window.__TAP_KIT_LOADER_LOADING__) {\n return window.__TAP_KIT_LOADER_LOADING__;\n }\n\n // Create loading promise\n const loadingPromise = new Promise<void>((resolve, reject) => {\n if (typeof document === \"undefined\") {\n reject(new Error(\"TapKit requires browser environment (document is undefined)\"));\n return;\n }\n\n // Local core mode: Load IIFE directly\n if (isLocalCoreMode()) {\n // Check if TapKit is already loaded (from other pages in playground)\n if (window.TapKit && window.TapKitLoaded === true) {\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n return;\n }\n\n const coreURL = getLocalCoreURL();\n\n const script = document.createElement(\"script\");\n script.src = coreURL;\n script.async = true;\n\n script.onload = () => {\n // IIFE directly sets window.TapKit\n // Set the loaded flag manually since we bypass loader.js\n if (window.TapKit) {\n window.TapKitLoaded = true;\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n } else {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(\"TapKit not available after loading local core\"));\n }\n };\n\n script.onerror = () => {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`Failed to load local TapKit core: ${coreURL}`));\n };\n\n document.head.appendChild(script);\n return;\n }\n\n // CDN mode: Load loader.js\n const loaderURL = getLoaderURL();\n const script = document.createElement(\"script\");\n script.src = loaderURL;\n script.async = true;\n\n script.onload = () => {\n // The loader script will load the actual SDK\n // We need to wait a bit for the loader to fetch and load the SDK\n const checkSDK = createSDKChecker(resolve, reject, timeoutMs);\n checkSDK();\n };\n\n script.onerror = () => {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`));\n };\n\n // Check if script already exists\n const existingScript = document.querySelector(`script[src=\"${loaderURL}\"]`);\n\n if (existingScript) {\n // Script already added but not yet loaded\n existingScript.addEventListener(\"load\", () => {\n const checkSDK = createSDKChecker(resolve, reject, timeoutMs);\n checkSDK();\n });\n existingScript.addEventListener(\"error\", () => reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`)));\n } else {\n document.head.appendChild(script);\n }\n });\n\n window.__TAP_KIT_LOADER_LOADING__ = loadingPromise;\n return loadingPromise;\n}\n","/**\n * createTapKit - Factory API for TapKit\n *\n * Non-React, framework-agnostic API with full configuration support.\n * Wraps the <tap-kit> Web Component with imperative control.\n *\n * @example\n * ```typescript\n * import { createTapKit } from '@coxwave/tap-kit';\n *\n * const tapkit = createTapKit({\n * apiKey: 'your-key',\n * userId: 'user-123',\n * courseId: 'course-456',\n * clipId: 'clip-789',\n * apiUrl: 'https://tapapistaging.coxwave.link',\n * onReady: () => console.log('Ready!'),\n * });\n *\n * tapkit.mount();\n * await tapkit.ready;\n * tapkit.show();\n * ```\n *\n * @see useTapKit for React projects (recommended)\n */\n\nimport type { Course, EventManager, TapKitElement, VideoController } from \"@coxwave/tap-kit-types\";\nimport { loadCDNLoader } from \"../loader\";\nimport type { CreateTapKitOptions, FactoryEventHandlers, TapKitFactoryControl } from \"./types\";\n\n/**\n * Extract event handlers from options\n */\nfunction extractHandlers(options: CreateTapKitOptions): FactoryEventHandlers {\n return {\n onReady: options.onReady,\n onError: options.onError,\n onTimelineSeek: options.onTimelineSeek,\n onAlarmFadeIn: options.onAlarmFadeIn,\n };\n}\n\n/**\n * Create a noop stub for SSR environments\n */\nfunction createSSRStub(): TapKitFactoryControl {\n const noopPromise = Promise.resolve();\n\n return {\n ready: noopPromise,\n isReady: false,\n isOpen: false,\n isMounted: false,\n mount: () => {},\n unmount: () => {},\n show: () => {},\n hide: () => {},\n setCourse: () => {},\n events: {\n seekTimeline: () => {},\n onTimelineSeek: () => () => {},\n onAlarmFadeIn: () => () => {},\n },\n video: {\n bind: () => {},\n unbind: () => {},\n },\n element: null,\n destroy: () => {},\n };\n}\n\n/**\n * Apply configuration options to TapKit element\n */\nfunction applyOptionsToElement(element: TapKitElement, options: CreateTapKitOptions): void {\n // Required\n element.apiKey = options.apiKey;\n\n // Course info\n if (options.userId !== undefined) element.userId = options.userId;\n if (options.courseId !== undefined) element.courseId = options.courseId;\n if (options.clipId !== undefined) element.clipId = options.clipId;\n if (options.clipPlayHead !== undefined) element.clipPlayHead = options.clipPlayHead;\n\n // Display options\n if (options.mode !== undefined) element.mode = options.mode;\n if (options.language !== undefined) element.language = options.language;\n if (options.allowLayoutToggle !== undefined) element.allowLayoutToggle = options.allowLayoutToggle;\n if (options.buttonId !== undefined) element.buttonId = options.buttonId;\n\n // Environment options\n if (options.apiUrl !== undefined) element.apiUrl = options.apiUrl;\n if (options.tapUrl !== undefined) element.tapUrl = options.tapUrl;\n if (options.environment !== undefined) element.environment = options.environment;\n\n // Video target\n if (options.videoTarget !== undefined) element.videoTarget = options.videoTarget;\n\n // Debug\n if (options.debug !== undefined) element.debug = options.debug;\n}\n\n/**\n * Create TapKit factory instance\n *\n * Creates a <tap-kit> Web Component programmatically and returns\n * a control object for imperative management.\n *\n * **Important for React users:**\n * This API is designed for non-React environments. For React projects,\n * use `useTapKit` hook instead for proper lifecycle management.\n *\n * @param options - Configuration options including apiKey, course info, and event handlers\n * @returns Control object for managing TapKit instance\n *\n * @example\n * ```typescript\n * // Vanilla JavaScript\n * const tapkit = createTapKit({\n * apiKey: 'your-key',\n * userId: 'user-123',\n * courseId: 'course-456',\n * clipId: 'clip-789',\n * mode: 'floating',\n * onTimelineSeek: (time, clipId) => {\n * videoElement.currentTime = time;\n * },\n * });\n *\n * // Mount to DOM (required)\n * tapkit.mount();\n *\n * // Wait for ready and show\n * await tapkit.ready;\n * tapkit.show();\n *\n * // Later: cleanup\n * tapkit.destroy();\n * ```\n */\nexport function createTapKit(options: CreateTapKitOptions): TapKitFactoryControl {\n // SSR safety: return noop stub in non-browser environment\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return createSSRStub();\n }\n\n // State (closure-based privacy)\n let element: TapKitElement | null = null;\n let isMounted = false;\n let isReady = false;\n let isDestroyed = false;\n\n // Store handlers in mutable reference for access in callbacks\n const handlers = extractHandlers(options);\n\n // Ready promise with resolver\n let readyResolve: () => void;\n let readyReject: (error: Error) => void;\n const readyPromise = new Promise<void>((resolve, reject) => {\n readyResolve = resolve;\n readyReject = reject;\n });\n\n // Queue for calls made before ready\n const pendingCalls: Array<{\n target: \"element\" | \"events\" | \"video\";\n method: string;\n args: unknown[];\n }> = [];\n\n // Cleanup functions for event subscriptions\n const cleanupFns: Array<() => void> = [];\n\n // CDN loading promise\n const cdnPromise = loadCDNLoader().catch((error) => {\n const loadError = error instanceof Error ? error : new Error(\"Failed to load TapKit CDN\");\n readyReject(loadError);\n handlers.onError?.(loadError);\n throw loadError;\n });\n\n /**\n * Create element after CDN is loaded\n */\n async function ensureElement(): Promise<TapKitElement> {\n if (isDestroyed) {\n throw new Error(\"[createTapKit] Instance has been destroyed\");\n }\n\n if (element) {\n return element;\n }\n\n await cdnPromise;\n\n // Create element\n element = document.createElement(\"tap-kit\");\n\n // Apply options\n applyOptionsToElement(element, options);\n\n // Set up event listeners\n element.addEventListener(\"tap-kit:ready\", () => {\n isReady = true;\n readyResolve();\n handlers.onReady?.();\n\n // Replay pending calls\n replayPendingCalls();\n });\n\n element.addEventListener(\"tap-kit:error\", (e) => {\n const customEvent = e as CustomEvent<{ error: Error }>;\n const error = customEvent.detail?.error ?? new Error(\"Unknown error\");\n readyReject(error);\n handlers.onError?.(error);\n });\n\n // Set up EventManager callbacks\n setupEventCallbacks();\n\n return element;\n }\n\n /**\n * Set up EventManager callbacks after element is ready\n */\n function setupEventCallbacks(): void {\n if (!element) return;\n\n // Use element.ready to ensure events API is available\n element.ready\n .then(() => {\n if (!element?.events) return;\n\n // Register stable wrappers and store cleanup functions\n const unsubTimelineSeek = element.events.onTimelineSeek((clipPlayHead, clipId) => {\n handlers.onTimelineSeek?.(clipPlayHead, clipId);\n });\n cleanupFns.push(unsubTimelineSeek);\n\n const unsubAlarmFadeIn = element.events.onAlarmFadeIn((messageInfo) => {\n handlers.onAlarmFadeIn?.(messageInfo);\n });\n cleanupFns.push(unsubAlarmFadeIn);\n })\n .catch(() => {\n // Ignore - error already handled by tap-kit:error event\n });\n }\n\n /**\n * Replay calls that were made before ready\n */\n function replayPendingCalls(): void {\n if (!element || !isReady) return;\n\n for (const call of pendingCalls) {\n try {\n if (call.target === \"element\") {\n (element as unknown as Record<string, (...args: unknown[]) => void>)[call.method]?.(...call.args);\n } else if (call.target === \"events\" && element.events) {\n (element.events as unknown as Record<string, (...args: unknown[]) => void>)[call.method]?.(...call.args);\n } else if (call.target === \"video\" && element.video) {\n (element.video as unknown as Record<string, (...args: unknown[]) => void>)[call.method]?.(...call.args);\n }\n } catch (e) {\n console.warn(`[createTapKit] Failed to replay ${call.method}:`, e);\n }\n }\n\n // Clear queue\n pendingCalls.length = 0;\n }\n\n /**\n * Queue a call to be executed when ready\n */\n function queueCall(target: \"element\" | \"events\" | \"video\", method: string, args: unknown[]): void {\n if (isReady && element) {\n // Execute immediately if ready\n try {\n if (target === \"element\") {\n (element as unknown as Record<string, (...args: unknown[]) => void>)[method]?.(...args);\n } else if (target === \"events\" && element.events) {\n (element.events as unknown as Record<string, (...args: unknown[]) => void>)[method]?.(...args);\n } else if (target === \"video\" && element.video) {\n (element.video as unknown as Record<string, (...args: unknown[]) => void>)[method]?.(...args);\n }\n } catch (e) {\n console.warn(`[createTapKit] Failed to call ${method}:`, e);\n }\n } else {\n // Queue for later\n pendingCalls.push({ target, method, args });\n }\n }\n\n // Create events proxy\n const eventsProxy: EventManager = {\n seekTimeline: (params) => queueCall(\"events\", \"seekTimeline\", [params]),\n onTimelineSeek: (callback) => {\n if (isReady && element?.events) {\n return element.events.onTimelineSeek(callback);\n }\n // Return noop unsubscribe for queued calls\n queueCall(\"events\", \"onTimelineSeek\", [callback]);\n return () => {};\n },\n onAlarmFadeIn: (handler) => {\n if (isReady && element?.events) {\n return element.events.onAlarmFadeIn(handler);\n }\n // Return noop unsubscribe for queued calls\n queueCall(\"events\", \"onAlarmFadeIn\", [handler]);\n return () => {};\n },\n };\n\n // Create video proxy\n const videoProxy: VideoController = {\n bind: (config, clipId) => queueCall(\"video\", \"bind\", [config, clipId]),\n unbind: () => queueCall(\"video\", \"unbind\", []),\n };\n\n // Start element creation (non-blocking)\n ensureElement().catch(() => {\n // Error already handled in ensureElement\n });\n\n // Return control object\n const control: TapKitFactoryControl = {\n get ready() {\n return readyPromise;\n },\n\n get isReady() {\n return isReady;\n },\n\n get isOpen() {\n return element?.isOpen ?? false;\n },\n\n get isMounted() {\n return isMounted;\n },\n\n mount(container: HTMLElement = document.body) {\n if (isDestroyed) {\n console.warn(\"[createTapKit] Cannot mount: instance destroyed\");\n return;\n }\n\n // Defensive: already mounted to this container\n if (element && element.parentNode === container) {\n console.warn(\"[createTapKit] Already mounted to this container\");\n return;\n }\n\n // If mounted elsewhere, move to new container\n if (element?.parentNode) {\n element.parentNode.removeChild(element);\n }\n\n // Wait for element creation then mount\n ensureElement()\n .then((el) => {\n if (!isDestroyed) {\n container.appendChild(el);\n isMounted = true;\n }\n })\n .catch((error) => {\n console.error(\"[createTapKit] Failed to mount:\", error);\n });\n },\n\n unmount() {\n if (element?.parentNode) {\n element.parentNode.removeChild(element);\n isMounted = false;\n }\n },\n\n show() {\n if (isDestroyed) {\n console.warn(\"[createTapKit] Cannot show: instance destroyed\");\n return;\n }\n queueCall(\"element\", \"show\", []);\n },\n\n hide() {\n if (isDestroyed) {\n console.warn(\"[createTapKit] Cannot hide: instance destroyed\");\n return;\n }\n queueCall(\"element\", \"hide\", []);\n },\n\n setCourse(course: Partial<Course> & { courseId: string; clipId: string }) {\n if (isDestroyed) {\n console.warn(\"[createTapKit] Cannot setCourse: instance destroyed\");\n return;\n }\n queueCall(\"element\", \"setCourse\", [course]);\n },\n\n get events() {\n return eventsProxy;\n },\n\n get video() {\n return videoProxy;\n },\n\n get element() {\n return element;\n },\n\n destroy() {\n if (isDestroyed) return;\n\n isDestroyed = true;\n\n // Run cleanup functions (unsubscribe event handlers)\n for (const cleanup of cleanupFns) {\n try {\n cleanup();\n } catch {\n // Ignore cleanup errors\n }\n }\n cleanupFns.length = 0;\n\n // Unmount if mounted\n if (element?.parentNode) {\n element.parentNode.removeChild(element);\n }\n\n // Clear state\n element = null;\n isMounted = false;\n isReady = false;\n pendingCalls.length = 0;\n },\n };\n\n return control;\n}\n","import type { TapKitConfig, TapKitInitParams, TapKitInstance } from \"@coxwave/tap-kit-types\";\nimport { loadCDNLoader } from \"./loader\";\n\n/**\n * TapKit - Official TapKit Web SDK\n */\nexport class TapKit implements TapKitInstance {\n private instance: TapKitInstance | null = null;\n #loading: Promise<void>;\n #config: TapKitConfig;\n #loadError: Error | null = null;\n #eventsProxy?: TapKitInstance[\"events\"]; // Cached proxy for events API\n #videoProxy?: TapKitInstance[\"video\"]; // Cached proxy for video API\n\n constructor(config: TapKitConfig) {\n this.#config = config;\n this.#loading = this.load();\n }\n\n private async load(): Promise<void> {\n try {\n await loadCDNLoader();\n\n if (!window.TapKit) {\n throw new Error(\"TapKit not available after loading CDN loader\");\n }\n\n this.instance = new window.TapKit(this.#config);\n } catch (err) {\n this.#loadError = err instanceof Error ? err : new Error(String(err));\n throw this.#loadError;\n }\n }\n\n /**\n * Promise that resolves when CDN is loaded and TapKit instance is available\n * (ready to call init())\n *\n * @example\n * ```typescript\n * const kit = new TapKit({ apiKey });\n * await kit.loaded; // CDN downloaded, window.TapKit available\n * await kit.init({...}); // Now safe to call init()\n * ```\n *\n * @see ready - For waiting until AFTER init() completes (iframe initialized)\n */\n get loaded(): Promise<void> {\n return this.#loading.then(() => {\n if (this.#loadError) {\n throw this.#loadError;\n }\n if (!this.instance) {\n throw new Error(\n `TapKit instance not initialized after loading. This may indicate a CDN loading failure for apiKey: ${this.#config.apiKey}`\n );\n }\n });\n }\n\n /**\n * Promise that resolves when SDK is fully initialized\n * (after init() is called and iframe is ready)\n *\n * @example\n * ```typescript\n * const kit = new TapKit({ apiKey });\n * await kit.init({...});\n * await kit.ready; // iframe is fully initialized\n * ```\n *\n * @see loaded - For waiting until CDN load completes (before init)\n */\n get ready(): Promise<void> {\n return this.#loading.then(() => {\n if (this.#loadError) {\n throw this.#loadError;\n }\n if (!this.instance) {\n throw new Error(\n `TapKit instance not initialized after loading. This may indicate a CDN loading failure for apiKey: ${this.#config.apiKey}`\n );\n }\n return this.instance.ready;\n });\n }\n\n /**\n * Create a proxy for video API that waits for ready before delegating\n */\n private createVideoProxy(): TapKitInstance[\"video\"] {\n return {\n bind: (config, clipId) => {\n // Queue the bind call - will execute after ready\n this.ready.then(() => {\n this.instance?.video.bind(config, clipId);\n });\n },\n unbind: () => {\n // Unbind can be called immediately if instance exists\n this.instance?.video?.unbind();\n },\n };\n }\n\n /**\n * Create events API that returns unsubscribe functions synchronously\n * Queues the actual subscription until ready, but returns unsubscribe immediately\n */\n private createEventsProxy(): TapKitInstance[\"events\"] {\n return {\n seekTimeline: (params) => {\n this.ready.then(() => {\n this.instance?.events.seekTimeline(params);\n });\n },\n onTimelineSeek: (callback) => {\n let unsubscribe: (() => void) | undefined;\n let cancelled = false;\n\n // Queue subscription\n this.ready.then(() => {\n if (cancelled) return;\n unsubscribe = this.instance?.events.onTimelineSeek(callback);\n });\n\n // Return synchronous unsubscribe function\n return () => {\n cancelled = true;\n unsubscribe?.();\n };\n },\n onAlarmFadeIn: (handler) => {\n let unsubscribe: (() => void) | undefined;\n let cancelled = false;\n\n this.ready.then(() => {\n if (cancelled) return;\n unsubscribe = this.instance?.events.onAlarmFadeIn(handler);\n });\n\n return () => {\n cancelled = true;\n unsubscribe?.();\n };\n },\n };\n }\n\n get events() {\n // Cache proxy to avoid creating new object on every access\n if (!this.#eventsProxy) {\n this.#eventsProxy = this.createEventsProxy();\n }\n return this.#eventsProxy;\n }\n\n get isOpen(): boolean {\n return this.instance?.isOpen ?? false;\n }\n\n get isInitialized(): boolean {\n return this.instance?.isInitialized ?? false;\n }\n\n get video() {\n // Cache proxy to avoid creating new object on every access\n if (!this.#videoProxy) {\n this.#videoProxy = this.createVideoProxy();\n }\n return this.#videoProxy;\n }\n\n async init(params: TapKitInitParams): Promise<() => void> {\n await this.#loading;\n if (!this.instance) {\n throw new Error(\"TapKit instance not available after loading\");\n }\n return await this.instance.init(params);\n }\n\n destroy(): void {\n if (this.instance) {\n this.instance.destroy();\n this.instance = null;\n }\n // Clear proxy references to prevent memory leaks\n this.#eventsProxy = undefined;\n this.#videoProxy = undefined;\n }\n\n show(): void {\n if (this.instance) {\n this.instance.show();\n }\n }\n\n hide(): void {\n if (this.instance) {\n this.instance.hide();\n }\n }\n\n setCourse(course: Parameters<TapKitInstance[\"setCourse\"]>[0]): void {\n if (this.instance) {\n this.instance.setCourse(course);\n }\n }\n\n get [Symbol.toStringTag]() {\n return \"TapKit\";\n }\n}\n"]}
|
package/dist/react.js
CHANGED
|
@@ -122,11 +122,7 @@ function createSDKChecker(resolve, reject, timeoutMs) {
|
|
|
122
122
|
const elapsed = Date.now() - startTime;
|
|
123
123
|
if (elapsed > timeoutMs) {
|
|
124
124
|
window.__TAP_KIT_LOADER_LOADING__ = void 0;
|
|
125
|
-
reject(
|
|
126
|
-
new Error(
|
|
127
|
-
`TapKit loader timeout: SDK not available after ${timeoutMs}ms`
|
|
128
|
-
)
|
|
129
|
-
);
|
|
125
|
+
reject(new Error(`TapKit loader timeout: SDK not available after ${timeoutMs}ms`));
|
|
130
126
|
return;
|
|
131
127
|
}
|
|
132
128
|
if (typeof requestIdleCallback !== "undefined") {
|
|
@@ -146,11 +142,7 @@ function loadCDNLoader(timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
|
146
142
|
}
|
|
147
143
|
const loadingPromise = new Promise((resolve, reject) => {
|
|
148
144
|
if (typeof document === "undefined") {
|
|
149
|
-
reject(
|
|
150
|
-
new Error(
|
|
151
|
-
"TapKit requires browser environment (document is undefined)"
|
|
152
|
-
)
|
|
153
|
-
);
|
|
145
|
+
reject(new Error("TapKit requires browser environment (document is undefined)"));
|
|
154
146
|
return;
|
|
155
147
|
}
|
|
156
148
|
if (isLocalCoreMode()) {
|
|
@@ -200,10 +192,7 @@ function loadCDNLoader(timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
|
200
192
|
const checkSDK = createSDKChecker(resolve, reject, timeoutMs);
|
|
201
193
|
checkSDK();
|
|
202
194
|
});
|
|
203
|
-
existingScript.addEventListener(
|
|
204
|
-
"error",
|
|
205
|
-
() => reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`))
|
|
206
|
-
);
|
|
195
|
+
existingScript.addEventListener("error", () => reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`)));
|
|
207
196
|
} else {
|
|
208
197
|
document.head.appendChild(script);
|
|
209
198
|
}
|
|
@@ -351,16 +340,13 @@ function useTapKit(options) {
|
|
|
351
340
|
}
|
|
352
341
|
elementRef.current.hide();
|
|
353
342
|
}, []);
|
|
354
|
-
const setCourse = React.useCallback(
|
|
355
|
-
(
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
},
|
|
362
|
-
[]
|
|
363
|
-
);
|
|
343
|
+
const setCourse = React.useCallback((course) => {
|
|
344
|
+
if (!elementRef.current) {
|
|
345
|
+
console.warn("[useTapKit] Cannot setCourse: element not mounted");
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
elementRef.current.setCourse(course);
|
|
349
|
+
}, []);
|
|
364
350
|
const video = React.useMemo(
|
|
365
351
|
() => ({
|
|
366
352
|
bind: (adapter, clipId) => {
|
package/dist/react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/react/types.ts","../src/react/TapKit.tsx","../src/loader.ts","../src/react/useTapKit.ts"],"names":["React","TapKit","useRef","useImperativeHandle","useCallback","useEffect","jsx","script","useState","useMemo"],"mappings":";;;;;;;;;;;;AAqGO,IAAM,iBAAA,GAAoB;AAAA,EAC/B,eAAA,EAAiB,SAAA;AAAA,EACjB,eAAA,EAAiB;AACnB,CAAA;AAOO,IAAM,WAAA,GAAc,MAAA,CAAO,IAAA,CAAK,iBAAiB,CAAA;ACtCjD,IAAM,MAAA,GAASA,sBAAA,CAAM,UAAA,CAAuC,SAASC,OAAAA,CAC1E,EAAE,OAAA,EAAS,GAAG,SAAA,EAAU,EACxB,YAAA,EACA;AACA,EAAA,MAAM,UAAA,GAAaC,aAA6B,IAAI,CAAA;AAGpD,EAAAC,yBAAA,CAAoB,YAAA,EAAc,MAAM,UAAA,CAAW,OAAA,EAA0B,EAAE,CAAA;AAG/E,EAAA,MAAM,aAAA,GAAgBC,iBAAA;AAAA,IACpB,CAAC,OAAA,KAAkC;AACjC,MAAA,UAAA,CAAW,OAAA,GAAU,OAAA;AAErB,MAAA,IAAI,OAAA,EAAS;AAGX,QAAA,IAAI,OAAA,CAAQ,QAAQ,MAAA,EAAQ;AAC1B,UAAA,OAAA,CAAQ,MAAA,GAAS,QAAQ,OAAA,CAAQ,MAAA;AAAA,QACnC;AAIA,QAAA,IAAI,OAAA,CAAQ,QAAQ,WAAA,EAAa;AAC/B,UAAA,OAAA,CAAQ,WAAA,GAAc,QAAQ,OAAA,CAAQ,WAAA;AAAA,QACxC;AAKA,QAAA,IAAI,OAAA,CAAQ,OAAA,CAAQ,iBAAA,KAAsB,MAAA,EAAW;AACnD,UAAA,OAAA,CAAQ,iBAAA,GAAoB,QAAQ,OAAA,CAAQ,iBAAA;AAAA,QAC9C;AAAA,MAIF;AAEA,MAAA,OAAA,CAAQ,YAAY,OAAO,CAAA;AAAA,IAC7B,CAAA;AAAA,IACA,CAAC,OAAA,CAAQ,WAAA,EAAa,OAAA,CAAQ,OAAA,CAAQ,MAAA,EAAQ,OAAA,CAAQ,OAAA,CAAQ,WAAA,EAAa,OAAA,CAAQ,OAAA,CAAQ,iBAAiB;AAAA,GAC9G;AAIA,EAAA,MAAM,WAAA,GAAcF,YAAA,CAAO,OAAA,CAAQ,QAAQ,CAAA;AAC3C,EAAA,WAAA,CAAY,UAAU,OAAA,CAAQ,QAAA;AAG9B,EAAAG,eAAA,CAAU,MAAM;AACd,IAAA,MAAM,UAAU,UAAA,CAAW,OAAA;AAC3B,IAAA,IAAI,CAAC,OAAA,EAAS;AAEd,IAAA,MAAM,YAGD,EAAC;AAGN,IAAA,KAAA,MAAW,aAAa,WAAA,EAAa;AACnC,MAAA,MAAM,UAAA,GAAa,kBAAkB,SAA2C,CAAA;AAGhF,MAAA,MAAM,QAAA,GAAW,CAAC,CAAA,KAAa;AAC7B,QAAA,MAAM,OAAA,GAAU,WAAA,CAAY,OAAA,CAAQ,UAAU,CAAA;AAC9C,QAAA,IAAI,CAAC,OAAA,EAAS;AAEd,QAAA,MAAM,WAAA,GAAc,CAAA;AACpB,QAAA,IAAI,UAAA,KAAe,SAAA,IAAa,WAAA,CAAY,MAAA,EAAQ,KAAA,EAAO;AAGzD,UAAC,OAAA,CAAgB,WAAA,CAAY,MAAA,CAAO,KAAK,CAAA;AAAA,QAC3C,CAAA,MAAA,IAAW,eAAe,SAAA,EAAW;AAGnC,UAAC,OAAA,EAAgB;AAAA,QACnB;AAAA,MACF,CAAA;AAEA,MAAA,OAAA,CAAQ,gBAAA,CAAiB,WAAW,QAAQ,CAAA;AAC5C,MAAA,SAAA,CAAU,KAAK,EAAE,KAAA,EAAO,SAAA,EAAW,OAAA,EAAS,UAAU,CAAA;AAAA,IACxD;AAEA,IAAA,OAAO,MAAM;AAEX,MAAA,KAAA,MAAW,EAAE,KAAA,EAAO,OAAA,EAAQ,IAAK,SAAA,EAAW;AAC1C,QAAA,OAAA,CAAQ,mBAAA,CAAoB,OAAO,OAAO,CAAA;AAAA,MAC5C;AAAA,IACF,CAAA;AAAA,EACF,CAAA,EAAG,EAAE,CAAA;AAIL,EAAAA,eAAA,CAAU,MAAM;AACd,IAAA,MAAM,UAAU,UAAA,CAAW,OAAA;AAC3B,IAAA,IAAI,CAAC,OAAA,EAAS;AAEd,IAAA,OAAA,CAAQ,WAAA,GAAc,QAAQ,OAAA,CAAQ,WAAA;AAAA,EACxC,CAAA,EAAG,CAAC,OAAA,CAAQ,OAAA,CAAQ,WAAW,CAAC,CAAA;AAIhC,EAAAA,eAAA,CAAU,MAAM;AACd,IAAA,MAAM,UAAU,UAAA,CAAW,OAAA;AAC3B,IAAA,IAAI,CAAC,OAAA,IAAW,OAAA,CAAQ,OAAA,CAAQ,sBAAsB,MAAA,EAAW;AAEjE,IAAA,OAAA,CAAQ,iBAAA,GAAoB,QAAQ,OAAA,CAAQ,iBAAA;AAAA,EAC9C,CAAA,EAAG,CAAC,OAAA,CAAQ,OAAA,CAAQ,iBAAiB,CAAC,CAAA;AAKtC,EAAA,MAAM,EAAE,OAAA,EAAS,WAAA,EAAY,GAAI,OAAA;AAIjC,EAAA,IAAI,CAAC,WAAA,EAAa;AAChB,IAAA,OAAO,IAAA;AAAA,EACT;AAIA,EAAA,uBACEC,cAAA;AAAA,IAAC,SAAA;AAAA,IAAA;AAAA,MACC,GAAA,EAAK,aAAA;AAAA,MACL,WAAS,OAAA,CAAQ,MAAA;AAAA,MACjB,aAAW,OAAA,CAAQ,QAAA;AAAA,MACnB,WAAS,OAAA,CAAQ,MAAA;AAAA,MACjB,kBAAgB,OAAA,CAAQ,YAAA;AAAA,MACxB,UAAU,OAAA,CAAQ,QAAA;AAAA,MAClB,aAAW,OAAA,CAAQ,QAAA;AAAA,MACnB,MAAM,OAAA,CAAQ,IAAA;AAAA,MACd,OAAO,OAAA,CAAQ,KAAA;AAAA,MACf,WAAS,OAAA,CAAQ,MAAA;AAAA,MACjB,WAAS,OAAA,CAAQ,MAAA;AAAA,MACjB,aAAa,OAAA,CAAQ,WAAA;AAAA,MACpB,GAAG;AAAA;AAAA,GACN;AAEJ,CAAC;;;ACtMD,IAAM,sBAAA,GACJ,OAAO,0BAAA,KAA+B,WAAA,GAClC,0BAAA,GACA,2CAAA;AACN,IAAM,kBAAA,GAAqB,GAAA;AAC3B,IAAM,wBAAA,GAA2B,GAAA;AAKjC,SAAS,YAAA,GAAuB;AAC9B,EAAA,OAAO,MAAA,EAAQ,sBAAA,GACX,MAAA,CAAO,sBAAA,GACP,sBAAA;AACN;AAOA,SAAS,eAAA,GAA2B;AAClC,EAAA,OAAO,OAAO,MAAA,KAAW,WAAA,IAAe,CAAC,CAAC,MAAA,CAAO,oBAAA;AACnD;AAKA,SAAS,eAAA,GAA0B;AACjC,EAAA,OAAO,OAAO,oBAAA,IAAwB,EAAA;AACxC;AAUA,SAAS,gBAAA,CACP,OAAA,EACA,MAAA,EACA,SAAA,EACY;AACZ,EAAA,MAAM,SAAA,GAAY,KAAK,GAAA,EAAI;AAE3B,EAAA,MAAM,WAAW,MAAY;AAG3B,IAAA,IAAI,MAAA,CAAO,MAAA,IAAU,MAAA,CAAO,YAAA,KAAiB,IAAA,EAAM;AACjD,MAAA,MAAA,CAAO,yBAAA,GAA4B,IAAA;AACnC,MAAA,MAAA,CAAO,0BAAA,GAA6B,MAAA;AACpC,MAAA,OAAA,EAAQ;AACR,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAG7B,IAAA,IAAI,UAAU,SAAA,EAAW;AACvB,MAAA,MAAA,CAAO,0BAAA,GAA6B,MAAA;AACpC,MAAA,MAAA;AAAA,QACE,IAAI,KAAA;AAAA,UACF,kDAAkD,SAAS,CAAA,EAAA;AAAA;AAC7D,OACF;AACA,MAAA;AAAA,IACF;AAIA,IAAA,IAAI,OAAO,wBAAwB,WAAA,EAAa;AAC9C,MAAA,mBAAA,CAAoB,QAAA,EAAU,EAAE,OAAA,EAAS,wBAAA,EAA0B,CAAA;AAAA,IACrE,CAAA,MAAO;AACL,MAAA,UAAA,CAAW,UAAU,wBAAwB,CAAA;AAAA,IAC/C;AAAA,EACF,CAAA;AAEA,EAAA,OAAO,QAAA;AACT;AAYO,SAAS,aAAA,CACd,YAAoB,kBAAA,EACL;AAEf,EAAA,IAAI,MAAA,CAAO,yBAAA,IAA6B,MAAA,CAAO,MAAA,EAAQ;AACrD,IAAA,OAAO,QAAQ,OAAA,EAAQ;AAAA,EACzB;AAGA,EAAA,IAAI,OAAO,0BAAA,EAA4B;AACrC,IAAA,OAAO,MAAA,CAAO,0BAAA;AAAA,EAChB;AAGA,EAAA,MAAM,cAAA,GAAiB,IAAI,OAAA,CAAc,CAAC,SAAS,MAAA,KAAW;AAC5D,IAAA,IAAI,OAAO,aAAa,WAAA,EAAa;AACnC,MAAA,MAAA;AAAA,QACE,IAAI,KAAA;AAAA,UACF;AAAA;AACF,OACF;AACA,MAAA;AAAA,IACF;AAGA,IAAA,IAAI,iBAAgB,EAAG;AAErB,MAAA,IAAI,MAAA,CAAO,MAAA,IAAU,MAAA,CAAO,YAAA,KAAiB,IAAA,EAAM;AACjD,QAAA,MAAA,CAAO,yBAAA,GAA4B,IAAA;AACnC,QAAA,MAAA,CAAO,0BAAA,GAA6B,MAAA;AACpC,QAAA,OAAA,EAAQ;AACR,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,UAAU,eAAA,EAAgB;AAEhC,MAAA,MAAMC,OAAAA,GAAS,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC9C,MAAAA,QAAO,GAAA,GAAM,OAAA;AACb,MAAAA,QAAO,KAAA,GAAQ,IAAA;AAEf,MAAAA,OAAAA,CAAO,SAAS,MAAM;AAGpB,QAAA,IAAI,OAAO,MAAA,EAAQ;AACjB,UAAA,MAAA,CAAO,YAAA,GAAe,IAAA;AACtB,UAAA,MAAA,CAAO,yBAAA,GAA4B,IAAA;AACnC,UAAA,MAAA,CAAO,0BAAA,GAA6B,MAAA;AACpC,UAAA,OAAA,EAAQ;AAAA,QACV,CAAA,MAAO;AACL,UAAA,MAAA,CAAO,0BAAA,GAA6B,MAAA;AACpC,UAAA,MAAA,CAAO,IAAI,KAAA,CAAM,+CAA+C,CAAC,CAAA;AAAA,QACnE;AAAA,MACF,CAAA;AAEA,MAAAA,OAAAA,CAAO,UAAU,MAAM;AACrB,QAAA,MAAA,CAAO,0BAAA,GAA6B,MAAA;AACpC,QAAA,MAAA,CAAO,IAAI,KAAA,CAAM,CAAA,kCAAA,EAAqC,OAAO,EAAE,CAAC,CAAA;AAAA,MAClE,CAAA;AAEA,MAAA,QAAA,CAAS,IAAA,CAAK,YAAYA,OAAM,CAAA;AAChC,MAAA;AAAA,IACF;AAGA,IAAA,MAAM,YAAY,YAAA,EAAa;AAC/B,IAAA,MAAM,MAAA,GAAS,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC9C,IAAA,MAAA,CAAO,GAAA,GAAM,SAAA;AACb,IAAA,MAAA,CAAO,KAAA,GAAQ,IAAA;AAEf,IAAA,MAAA,CAAO,SAAS,MAAM;AAGpB,MAAA,MAAM,QAAA,GAAW,gBAAA,CAAiB,OAAA,EAAS,MAAA,EAAQ,SAAS,CAAA;AAC5D,MAAA,QAAA,EAAS;AAAA,IACX,CAAA;AAEA,IAAA,MAAA,CAAO,UAAU,MAAM;AACrB,MAAA,MAAA,CAAO,0BAAA,GAA6B,MAAA;AACpC,MAAA,MAAA,CAAO,IAAI,KAAA,CAAM,CAAA,kCAAA,EAAqC,SAAS,EAAE,CAAC,CAAA;AAAA,IACpE,CAAA;AAGA,IAAA,MAAM,cAAA,GAAiB,QAAA,CAAS,aAAA,CAAc,CAAA,YAAA,EAAe,SAAS,CAAA,EAAA,CAAI,CAAA;AAE1E,IAAA,IAAI,cAAA,EAAgB;AAElB,MAAA,cAAA,CAAe,gBAAA,CAAiB,QAAQ,MAAM;AAC5C,QAAA,MAAM,QAAA,GAAW,gBAAA,CAAiB,OAAA,EAAS,MAAA,EAAQ,SAAS,CAAA;AAC5D,QAAA,QAAA,EAAS;AAAA,MACX,CAAC,CAAA;AACD,MAAA,cAAA,CAAe,gBAAA;AAAA,QAAiB,OAAA;AAAA,QAAS,MACvC,MAAA,CAAO,IAAI,MAAM,CAAA,kCAAA,EAAqC,SAAS,EAAE,CAAC;AAAA,OACpE;AAAA,IACF,CAAA,MAAO;AACL,MAAA,QAAA,CAAS,IAAA,CAAK,YAAY,MAAM,CAAA;AAAA,IAClC;AAAA,EACF,CAAC,CAAA;AAED,EAAA,MAAA,CAAO,0BAAA,GAA6B,cAAA;AACpC,EAAA,OAAO,cAAA;AACT;;;ACtGA,SAAS,gBAAgB,OAAA,EAAgD;AACvE,EAAA,OAAO;AAAA,IACL,SAAS,OAAA,CAAQ,OAAA;AAAA,IACjB,SAAS,OAAA,CAAQ,OAAA;AAAA,IACjB,gBAAgB,OAAA,CAAQ,cAAA;AAAA,IACxB,eAAe,OAAA,CAAQ;AAAA,GACzB;AACF;AAwCO,SAAS,UAAU,OAAA,EAA4C;AAEpE,EAAA,MAAM,UAAA,GAAaL,aAA6B,IAAI,CAAA;AAGpD,EAAA,MAAM,gBAAA,GAAmBA,aAGf,IAAI,CAAA;AAGd,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAIM,eAAS,KAAK,CAAA;AACpD,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAIA,eAAS,KAAK,CAAA;AAC5C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIA,eAAuB,IAAI,CAAA;AAGrD,EAAA,MAAM,WAAA,GAAcN,YAAAA,CAAO,eAAA,CAAgB,OAAO,CAAC,CAAA;AACnD,EAAA,WAAA,CAAY,OAAA,GAAU,gBAAgB,OAAO,CAAA;AAG7C,EAAAG,gBAAU,MAAM;AACd,IAAA,aAAA,EAAc,CACX,KAAK,MAAM;AACV,MAAA,cAAA,CAAe,IAAI,CAAA;AAAA,IACrB,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,GAAA,KAAQ;AACd,MAAA,MAAM,YAAY,GAAA,YAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,MAAM,2BAA2B,CAAA;AACpF,MAAA,QAAA,CAAS,SAAS,CAAA;AAClB,MAAA,WAAA,CAAY,OAAA,CAAQ,UAAU,SAAS,CAAA;AAAA,IACzC,CAAC,CAAA;AAAA,EACL,CAAA,EAAG,EAAE,CAAA;AAGL,EAAA,MAAM,WAAA,GAAcD,iBAAAA,CAAY,CAAC,QAAA,KAAmC;AAClE,IAAA,UAAA,CAAW,OAAA,GAAU,QAAA;AAErB,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,UAAA,CAAW,KAAK,CAAA;AAChB,MAAA;AAAA,IACF;AAGA,IAAA,MAAM,cAAc,MAAM;AACxB,MAAA,UAAA,CAAW,IAAI,CAAA;AACf,MAAA,WAAA,CAAY,QAAQ,OAAA,IAAU;AAAA,IAChC,CAAA;AAEA,IAAA,MAAM,WAAA,GAAc,CAAC,CAAA,KAAa;AAChC,MAAA,MAAM,WAAA,GAAc,CAAA;AACpB,MAAA,MAAM,MAAM,WAAA,CAAY,MAAA,EAAQ,KAAA,IAAS,IAAI,MAAM,eAAe,CAAA;AAClE,MAAA,QAAA,CAAS,GAAG,CAAA;AACZ,MAAA,WAAA,CAAY,OAAA,CAAQ,UAAU,GAAG,CAAA;AAAA,IACnC,CAAA;AAEA,IAAA,QAAA,CAAS,gBAAA,CAAiB,iBAAiB,WAAW,CAAA;AACtD,IAAA,QAAA,CAAS,gBAAA,CAAiB,iBAAiB,WAAW,CAAA;AAGtD,IAAA,IAAI,SAAS,aAAA,EAAe;AAC1B,MAAA,UAAA,CAAW,IAAI,CAAA;AAAA,IACjB;AAIA,IAAA,MAAM,qBAAqB,MAAM;AAC/B,MAAA,IAAI,SAAS,MAAA,IAAU,OAAO,QAAA,CAAS,MAAA,CAAO,mBAAmB,UAAA,EAAY;AAG3E,QAAA,QAAA,CAAS,MAAA,CAAO,cAAA,CAAe,CAAC,YAAA,EAAc,MAAA,KAAW;AACvD,UAAA,WAAA,CAAY,OAAA,CAAQ,cAAA,GAAiB,YAAA,EAAc,MAAM,CAAA;AAAA,QAC3D,CAAC,CAAA;AACD,QAAA,QAAA,CAAS,MAAA,CAAO,aAAA,CAAc,CAAC,WAAA,KAAgB;AAC7C,UAAA,WAAA,CAAY,OAAA,CAAQ,gBAAgB,WAAW,CAAA;AAAA,QACjD,CAAC,CAAA;AAAA,MACH;AAGA,MAAA,IAAI,gBAAA,CAAiB,OAAA,IAAW,QAAA,CAAS,KAAA,EAAO;AAC9C,QAAA,QAAA,CAAS,MAAM,IAAA,CAAK,gBAAA,CAAiB,QAAQ,OAAA,EAAS,gBAAA,CAAiB,QAAQ,MAAM,CAAA;AACrF,QAAA,gBAAA,CAAiB,OAAA,GAAU,IAAA;AAAA,MAC7B;AAAA,IACF,CAAA;AAGA,IAAA,IAAI,SAAS,KAAA,IAAS,OAAO,QAAA,CAAS,KAAA,CAAM,SAAS,UAAA,EAAY;AAC/D,MAAA,QAAA,CAAS,KAAA,CAAM,IAAA,CAAK,kBAAkB,CAAA,CAAE,MAAM,MAAM;AAAA,MAEpD,CAAC,CAAA;AAAA,IACH,CAAA,MAAA,IAAW,SAAS,aAAA,EAAe;AAEjC,MAAA,kBAAA,EAAmB;AAAA,IACrB,CAAA,MAAO;AAEL,MAAA,QAAA,CAAS,gBAAA,CAAiB,iBAAiB,kBAAA,EAAoB;AAAA,QAC7D,IAAA,EAAM;AAAA,OACP,CAAA;AAAA,IACH;AAAA,EACF,CAAA,EAAG,EAAE,CAAA;AAKL,EAAA,MAAM,aAAA,GAAgBK,aAAA;AAAA,IACpB,OAAO;AAAA,MACL,QAAQ,OAAA,CAAQ,MAAA;AAAA,MAChB,QAAQ,OAAA,CAAQ,MAAA;AAAA,MAChB,UAAU,OAAA,CAAQ,QAAA;AAAA,MAClB,QAAQ,OAAA,CAAQ,MAAA;AAAA,MAChB,cAAc,OAAA,CAAQ,YAAA;AAAA,MACtB,UAAU,OAAA,CAAQ,QAAA;AAAA,MAClB,UAAU,OAAA,CAAQ,QAAA;AAAA,MAClB,MAAM,OAAA,CAAQ,IAAA;AAAA,MACd,mBAAmB,OAAA,CAAQ,iBAAA;AAAA,MAC3B,aAAa,OAAA,CAAQ,WAAA;AAAA,MACrB,OAAO,OAAA,CAAQ,KAAA;AAAA,MACf,QAAQ,OAAA,CAAQ,MAAA;AAAA,MAChB,QAAQ,OAAA,CAAQ,MAAA;AAAA,MAChB,aAAa,OAAA,CAAQ;AAAA;AAAA,KAEvB,CAAA;AAAA,IACA;AAAA,MACE,OAAA,CAAQ,MAAA;AAAA,MACR,OAAA,CAAQ,MAAA;AAAA,MACR,OAAA,CAAQ,QAAA;AAAA,MACR,OAAA,CAAQ,MAAA;AAAA,MACR,OAAA,CAAQ,YAAA;AAAA,MACR,OAAA,CAAQ,QAAA;AAAA,MACR,OAAA,CAAQ,QAAA;AAAA,MACR,OAAA,CAAQ,IAAA;AAAA,MACR,OAAA,CAAQ,iBAAA;AAAA,MACR,OAAA,CAAQ,WAAA;AAAA,MACR,OAAA,CAAQ,KAAA;AAAA,MACR,OAAA,CAAQ,MAAA;AAAA,MACR,OAAA,CAAQ,MAAA;AAAA,MACR,OAAA,CAAQ;AAAA;AACV,GACF;AAOA,EAAA,MAAM,QAAA,GAAWA,aAAA;AAAA,IACf,OAAO;AAAA,MACL,OAAA,EAAS,YAAY,OAAA,CAAQ,OAAA;AAAA,MAC7B,OAAA,EAAS,YAAY,OAAA,CAAQ,OAAA;AAAA,MAC7B,cAAA,EAAgB,YAAY,OAAA,CAAQ,cAAA;AAAA,MACpC,aAAA,EAAe,YAAY,OAAA,CAAQ;AAAA,KACrC,CAAA;AAAA,IACA;AAAC,GACH;AAGA,EAAA,MAAM,OAAA,GAAUA,aAAA;AAAA,IACd,OAAO;AAAA,MACL,WAAA;AAAA,MACA,OAAA,EAAS,aAAA;AAAA,MACT,QAAA;AAAA,MACA;AAAA,KACF,CAAA;AAAA,IACA,CAAC,WAAA,EAAa,aAAA,EAAe,QAAA,EAAU,WAAW;AAAA,GACpD;AAGA,EAAA,MAAM,IAAA,GAAOL,kBAAY,MAAM;AAC7B,IAAA,IAAI,CAAC,WAAW,OAAA,EAAS;AACvB,MAAA,OAAA,CAAQ,KAAK,8CAA8C,CAAA;AAC3D,MAAA;AAAA,IACF;AACA,IAAA,UAAA,CAAW,QAAQ,IAAA,EAAK;AAAA,EAC1B,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,IAAA,GAAOA,kBAAY,MAAM;AAC7B,IAAA,IAAI,CAAC,WAAW,OAAA,EAAS;AACvB,MAAA,OAAA,CAAQ,KAAK,8CAA8C,CAAA;AAC3D,MAAA;AAAA,IACF;AACA,IAAA,UAAA,CAAW,QAAQ,IAAA,EAAK;AAAA,EAC1B,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,SAAA,GAAYA,iBAAAA;AAAA,IAChB,CAAC,MAAA,KAAyF;AACxF,MAAA,IAAI,CAAC,WAAW,OAAA,EAAS;AACvB,QAAA,OAAA,CAAQ,KAAK,mDAAmD,CAAA;AAChE,QAAA;AAAA,MACF;AACA,MAAA,UAAA,CAAW,OAAA,CAAQ,UAAU,MAAM,CAAA;AAAA,IACrC,CAAA;AAAA,IACA;AAAC,GACH;AAGA,EAAA,MAAM,KAAA,GAAQK,aAAA;AAAA,IACZ,OAAO;AAAA,MACL,IAAA,EAAM,CAAC,OAAA,EAAuB,MAAA,KAAoB;AAEhD,QAAA,IAAI,UAAA,CAAW,SAAS,KAAA,EAAO;AAC7B,UAAA,UAAA,CAAW,OAAA,CAAQ,KAAA,CAAM,IAAA,CAAK,OAAA,EAAS,MAAM,CAAA;AAC7C,UAAA;AAAA,QACF;AAGA,QAAA,gBAAA,CAAiB,OAAA,GAAU,EAAE,OAAA,EAAS,MAAA,EAAO;AAAA,MAC/C,CAAA;AAAA,MACA,QAAQ,MAAM;AAEZ,QAAA,gBAAA,CAAiB,OAAA,GAAU,IAAA;AAC3B,QAAA,UAAA,CAAW,OAAA,EAAS,OAAO,MAAA,EAAO;AAAA,MACpC;AAAA,KACF,CAAA;AAAA,IACA;AAAC,GACH;AAEA,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,OAAA;AAAA,IACA,KAAA;AAAA,IACA,IAAA;AAAA,IACA,IAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GACF;AACF","file":"react.js","sourcesContent":["/**\n * React-specific type definitions for TapKit\n *\n * Defines event handler types and control patterns for React integration.\n */\n\nimport type { AlarmMessageInstanceType, TapKitElement } from \"@coxwave/tap-kit-types\";\n\n/**\n * Video player adapter interface for timeline synchronization\n *\n * Supports custom video players (YouTube, Vimeo, etc.) that don't use HTMLVideoElement.\n */\nexport interface VideoAdapter {\n /** Get current playback time in seconds */\n getCurrentTime: () => number;\n /** Set playback time in seconds */\n setCurrentTime: (time: number, clipId?: string) => void;\n}\n\n/**\n * Event handler type definitions for TapKit Web Component\n *\n * Maps CustomEvent types to React callback signatures.\n */\nexport interface TapKitEventHandlers {\n /**\n * Called when TapKit SDK is ready\n * @example onReady={() => console.log('TapKit ready!')}\n */\n onReady?: () => void;\n\n /**\n * Called when initialization or runtime error occurs\n * @example onError={(error) => console.error('TapKit error:', error)}\n */\n onError?: (error: Error) => void;\n\n /**\n * Called when timeline seek event occurs\n * @example onTimelineSeek={(clipPlayHead, clipId) => console.log('Seek:', clipPlayHead)}\n */\n onTimelineSeek?: (clipPlayHead: number, clipId: string) => void;\n\n /**\n * Called when alarm fade-in occurs\n * @example onAlarmFadeIn={(messageInfo) => console.log('Alarm:', messageInfo)}\n */\n onAlarmFadeIn?: (messageInfo: AlarmMessageInstanceType) => void;\n}\n\n/**\n * Control object pattern for managing TapKit instance\n *\n * Separates instance management (setInstance) from configuration (options)\n * and event handling (handlers). Inspired by ChatKit's control pattern.\n *\n * @example\n * ```tsx\n * const tapkit = useTapKit({ apiKey: 'key', onReady: () => {} });\n * <TapKit control={tapkit.control} />\n * ```\n */\nexport interface TapKitControl<T> {\n /**\n * Set the TapKit element instance reference\n *\n * Called by TapKit component to register the element instance.\n * Enables imperative control via useTapKit methods.\n */\n setInstance: (instance: TapKitElement | null) => void;\n\n /**\n * Configuration options (non-function values)\n *\n * All options except event handlers.\n */\n options: T;\n\n /**\n * Event handler callbacks (function values)\n *\n * Separated from options for easier event listener management.\n */\n handlers: TapKitEventHandlers;\n\n /**\n * Whether CDN is loaded and Web Component is available\n *\n * @internal Used by TapKit component to delay rendering\n */\n isCdnLoaded: boolean;\n}\n\n/**\n * Map of TapKit event names to their handler keys\n *\n * Used for automatic event listener binding in TapKit component.\n *\n * @internal\n */\nexport const EVENT_HANDLER_MAP = {\n \"tap-kit:ready\": \"onReady\",\n \"tap-kit:error\": \"onError\",\n} as const;\n\n/**\n * List of TapKit event names\n *\n * @internal\n */\nexport const EVENT_NAMES = Object.keys(EVENT_HANDLER_MAP) as (keyof typeof EVENT_HANDLER_MAP)[];\n","/**\n * TapKit React Component\n *\n * Declarative React wrapper for <tap-kit> Web Component.\n * Use this with useTapKit hook for a clean separation of control and rendering.\n *\n * @example\n * ```tsx\n * 'use client';\n *\n * import { TapKit, useTapKit } from '@coxwave/tap-kit/react';\n *\n * function MyApp() {\n * const tapkit = useTapKit({\n * apiKey: 'your-key',\n * userId: 'user-123',\n * courseId: 'course-456',\n * clipId: 'clip-789',\n * onReady: () => console.log('TapKit ready!'),\n * onError: (error) => console.error('Error:', error),\n * });\n *\n * return (\n * <div>\n * <button onClick={tapkit.show} disabled={!tapkit.isReady}>\n * Show Chat\n * </button>\n * <TapKit control={tapkit.control} style={{ height: '600px' }} />\n * </div>\n * );\n * }\n * ```\n *\n * **Note for Next.js App Router users:**\n * Make sure to add 'use client' at the top of your component file.\n *\n * @see https://edutap-ai-docs.vercel.app/docs/guides/react\n */\n\nimport type { TapKitElement } from \"@coxwave/tap-kit-types\";\nimport React, { useCallback, useEffect, useImperativeHandle, useRef } from \"react\";\nimport type { TapKitControl } from \"./types\";\nimport { EVENT_HANDLER_MAP, EVENT_NAMES } from \"./types\";\nimport type { TapKitOptions } from \"./useTapKit\";\n\n/**\n * Props for TapKit React component\n */\nexport interface TapKitProps extends Omit<React.HTMLAttributes<TapKitElement>, \"children\" | \"dangerouslySetInnerHTML\"> {\n /**\n * Control object from useTapKit hook\n *\n * Provides instance management, configuration, and event handlers.\n */\n control: TapKitControl<TapKitOptions>;\n}\n\n// Note: JSX IntrinsicElements augmentation is provided by @coxwave/tap-kit-types\n// See: packages/tap-kit-types/src/tap-kit.d.ts\n\n/**\n * TapKit React Component\n *\n * Renders <tap-kit> Web Component with React integration.\n * Automatically manages instance lifecycle and event listeners.\n *\n * **Ref Access**: The forwarded ref provides direct access to the TapKitElement:\n * ```tsx\n * const tapkitRef = useRef<TapKitElement>(null);\n * <TapKit ref={tapkitRef} control={...} />\n * // tapkitRef.current?.show()\n * ```\n */\nexport const TapKit = React.forwardRef<TapKitElement, TapKitProps>(function TapKit(\n { control, ...htmlProps },\n forwardedRef\n) {\n const elementRef = useRef<TapKitElement | null>(null);\n\n // Forward TapKitElement to parent\n useImperativeHandle(forwardedRef, () => elementRef.current as TapKitElement, []);\n\n // Callback ref to register element when mounted\n const setElementRef = useCallback(\n (element: TapKitElement | null) => {\n elementRef.current = element;\n\n if (element) {\n // Set apiKey as property (not attribute) - required for LitElement\n // apiKey has { attribute: false } so it won't work via HTML attribute\n if (control.options.apiKey) {\n element.apiKey = control.options.apiKey;\n }\n\n // Set videoTarget as property (not attribute) - object type\n // videoTarget has { attribute: false } for lifecycle-safe video binding\n if (control.options.videoTarget) {\n element.videoTarget = control.options.videoTarget;\n }\n\n // Set allowLayoutToggle as property (not attribute) - Boolean type\n // HTML boolean attributes treat any string value (including \"false\") as true\n // Must be set via property to correctly handle false values\n if (control.options.allowLayoutToggle !== undefined) {\n element.allowLayoutToggle = control.options.allowLayoutToggle;\n }\n\n // Note: root is NOT set here - declarative API uses auto-detected parent\n // The element will detect declarative mode in connectedCallback\n }\n\n control.setInstance(element);\n },\n [control.setInstance, control.options.apiKey, control.options.videoTarget, control.options.allowLayoutToggle]\n );\n\n // Store handlers in ref to avoid effect re-runs on handler reference changes\n // Handlers are stable via useMemo in useTapKit, but use ref for extra safety\n const handlersRef = useRef(control.handlers);\n handlersRef.current = control.handlers;\n\n // Register event listeners (only once per element mount)\n useEffect(() => {\n const element = elementRef.current;\n if (!element) return;\n\n const listeners: Array<{\n event: string;\n handler: EventListener;\n }> = [];\n\n // Bind CustomEvent listeners (tap-kit:ready, tap-kit:error)\n for (const eventName of EVENT_NAMES) {\n const handlerKey = EVENT_HANDLER_MAP[eventName as keyof typeof EVENT_HANDLER_MAP];\n\n // Use closure over handlersRef to always get latest handler\n const listener = (e: Event) => {\n const handler = handlersRef.current[handlerKey];\n if (!handler) return;\n\n const customEvent = e as CustomEvent;\n if (handlerKey === \"onError\" && customEvent.detail?.error) {\n // onError receives error directly\n // biome-ignore lint/suspicious/noExplicitAny: Handler type is validated at runtime\n (handler as any)(customEvent.detail.error);\n } else if (handlerKey === \"onReady\") {\n // onReady receives no arguments\n // biome-ignore lint/suspicious/noExplicitAny: Handler type is validated at runtime\n (handler as any)();\n }\n };\n\n element.addEventListener(eventName, listener);\n listeners.push({ event: eventName, handler: listener });\n }\n\n return () => {\n // Cleanup all event listeners\n for (const { event, handler } of listeners) {\n element.removeEventListener(event, handler);\n }\n };\n }, []); // Empty deps - listeners use ref for handlers\n\n // Sync videoTarget property when it changes\n // (property-only, not attribute - must be set imperatively)\n useEffect(() => {\n const element = elementRef.current;\n if (!element) return;\n\n element.videoTarget = control.options.videoTarget;\n }, [control.options.videoTarget]);\n\n // Sync allowLayoutToggle property when it changes\n // (Boolean attributes don't work correctly with \"false\" string)\n useEffect(() => {\n const element = elementRef.current;\n if (!element || control.options.allowLayoutToggle === undefined) return;\n\n element.allowLayoutToggle = control.options.allowLayoutToggle;\n }, [control.options.allowLayoutToggle]);\n\n // Note: root sync removed - declarative API uses auto-detected parent\n // The element stays in its declared position\n\n const { options, isCdnLoaded } = control;\n\n // Wait for CDN to load before rendering Web Component\n // Without this, <tap-kit> is just an HTMLUnknownElement without methods\n if (!isCdnLoaded) {\n return null;\n }\n\n // Render <tap-kit> Web Component directly\n // Note: apiKey, videoTarget, allowLayoutToggle are set via ref callback (property-only)\n return (\n <tap-kit\n ref={setElementRef}\n user-id={options.userId}\n course-id={options.courseId}\n clip-id={options.clipId}\n clip-play-head={options.clipPlayHead}\n language={options.language}\n button-id={options.buttonId}\n mode={options.mode}\n debug={options.debug}\n tap-url={options.tapUrl}\n api-url={options.apiUrl}\n environment={options.environment}\n {...htmlProps}\n />\n );\n});\n","/**\n * CDN loader for TapKit\n * Dynamically loads the TapKit SDK from CDN\n *\n * For local testing, you can override the loader URL:\n * window.__TAP_KIT_LOADER_URL__ = '/tap-kit-core/loader.js';\n *\n * For local development (bypass loader, load IIFE directly):\n * window.__TAP_KIT_CORE_URL__ = '/packages/tap-kit-core/dist/index.global.js';\n */\n\n// Build-time constant injected by tsup define\ndeclare const __DEFAULT_CDN_LOADER_URL__: string;\n\nconst DEFAULT_CDN_LOADER_URL =\n typeof __DEFAULT_CDN_LOADER_URL__ !== \"undefined\"\n ? __DEFAULT_CDN_LOADER_URL__\n : \"https://files.edutap.ai/tap-sdk/loader.js\";\nconst DEFAULT_TIMEOUT_MS = 4000; // 4 seconds total timeout\nconst IDLE_CALLBACK_TIMEOUT_MS = 500; // 500ms for requestIdleCallback\n\n/**\n * Get the loader URL from window override or default CDN\n */\nfunction getLoaderURL(): string {\n return window?.__TAP_KIT_LOADER_URL__\n ? window.__TAP_KIT_LOADER_URL__\n : DEFAULT_CDN_LOADER_URL;\n}\n\n/**\n * Check if local core mode is enabled\n * When __TAP_KIT_CORE_URL__ is set, directly load the IIFE bundle\n * This bypasses the loader.js and loads tap-kit-core directly\n */\nfunction isLocalCoreMode(): boolean {\n return typeof window !== \"undefined\" && !!window.__TAP_KIT_CORE_URL__;\n}\n\n/**\n * Get the local core URL\n */\nfunction getLocalCoreURL(): string {\n return window.__TAP_KIT_CORE_URL__ || \"\";\n}\n\n/**\n * Creates a SDK checker function with timeout and retry logic\n * Uses requestIdleCallback to avoid blocking browser rendering\n * @param resolve - Promise resolve function\n * @param reject - Promise reject function\n * @param timeoutMs - Maximum time to wait for SDK to load (milliseconds)\n * @returns Checker function to be called repeatedly\n */\nfunction createSDKChecker(\n resolve: (value: void | PromiseLike<void>) => void,\n reject: (reason?: unknown) => void,\n timeoutMs: number,\n): () => void {\n const startTime = Date.now();\n\n const checkSDK = (): void => {\n // Check if real TapKit is loaded (not just stub)\n // Stub has TapKitLoaded flag set to true by loader.js after real SDK loads\n if (window.TapKit && window.TapKitLoaded === true) {\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n return;\n }\n\n const elapsed = Date.now() - startTime;\n\n // Check if exceeded timeout\n if (elapsed > timeoutMs) {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(\n new Error(\n `TapKit loader timeout: SDK not available after ${timeoutMs}ms`,\n ),\n );\n return;\n }\n\n // Use requestIdleCallback for better performance\n // Falls back to setTimeout if not available\n if (typeof requestIdleCallback !== \"undefined\") {\n requestIdleCallback(checkSDK, { timeout: IDLE_CALLBACK_TIMEOUT_MS });\n } else {\n setTimeout(checkSDK, IDLE_CALLBACK_TIMEOUT_MS);\n }\n };\n\n return checkSDK;\n}\n\n/**\n * Loads the CDN loader script\n * The loader will then fetch versions.json and load the appropriate SDK version\n *\n * If __TAP_KIT_CORE_URL__ is set, bypasses loader and loads IIFE directly\n *\n * @param timeoutMs - Maximum time to wait for SDK to load (default: 4000ms)\n * @returns Promise that resolves when SDK is loaded\n * @throws {Error} If loader fails to load or times out\n */\nexport function loadCDNLoader(\n timeoutMs: number = DEFAULT_TIMEOUT_MS,\n): Promise<void> {\n // If already loaded, return immediately\n if (window.__TAP_KIT_LOADER_LOADED__ && window.TapKit) {\n return Promise.resolve();\n }\n\n // If currently loading, return the existing promise\n if (window.__TAP_KIT_LOADER_LOADING__) {\n return window.__TAP_KIT_LOADER_LOADING__;\n }\n\n // Create loading promise\n const loadingPromise = new Promise<void>((resolve, reject) => {\n if (typeof document === \"undefined\") {\n reject(\n new Error(\n \"TapKit requires browser environment (document is undefined)\",\n ),\n );\n return;\n }\n\n // Local core mode: Load IIFE directly\n if (isLocalCoreMode()) {\n // Check if TapKit is already loaded (from other pages in playground)\n if (window.TapKit && window.TapKitLoaded === true) {\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n return;\n }\n\n const coreURL = getLocalCoreURL();\n\n const script = document.createElement(\"script\");\n script.src = coreURL;\n script.async = true;\n\n script.onload = () => {\n // IIFE directly sets window.TapKit\n // Set the loaded flag manually since we bypass loader.js\n if (window.TapKit) {\n window.TapKitLoaded = true;\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n } else {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(\"TapKit not available after loading local core\"));\n }\n };\n\n script.onerror = () => {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`Failed to load local TapKit core: ${coreURL}`));\n };\n\n document.head.appendChild(script);\n return;\n }\n\n // CDN mode: Load loader.js\n const loaderURL = getLoaderURL();\n const script = document.createElement(\"script\");\n script.src = loaderURL;\n script.async = true;\n\n script.onload = () => {\n // The loader script will load the actual SDK\n // We need to wait a bit for the loader to fetch and load the SDK\n const checkSDK = createSDKChecker(resolve, reject, timeoutMs);\n checkSDK();\n };\n\n script.onerror = () => {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`));\n };\n\n // Check if script already exists\n const existingScript = document.querySelector(`script[src=\"${loaderURL}\"]`);\n\n if (existingScript) {\n // Script already added but not yet loaded\n existingScript.addEventListener(\"load\", () => {\n const checkSDK = createSDKChecker(resolve, reject, timeoutMs);\n checkSDK();\n });\n existingScript.addEventListener(\"error\", () =>\n reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`)),\n );\n } else {\n document.head.appendChild(script);\n }\n });\n\n window.__TAP_KIT_LOADER_LOADING__ = loadingPromise;\n return loadingPromise;\n}\n","/**\n * useTapKit Hook - ChatKit-style control for TapKit Web Component\n *\n * This hook provides a control object to manage TapKit through the\n * <TapKit /> component. Inspired by OpenAI's ChatKit pattern.\n *\n * @example\n * ```tsx\n * 'use client';\n *\n * import { TapKit, useTapKit } from '@coxwave/tap-kit/react';\n *\n * function MyApp() {\n * const tapkit = useTapKit({\n * apiKey: 'your-key',\n * userId: 'user-123',\n * courseId: 'course-456',\n * clipId: 'clip-789',\n * onReady: () => console.log('Ready!'),\n * onError: (error) => console.error(error),\n * });\n *\n * return (\n * <div>\n * <button onClick={tapkit.show} disabled={!tapkit.isReady}>\n * Ask AI Tutor\n * </button>\n * <TapKit control={tapkit.control} />\n * </div>\n * );\n * }\n * ```\n */\n\nimport type { TapKitElement, VideoPlayerConfig } from \"@coxwave/tap-kit-types\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { loadCDNLoader } from \"../loader\";\nimport type { TapKitControl, TapKitEventHandlers, VideoAdapter } from \"./types\";\n\nexport interface UseTapKitOptions extends TapKitEventHandlers {\n /** API Key (required) */\n apiKey: string;\n /** User ID */\n userId?: string;\n /** Course ID */\n courseId?: string;\n /** Clip ID */\n clipId?: string;\n /** Clip playhead position */\n clipPlayHead?: number;\n /** Language */\n language?: \"ko\" | \"en\";\n /** Custom button element ID */\n buttonId?: string;\n /** Display mode */\n mode?: \"inline\" | \"floating\" | \"sidebar\";\n /**\n * Allow user to toggle between floating and sidebar layouts\n * Only applies when mode is \"floating\" or \"sidebar\"\n * @default true\n */\n allowLayoutToggle?: boolean;\n /**\n * Video player target for timeline synchronization (recommended)\n *\n * Supports HTMLVideoElement or VideoPlayerAdapter.\n * Survives lifecycle changes (mode change, reconnect).\n *\n * @example\n * ```tsx\n * // HTMLVideoElement\n * const tapkit = useTapKit({ videoTarget: videoRef.current, ... });\n *\n * // VideoPlayerAdapter\n * const tapkit = useTapKit({\n * videoTarget: {\n * getCurrentTime: () => player.currentTime,\n * setCurrentTime: (t) => player.currentTime = t\n * },\n * ...\n * });\n * ```\n */\n videoTarget?: VideoPlayerConfig;\n /** Debug mode */\n debug?: boolean;\n /** TAP Frontend URL */\n tapUrl?: string;\n /** API Backend URL */\n apiUrl?: string;\n /** Environment */\n environment?: \"dev\" | \"prod\" | \"staging\" | \"demo\";\n // Note: `root` option removed - declarative API (<TapKit />) stays in declared position\n // For imperative control over position, use createTapKit() with root option\n}\n\n/**\n * Configuration options type (non-function values)\n */\nexport type TapKitOptions = Omit<UseTapKitOptions, keyof TapKitEventHandlers>;\n\n/**\n * Extract event handlers from options (pure function)\n */\nfunction extractHandlers(options: UseTapKitOptions): TapKitEventHandlers {\n return {\n onReady: options.onReady,\n onError: options.onError,\n onTimelineSeek: options.onTimelineSeek,\n onAlarmFadeIn: options.onAlarmFadeIn,\n };\n}\n\nexport interface UseTapKitReturn {\n /** Control object for <TapKit /> component */\n control: TapKitControl<TapKitOptions>;\n /** Whether TapKit is ready */\n isReady: boolean;\n /** Error during initialization */\n error: Error | null;\n /** Show chat interface */\n show: () => void;\n /** Hide chat interface */\n hide: () => void;\n /** Set course information */\n setCourse: (course: { courseId: string; clipId: string; userId?: string; clipPlayHead?: number }) => void;\n /**\n * Video adapter control\n *\n * @deprecated Use `videoTarget` option in useTapKit instead for declarative binding.\n *\n * ```tsx\n * // ✅ Recommended (declarative)\n * const tapkit = useTapKit({ videoTarget: adapter, ... });\n *\n * // ❌ Deprecated (imperative)\n * tapkit.video.bind(adapter);\n * ```\n */\n video: {\n bind: (adapter: VideoAdapter, clipId?: string) => void;\n unbind: () => void;\n };\n}\n\n/**\n * Hook for managing TapKit Web Component (ChatKit Pattern)\n *\n * Returns a control object to pass to <TapKit /> component.\n * The component handles rendering, this hook handles state and methods.\n */\nexport function useTapKit(options: UseTapKitOptions): UseTapKitReturn {\n // Element ref - set by <TapKit /> component via control.setInstance\n const elementRef = useRef<TapKitElement | null>(null);\n\n // Pending video bind request (queued until ready)\n const pendingVideoBind = useRef<{\n adapter: VideoAdapter;\n clipId?: string;\n } | null>(null);\n\n // State\n const [isCdnLoaded, setIsCdnLoaded] = useState(false);\n const [isReady, setIsReady] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n // Store handlers in refs to avoid stale closures\n const handlersRef = useRef(extractHandlers(options));\n handlersRef.current = extractHandlers(options);\n\n // Load CDN on mount (defines <tap-kit> Web Component)\n useEffect(() => {\n loadCDNLoader()\n .then(() => {\n setIsCdnLoaded(true);\n })\n .catch((err) => {\n const loadError = err instanceof Error ? err : new Error(\"Failed to load TapKit CDN\");\n setError(loadError);\n handlersRef.current.onError?.(loadError);\n });\n }, []);\n\n // Set instance callback - called by <TapKit /> component\n const setInstance = useCallback((instance: TapKitElement | null) => {\n elementRef.current = instance;\n\n if (!instance) {\n setIsReady(false);\n return;\n }\n\n // Listen for ready event\n const handleReady = () => {\n setIsReady(true);\n handlersRef.current.onReady?.();\n };\n\n const handleError = (e: Event) => {\n const customEvent = e as CustomEvent<{ error: Error }>;\n const err = customEvent.detail?.error ?? new Error(\"Unknown error\");\n setError(err);\n handlersRef.current.onError?.(err);\n };\n\n instance.addEventListener(\"tap-kit:ready\", handleReady);\n instance.addEventListener(\"tap-kit:error\", handleError);\n\n // Check if already ready\n if (instance.isInitialized) {\n setIsReady(true);\n }\n\n // Setup EventManager callbacks when ready\n // Use stable wrappers that call handlersRef.current to always get latest handlers\n const setupEventHandlers = () => {\n if (instance.events && typeof instance.events.onTimelineSeek === \"function\") {\n // Register stable wrappers instead of direct refs\n // This ensures handler updates are reflected without re-registration\n instance.events.onTimelineSeek((clipPlayHead, clipId) => {\n handlersRef.current.onTimelineSeek?.(clipPlayHead, clipId);\n });\n instance.events.onAlarmFadeIn((messageInfo) => {\n handlersRef.current.onAlarmFadeIn?.(messageInfo);\n });\n }\n\n // Execute pending video bind if queued\n if (pendingVideoBind.current && instance.video) {\n instance.video.bind(pendingVideoBind.current.adapter, pendingVideoBind.current.clipId);\n pendingVideoBind.current = null;\n }\n };\n\n // Check if ready property exists (may not be available immediately)\n if (instance.ready && typeof instance.ready.then === \"function\") {\n instance.ready.then(setupEventHandlers).catch(() => {\n // Ignore - error already handled by tap-kit:error event\n });\n } else if (instance.isInitialized) {\n // Already initialized, setup handlers immediately\n setupEventHandlers();\n } else {\n // Fallback: wait for ready event then setup handlers\n instance.addEventListener(\"tap-kit:ready\", setupEventHandlers, {\n once: true,\n });\n }\n }, []);\n\n // Memoize config options using individual values to prevent infinite re-renders\n // (options object is recreated on every render, so we must depend on primitives)\n // Note: videoTarget is object reference - changes trigger re-render intentionally\n const configOptions = useMemo(\n () => ({\n apiKey: options.apiKey,\n userId: options.userId,\n courseId: options.courseId,\n clipId: options.clipId,\n clipPlayHead: options.clipPlayHead,\n language: options.language,\n buttonId: options.buttonId,\n mode: options.mode,\n allowLayoutToggle: options.allowLayoutToggle,\n videoTarget: options.videoTarget,\n debug: options.debug,\n tapUrl: options.tapUrl,\n apiUrl: options.apiUrl,\n environment: options.environment,\n // Note: root removed - declarative API stays in declared position\n }),\n [\n options.apiKey,\n options.userId,\n options.courseId,\n options.clipId,\n options.clipPlayHead,\n options.language,\n options.buttonId,\n options.mode,\n options.allowLayoutToggle,\n options.videoTarget,\n options.debug,\n options.tapUrl,\n options.apiUrl,\n options.environment,\n ]\n );\n\n // Create stable handlers object for control\n // NOTE: This object is intentionally created once (empty deps) and appears to capture\n // stale values. However, the actual handler invocations in TapKit.tsx use handlersRef.current\n // which always has the latest handlers. This object is just a stable reference marker\n // that tells TapKit.tsx which handlers exist - not the actual handlers to call.\n const handlers = useMemo<TapKitEventHandlers>(\n () => ({\n onReady: handlersRef.current.onReady,\n onError: handlersRef.current.onError,\n onTimelineSeek: handlersRef.current.onTimelineSeek,\n onAlarmFadeIn: handlersRef.current.onAlarmFadeIn,\n }),\n []\n );\n\n // Create control object\n const control = useMemo<TapKitControl<TapKitOptions>>(\n () => ({\n setInstance,\n options: configOptions,\n handlers,\n isCdnLoaded,\n }),\n [setInstance, configOptions, handlers, isCdnLoaded]\n );\n\n // Methods\n const show = useCallback(() => {\n if (!elementRef.current) {\n console.warn(\"[useTapKit] Cannot show: element not mounted\");\n return;\n }\n elementRef.current.show();\n }, []);\n\n const hide = useCallback(() => {\n if (!elementRef.current) {\n console.warn(\"[useTapKit] Cannot hide: element not mounted\");\n return;\n }\n elementRef.current.hide();\n }, []);\n\n const setCourse = useCallback(\n (course: { courseId: string; clipId: string; userId?: string; clipPlayHead?: number }) => {\n if (!elementRef.current) {\n console.warn(\"[useTapKit] Cannot setCourse: element not mounted\");\n return;\n }\n elementRef.current.setCourse(course);\n },\n []\n );\n\n // Video adapter control\n const video = useMemo(\n () => ({\n bind: (adapter: VideoAdapter, clipId?: string) => {\n // If video controller is ready, bind immediately\n if (elementRef.current?.video) {\n elementRef.current.video.bind(adapter, clipId);\n return;\n }\n\n // Queue for later execution when ready\n pendingVideoBind.current = { adapter, clipId };\n },\n unbind: () => {\n // Clear pending request\n pendingVideoBind.current = null;\n elementRef.current?.video?.unbind();\n },\n }),\n []\n );\n\n return {\n control,\n isReady,\n error,\n show,\n hide,\n setCourse,\n video,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/react/types.ts","../src/react/TapKit.tsx","../src/loader.ts","../src/react/useTapKit.ts"],"names":["React","TapKit","useRef","useImperativeHandle","useCallback","useEffect","jsx","script","useState","useMemo"],"mappings":"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","file":"react.js","sourcesContent":["/**\n * React-specific type definitions for TapKit\n *\n * Defines event handler types and control patterns for React integration.\n */\n\nimport type { AlarmMessageInstanceType, TapKitElement } from \"@coxwave/tap-kit-types\";\n\n/**\n * Video player adapter interface for timeline synchronization\n *\n * Supports custom video players (YouTube, Vimeo, etc.) that don't use HTMLVideoElement.\n */\nexport interface VideoAdapter {\n /** Get current playback time in seconds */\n getCurrentTime: () => number;\n /** Set playback time in seconds */\n setCurrentTime: (time: number, clipId?: string) => void;\n}\n\n/**\n * Event handler type definitions for TapKit Web Component\n *\n * Maps CustomEvent types to React callback signatures.\n */\nexport interface TapKitEventHandlers {\n /**\n * Called when TapKit SDK is ready\n * @example onReady={() => console.log('TapKit ready!')}\n */\n onReady?: () => void;\n\n /**\n * Called when initialization or runtime error occurs\n * @example onError={(error) => console.error('TapKit error:', error)}\n */\n onError?: (error: Error) => void;\n\n /**\n * Called when timeline seek event occurs\n * @example onTimelineSeek={(clipPlayHead, clipId) => console.log('Seek:', clipPlayHead)}\n */\n onTimelineSeek?: (clipPlayHead: number, clipId: string) => void;\n\n /**\n * Called when alarm fade-in occurs\n * @example onAlarmFadeIn={(messageInfo) => console.log('Alarm:', messageInfo)}\n */\n onAlarmFadeIn?: (messageInfo: AlarmMessageInstanceType) => void;\n}\n\n/**\n * Control object pattern for managing TapKit instance\n *\n * Separates instance management (setInstance) from configuration (options)\n * and event handling (handlers). Inspired by ChatKit's control pattern.\n *\n * @example\n * ```tsx\n * const tapkit = useTapKit({ apiKey: 'key', onReady: () => {} });\n * <TapKit control={tapkit.control} />\n * ```\n */\nexport interface TapKitControl<T> {\n /**\n * Set the TapKit element instance reference\n *\n * Called by TapKit component to register the element instance.\n * Enables imperative control via useTapKit methods.\n */\n setInstance: (instance: TapKitElement | null) => void;\n\n /**\n * Configuration options (non-function values)\n *\n * All options except event handlers.\n */\n options: T;\n\n /**\n * Event handler callbacks (function values)\n *\n * Separated from options for easier event listener management.\n */\n handlers: TapKitEventHandlers;\n\n /**\n * Whether CDN is loaded and Web Component is available\n *\n * @internal Used by TapKit component to delay rendering\n */\n isCdnLoaded: boolean;\n}\n\n/**\n * Map of TapKit event names to their handler keys\n *\n * Used for automatic event listener binding in TapKit component.\n *\n * @internal\n */\nexport const EVENT_HANDLER_MAP = {\n \"tap-kit:ready\": \"onReady\",\n \"tap-kit:error\": \"onError\",\n} as const;\n\n/**\n * List of TapKit event names\n *\n * @internal\n */\nexport const EVENT_NAMES = Object.keys(EVENT_HANDLER_MAP) as (keyof typeof EVENT_HANDLER_MAP)[];\n","/**\n * TapKit React Component\n *\n * Declarative React wrapper for <tap-kit> Web Component.\n * Use this with useTapKit hook for a clean separation of control and rendering.\n *\n * @example\n * ```tsx\n * 'use client';\n *\n * import { TapKit, useTapKit } from '@coxwave/tap-kit/react';\n *\n * function MyApp() {\n * const tapkit = useTapKit({\n * apiKey: 'your-key',\n * userId: 'user-123',\n * courseId: 'course-456',\n * clipId: 'clip-789',\n * onReady: () => console.log('TapKit ready!'),\n * onError: (error) => console.error('Error:', error),\n * });\n *\n * return (\n * <div>\n * <button onClick={tapkit.show} disabled={!tapkit.isReady}>\n * Show Chat\n * </button>\n * <TapKit control={tapkit.control} style={{ height: '600px' }} />\n * </div>\n * );\n * }\n * ```\n *\n * **Note for Next.js App Router users:**\n * Make sure to add 'use client' at the top of your component file.\n *\n * @see https://edutap-ai-docs.vercel.app/docs/guides/react\n */\n\nimport type { TapKitElement } from \"@coxwave/tap-kit-types\";\nimport React, { useCallback, useEffect, useImperativeHandle, useRef } from \"react\";\nimport type { TapKitControl } from \"./types\";\nimport { EVENT_HANDLER_MAP, EVENT_NAMES } from \"./types\";\nimport type { TapKitOptions } from \"./useTapKit\";\n\n/**\n * Props for TapKit React component\n */\nexport interface TapKitProps extends Omit<React.HTMLAttributes<TapKitElement>, \"children\" | \"dangerouslySetInnerHTML\"> {\n /**\n * Control object from useTapKit hook\n *\n * Provides instance management, configuration, and event handlers.\n */\n control: TapKitControl<TapKitOptions>;\n}\n\n// Note: JSX IntrinsicElements augmentation is provided by @coxwave/tap-kit-types\n// See: packages/tap-kit-types/src/tap-kit.d.ts\n\n/**\n * TapKit React Component\n *\n * Renders <tap-kit> Web Component with React integration.\n * Automatically manages instance lifecycle and event listeners.\n *\n * **Ref Access**: The forwarded ref provides direct access to the TapKitElement:\n * ```tsx\n * const tapkitRef = useRef<TapKitElement>(null);\n * <TapKit ref={tapkitRef} control={...} />\n * // tapkitRef.current?.show()\n * ```\n */\nexport const TapKit = React.forwardRef<TapKitElement, TapKitProps>(function TapKit({ control, ...htmlProps }, forwardedRef) {\n const elementRef = useRef<TapKitElement | null>(null);\n\n // Forward TapKitElement to parent\n useImperativeHandle(forwardedRef, () => elementRef.current as TapKitElement, []);\n\n // Callback ref to register element when mounted\n const setElementRef = useCallback(\n (element: TapKitElement | null) => {\n elementRef.current = element;\n\n if (element) {\n // Set apiKey as property (not attribute) - required for LitElement\n // apiKey has { attribute: false } so it won't work via HTML attribute\n if (control.options.apiKey) {\n element.apiKey = control.options.apiKey;\n }\n\n // Set videoTarget as property (not attribute) - object type\n // videoTarget has { attribute: false } for lifecycle-safe video binding\n if (control.options.videoTarget) {\n element.videoTarget = control.options.videoTarget;\n }\n\n // Set allowLayoutToggle as property (not attribute) - Boolean type\n // HTML boolean attributes treat any string value (including \"false\") as true\n // Must be set via property to correctly handle false values\n if (control.options.allowLayoutToggle !== undefined) {\n element.allowLayoutToggle = control.options.allowLayoutToggle;\n }\n\n // Note: root is NOT set here - declarative API uses auto-detected parent\n // The element will detect declarative mode in connectedCallback\n }\n\n control.setInstance(element);\n },\n [control.setInstance, control.options.apiKey, control.options.videoTarget, control.options.allowLayoutToggle]\n );\n\n // Store handlers in ref to avoid effect re-runs on handler reference changes\n // Handlers are stable via useMemo in useTapKit, but use ref for extra safety\n const handlersRef = useRef(control.handlers);\n handlersRef.current = control.handlers;\n\n // Register event listeners (only once per element mount)\n useEffect(() => {\n const element = elementRef.current;\n if (!element) return;\n\n const listeners: Array<{\n event: string;\n handler: EventListener;\n }> = [];\n\n // Bind CustomEvent listeners (tap-kit:ready, tap-kit:error)\n for (const eventName of EVENT_NAMES) {\n const handlerKey = EVENT_HANDLER_MAP[eventName as keyof typeof EVENT_HANDLER_MAP];\n\n // Use closure over handlersRef to always get latest handler\n const listener = (e: Event) => {\n const handler = handlersRef.current[handlerKey];\n if (!handler) return;\n\n const customEvent = e as CustomEvent;\n if (handlerKey === \"onError\" && customEvent.detail?.error) {\n // onError receives error directly\n // biome-ignore lint/suspicious/noExplicitAny: Handler type is validated at runtime\n (handler as any)(customEvent.detail.error);\n } else if (handlerKey === \"onReady\") {\n // onReady receives no arguments\n // biome-ignore lint/suspicious/noExplicitAny: Handler type is validated at runtime\n (handler as any)();\n }\n };\n\n element.addEventListener(eventName, listener);\n listeners.push({ event: eventName, handler: listener });\n }\n\n return () => {\n // Cleanup all event listeners\n for (const { event, handler } of listeners) {\n element.removeEventListener(event, handler);\n }\n };\n }, []); // Empty deps - listeners use ref for handlers\n\n // Sync videoTarget property when it changes\n // (property-only, not attribute - must be set imperatively)\n useEffect(() => {\n const element = elementRef.current;\n if (!element) return;\n\n element.videoTarget = control.options.videoTarget;\n }, [control.options.videoTarget]);\n\n // Sync allowLayoutToggle property when it changes\n // (Boolean attributes don't work correctly with \"false\" string)\n useEffect(() => {\n const element = elementRef.current;\n if (!element || control.options.allowLayoutToggle === undefined) return;\n\n element.allowLayoutToggle = control.options.allowLayoutToggle;\n }, [control.options.allowLayoutToggle]);\n\n // Note: root sync removed - declarative API uses auto-detected parent\n // The element stays in its declared position\n\n const { options, isCdnLoaded } = control;\n\n // Wait for CDN to load before rendering Web Component\n // Without this, <tap-kit> is just an HTMLUnknownElement without methods\n if (!isCdnLoaded) {\n return null;\n }\n\n // Render <tap-kit> Web Component directly\n // Note: apiKey, videoTarget, allowLayoutToggle are set via ref callback (property-only)\n return (\n <tap-kit\n ref={setElementRef}\n user-id={options.userId}\n course-id={options.courseId}\n clip-id={options.clipId}\n clip-play-head={options.clipPlayHead}\n language={options.language}\n button-id={options.buttonId}\n mode={options.mode}\n debug={options.debug}\n tap-url={options.tapUrl}\n api-url={options.apiUrl}\n environment={options.environment}\n {...htmlProps}\n />\n );\n});\n","/**\n * CDN loader for TapKit\n * Dynamically loads the TapKit SDK from CDN\n *\n * For local testing, you can override the loader URL:\n * window.__TAP_KIT_LOADER_URL__ = '/tap-kit-core/loader.js';\n *\n * For local development (bypass loader, load IIFE directly):\n * window.__TAP_KIT_CORE_URL__ = '/packages/tap-kit-core/dist/index.global.js';\n */\n\n// Build-time constant injected by tsup define\ndeclare const __DEFAULT_CDN_LOADER_URL__: string;\n\nconst DEFAULT_CDN_LOADER_URL =\n typeof __DEFAULT_CDN_LOADER_URL__ !== \"undefined\" ? __DEFAULT_CDN_LOADER_URL__ : \"https://files.edutap.ai/tap-sdk/loader.js\";\nconst DEFAULT_TIMEOUT_MS = 4000; // 4 seconds total timeout\nconst IDLE_CALLBACK_TIMEOUT_MS = 500; // 500ms for requestIdleCallback\n\n/**\n * Get the loader URL from window override or default CDN\n */\nfunction getLoaderURL(): string {\n return window?.__TAP_KIT_LOADER_URL__ ? window.__TAP_KIT_LOADER_URL__ : DEFAULT_CDN_LOADER_URL;\n}\n\n/**\n * Check if local core mode is enabled\n * When __TAP_KIT_CORE_URL__ is set, directly load the IIFE bundle\n * This bypasses the loader.js and loads tap-kit-core directly\n */\nfunction isLocalCoreMode(): boolean {\n return typeof window !== \"undefined\" && !!window.__TAP_KIT_CORE_URL__;\n}\n\n/**\n * Get the local core URL\n */\nfunction getLocalCoreURL(): string {\n return window.__TAP_KIT_CORE_URL__ || \"\";\n}\n\n/**\n * Creates a SDK checker function with timeout and retry logic\n * Uses requestIdleCallback to avoid blocking browser rendering\n * @param resolve - Promise resolve function\n * @param reject - Promise reject function\n * @param timeoutMs - Maximum time to wait for SDK to load (milliseconds)\n * @returns Checker function to be called repeatedly\n */\nfunction createSDKChecker(\n resolve: (value: void | PromiseLike<void>) => void,\n reject: (reason?: unknown) => void,\n timeoutMs: number\n): () => void {\n const startTime = Date.now();\n\n const checkSDK = (): void => {\n // Check if real TapKit is loaded (not just stub)\n // Stub has TapKitLoaded flag set to true by loader.js after real SDK loads\n if (window.TapKit && window.TapKitLoaded === true) {\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n return;\n }\n\n const elapsed = Date.now() - startTime;\n\n // Check if exceeded timeout\n if (elapsed > timeoutMs) {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`TapKit loader timeout: SDK not available after ${timeoutMs}ms`));\n return;\n }\n\n // Use requestIdleCallback for better performance\n // Falls back to setTimeout if not available\n if (typeof requestIdleCallback !== \"undefined\") {\n requestIdleCallback(checkSDK, { timeout: IDLE_CALLBACK_TIMEOUT_MS });\n } else {\n setTimeout(checkSDK, IDLE_CALLBACK_TIMEOUT_MS);\n }\n };\n\n return checkSDK;\n}\n\n/**\n * Loads the CDN loader script\n * The loader will then fetch versions.json and load the appropriate SDK version\n *\n * If __TAP_KIT_CORE_URL__ is set, bypasses loader and loads IIFE directly\n *\n * @param timeoutMs - Maximum time to wait for SDK to load (default: 4000ms)\n * @returns Promise that resolves when SDK is loaded\n * @throws {Error} If loader fails to load or times out\n */\nexport function loadCDNLoader(timeoutMs: number = DEFAULT_TIMEOUT_MS): Promise<void> {\n // If already loaded, return immediately\n if (window.__TAP_KIT_LOADER_LOADED__ && window.TapKit) {\n return Promise.resolve();\n }\n\n // If currently loading, return the existing promise\n if (window.__TAP_KIT_LOADER_LOADING__) {\n return window.__TAP_KIT_LOADER_LOADING__;\n }\n\n // Create loading promise\n const loadingPromise = new Promise<void>((resolve, reject) => {\n if (typeof document === \"undefined\") {\n reject(new Error(\"TapKit requires browser environment (document is undefined)\"));\n return;\n }\n\n // Local core mode: Load IIFE directly\n if (isLocalCoreMode()) {\n // Check if TapKit is already loaded (from other pages in playground)\n if (window.TapKit && window.TapKitLoaded === true) {\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n return;\n }\n\n const coreURL = getLocalCoreURL();\n\n const script = document.createElement(\"script\");\n script.src = coreURL;\n script.async = true;\n\n script.onload = () => {\n // IIFE directly sets window.TapKit\n // Set the loaded flag manually since we bypass loader.js\n if (window.TapKit) {\n window.TapKitLoaded = true;\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n } else {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(\"TapKit not available after loading local core\"));\n }\n };\n\n script.onerror = () => {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`Failed to load local TapKit core: ${coreURL}`));\n };\n\n document.head.appendChild(script);\n return;\n }\n\n // CDN mode: Load loader.js\n const loaderURL = getLoaderURL();\n const script = document.createElement(\"script\");\n script.src = loaderURL;\n script.async = true;\n\n script.onload = () => {\n // The loader script will load the actual SDK\n // We need to wait a bit for the loader to fetch and load the SDK\n const checkSDK = createSDKChecker(resolve, reject, timeoutMs);\n checkSDK();\n };\n\n script.onerror = () => {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`));\n };\n\n // Check if script already exists\n const existingScript = document.querySelector(`script[src=\"${loaderURL}\"]`);\n\n if (existingScript) {\n // Script already added but not yet loaded\n existingScript.addEventListener(\"load\", () => {\n const checkSDK = createSDKChecker(resolve, reject, timeoutMs);\n checkSDK();\n });\n existingScript.addEventListener(\"error\", () => reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`)));\n } else {\n document.head.appendChild(script);\n }\n });\n\n window.__TAP_KIT_LOADER_LOADING__ = loadingPromise;\n return loadingPromise;\n}\n","/**\n * useTapKit Hook - ChatKit-style control for TapKit Web Component\n *\n * This hook provides a control object to manage TapKit through the\n * <TapKit /> component. Inspired by OpenAI's ChatKit pattern.\n *\n * @example\n * ```tsx\n * 'use client';\n *\n * import { TapKit, useTapKit } from '@coxwave/tap-kit/react';\n *\n * function MyApp() {\n * const tapkit = useTapKit({\n * apiKey: 'your-key',\n * userId: 'user-123',\n * courseId: 'course-456',\n * clipId: 'clip-789',\n * onReady: () => console.log('Ready!'),\n * onError: (error) => console.error(error),\n * });\n *\n * return (\n * <div>\n * <button onClick={tapkit.show} disabled={!tapkit.isReady}>\n * Ask AI Tutor\n * </button>\n * <TapKit control={tapkit.control} />\n * </div>\n * );\n * }\n * ```\n */\n\nimport type { TapKitElement, VideoPlayerConfig } from \"@coxwave/tap-kit-types\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { loadCDNLoader } from \"../loader\";\nimport type { TapKitControl, TapKitEventHandlers, VideoAdapter } from \"./types\";\n\nexport interface UseTapKitOptions extends TapKitEventHandlers {\n /** API Key (required) */\n apiKey: string;\n /** User ID */\n userId?: string;\n /** Course ID */\n courseId?: string;\n /** Clip ID */\n clipId?: string;\n /** Clip playhead position */\n clipPlayHead?: number;\n /** Language */\n language?: \"ko\" | \"en\";\n /** Custom button element ID */\n buttonId?: string;\n /** Display mode */\n mode?: \"inline\" | \"floating\" | \"sidebar\";\n /**\n * Allow user to toggle between floating and sidebar layouts\n * Only applies when mode is \"floating\" or \"sidebar\"\n * @default true\n */\n allowLayoutToggle?: boolean;\n /**\n * Video player target for timeline synchronization (recommended)\n *\n * Supports HTMLVideoElement or VideoPlayerAdapter.\n * Survives lifecycle changes (mode change, reconnect).\n *\n * @example\n * ```tsx\n * // HTMLVideoElement\n * const tapkit = useTapKit({ videoTarget: videoRef.current, ... });\n *\n * // VideoPlayerAdapter\n * const tapkit = useTapKit({\n * videoTarget: {\n * getCurrentTime: () => player.currentTime,\n * setCurrentTime: (t) => player.currentTime = t\n * },\n * ...\n * });\n * ```\n */\n videoTarget?: VideoPlayerConfig;\n /** Debug mode */\n debug?: boolean;\n /** TAP Frontend URL */\n tapUrl?: string;\n /** API Backend URL */\n apiUrl?: string;\n /** Environment */\n environment?: \"dev\" | \"prod\" | \"staging\" | \"demo\";\n // Note: `root` option removed - declarative API (<TapKit />) stays in declared position\n // For imperative control over position, use createTapKit() with root option\n}\n\n/**\n * Configuration options type (non-function values)\n */\nexport type TapKitOptions = Omit<UseTapKitOptions, keyof TapKitEventHandlers>;\n\n/**\n * Extract event handlers from options (pure function)\n */\nfunction extractHandlers(options: UseTapKitOptions): TapKitEventHandlers {\n return {\n onReady: options.onReady,\n onError: options.onError,\n onTimelineSeek: options.onTimelineSeek,\n onAlarmFadeIn: options.onAlarmFadeIn,\n };\n}\n\nexport interface UseTapKitReturn {\n /** Control object for <TapKit /> component */\n control: TapKitControl<TapKitOptions>;\n /** Whether TapKit is ready */\n isReady: boolean;\n /** Error during initialization */\n error: Error | null;\n /** Show chat interface */\n show: () => void;\n /** Hide chat interface */\n hide: () => void;\n /** Set course information */\n setCourse: (course: { courseId: string; clipId: string; userId?: string; clipPlayHead?: number }) => void;\n /**\n * Video adapter control\n *\n * @deprecated Use `videoTarget` option in useTapKit instead for declarative binding.\n *\n * ```tsx\n * // ✅ Recommended (declarative)\n * const tapkit = useTapKit({ videoTarget: adapter, ... });\n *\n * // ❌ Deprecated (imperative)\n * tapkit.video.bind(adapter);\n * ```\n */\n video: {\n bind: (adapter: VideoAdapter, clipId?: string) => void;\n unbind: () => void;\n };\n}\n\n/**\n * Hook for managing TapKit Web Component (ChatKit Pattern)\n *\n * Returns a control object to pass to <TapKit /> component.\n * The component handles rendering, this hook handles state and methods.\n */\nexport function useTapKit(options: UseTapKitOptions): UseTapKitReturn {\n // Element ref - set by <TapKit /> component via control.setInstance\n const elementRef = useRef<TapKitElement | null>(null);\n\n // Pending video bind request (queued until ready)\n const pendingVideoBind = useRef<{\n adapter: VideoAdapter;\n clipId?: string;\n } | null>(null);\n\n // State\n const [isCdnLoaded, setIsCdnLoaded] = useState(false);\n const [isReady, setIsReady] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n // Store handlers in refs to avoid stale closures\n const handlersRef = useRef(extractHandlers(options));\n handlersRef.current = extractHandlers(options);\n\n // Load CDN on mount (defines <tap-kit> Web Component)\n useEffect(() => {\n loadCDNLoader()\n .then(() => {\n setIsCdnLoaded(true);\n })\n .catch((err) => {\n const loadError = err instanceof Error ? err : new Error(\"Failed to load TapKit CDN\");\n setError(loadError);\n handlersRef.current.onError?.(loadError);\n });\n }, []);\n\n // Set instance callback - called by <TapKit /> component\n const setInstance = useCallback((instance: TapKitElement | null) => {\n elementRef.current = instance;\n\n if (!instance) {\n setIsReady(false);\n return;\n }\n\n // Listen for ready event\n const handleReady = () => {\n setIsReady(true);\n handlersRef.current.onReady?.();\n };\n\n const handleError = (e: Event) => {\n const customEvent = e as CustomEvent<{ error: Error }>;\n const err = customEvent.detail?.error ?? new Error(\"Unknown error\");\n setError(err);\n handlersRef.current.onError?.(err);\n };\n\n instance.addEventListener(\"tap-kit:ready\", handleReady);\n instance.addEventListener(\"tap-kit:error\", handleError);\n\n // Check if already ready\n if (instance.isInitialized) {\n setIsReady(true);\n }\n\n // Setup EventManager callbacks when ready\n // Use stable wrappers that call handlersRef.current to always get latest handlers\n const setupEventHandlers = () => {\n if (instance.events && typeof instance.events.onTimelineSeek === \"function\") {\n // Register stable wrappers instead of direct refs\n // This ensures handler updates are reflected without re-registration\n instance.events.onTimelineSeek((clipPlayHead, clipId) => {\n handlersRef.current.onTimelineSeek?.(clipPlayHead, clipId);\n });\n instance.events.onAlarmFadeIn((messageInfo) => {\n handlersRef.current.onAlarmFadeIn?.(messageInfo);\n });\n }\n\n // Execute pending video bind if queued\n if (pendingVideoBind.current && instance.video) {\n instance.video.bind(pendingVideoBind.current.adapter, pendingVideoBind.current.clipId);\n pendingVideoBind.current = null;\n }\n };\n\n // Check if ready property exists (may not be available immediately)\n if (instance.ready && typeof instance.ready.then === \"function\") {\n instance.ready.then(setupEventHandlers).catch(() => {\n // Ignore - error already handled by tap-kit:error event\n });\n } else if (instance.isInitialized) {\n // Already initialized, setup handlers immediately\n setupEventHandlers();\n } else {\n // Fallback: wait for ready event then setup handlers\n instance.addEventListener(\"tap-kit:ready\", setupEventHandlers, {\n once: true,\n });\n }\n }, []);\n\n // Memoize config options using individual values to prevent infinite re-renders\n // (options object is recreated on every render, so we must depend on primitives)\n // Note: videoTarget is object reference - changes trigger re-render intentionally\n const configOptions = useMemo(\n () => ({\n apiKey: options.apiKey,\n userId: options.userId,\n courseId: options.courseId,\n clipId: options.clipId,\n clipPlayHead: options.clipPlayHead,\n language: options.language,\n buttonId: options.buttonId,\n mode: options.mode,\n allowLayoutToggle: options.allowLayoutToggle,\n videoTarget: options.videoTarget,\n debug: options.debug,\n tapUrl: options.tapUrl,\n apiUrl: options.apiUrl,\n environment: options.environment,\n // Note: root removed - declarative API stays in declared position\n }),\n [\n options.apiKey,\n options.userId,\n options.courseId,\n options.clipId,\n options.clipPlayHead,\n options.language,\n options.buttonId,\n options.mode,\n options.allowLayoutToggle,\n options.videoTarget,\n options.debug,\n options.tapUrl,\n options.apiUrl,\n options.environment,\n ]\n );\n\n // Create stable handlers object for control\n // NOTE: This object is intentionally created once (empty deps) and appears to capture\n // stale values. However, the actual handler invocations in TapKit.tsx use handlersRef.current\n // which always has the latest handlers. This object is just a stable reference marker\n // that tells TapKit.tsx which handlers exist - not the actual handlers to call.\n const handlers = useMemo<TapKitEventHandlers>(\n () => ({\n onReady: handlersRef.current.onReady,\n onError: handlersRef.current.onError,\n onTimelineSeek: handlersRef.current.onTimelineSeek,\n onAlarmFadeIn: handlersRef.current.onAlarmFadeIn,\n }),\n []\n );\n\n // Create control object\n const control = useMemo<TapKitControl<TapKitOptions>>(\n () => ({\n setInstance,\n options: configOptions,\n handlers,\n isCdnLoaded,\n }),\n [setInstance, configOptions, handlers, isCdnLoaded]\n );\n\n // Methods\n const show = useCallback(() => {\n if (!elementRef.current) {\n console.warn(\"[useTapKit] Cannot show: element not mounted\");\n return;\n }\n elementRef.current.show();\n }, []);\n\n const hide = useCallback(() => {\n if (!elementRef.current) {\n console.warn(\"[useTapKit] Cannot hide: element not mounted\");\n return;\n }\n elementRef.current.hide();\n }, []);\n\n const setCourse = useCallback((course: { courseId: string; clipId: string; userId?: string; clipPlayHead?: number }) => {\n if (!elementRef.current) {\n console.warn(\"[useTapKit] Cannot setCourse: element not mounted\");\n return;\n }\n elementRef.current.setCourse(course);\n }, []);\n\n // Video adapter control\n const video = useMemo(\n () => ({\n bind: (adapter: VideoAdapter, clipId?: string) => {\n // If video controller is ready, bind immediately\n if (elementRef.current?.video) {\n elementRef.current.video.bind(adapter, clipId);\n return;\n }\n\n // Queue for later execution when ready\n pendingVideoBind.current = { adapter, clipId };\n },\n unbind: () => {\n // Clear pending request\n pendingVideoBind.current = null;\n elementRef.current?.video?.unbind();\n },\n }),\n []\n );\n\n return {\n control,\n isReady,\n error,\n show,\n hide,\n setCourse,\n video,\n };\n}\n"]}
|
package/dist/react.mjs
CHANGED
|
@@ -116,11 +116,7 @@ function createSDKChecker(resolve, reject, timeoutMs) {
|
|
|
116
116
|
const elapsed = Date.now() - startTime;
|
|
117
117
|
if (elapsed > timeoutMs) {
|
|
118
118
|
window.__TAP_KIT_LOADER_LOADING__ = void 0;
|
|
119
|
-
reject(
|
|
120
|
-
new Error(
|
|
121
|
-
`TapKit loader timeout: SDK not available after ${timeoutMs}ms`
|
|
122
|
-
)
|
|
123
|
-
);
|
|
119
|
+
reject(new Error(`TapKit loader timeout: SDK not available after ${timeoutMs}ms`));
|
|
124
120
|
return;
|
|
125
121
|
}
|
|
126
122
|
if (typeof requestIdleCallback !== "undefined") {
|
|
@@ -140,11 +136,7 @@ function loadCDNLoader(timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
|
140
136
|
}
|
|
141
137
|
const loadingPromise = new Promise((resolve, reject) => {
|
|
142
138
|
if (typeof document === "undefined") {
|
|
143
|
-
reject(
|
|
144
|
-
new Error(
|
|
145
|
-
"TapKit requires browser environment (document is undefined)"
|
|
146
|
-
)
|
|
147
|
-
);
|
|
139
|
+
reject(new Error("TapKit requires browser environment (document is undefined)"));
|
|
148
140
|
return;
|
|
149
141
|
}
|
|
150
142
|
if (isLocalCoreMode()) {
|
|
@@ -194,10 +186,7 @@ function loadCDNLoader(timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
|
194
186
|
const checkSDK = createSDKChecker(resolve, reject, timeoutMs);
|
|
195
187
|
checkSDK();
|
|
196
188
|
});
|
|
197
|
-
existingScript.addEventListener(
|
|
198
|
-
"error",
|
|
199
|
-
() => reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`))
|
|
200
|
-
);
|
|
189
|
+
existingScript.addEventListener("error", () => reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`)));
|
|
201
190
|
} else {
|
|
202
191
|
document.head.appendChild(script);
|
|
203
192
|
}
|
|
@@ -345,16 +334,13 @@ function useTapKit(options) {
|
|
|
345
334
|
}
|
|
346
335
|
elementRef.current.hide();
|
|
347
336
|
}, []);
|
|
348
|
-
const setCourse = useCallback(
|
|
349
|
-
(
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
},
|
|
356
|
-
[]
|
|
357
|
-
);
|
|
337
|
+
const setCourse = useCallback((course) => {
|
|
338
|
+
if (!elementRef.current) {
|
|
339
|
+
console.warn("[useTapKit] Cannot setCourse: element not mounted");
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
elementRef.current.setCourse(course);
|
|
343
|
+
}, []);
|
|
358
344
|
const video = useMemo(
|
|
359
345
|
() => ({
|
|
360
346
|
bind: (adapter, clipId) => {
|
package/dist/react.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/react/types.ts","../src/react/TapKit.tsx","../src/loader.ts","../src/react/useTapKit.ts"],"names":["TapKit","script","useRef","useEffect","useCallback"],"mappings":"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","file":"react.mjs","sourcesContent":["/**\n * React-specific type definitions for TapKit\n *\n * Defines event handler types and control patterns for React integration.\n */\n\nimport type { AlarmMessageInstanceType, TapKitElement } from \"@coxwave/tap-kit-types\";\n\n/**\n * Video player adapter interface for timeline synchronization\n *\n * Supports custom video players (YouTube, Vimeo, etc.) that don't use HTMLVideoElement.\n */\nexport interface VideoAdapter {\n /** Get current playback time in seconds */\n getCurrentTime: () => number;\n /** Set playback time in seconds */\n setCurrentTime: (time: number, clipId?: string) => void;\n}\n\n/**\n * Event handler type definitions for TapKit Web Component\n *\n * Maps CustomEvent types to React callback signatures.\n */\nexport interface TapKitEventHandlers {\n /**\n * Called when TapKit SDK is ready\n * @example onReady={() => console.log('TapKit ready!')}\n */\n onReady?: () => void;\n\n /**\n * Called when initialization or runtime error occurs\n * @example onError={(error) => console.error('TapKit error:', error)}\n */\n onError?: (error: Error) => void;\n\n /**\n * Called when timeline seek event occurs\n * @example onTimelineSeek={(clipPlayHead, clipId) => console.log('Seek:', clipPlayHead)}\n */\n onTimelineSeek?: (clipPlayHead: number, clipId: string) => void;\n\n /**\n * Called when alarm fade-in occurs\n * @example onAlarmFadeIn={(messageInfo) => console.log('Alarm:', messageInfo)}\n */\n onAlarmFadeIn?: (messageInfo: AlarmMessageInstanceType) => void;\n}\n\n/**\n * Control object pattern for managing TapKit instance\n *\n * Separates instance management (setInstance) from configuration (options)\n * and event handling (handlers). Inspired by ChatKit's control pattern.\n *\n * @example\n * ```tsx\n * const tapkit = useTapKit({ apiKey: 'key', onReady: () => {} });\n * <TapKit control={tapkit.control} />\n * ```\n */\nexport interface TapKitControl<T> {\n /**\n * Set the TapKit element instance reference\n *\n * Called by TapKit component to register the element instance.\n * Enables imperative control via useTapKit methods.\n */\n setInstance: (instance: TapKitElement | null) => void;\n\n /**\n * Configuration options (non-function values)\n *\n * All options except event handlers.\n */\n options: T;\n\n /**\n * Event handler callbacks (function values)\n *\n * Separated from options for easier event listener management.\n */\n handlers: TapKitEventHandlers;\n\n /**\n * Whether CDN is loaded and Web Component is available\n *\n * @internal Used by TapKit component to delay rendering\n */\n isCdnLoaded: boolean;\n}\n\n/**\n * Map of TapKit event names to their handler keys\n *\n * Used for automatic event listener binding in TapKit component.\n *\n * @internal\n */\nexport const EVENT_HANDLER_MAP = {\n \"tap-kit:ready\": \"onReady\",\n \"tap-kit:error\": \"onError\",\n} as const;\n\n/**\n * List of TapKit event names\n *\n * @internal\n */\nexport const EVENT_NAMES = Object.keys(EVENT_HANDLER_MAP) as (keyof typeof EVENT_HANDLER_MAP)[];\n","/**\n * TapKit React Component\n *\n * Declarative React wrapper for <tap-kit> Web Component.\n * Use this with useTapKit hook for a clean separation of control and rendering.\n *\n * @example\n * ```tsx\n * 'use client';\n *\n * import { TapKit, useTapKit } from '@coxwave/tap-kit/react';\n *\n * function MyApp() {\n * const tapkit = useTapKit({\n * apiKey: 'your-key',\n * userId: 'user-123',\n * courseId: 'course-456',\n * clipId: 'clip-789',\n * onReady: () => console.log('TapKit ready!'),\n * onError: (error) => console.error('Error:', error),\n * });\n *\n * return (\n * <div>\n * <button onClick={tapkit.show} disabled={!tapkit.isReady}>\n * Show Chat\n * </button>\n * <TapKit control={tapkit.control} style={{ height: '600px' }} />\n * </div>\n * );\n * }\n * ```\n *\n * **Note for Next.js App Router users:**\n * Make sure to add 'use client' at the top of your component file.\n *\n * @see https://edutap-ai-docs.vercel.app/docs/guides/react\n */\n\nimport type { TapKitElement } from \"@coxwave/tap-kit-types\";\nimport React, { useCallback, useEffect, useImperativeHandle, useRef } from \"react\";\nimport type { TapKitControl } from \"./types\";\nimport { EVENT_HANDLER_MAP, EVENT_NAMES } from \"./types\";\nimport type { TapKitOptions } from \"./useTapKit\";\n\n/**\n * Props for TapKit React component\n */\nexport interface TapKitProps extends Omit<React.HTMLAttributes<TapKitElement>, \"children\" | \"dangerouslySetInnerHTML\"> {\n /**\n * Control object from useTapKit hook\n *\n * Provides instance management, configuration, and event handlers.\n */\n control: TapKitControl<TapKitOptions>;\n}\n\n// Note: JSX IntrinsicElements augmentation is provided by @coxwave/tap-kit-types\n// See: packages/tap-kit-types/src/tap-kit.d.ts\n\n/**\n * TapKit React Component\n *\n * Renders <tap-kit> Web Component with React integration.\n * Automatically manages instance lifecycle and event listeners.\n *\n * **Ref Access**: The forwarded ref provides direct access to the TapKitElement:\n * ```tsx\n * const tapkitRef = useRef<TapKitElement>(null);\n * <TapKit ref={tapkitRef} control={...} />\n * // tapkitRef.current?.show()\n * ```\n */\nexport const TapKit = React.forwardRef<TapKitElement, TapKitProps>(function TapKit(\n { control, ...htmlProps },\n forwardedRef\n) {\n const elementRef = useRef<TapKitElement | null>(null);\n\n // Forward TapKitElement to parent\n useImperativeHandle(forwardedRef, () => elementRef.current as TapKitElement, []);\n\n // Callback ref to register element when mounted\n const setElementRef = useCallback(\n (element: TapKitElement | null) => {\n elementRef.current = element;\n\n if (element) {\n // Set apiKey as property (not attribute) - required for LitElement\n // apiKey has { attribute: false } so it won't work via HTML attribute\n if (control.options.apiKey) {\n element.apiKey = control.options.apiKey;\n }\n\n // Set videoTarget as property (not attribute) - object type\n // videoTarget has { attribute: false } for lifecycle-safe video binding\n if (control.options.videoTarget) {\n element.videoTarget = control.options.videoTarget;\n }\n\n // Set allowLayoutToggle as property (not attribute) - Boolean type\n // HTML boolean attributes treat any string value (including \"false\") as true\n // Must be set via property to correctly handle false values\n if (control.options.allowLayoutToggle !== undefined) {\n element.allowLayoutToggle = control.options.allowLayoutToggle;\n }\n\n // Note: root is NOT set here - declarative API uses auto-detected parent\n // The element will detect declarative mode in connectedCallback\n }\n\n control.setInstance(element);\n },\n [control.setInstance, control.options.apiKey, control.options.videoTarget, control.options.allowLayoutToggle]\n );\n\n // Store handlers in ref to avoid effect re-runs on handler reference changes\n // Handlers are stable via useMemo in useTapKit, but use ref for extra safety\n const handlersRef = useRef(control.handlers);\n handlersRef.current = control.handlers;\n\n // Register event listeners (only once per element mount)\n useEffect(() => {\n const element = elementRef.current;\n if (!element) return;\n\n const listeners: Array<{\n event: string;\n handler: EventListener;\n }> = [];\n\n // Bind CustomEvent listeners (tap-kit:ready, tap-kit:error)\n for (const eventName of EVENT_NAMES) {\n const handlerKey = EVENT_HANDLER_MAP[eventName as keyof typeof EVENT_HANDLER_MAP];\n\n // Use closure over handlersRef to always get latest handler\n const listener = (e: Event) => {\n const handler = handlersRef.current[handlerKey];\n if (!handler) return;\n\n const customEvent = e as CustomEvent;\n if (handlerKey === \"onError\" && customEvent.detail?.error) {\n // onError receives error directly\n // biome-ignore lint/suspicious/noExplicitAny: Handler type is validated at runtime\n (handler as any)(customEvent.detail.error);\n } else if (handlerKey === \"onReady\") {\n // onReady receives no arguments\n // biome-ignore lint/suspicious/noExplicitAny: Handler type is validated at runtime\n (handler as any)();\n }\n };\n\n element.addEventListener(eventName, listener);\n listeners.push({ event: eventName, handler: listener });\n }\n\n return () => {\n // Cleanup all event listeners\n for (const { event, handler } of listeners) {\n element.removeEventListener(event, handler);\n }\n };\n }, []); // Empty deps - listeners use ref for handlers\n\n // Sync videoTarget property when it changes\n // (property-only, not attribute - must be set imperatively)\n useEffect(() => {\n const element = elementRef.current;\n if (!element) return;\n\n element.videoTarget = control.options.videoTarget;\n }, [control.options.videoTarget]);\n\n // Sync allowLayoutToggle property when it changes\n // (Boolean attributes don't work correctly with \"false\" string)\n useEffect(() => {\n const element = elementRef.current;\n if (!element || control.options.allowLayoutToggle === undefined) return;\n\n element.allowLayoutToggle = control.options.allowLayoutToggle;\n }, [control.options.allowLayoutToggle]);\n\n // Note: root sync removed - declarative API uses auto-detected parent\n // The element stays in its declared position\n\n const { options, isCdnLoaded } = control;\n\n // Wait for CDN to load before rendering Web Component\n // Without this, <tap-kit> is just an HTMLUnknownElement without methods\n if (!isCdnLoaded) {\n return null;\n }\n\n // Render <tap-kit> Web Component directly\n // Note: apiKey, videoTarget, allowLayoutToggle are set via ref callback (property-only)\n return (\n <tap-kit\n ref={setElementRef}\n user-id={options.userId}\n course-id={options.courseId}\n clip-id={options.clipId}\n clip-play-head={options.clipPlayHead}\n language={options.language}\n button-id={options.buttonId}\n mode={options.mode}\n debug={options.debug}\n tap-url={options.tapUrl}\n api-url={options.apiUrl}\n environment={options.environment}\n {...htmlProps}\n />\n );\n});\n","/**\n * CDN loader for TapKit\n * Dynamically loads the TapKit SDK from CDN\n *\n * For local testing, you can override the loader URL:\n * window.__TAP_KIT_LOADER_URL__ = '/tap-kit-core/loader.js';\n *\n * For local development (bypass loader, load IIFE directly):\n * window.__TAP_KIT_CORE_URL__ = '/packages/tap-kit-core/dist/index.global.js';\n */\n\n// Build-time constant injected by tsup define\ndeclare const __DEFAULT_CDN_LOADER_URL__: string;\n\nconst DEFAULT_CDN_LOADER_URL =\n typeof __DEFAULT_CDN_LOADER_URL__ !== \"undefined\"\n ? __DEFAULT_CDN_LOADER_URL__\n : \"https://files.edutap.ai/tap-sdk/loader.js\";\nconst DEFAULT_TIMEOUT_MS = 4000; // 4 seconds total timeout\nconst IDLE_CALLBACK_TIMEOUT_MS = 500; // 500ms for requestIdleCallback\n\n/**\n * Get the loader URL from window override or default CDN\n */\nfunction getLoaderURL(): string {\n return window?.__TAP_KIT_LOADER_URL__\n ? window.__TAP_KIT_LOADER_URL__\n : DEFAULT_CDN_LOADER_URL;\n}\n\n/**\n * Check if local core mode is enabled\n * When __TAP_KIT_CORE_URL__ is set, directly load the IIFE bundle\n * This bypasses the loader.js and loads tap-kit-core directly\n */\nfunction isLocalCoreMode(): boolean {\n return typeof window !== \"undefined\" && !!window.__TAP_KIT_CORE_URL__;\n}\n\n/**\n * Get the local core URL\n */\nfunction getLocalCoreURL(): string {\n return window.__TAP_KIT_CORE_URL__ || \"\";\n}\n\n/**\n * Creates a SDK checker function with timeout and retry logic\n * Uses requestIdleCallback to avoid blocking browser rendering\n * @param resolve - Promise resolve function\n * @param reject - Promise reject function\n * @param timeoutMs - Maximum time to wait for SDK to load (milliseconds)\n * @returns Checker function to be called repeatedly\n */\nfunction createSDKChecker(\n resolve: (value: void | PromiseLike<void>) => void,\n reject: (reason?: unknown) => void,\n timeoutMs: number,\n): () => void {\n const startTime = Date.now();\n\n const checkSDK = (): void => {\n // Check if real TapKit is loaded (not just stub)\n // Stub has TapKitLoaded flag set to true by loader.js after real SDK loads\n if (window.TapKit && window.TapKitLoaded === true) {\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n return;\n }\n\n const elapsed = Date.now() - startTime;\n\n // Check if exceeded timeout\n if (elapsed > timeoutMs) {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(\n new Error(\n `TapKit loader timeout: SDK not available after ${timeoutMs}ms`,\n ),\n );\n return;\n }\n\n // Use requestIdleCallback for better performance\n // Falls back to setTimeout if not available\n if (typeof requestIdleCallback !== \"undefined\") {\n requestIdleCallback(checkSDK, { timeout: IDLE_CALLBACK_TIMEOUT_MS });\n } else {\n setTimeout(checkSDK, IDLE_CALLBACK_TIMEOUT_MS);\n }\n };\n\n return checkSDK;\n}\n\n/**\n * Loads the CDN loader script\n * The loader will then fetch versions.json and load the appropriate SDK version\n *\n * If __TAP_KIT_CORE_URL__ is set, bypasses loader and loads IIFE directly\n *\n * @param timeoutMs - Maximum time to wait for SDK to load (default: 4000ms)\n * @returns Promise that resolves when SDK is loaded\n * @throws {Error} If loader fails to load or times out\n */\nexport function loadCDNLoader(\n timeoutMs: number = DEFAULT_TIMEOUT_MS,\n): Promise<void> {\n // If already loaded, return immediately\n if (window.__TAP_KIT_LOADER_LOADED__ && window.TapKit) {\n return Promise.resolve();\n }\n\n // If currently loading, return the existing promise\n if (window.__TAP_KIT_LOADER_LOADING__) {\n return window.__TAP_KIT_LOADER_LOADING__;\n }\n\n // Create loading promise\n const loadingPromise = new Promise<void>((resolve, reject) => {\n if (typeof document === \"undefined\") {\n reject(\n new Error(\n \"TapKit requires browser environment (document is undefined)\",\n ),\n );\n return;\n }\n\n // Local core mode: Load IIFE directly\n if (isLocalCoreMode()) {\n // Check if TapKit is already loaded (from other pages in playground)\n if (window.TapKit && window.TapKitLoaded === true) {\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n return;\n }\n\n const coreURL = getLocalCoreURL();\n\n const script = document.createElement(\"script\");\n script.src = coreURL;\n script.async = true;\n\n script.onload = () => {\n // IIFE directly sets window.TapKit\n // Set the loaded flag manually since we bypass loader.js\n if (window.TapKit) {\n window.TapKitLoaded = true;\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n } else {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(\"TapKit not available after loading local core\"));\n }\n };\n\n script.onerror = () => {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`Failed to load local TapKit core: ${coreURL}`));\n };\n\n document.head.appendChild(script);\n return;\n }\n\n // CDN mode: Load loader.js\n const loaderURL = getLoaderURL();\n const script = document.createElement(\"script\");\n script.src = loaderURL;\n script.async = true;\n\n script.onload = () => {\n // The loader script will load the actual SDK\n // We need to wait a bit for the loader to fetch and load the SDK\n const checkSDK = createSDKChecker(resolve, reject, timeoutMs);\n checkSDK();\n };\n\n script.onerror = () => {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`));\n };\n\n // Check if script already exists\n const existingScript = document.querySelector(`script[src=\"${loaderURL}\"]`);\n\n if (existingScript) {\n // Script already added but not yet loaded\n existingScript.addEventListener(\"load\", () => {\n const checkSDK = createSDKChecker(resolve, reject, timeoutMs);\n checkSDK();\n });\n existingScript.addEventListener(\"error\", () =>\n reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`)),\n );\n } else {\n document.head.appendChild(script);\n }\n });\n\n window.__TAP_KIT_LOADER_LOADING__ = loadingPromise;\n return loadingPromise;\n}\n","/**\n * useTapKit Hook - ChatKit-style control for TapKit Web Component\n *\n * This hook provides a control object to manage TapKit through the\n * <TapKit /> component. Inspired by OpenAI's ChatKit pattern.\n *\n * @example\n * ```tsx\n * 'use client';\n *\n * import { TapKit, useTapKit } from '@coxwave/tap-kit/react';\n *\n * function MyApp() {\n * const tapkit = useTapKit({\n * apiKey: 'your-key',\n * userId: 'user-123',\n * courseId: 'course-456',\n * clipId: 'clip-789',\n * onReady: () => console.log('Ready!'),\n * onError: (error) => console.error(error),\n * });\n *\n * return (\n * <div>\n * <button onClick={tapkit.show} disabled={!tapkit.isReady}>\n * Ask AI Tutor\n * </button>\n * <TapKit control={tapkit.control} />\n * </div>\n * );\n * }\n * ```\n */\n\nimport type { TapKitElement, VideoPlayerConfig } from \"@coxwave/tap-kit-types\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { loadCDNLoader } from \"../loader\";\nimport type { TapKitControl, TapKitEventHandlers, VideoAdapter } from \"./types\";\n\nexport interface UseTapKitOptions extends TapKitEventHandlers {\n /** API Key (required) */\n apiKey: string;\n /** User ID */\n userId?: string;\n /** Course ID */\n courseId?: string;\n /** Clip ID */\n clipId?: string;\n /** Clip playhead position */\n clipPlayHead?: number;\n /** Language */\n language?: \"ko\" | \"en\";\n /** Custom button element ID */\n buttonId?: string;\n /** Display mode */\n mode?: \"inline\" | \"floating\" | \"sidebar\";\n /**\n * Allow user to toggle between floating and sidebar layouts\n * Only applies when mode is \"floating\" or \"sidebar\"\n * @default true\n */\n allowLayoutToggle?: boolean;\n /**\n * Video player target for timeline synchronization (recommended)\n *\n * Supports HTMLVideoElement or VideoPlayerAdapter.\n * Survives lifecycle changes (mode change, reconnect).\n *\n * @example\n * ```tsx\n * // HTMLVideoElement\n * const tapkit = useTapKit({ videoTarget: videoRef.current, ... });\n *\n * // VideoPlayerAdapter\n * const tapkit = useTapKit({\n * videoTarget: {\n * getCurrentTime: () => player.currentTime,\n * setCurrentTime: (t) => player.currentTime = t\n * },\n * ...\n * });\n * ```\n */\n videoTarget?: VideoPlayerConfig;\n /** Debug mode */\n debug?: boolean;\n /** TAP Frontend URL */\n tapUrl?: string;\n /** API Backend URL */\n apiUrl?: string;\n /** Environment */\n environment?: \"dev\" | \"prod\" | \"staging\" | \"demo\";\n // Note: `root` option removed - declarative API (<TapKit />) stays in declared position\n // For imperative control over position, use createTapKit() with root option\n}\n\n/**\n * Configuration options type (non-function values)\n */\nexport type TapKitOptions = Omit<UseTapKitOptions, keyof TapKitEventHandlers>;\n\n/**\n * Extract event handlers from options (pure function)\n */\nfunction extractHandlers(options: UseTapKitOptions): TapKitEventHandlers {\n return {\n onReady: options.onReady,\n onError: options.onError,\n onTimelineSeek: options.onTimelineSeek,\n onAlarmFadeIn: options.onAlarmFadeIn,\n };\n}\n\nexport interface UseTapKitReturn {\n /** Control object for <TapKit /> component */\n control: TapKitControl<TapKitOptions>;\n /** Whether TapKit is ready */\n isReady: boolean;\n /** Error during initialization */\n error: Error | null;\n /** Show chat interface */\n show: () => void;\n /** Hide chat interface */\n hide: () => void;\n /** Set course information */\n setCourse: (course: { courseId: string; clipId: string; userId?: string; clipPlayHead?: number }) => void;\n /**\n * Video adapter control\n *\n * @deprecated Use `videoTarget` option in useTapKit instead for declarative binding.\n *\n * ```tsx\n * // ✅ Recommended (declarative)\n * const tapkit = useTapKit({ videoTarget: adapter, ... });\n *\n * // ❌ Deprecated (imperative)\n * tapkit.video.bind(adapter);\n * ```\n */\n video: {\n bind: (adapter: VideoAdapter, clipId?: string) => void;\n unbind: () => void;\n };\n}\n\n/**\n * Hook for managing TapKit Web Component (ChatKit Pattern)\n *\n * Returns a control object to pass to <TapKit /> component.\n * The component handles rendering, this hook handles state and methods.\n */\nexport function useTapKit(options: UseTapKitOptions): UseTapKitReturn {\n // Element ref - set by <TapKit /> component via control.setInstance\n const elementRef = useRef<TapKitElement | null>(null);\n\n // Pending video bind request (queued until ready)\n const pendingVideoBind = useRef<{\n adapter: VideoAdapter;\n clipId?: string;\n } | null>(null);\n\n // State\n const [isCdnLoaded, setIsCdnLoaded] = useState(false);\n const [isReady, setIsReady] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n // Store handlers in refs to avoid stale closures\n const handlersRef = useRef(extractHandlers(options));\n handlersRef.current = extractHandlers(options);\n\n // Load CDN on mount (defines <tap-kit> Web Component)\n useEffect(() => {\n loadCDNLoader()\n .then(() => {\n setIsCdnLoaded(true);\n })\n .catch((err) => {\n const loadError = err instanceof Error ? err : new Error(\"Failed to load TapKit CDN\");\n setError(loadError);\n handlersRef.current.onError?.(loadError);\n });\n }, []);\n\n // Set instance callback - called by <TapKit /> component\n const setInstance = useCallback((instance: TapKitElement | null) => {\n elementRef.current = instance;\n\n if (!instance) {\n setIsReady(false);\n return;\n }\n\n // Listen for ready event\n const handleReady = () => {\n setIsReady(true);\n handlersRef.current.onReady?.();\n };\n\n const handleError = (e: Event) => {\n const customEvent = e as CustomEvent<{ error: Error }>;\n const err = customEvent.detail?.error ?? new Error(\"Unknown error\");\n setError(err);\n handlersRef.current.onError?.(err);\n };\n\n instance.addEventListener(\"tap-kit:ready\", handleReady);\n instance.addEventListener(\"tap-kit:error\", handleError);\n\n // Check if already ready\n if (instance.isInitialized) {\n setIsReady(true);\n }\n\n // Setup EventManager callbacks when ready\n // Use stable wrappers that call handlersRef.current to always get latest handlers\n const setupEventHandlers = () => {\n if (instance.events && typeof instance.events.onTimelineSeek === \"function\") {\n // Register stable wrappers instead of direct refs\n // This ensures handler updates are reflected without re-registration\n instance.events.onTimelineSeek((clipPlayHead, clipId) => {\n handlersRef.current.onTimelineSeek?.(clipPlayHead, clipId);\n });\n instance.events.onAlarmFadeIn((messageInfo) => {\n handlersRef.current.onAlarmFadeIn?.(messageInfo);\n });\n }\n\n // Execute pending video bind if queued\n if (pendingVideoBind.current && instance.video) {\n instance.video.bind(pendingVideoBind.current.adapter, pendingVideoBind.current.clipId);\n pendingVideoBind.current = null;\n }\n };\n\n // Check if ready property exists (may not be available immediately)\n if (instance.ready && typeof instance.ready.then === \"function\") {\n instance.ready.then(setupEventHandlers).catch(() => {\n // Ignore - error already handled by tap-kit:error event\n });\n } else if (instance.isInitialized) {\n // Already initialized, setup handlers immediately\n setupEventHandlers();\n } else {\n // Fallback: wait for ready event then setup handlers\n instance.addEventListener(\"tap-kit:ready\", setupEventHandlers, {\n once: true,\n });\n }\n }, []);\n\n // Memoize config options using individual values to prevent infinite re-renders\n // (options object is recreated on every render, so we must depend on primitives)\n // Note: videoTarget is object reference - changes trigger re-render intentionally\n const configOptions = useMemo(\n () => ({\n apiKey: options.apiKey,\n userId: options.userId,\n courseId: options.courseId,\n clipId: options.clipId,\n clipPlayHead: options.clipPlayHead,\n language: options.language,\n buttonId: options.buttonId,\n mode: options.mode,\n allowLayoutToggle: options.allowLayoutToggle,\n videoTarget: options.videoTarget,\n debug: options.debug,\n tapUrl: options.tapUrl,\n apiUrl: options.apiUrl,\n environment: options.environment,\n // Note: root removed - declarative API stays in declared position\n }),\n [\n options.apiKey,\n options.userId,\n options.courseId,\n options.clipId,\n options.clipPlayHead,\n options.language,\n options.buttonId,\n options.mode,\n options.allowLayoutToggle,\n options.videoTarget,\n options.debug,\n options.tapUrl,\n options.apiUrl,\n options.environment,\n ]\n );\n\n // Create stable handlers object for control\n // NOTE: This object is intentionally created once (empty deps) and appears to capture\n // stale values. However, the actual handler invocations in TapKit.tsx use handlersRef.current\n // which always has the latest handlers. This object is just a stable reference marker\n // that tells TapKit.tsx which handlers exist - not the actual handlers to call.\n const handlers = useMemo<TapKitEventHandlers>(\n () => ({\n onReady: handlersRef.current.onReady,\n onError: handlersRef.current.onError,\n onTimelineSeek: handlersRef.current.onTimelineSeek,\n onAlarmFadeIn: handlersRef.current.onAlarmFadeIn,\n }),\n []\n );\n\n // Create control object\n const control = useMemo<TapKitControl<TapKitOptions>>(\n () => ({\n setInstance,\n options: configOptions,\n handlers,\n isCdnLoaded,\n }),\n [setInstance, configOptions, handlers, isCdnLoaded]\n );\n\n // Methods\n const show = useCallback(() => {\n if (!elementRef.current) {\n console.warn(\"[useTapKit] Cannot show: element not mounted\");\n return;\n }\n elementRef.current.show();\n }, []);\n\n const hide = useCallback(() => {\n if (!elementRef.current) {\n console.warn(\"[useTapKit] Cannot hide: element not mounted\");\n return;\n }\n elementRef.current.hide();\n }, []);\n\n const setCourse = useCallback(\n (course: { courseId: string; clipId: string; userId?: string; clipPlayHead?: number }) => {\n if (!elementRef.current) {\n console.warn(\"[useTapKit] Cannot setCourse: element not mounted\");\n return;\n }\n elementRef.current.setCourse(course);\n },\n []\n );\n\n // Video adapter control\n const video = useMemo(\n () => ({\n bind: (adapter: VideoAdapter, clipId?: string) => {\n // If video controller is ready, bind immediately\n if (elementRef.current?.video) {\n elementRef.current.video.bind(adapter, clipId);\n return;\n }\n\n // Queue for later execution when ready\n pendingVideoBind.current = { adapter, clipId };\n },\n unbind: () => {\n // Clear pending request\n pendingVideoBind.current = null;\n elementRef.current?.video?.unbind();\n },\n }),\n []\n );\n\n return {\n control,\n isReady,\n error,\n show,\n hide,\n setCourse,\n video,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/react/types.ts","../src/react/TapKit.tsx","../src/loader.ts","../src/react/useTapKit.ts"],"names":["TapKit","script","useRef","useEffect","useCallback"],"mappings":";;;;;;AAqGO,IAAM,iBAAA,GAAoB;AAAA,EAC/B,eAAA,EAAiB,SAAA;AAAA,EACjB,eAAA,EAAiB;AACnB,CAAA;AAOO,IAAM,WAAA,GAAc,MAAA,CAAO,IAAA,CAAK,iBAAiB,CAAA;ACtCjD,IAAM,MAAA,GAAS,KAAA,CAAM,UAAA,CAAuC,SAASA,OAAAA,CAAO,EAAE,OAAA,EAAS,GAAG,SAAA,EAAU,EAAG,YAAA,EAAc;AAC1H,EAAA,MAAM,UAAA,GAAa,OAA6B,IAAI,CAAA;AAGpD,EAAA,mBAAA,CAAoB,YAAA,EAAc,MAAM,UAAA,CAAW,OAAA,EAA0B,EAAE,CAAA;AAG/E,EAAA,MAAM,aAAA,GAAgB,WAAA;AAAA,IACpB,CAAC,OAAA,KAAkC;AACjC,MAAA,UAAA,CAAW,OAAA,GAAU,OAAA;AAErB,MAAA,IAAI,OAAA,EAAS;AAGX,QAAA,IAAI,OAAA,CAAQ,QAAQ,MAAA,EAAQ;AAC1B,UAAA,OAAA,CAAQ,MAAA,GAAS,QAAQ,OAAA,CAAQ,MAAA;AAAA,QACnC;AAIA,QAAA,IAAI,OAAA,CAAQ,QAAQ,WAAA,EAAa;AAC/B,UAAA,OAAA,CAAQ,WAAA,GAAc,QAAQ,OAAA,CAAQ,WAAA;AAAA,QACxC;AAKA,QAAA,IAAI,OAAA,CAAQ,OAAA,CAAQ,iBAAA,KAAsB,MAAA,EAAW;AACnD,UAAA,OAAA,CAAQ,iBAAA,GAAoB,QAAQ,OAAA,CAAQ,iBAAA;AAAA,QAC9C;AAAA,MAIF;AAEA,MAAA,OAAA,CAAQ,YAAY,OAAO,CAAA;AAAA,IAC7B,CAAA;AAAA,IACA,CAAC,OAAA,CAAQ,WAAA,EAAa,OAAA,CAAQ,OAAA,CAAQ,MAAA,EAAQ,OAAA,CAAQ,OAAA,CAAQ,WAAA,EAAa,OAAA,CAAQ,OAAA,CAAQ,iBAAiB;AAAA,GAC9G;AAIA,EAAA,MAAM,WAAA,GAAc,MAAA,CAAO,OAAA,CAAQ,QAAQ,CAAA;AAC3C,EAAA,WAAA,CAAY,UAAU,OAAA,CAAQ,QAAA;AAG9B,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,UAAU,UAAA,CAAW,OAAA;AAC3B,IAAA,IAAI,CAAC,OAAA,EAAS;AAEd,IAAA,MAAM,YAGD,EAAC;AAGN,IAAA,KAAA,MAAW,aAAa,WAAA,EAAa;AACnC,MAAA,MAAM,UAAA,GAAa,kBAAkB,SAA2C,CAAA;AAGhF,MAAA,MAAM,QAAA,GAAW,CAAC,CAAA,KAAa;AAC7B,QAAA,MAAM,OAAA,GAAU,WAAA,CAAY,OAAA,CAAQ,UAAU,CAAA;AAC9C,QAAA,IAAI,CAAC,OAAA,EAAS;AAEd,QAAA,MAAM,WAAA,GAAc,CAAA;AACpB,QAAA,IAAI,UAAA,KAAe,SAAA,IAAa,WAAA,CAAY,MAAA,EAAQ,KAAA,EAAO;AAGzD,UAAC,OAAA,CAAgB,WAAA,CAAY,MAAA,CAAO,KAAK,CAAA;AAAA,QAC3C,CAAA,MAAA,IAAW,eAAe,SAAA,EAAW;AAGnC,UAAC,OAAA,EAAgB;AAAA,QACnB;AAAA,MACF,CAAA;AAEA,MAAA,OAAA,CAAQ,gBAAA,CAAiB,WAAW,QAAQ,CAAA;AAC5C,MAAA,SAAA,CAAU,KAAK,EAAE,KAAA,EAAO,SAAA,EAAW,OAAA,EAAS,UAAU,CAAA;AAAA,IACxD;AAEA,IAAA,OAAO,MAAM;AAEX,MAAA,KAAA,MAAW,EAAE,KAAA,EAAO,OAAA,EAAQ,IAAK,SAAA,EAAW;AAC1C,QAAA,OAAA,CAAQ,mBAAA,CAAoB,OAAO,OAAO,CAAA;AAAA,MAC5C;AAAA,IACF,CAAA;AAAA,EACF,CAAA,EAAG,EAAE,CAAA;AAIL,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,UAAU,UAAA,CAAW,OAAA;AAC3B,IAAA,IAAI,CAAC,OAAA,EAAS;AAEd,IAAA,OAAA,CAAQ,WAAA,GAAc,QAAQ,OAAA,CAAQ,WAAA;AAAA,EACxC,CAAA,EAAG,CAAC,OAAA,CAAQ,OAAA,CAAQ,WAAW,CAAC,CAAA;AAIhC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,UAAU,UAAA,CAAW,OAAA;AAC3B,IAAA,IAAI,CAAC,OAAA,IAAW,OAAA,CAAQ,OAAA,CAAQ,sBAAsB,MAAA,EAAW;AAEjE,IAAA,OAAA,CAAQ,iBAAA,GAAoB,QAAQ,OAAA,CAAQ,iBAAA;AAAA,EAC9C,CAAA,EAAG,CAAC,OAAA,CAAQ,OAAA,CAAQ,iBAAiB,CAAC,CAAA;AAKtC,EAAA,MAAM,EAAE,OAAA,EAAS,WAAA,EAAY,GAAI,OAAA;AAIjC,EAAA,IAAI,CAAC,WAAA,EAAa;AAChB,IAAA,OAAO,IAAA;AAAA,EACT;AAIA,EAAA,uBACE,GAAA;AAAA,IAAC,SAAA;AAAA,IAAA;AAAA,MACC,GAAA,EAAK,aAAA;AAAA,MACL,WAAS,OAAA,CAAQ,MAAA;AAAA,MACjB,aAAW,OAAA,CAAQ,QAAA;AAAA,MACnB,WAAS,OAAA,CAAQ,MAAA;AAAA,MACjB,kBAAgB,OAAA,CAAQ,YAAA;AAAA,MACxB,UAAU,OAAA,CAAQ,QAAA;AAAA,MAClB,aAAW,OAAA,CAAQ,QAAA;AAAA,MACnB,MAAM,OAAA,CAAQ,IAAA;AAAA,MACd,OAAO,OAAA,CAAQ,KAAA;AAAA,MACf,WAAS,OAAA,CAAQ,MAAA;AAAA,MACjB,WAAS,OAAA,CAAQ,MAAA;AAAA,MACjB,aAAa,OAAA,CAAQ,WAAA;AAAA,MACpB,GAAG;AAAA;AAAA,GACN;AAEJ,CAAC;;;ACnMD,IAAM,sBAAA,GACJ,OAAO,0BAAA,KAA+B,WAAA,GAAc,0BAAA,GAA6B,2CAAA;AACnF,IAAM,kBAAA,GAAqB,GAAA;AAC3B,IAAM,wBAAA,GAA2B,GAAA;AAKjC,SAAS,YAAA,GAAuB;AAC9B,EAAA,OAAO,MAAA,EAAQ,sBAAA,GAAyB,MAAA,CAAO,sBAAA,GAAyB,sBAAA;AAC1E;AAOA,SAAS,eAAA,GAA2B;AAClC,EAAA,OAAO,OAAO,MAAA,KAAW,WAAA,IAAe,CAAC,CAAC,MAAA,CAAO,oBAAA;AACnD;AAKA,SAAS,eAAA,GAA0B;AACjC,EAAA,OAAO,OAAO,oBAAA,IAAwB,EAAA;AACxC;AAUA,SAAS,gBAAA,CACP,OAAA,EACA,MAAA,EACA,SAAA,EACY;AACZ,EAAA,MAAM,SAAA,GAAY,KAAK,GAAA,EAAI;AAE3B,EAAA,MAAM,WAAW,MAAY;AAG3B,IAAA,IAAI,MAAA,CAAO,MAAA,IAAU,MAAA,CAAO,YAAA,KAAiB,IAAA,EAAM;AACjD,MAAA,MAAA,CAAO,yBAAA,GAA4B,IAAA;AACnC,MAAA,MAAA,CAAO,0BAAA,GAA6B,MAAA;AACpC,MAAA,OAAA,EAAQ;AACR,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAG7B,IAAA,IAAI,UAAU,SAAA,EAAW;AACvB,MAAA,MAAA,CAAO,0BAAA,GAA6B,MAAA;AACpC,MAAA,MAAA,CAAO,IAAI,KAAA,CAAM,CAAA,+CAAA,EAAkD,SAAS,IAAI,CAAC,CAAA;AACjF,MAAA;AAAA,IACF;AAIA,IAAA,IAAI,OAAO,wBAAwB,WAAA,EAAa;AAC9C,MAAA,mBAAA,CAAoB,QAAA,EAAU,EAAE,OAAA,EAAS,wBAAA,EAA0B,CAAA;AAAA,IACrE,CAAA,MAAO;AACL,MAAA,UAAA,CAAW,UAAU,wBAAwB,CAAA;AAAA,IAC/C;AAAA,EACF,CAAA;AAEA,EAAA,OAAO,QAAA;AACT;AAYO,SAAS,aAAA,CAAc,YAAoB,kBAAA,EAAmC;AAEnF,EAAA,IAAI,MAAA,CAAO,yBAAA,IAA6B,MAAA,CAAO,MAAA,EAAQ;AACrD,IAAA,OAAO,QAAQ,OAAA,EAAQ;AAAA,EACzB;AAGA,EAAA,IAAI,OAAO,0BAAA,EAA4B;AACrC,IAAA,OAAO,MAAA,CAAO,0BAAA;AAAA,EAChB;AAGA,EAAA,MAAM,cAAA,GAAiB,IAAI,OAAA,CAAc,CAAC,SAAS,MAAA,KAAW;AAC5D,IAAA,IAAI,OAAO,aAAa,WAAA,EAAa;AACnC,MAAA,MAAA,CAAO,IAAI,KAAA,CAAM,6DAA6D,CAAC,CAAA;AAC/E,MAAA;AAAA,IACF;AAGA,IAAA,IAAI,iBAAgB,EAAG;AAErB,MAAA,IAAI,MAAA,CAAO,MAAA,IAAU,MAAA,CAAO,YAAA,KAAiB,IAAA,EAAM;AACjD,QAAA,MAAA,CAAO,yBAAA,GAA4B,IAAA;AACnC,QAAA,MAAA,CAAO,0BAAA,GAA6B,MAAA;AACpC,QAAA,OAAA,EAAQ;AACR,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,UAAU,eAAA,EAAgB;AAEhC,MAAA,MAAMC,OAAAA,GAAS,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC9C,MAAAA,QAAO,GAAA,GAAM,OAAA;AACb,MAAAA,QAAO,KAAA,GAAQ,IAAA;AAEf,MAAAA,OAAAA,CAAO,SAAS,MAAM;AAGpB,QAAA,IAAI,OAAO,MAAA,EAAQ;AACjB,UAAA,MAAA,CAAO,YAAA,GAAe,IAAA;AACtB,UAAA,MAAA,CAAO,yBAAA,GAA4B,IAAA;AACnC,UAAA,MAAA,CAAO,0BAAA,GAA6B,MAAA;AACpC,UAAA,OAAA,EAAQ;AAAA,QACV,CAAA,MAAO;AACL,UAAA,MAAA,CAAO,0BAAA,GAA6B,MAAA;AACpC,UAAA,MAAA,CAAO,IAAI,KAAA,CAAM,+CAA+C,CAAC,CAAA;AAAA,QACnE;AAAA,MACF,CAAA;AAEA,MAAAA,OAAAA,CAAO,UAAU,MAAM;AACrB,QAAA,MAAA,CAAO,0BAAA,GAA6B,MAAA;AACpC,QAAA,MAAA,CAAO,IAAI,KAAA,CAAM,CAAA,kCAAA,EAAqC,OAAO,EAAE,CAAC,CAAA;AAAA,MAClE,CAAA;AAEA,MAAA,QAAA,CAAS,IAAA,CAAK,YAAYA,OAAM,CAAA;AAChC,MAAA;AAAA,IACF;AAGA,IAAA,MAAM,YAAY,YAAA,EAAa;AAC/B,IAAA,MAAM,MAAA,GAAS,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC9C,IAAA,MAAA,CAAO,GAAA,GAAM,SAAA;AACb,IAAA,MAAA,CAAO,KAAA,GAAQ,IAAA;AAEf,IAAA,MAAA,CAAO,SAAS,MAAM;AAGpB,MAAA,MAAM,QAAA,GAAW,gBAAA,CAAiB,OAAA,EAAS,MAAA,EAAQ,SAAS,CAAA;AAC5D,MAAA,QAAA,EAAS;AAAA,IACX,CAAA;AAEA,IAAA,MAAA,CAAO,UAAU,MAAM;AACrB,MAAA,MAAA,CAAO,0BAAA,GAA6B,MAAA;AACpC,MAAA,MAAA,CAAO,IAAI,KAAA,CAAM,CAAA,kCAAA,EAAqC,SAAS,EAAE,CAAC,CAAA;AAAA,IACpE,CAAA;AAGA,IAAA,MAAM,cAAA,GAAiB,QAAA,CAAS,aAAA,CAAc,CAAA,YAAA,EAAe,SAAS,CAAA,EAAA,CAAI,CAAA;AAE1E,IAAA,IAAI,cAAA,EAAgB;AAElB,MAAA,cAAA,CAAe,gBAAA,CAAiB,QAAQ,MAAM;AAC5C,QAAA,MAAM,QAAA,GAAW,gBAAA,CAAiB,OAAA,EAAS,MAAA,EAAQ,SAAS,CAAA;AAC5D,QAAA,QAAA,EAAS;AAAA,MACX,CAAC,CAAA;AACD,MAAA,cAAA,CAAe,gBAAA,CAAiB,OAAA,EAAS,MAAM,MAAA,CAAO,IAAI,MAAM,CAAA,kCAAA,EAAqC,SAAS,CAAA,CAAE,CAAC,CAAC,CAAA;AAAA,IACpH,CAAA,MAAO;AACL,MAAA,QAAA,CAAS,IAAA,CAAK,YAAY,MAAM,CAAA;AAAA,IAClC;AAAA,EACF,CAAC,CAAA;AAED,EAAA,MAAA,CAAO,0BAAA,GAA6B,cAAA;AACpC,EAAA,OAAO,cAAA;AACT;;;ACtFA,SAAS,gBAAgB,OAAA,EAAgD;AACvE,EAAA,OAAO;AAAA,IACL,SAAS,OAAA,CAAQ,OAAA;AAAA,IACjB,SAAS,OAAA,CAAQ,OAAA;AAAA,IACjB,gBAAgB,OAAA,CAAQ,cAAA;AAAA,IACxB,eAAe,OAAA,CAAQ;AAAA,GACzB;AACF;AAwCO,SAAS,UAAU,OAAA,EAA4C;AAEpE,EAAA,MAAM,UAAA,GAAaC,OAA6B,IAAI,CAAA;AAGpD,EAAA,MAAM,gBAAA,GAAmBA,OAGf,IAAI,CAAA;AAGd,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,SAAS,KAAK,CAAA;AACpD,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,KAAK,CAAA;AAC5C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAuB,IAAI,CAAA;AAGrD,EAAA,MAAM,WAAA,GAAcA,MAAAA,CAAO,eAAA,CAAgB,OAAO,CAAC,CAAA;AACnD,EAAA,WAAA,CAAY,OAAA,GAAU,gBAAgB,OAAO,CAAA;AAG7C,EAAAC,UAAU,MAAM;AACd,IAAA,aAAA,EAAc,CACX,KAAK,MAAM;AACV,MAAA,cAAA,CAAe,IAAI,CAAA;AAAA,IACrB,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,GAAA,KAAQ;AACd,MAAA,MAAM,YAAY,GAAA,YAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,MAAM,2BAA2B,CAAA;AACpF,MAAA,QAAA,CAAS,SAAS,CAAA;AAClB,MAAA,WAAA,CAAY,OAAA,CAAQ,UAAU,SAAS,CAAA;AAAA,IACzC,CAAC,CAAA;AAAA,EACL,CAAA,EAAG,EAAE,CAAA;AAGL,EAAA,MAAM,WAAA,GAAcC,WAAAA,CAAY,CAAC,QAAA,KAAmC;AAClE,IAAA,UAAA,CAAW,OAAA,GAAU,QAAA;AAErB,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,UAAA,CAAW,KAAK,CAAA;AAChB,MAAA;AAAA,IACF;AAGA,IAAA,MAAM,cAAc,MAAM;AACxB,MAAA,UAAA,CAAW,IAAI,CAAA;AACf,MAAA,WAAA,CAAY,QAAQ,OAAA,IAAU;AAAA,IAChC,CAAA;AAEA,IAAA,MAAM,WAAA,GAAc,CAAC,CAAA,KAAa;AAChC,MAAA,MAAM,WAAA,GAAc,CAAA;AACpB,MAAA,MAAM,MAAM,WAAA,CAAY,MAAA,EAAQ,KAAA,IAAS,IAAI,MAAM,eAAe,CAAA;AAClE,MAAA,QAAA,CAAS,GAAG,CAAA;AACZ,MAAA,WAAA,CAAY,OAAA,CAAQ,UAAU,GAAG,CAAA;AAAA,IACnC,CAAA;AAEA,IAAA,QAAA,CAAS,gBAAA,CAAiB,iBAAiB,WAAW,CAAA;AACtD,IAAA,QAAA,CAAS,gBAAA,CAAiB,iBAAiB,WAAW,CAAA;AAGtD,IAAA,IAAI,SAAS,aAAA,EAAe;AAC1B,MAAA,UAAA,CAAW,IAAI,CAAA;AAAA,IACjB;AAIA,IAAA,MAAM,qBAAqB,MAAM;AAC/B,MAAA,IAAI,SAAS,MAAA,IAAU,OAAO,QAAA,CAAS,MAAA,CAAO,mBAAmB,UAAA,EAAY;AAG3E,QAAA,QAAA,CAAS,MAAA,CAAO,cAAA,CAAe,CAAC,YAAA,EAAc,MAAA,KAAW;AACvD,UAAA,WAAA,CAAY,OAAA,CAAQ,cAAA,GAAiB,YAAA,EAAc,MAAM,CAAA;AAAA,QAC3D,CAAC,CAAA;AACD,QAAA,QAAA,CAAS,MAAA,CAAO,aAAA,CAAc,CAAC,WAAA,KAAgB;AAC7C,UAAA,WAAA,CAAY,OAAA,CAAQ,gBAAgB,WAAW,CAAA;AAAA,QACjD,CAAC,CAAA;AAAA,MACH;AAGA,MAAA,IAAI,gBAAA,CAAiB,OAAA,IAAW,QAAA,CAAS,KAAA,EAAO;AAC9C,QAAA,QAAA,CAAS,MAAM,IAAA,CAAK,gBAAA,CAAiB,QAAQ,OAAA,EAAS,gBAAA,CAAiB,QAAQ,MAAM,CAAA;AACrF,QAAA,gBAAA,CAAiB,OAAA,GAAU,IAAA;AAAA,MAC7B;AAAA,IACF,CAAA;AAGA,IAAA,IAAI,SAAS,KAAA,IAAS,OAAO,QAAA,CAAS,KAAA,CAAM,SAAS,UAAA,EAAY;AAC/D,MAAA,QAAA,CAAS,KAAA,CAAM,IAAA,CAAK,kBAAkB,CAAA,CAAE,MAAM,MAAM;AAAA,MAEpD,CAAC,CAAA;AAAA,IACH,CAAA,MAAA,IAAW,SAAS,aAAA,EAAe;AAEjC,MAAA,kBAAA,EAAmB;AAAA,IACrB,CAAA,MAAO;AAEL,MAAA,QAAA,CAAS,gBAAA,CAAiB,iBAAiB,kBAAA,EAAoB;AAAA,QAC7D,IAAA,EAAM;AAAA,OACP,CAAA;AAAA,IACH;AAAA,EACF,CAAA,EAAG,EAAE,CAAA;AAKL,EAAA,MAAM,aAAA,GAAgB,OAAA;AAAA,IACpB,OAAO;AAAA,MACL,QAAQ,OAAA,CAAQ,MAAA;AAAA,MAChB,QAAQ,OAAA,CAAQ,MAAA;AAAA,MAChB,UAAU,OAAA,CAAQ,QAAA;AAAA,MAClB,QAAQ,OAAA,CAAQ,MAAA;AAAA,MAChB,cAAc,OAAA,CAAQ,YAAA;AAAA,MACtB,UAAU,OAAA,CAAQ,QAAA;AAAA,MAClB,UAAU,OAAA,CAAQ,QAAA;AAAA,MAClB,MAAM,OAAA,CAAQ,IAAA;AAAA,MACd,mBAAmB,OAAA,CAAQ,iBAAA;AAAA,MAC3B,aAAa,OAAA,CAAQ,WAAA;AAAA,MACrB,OAAO,OAAA,CAAQ,KAAA;AAAA,MACf,QAAQ,OAAA,CAAQ,MAAA;AAAA,MAChB,QAAQ,OAAA,CAAQ,MAAA;AAAA,MAChB,aAAa,OAAA,CAAQ;AAAA;AAAA,KAEvB,CAAA;AAAA,IACA;AAAA,MACE,OAAA,CAAQ,MAAA;AAAA,MACR,OAAA,CAAQ,MAAA;AAAA,MACR,OAAA,CAAQ,QAAA;AAAA,MACR,OAAA,CAAQ,MAAA;AAAA,MACR,OAAA,CAAQ,YAAA;AAAA,MACR,OAAA,CAAQ,QAAA;AAAA,MACR,OAAA,CAAQ,QAAA;AAAA,MACR,OAAA,CAAQ,IAAA;AAAA,MACR,OAAA,CAAQ,iBAAA;AAAA,MACR,OAAA,CAAQ,WAAA;AAAA,MACR,OAAA,CAAQ,KAAA;AAAA,MACR,OAAA,CAAQ,MAAA;AAAA,MACR,OAAA,CAAQ,MAAA;AAAA,MACR,OAAA,CAAQ;AAAA;AACV,GACF;AAOA,EAAA,MAAM,QAAA,GAAW,OAAA;AAAA,IACf,OAAO;AAAA,MACL,OAAA,EAAS,YAAY,OAAA,CAAQ,OAAA;AAAA,MAC7B,OAAA,EAAS,YAAY,OAAA,CAAQ,OAAA;AAAA,MAC7B,cAAA,EAAgB,YAAY,OAAA,CAAQ,cAAA;AAAA,MACpC,aAAA,EAAe,YAAY,OAAA,CAAQ;AAAA,KACrC,CAAA;AAAA,IACA;AAAC,GACH;AAGA,EAAA,MAAM,OAAA,GAAU,OAAA;AAAA,IACd,OAAO;AAAA,MACL,WAAA;AAAA,MACA,OAAA,EAAS,aAAA;AAAA,MACT,QAAA;AAAA,MACA;AAAA,KACF,CAAA;AAAA,IACA,CAAC,WAAA,EAAa,aAAA,EAAe,QAAA,EAAU,WAAW;AAAA,GACpD;AAGA,EAAA,MAAM,IAAA,GAAOA,YAAY,MAAM;AAC7B,IAAA,IAAI,CAAC,WAAW,OAAA,EAAS;AACvB,MAAA,OAAA,CAAQ,KAAK,8CAA8C,CAAA;AAC3D,MAAA;AAAA,IACF;AACA,IAAA,UAAA,CAAW,QAAQ,IAAA,EAAK;AAAA,EAC1B,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,IAAA,GAAOA,YAAY,MAAM;AAC7B,IAAA,IAAI,CAAC,WAAW,OAAA,EAAS;AACvB,MAAA,OAAA,CAAQ,KAAK,8CAA8C,CAAA;AAC3D,MAAA;AAAA,IACF;AACA,IAAA,UAAA,CAAW,QAAQ,IAAA,EAAK;AAAA,EAC1B,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,SAAA,GAAYA,WAAAA,CAAY,CAAC,MAAA,KAAyF;AACtH,IAAA,IAAI,CAAC,WAAW,OAAA,EAAS;AACvB,MAAA,OAAA,CAAQ,KAAK,mDAAmD,CAAA;AAChE,MAAA;AAAA,IACF;AACA,IAAA,UAAA,CAAW,OAAA,CAAQ,UAAU,MAAM,CAAA;AAAA,EACrC,CAAA,EAAG,EAAE,CAAA;AAGL,EAAA,MAAM,KAAA,GAAQ,OAAA;AAAA,IACZ,OAAO;AAAA,MACL,IAAA,EAAM,CAAC,OAAA,EAAuB,MAAA,KAAoB;AAEhD,QAAA,IAAI,UAAA,CAAW,SAAS,KAAA,EAAO;AAC7B,UAAA,UAAA,CAAW,OAAA,CAAQ,KAAA,CAAM,IAAA,CAAK,OAAA,EAAS,MAAM,CAAA;AAC7C,UAAA;AAAA,QACF;AAGA,QAAA,gBAAA,CAAiB,OAAA,GAAU,EAAE,OAAA,EAAS,MAAA,EAAO;AAAA,MAC/C,CAAA;AAAA,MACA,QAAQ,MAAM;AAEZ,QAAA,gBAAA,CAAiB,OAAA,GAAU,IAAA;AAC3B,QAAA,UAAA,CAAW,OAAA,EAAS,OAAO,MAAA,EAAO;AAAA,MACpC;AAAA,KACF,CAAA;AAAA,IACA;AAAC,GACH;AAEA,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,OAAA;AAAA,IACA,KAAA;AAAA,IACA,IAAA;AAAA,IACA,IAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GACF;AACF","file":"react.mjs","sourcesContent":["/**\n * React-specific type definitions for TapKit\n *\n * Defines event handler types and control patterns for React integration.\n */\n\nimport type { AlarmMessageInstanceType, TapKitElement } from \"@coxwave/tap-kit-types\";\n\n/**\n * Video player adapter interface for timeline synchronization\n *\n * Supports custom video players (YouTube, Vimeo, etc.) that don't use HTMLVideoElement.\n */\nexport interface VideoAdapter {\n /** Get current playback time in seconds */\n getCurrentTime: () => number;\n /** Set playback time in seconds */\n setCurrentTime: (time: number, clipId?: string) => void;\n}\n\n/**\n * Event handler type definitions for TapKit Web Component\n *\n * Maps CustomEvent types to React callback signatures.\n */\nexport interface TapKitEventHandlers {\n /**\n * Called when TapKit SDK is ready\n * @example onReady={() => console.log('TapKit ready!')}\n */\n onReady?: () => void;\n\n /**\n * Called when initialization or runtime error occurs\n * @example onError={(error) => console.error('TapKit error:', error)}\n */\n onError?: (error: Error) => void;\n\n /**\n * Called when timeline seek event occurs\n * @example onTimelineSeek={(clipPlayHead, clipId) => console.log('Seek:', clipPlayHead)}\n */\n onTimelineSeek?: (clipPlayHead: number, clipId: string) => void;\n\n /**\n * Called when alarm fade-in occurs\n * @example onAlarmFadeIn={(messageInfo) => console.log('Alarm:', messageInfo)}\n */\n onAlarmFadeIn?: (messageInfo: AlarmMessageInstanceType) => void;\n}\n\n/**\n * Control object pattern for managing TapKit instance\n *\n * Separates instance management (setInstance) from configuration (options)\n * and event handling (handlers). Inspired by ChatKit's control pattern.\n *\n * @example\n * ```tsx\n * const tapkit = useTapKit({ apiKey: 'key', onReady: () => {} });\n * <TapKit control={tapkit.control} />\n * ```\n */\nexport interface TapKitControl<T> {\n /**\n * Set the TapKit element instance reference\n *\n * Called by TapKit component to register the element instance.\n * Enables imperative control via useTapKit methods.\n */\n setInstance: (instance: TapKitElement | null) => void;\n\n /**\n * Configuration options (non-function values)\n *\n * All options except event handlers.\n */\n options: T;\n\n /**\n * Event handler callbacks (function values)\n *\n * Separated from options for easier event listener management.\n */\n handlers: TapKitEventHandlers;\n\n /**\n * Whether CDN is loaded and Web Component is available\n *\n * @internal Used by TapKit component to delay rendering\n */\n isCdnLoaded: boolean;\n}\n\n/**\n * Map of TapKit event names to their handler keys\n *\n * Used for automatic event listener binding in TapKit component.\n *\n * @internal\n */\nexport const EVENT_HANDLER_MAP = {\n \"tap-kit:ready\": \"onReady\",\n \"tap-kit:error\": \"onError\",\n} as const;\n\n/**\n * List of TapKit event names\n *\n * @internal\n */\nexport const EVENT_NAMES = Object.keys(EVENT_HANDLER_MAP) as (keyof typeof EVENT_HANDLER_MAP)[];\n","/**\n * TapKit React Component\n *\n * Declarative React wrapper for <tap-kit> Web Component.\n * Use this with useTapKit hook for a clean separation of control and rendering.\n *\n * @example\n * ```tsx\n * 'use client';\n *\n * import { TapKit, useTapKit } from '@coxwave/tap-kit/react';\n *\n * function MyApp() {\n * const tapkit = useTapKit({\n * apiKey: 'your-key',\n * userId: 'user-123',\n * courseId: 'course-456',\n * clipId: 'clip-789',\n * onReady: () => console.log('TapKit ready!'),\n * onError: (error) => console.error('Error:', error),\n * });\n *\n * return (\n * <div>\n * <button onClick={tapkit.show} disabled={!tapkit.isReady}>\n * Show Chat\n * </button>\n * <TapKit control={tapkit.control} style={{ height: '600px' }} />\n * </div>\n * );\n * }\n * ```\n *\n * **Note for Next.js App Router users:**\n * Make sure to add 'use client' at the top of your component file.\n *\n * @see https://edutap-ai-docs.vercel.app/docs/guides/react\n */\n\nimport type { TapKitElement } from \"@coxwave/tap-kit-types\";\nimport React, { useCallback, useEffect, useImperativeHandle, useRef } from \"react\";\nimport type { TapKitControl } from \"./types\";\nimport { EVENT_HANDLER_MAP, EVENT_NAMES } from \"./types\";\nimport type { TapKitOptions } from \"./useTapKit\";\n\n/**\n * Props for TapKit React component\n */\nexport interface TapKitProps extends Omit<React.HTMLAttributes<TapKitElement>, \"children\" | \"dangerouslySetInnerHTML\"> {\n /**\n * Control object from useTapKit hook\n *\n * Provides instance management, configuration, and event handlers.\n */\n control: TapKitControl<TapKitOptions>;\n}\n\n// Note: JSX IntrinsicElements augmentation is provided by @coxwave/tap-kit-types\n// See: packages/tap-kit-types/src/tap-kit.d.ts\n\n/**\n * TapKit React Component\n *\n * Renders <tap-kit> Web Component with React integration.\n * Automatically manages instance lifecycle and event listeners.\n *\n * **Ref Access**: The forwarded ref provides direct access to the TapKitElement:\n * ```tsx\n * const tapkitRef = useRef<TapKitElement>(null);\n * <TapKit ref={tapkitRef} control={...} />\n * // tapkitRef.current?.show()\n * ```\n */\nexport const TapKit = React.forwardRef<TapKitElement, TapKitProps>(function TapKit({ control, ...htmlProps }, forwardedRef) {\n const elementRef = useRef<TapKitElement | null>(null);\n\n // Forward TapKitElement to parent\n useImperativeHandle(forwardedRef, () => elementRef.current as TapKitElement, []);\n\n // Callback ref to register element when mounted\n const setElementRef = useCallback(\n (element: TapKitElement | null) => {\n elementRef.current = element;\n\n if (element) {\n // Set apiKey as property (not attribute) - required for LitElement\n // apiKey has { attribute: false } so it won't work via HTML attribute\n if (control.options.apiKey) {\n element.apiKey = control.options.apiKey;\n }\n\n // Set videoTarget as property (not attribute) - object type\n // videoTarget has { attribute: false } for lifecycle-safe video binding\n if (control.options.videoTarget) {\n element.videoTarget = control.options.videoTarget;\n }\n\n // Set allowLayoutToggle as property (not attribute) - Boolean type\n // HTML boolean attributes treat any string value (including \"false\") as true\n // Must be set via property to correctly handle false values\n if (control.options.allowLayoutToggle !== undefined) {\n element.allowLayoutToggle = control.options.allowLayoutToggle;\n }\n\n // Note: root is NOT set here - declarative API uses auto-detected parent\n // The element will detect declarative mode in connectedCallback\n }\n\n control.setInstance(element);\n },\n [control.setInstance, control.options.apiKey, control.options.videoTarget, control.options.allowLayoutToggle]\n );\n\n // Store handlers in ref to avoid effect re-runs on handler reference changes\n // Handlers are stable via useMemo in useTapKit, but use ref for extra safety\n const handlersRef = useRef(control.handlers);\n handlersRef.current = control.handlers;\n\n // Register event listeners (only once per element mount)\n useEffect(() => {\n const element = elementRef.current;\n if (!element) return;\n\n const listeners: Array<{\n event: string;\n handler: EventListener;\n }> = [];\n\n // Bind CustomEvent listeners (tap-kit:ready, tap-kit:error)\n for (const eventName of EVENT_NAMES) {\n const handlerKey = EVENT_HANDLER_MAP[eventName as keyof typeof EVENT_HANDLER_MAP];\n\n // Use closure over handlersRef to always get latest handler\n const listener = (e: Event) => {\n const handler = handlersRef.current[handlerKey];\n if (!handler) return;\n\n const customEvent = e as CustomEvent;\n if (handlerKey === \"onError\" && customEvent.detail?.error) {\n // onError receives error directly\n // biome-ignore lint/suspicious/noExplicitAny: Handler type is validated at runtime\n (handler as any)(customEvent.detail.error);\n } else if (handlerKey === \"onReady\") {\n // onReady receives no arguments\n // biome-ignore lint/suspicious/noExplicitAny: Handler type is validated at runtime\n (handler as any)();\n }\n };\n\n element.addEventListener(eventName, listener);\n listeners.push({ event: eventName, handler: listener });\n }\n\n return () => {\n // Cleanup all event listeners\n for (const { event, handler } of listeners) {\n element.removeEventListener(event, handler);\n }\n };\n }, []); // Empty deps - listeners use ref for handlers\n\n // Sync videoTarget property when it changes\n // (property-only, not attribute - must be set imperatively)\n useEffect(() => {\n const element = elementRef.current;\n if (!element) return;\n\n element.videoTarget = control.options.videoTarget;\n }, [control.options.videoTarget]);\n\n // Sync allowLayoutToggle property when it changes\n // (Boolean attributes don't work correctly with \"false\" string)\n useEffect(() => {\n const element = elementRef.current;\n if (!element || control.options.allowLayoutToggle === undefined) return;\n\n element.allowLayoutToggle = control.options.allowLayoutToggle;\n }, [control.options.allowLayoutToggle]);\n\n // Note: root sync removed - declarative API uses auto-detected parent\n // The element stays in its declared position\n\n const { options, isCdnLoaded } = control;\n\n // Wait for CDN to load before rendering Web Component\n // Without this, <tap-kit> is just an HTMLUnknownElement without methods\n if (!isCdnLoaded) {\n return null;\n }\n\n // Render <tap-kit> Web Component directly\n // Note: apiKey, videoTarget, allowLayoutToggle are set via ref callback (property-only)\n return (\n <tap-kit\n ref={setElementRef}\n user-id={options.userId}\n course-id={options.courseId}\n clip-id={options.clipId}\n clip-play-head={options.clipPlayHead}\n language={options.language}\n button-id={options.buttonId}\n mode={options.mode}\n debug={options.debug}\n tap-url={options.tapUrl}\n api-url={options.apiUrl}\n environment={options.environment}\n {...htmlProps}\n />\n );\n});\n","/**\n * CDN loader for TapKit\n * Dynamically loads the TapKit SDK from CDN\n *\n * For local testing, you can override the loader URL:\n * window.__TAP_KIT_LOADER_URL__ = '/tap-kit-core/loader.js';\n *\n * For local development (bypass loader, load IIFE directly):\n * window.__TAP_KIT_CORE_URL__ = '/packages/tap-kit-core/dist/index.global.js';\n */\n\n// Build-time constant injected by tsup define\ndeclare const __DEFAULT_CDN_LOADER_URL__: string;\n\nconst DEFAULT_CDN_LOADER_URL =\n typeof __DEFAULT_CDN_LOADER_URL__ !== \"undefined\" ? __DEFAULT_CDN_LOADER_URL__ : \"https://files.edutap.ai/tap-sdk/loader.js\";\nconst DEFAULT_TIMEOUT_MS = 4000; // 4 seconds total timeout\nconst IDLE_CALLBACK_TIMEOUT_MS = 500; // 500ms for requestIdleCallback\n\n/**\n * Get the loader URL from window override or default CDN\n */\nfunction getLoaderURL(): string {\n return window?.__TAP_KIT_LOADER_URL__ ? window.__TAP_KIT_LOADER_URL__ : DEFAULT_CDN_LOADER_URL;\n}\n\n/**\n * Check if local core mode is enabled\n * When __TAP_KIT_CORE_URL__ is set, directly load the IIFE bundle\n * This bypasses the loader.js and loads tap-kit-core directly\n */\nfunction isLocalCoreMode(): boolean {\n return typeof window !== \"undefined\" && !!window.__TAP_KIT_CORE_URL__;\n}\n\n/**\n * Get the local core URL\n */\nfunction getLocalCoreURL(): string {\n return window.__TAP_KIT_CORE_URL__ || \"\";\n}\n\n/**\n * Creates a SDK checker function with timeout and retry logic\n * Uses requestIdleCallback to avoid blocking browser rendering\n * @param resolve - Promise resolve function\n * @param reject - Promise reject function\n * @param timeoutMs - Maximum time to wait for SDK to load (milliseconds)\n * @returns Checker function to be called repeatedly\n */\nfunction createSDKChecker(\n resolve: (value: void | PromiseLike<void>) => void,\n reject: (reason?: unknown) => void,\n timeoutMs: number\n): () => void {\n const startTime = Date.now();\n\n const checkSDK = (): void => {\n // Check if real TapKit is loaded (not just stub)\n // Stub has TapKitLoaded flag set to true by loader.js after real SDK loads\n if (window.TapKit && window.TapKitLoaded === true) {\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n return;\n }\n\n const elapsed = Date.now() - startTime;\n\n // Check if exceeded timeout\n if (elapsed > timeoutMs) {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`TapKit loader timeout: SDK not available after ${timeoutMs}ms`));\n return;\n }\n\n // Use requestIdleCallback for better performance\n // Falls back to setTimeout if not available\n if (typeof requestIdleCallback !== \"undefined\") {\n requestIdleCallback(checkSDK, { timeout: IDLE_CALLBACK_TIMEOUT_MS });\n } else {\n setTimeout(checkSDK, IDLE_CALLBACK_TIMEOUT_MS);\n }\n };\n\n return checkSDK;\n}\n\n/**\n * Loads the CDN loader script\n * The loader will then fetch versions.json and load the appropriate SDK version\n *\n * If __TAP_KIT_CORE_URL__ is set, bypasses loader and loads IIFE directly\n *\n * @param timeoutMs - Maximum time to wait for SDK to load (default: 4000ms)\n * @returns Promise that resolves when SDK is loaded\n * @throws {Error} If loader fails to load or times out\n */\nexport function loadCDNLoader(timeoutMs: number = DEFAULT_TIMEOUT_MS): Promise<void> {\n // If already loaded, return immediately\n if (window.__TAP_KIT_LOADER_LOADED__ && window.TapKit) {\n return Promise.resolve();\n }\n\n // If currently loading, return the existing promise\n if (window.__TAP_KIT_LOADER_LOADING__) {\n return window.__TAP_KIT_LOADER_LOADING__;\n }\n\n // Create loading promise\n const loadingPromise = new Promise<void>((resolve, reject) => {\n if (typeof document === \"undefined\") {\n reject(new Error(\"TapKit requires browser environment (document is undefined)\"));\n return;\n }\n\n // Local core mode: Load IIFE directly\n if (isLocalCoreMode()) {\n // Check if TapKit is already loaded (from other pages in playground)\n if (window.TapKit && window.TapKitLoaded === true) {\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n return;\n }\n\n const coreURL = getLocalCoreURL();\n\n const script = document.createElement(\"script\");\n script.src = coreURL;\n script.async = true;\n\n script.onload = () => {\n // IIFE directly sets window.TapKit\n // Set the loaded flag manually since we bypass loader.js\n if (window.TapKit) {\n window.TapKitLoaded = true;\n window.__TAP_KIT_LOADER_LOADED__ = true;\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n resolve();\n } else {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(\"TapKit not available after loading local core\"));\n }\n };\n\n script.onerror = () => {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`Failed to load local TapKit core: ${coreURL}`));\n };\n\n document.head.appendChild(script);\n return;\n }\n\n // CDN mode: Load loader.js\n const loaderURL = getLoaderURL();\n const script = document.createElement(\"script\");\n script.src = loaderURL;\n script.async = true;\n\n script.onload = () => {\n // The loader script will load the actual SDK\n // We need to wait a bit for the loader to fetch and load the SDK\n const checkSDK = createSDKChecker(resolve, reject, timeoutMs);\n checkSDK();\n };\n\n script.onerror = () => {\n window.__TAP_KIT_LOADER_LOADING__ = undefined;\n reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`));\n };\n\n // Check if script already exists\n const existingScript = document.querySelector(`script[src=\"${loaderURL}\"]`);\n\n if (existingScript) {\n // Script already added but not yet loaded\n existingScript.addEventListener(\"load\", () => {\n const checkSDK = createSDKChecker(resolve, reject, timeoutMs);\n checkSDK();\n });\n existingScript.addEventListener(\"error\", () => reject(new Error(`Failed to load TapKit CDN loader: ${loaderURL}`)));\n } else {\n document.head.appendChild(script);\n }\n });\n\n window.__TAP_KIT_LOADER_LOADING__ = loadingPromise;\n return loadingPromise;\n}\n","/**\n * useTapKit Hook - ChatKit-style control for TapKit Web Component\n *\n * This hook provides a control object to manage TapKit through the\n * <TapKit /> component. Inspired by OpenAI's ChatKit pattern.\n *\n * @example\n * ```tsx\n * 'use client';\n *\n * import { TapKit, useTapKit } from '@coxwave/tap-kit/react';\n *\n * function MyApp() {\n * const tapkit = useTapKit({\n * apiKey: 'your-key',\n * userId: 'user-123',\n * courseId: 'course-456',\n * clipId: 'clip-789',\n * onReady: () => console.log('Ready!'),\n * onError: (error) => console.error(error),\n * });\n *\n * return (\n * <div>\n * <button onClick={tapkit.show} disabled={!tapkit.isReady}>\n * Ask AI Tutor\n * </button>\n * <TapKit control={tapkit.control} />\n * </div>\n * );\n * }\n * ```\n */\n\nimport type { TapKitElement, VideoPlayerConfig } from \"@coxwave/tap-kit-types\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { loadCDNLoader } from \"../loader\";\nimport type { TapKitControl, TapKitEventHandlers, VideoAdapter } from \"./types\";\n\nexport interface UseTapKitOptions extends TapKitEventHandlers {\n /** API Key (required) */\n apiKey: string;\n /** User ID */\n userId?: string;\n /** Course ID */\n courseId?: string;\n /** Clip ID */\n clipId?: string;\n /** Clip playhead position */\n clipPlayHead?: number;\n /** Language */\n language?: \"ko\" | \"en\";\n /** Custom button element ID */\n buttonId?: string;\n /** Display mode */\n mode?: \"inline\" | \"floating\" | \"sidebar\";\n /**\n * Allow user to toggle between floating and sidebar layouts\n * Only applies when mode is \"floating\" or \"sidebar\"\n * @default true\n */\n allowLayoutToggle?: boolean;\n /**\n * Video player target for timeline synchronization (recommended)\n *\n * Supports HTMLVideoElement or VideoPlayerAdapter.\n * Survives lifecycle changes (mode change, reconnect).\n *\n * @example\n * ```tsx\n * // HTMLVideoElement\n * const tapkit = useTapKit({ videoTarget: videoRef.current, ... });\n *\n * // VideoPlayerAdapter\n * const tapkit = useTapKit({\n * videoTarget: {\n * getCurrentTime: () => player.currentTime,\n * setCurrentTime: (t) => player.currentTime = t\n * },\n * ...\n * });\n * ```\n */\n videoTarget?: VideoPlayerConfig;\n /** Debug mode */\n debug?: boolean;\n /** TAP Frontend URL */\n tapUrl?: string;\n /** API Backend URL */\n apiUrl?: string;\n /** Environment */\n environment?: \"dev\" | \"prod\" | \"staging\" | \"demo\";\n // Note: `root` option removed - declarative API (<TapKit />) stays in declared position\n // For imperative control over position, use createTapKit() with root option\n}\n\n/**\n * Configuration options type (non-function values)\n */\nexport type TapKitOptions = Omit<UseTapKitOptions, keyof TapKitEventHandlers>;\n\n/**\n * Extract event handlers from options (pure function)\n */\nfunction extractHandlers(options: UseTapKitOptions): TapKitEventHandlers {\n return {\n onReady: options.onReady,\n onError: options.onError,\n onTimelineSeek: options.onTimelineSeek,\n onAlarmFadeIn: options.onAlarmFadeIn,\n };\n}\n\nexport interface UseTapKitReturn {\n /** Control object for <TapKit /> component */\n control: TapKitControl<TapKitOptions>;\n /** Whether TapKit is ready */\n isReady: boolean;\n /** Error during initialization */\n error: Error | null;\n /** Show chat interface */\n show: () => void;\n /** Hide chat interface */\n hide: () => void;\n /** Set course information */\n setCourse: (course: { courseId: string; clipId: string; userId?: string; clipPlayHead?: number }) => void;\n /**\n * Video adapter control\n *\n * @deprecated Use `videoTarget` option in useTapKit instead for declarative binding.\n *\n * ```tsx\n * // ✅ Recommended (declarative)\n * const tapkit = useTapKit({ videoTarget: adapter, ... });\n *\n * // ❌ Deprecated (imperative)\n * tapkit.video.bind(adapter);\n * ```\n */\n video: {\n bind: (adapter: VideoAdapter, clipId?: string) => void;\n unbind: () => void;\n };\n}\n\n/**\n * Hook for managing TapKit Web Component (ChatKit Pattern)\n *\n * Returns a control object to pass to <TapKit /> component.\n * The component handles rendering, this hook handles state and methods.\n */\nexport function useTapKit(options: UseTapKitOptions): UseTapKitReturn {\n // Element ref - set by <TapKit /> component via control.setInstance\n const elementRef = useRef<TapKitElement | null>(null);\n\n // Pending video bind request (queued until ready)\n const pendingVideoBind = useRef<{\n adapter: VideoAdapter;\n clipId?: string;\n } | null>(null);\n\n // State\n const [isCdnLoaded, setIsCdnLoaded] = useState(false);\n const [isReady, setIsReady] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n // Store handlers in refs to avoid stale closures\n const handlersRef = useRef(extractHandlers(options));\n handlersRef.current = extractHandlers(options);\n\n // Load CDN on mount (defines <tap-kit> Web Component)\n useEffect(() => {\n loadCDNLoader()\n .then(() => {\n setIsCdnLoaded(true);\n })\n .catch((err) => {\n const loadError = err instanceof Error ? err : new Error(\"Failed to load TapKit CDN\");\n setError(loadError);\n handlersRef.current.onError?.(loadError);\n });\n }, []);\n\n // Set instance callback - called by <TapKit /> component\n const setInstance = useCallback((instance: TapKitElement | null) => {\n elementRef.current = instance;\n\n if (!instance) {\n setIsReady(false);\n return;\n }\n\n // Listen for ready event\n const handleReady = () => {\n setIsReady(true);\n handlersRef.current.onReady?.();\n };\n\n const handleError = (e: Event) => {\n const customEvent = e as CustomEvent<{ error: Error }>;\n const err = customEvent.detail?.error ?? new Error(\"Unknown error\");\n setError(err);\n handlersRef.current.onError?.(err);\n };\n\n instance.addEventListener(\"tap-kit:ready\", handleReady);\n instance.addEventListener(\"tap-kit:error\", handleError);\n\n // Check if already ready\n if (instance.isInitialized) {\n setIsReady(true);\n }\n\n // Setup EventManager callbacks when ready\n // Use stable wrappers that call handlersRef.current to always get latest handlers\n const setupEventHandlers = () => {\n if (instance.events && typeof instance.events.onTimelineSeek === \"function\") {\n // Register stable wrappers instead of direct refs\n // This ensures handler updates are reflected without re-registration\n instance.events.onTimelineSeek((clipPlayHead, clipId) => {\n handlersRef.current.onTimelineSeek?.(clipPlayHead, clipId);\n });\n instance.events.onAlarmFadeIn((messageInfo) => {\n handlersRef.current.onAlarmFadeIn?.(messageInfo);\n });\n }\n\n // Execute pending video bind if queued\n if (pendingVideoBind.current && instance.video) {\n instance.video.bind(pendingVideoBind.current.adapter, pendingVideoBind.current.clipId);\n pendingVideoBind.current = null;\n }\n };\n\n // Check if ready property exists (may not be available immediately)\n if (instance.ready && typeof instance.ready.then === \"function\") {\n instance.ready.then(setupEventHandlers).catch(() => {\n // Ignore - error already handled by tap-kit:error event\n });\n } else if (instance.isInitialized) {\n // Already initialized, setup handlers immediately\n setupEventHandlers();\n } else {\n // Fallback: wait for ready event then setup handlers\n instance.addEventListener(\"tap-kit:ready\", setupEventHandlers, {\n once: true,\n });\n }\n }, []);\n\n // Memoize config options using individual values to prevent infinite re-renders\n // (options object is recreated on every render, so we must depend on primitives)\n // Note: videoTarget is object reference - changes trigger re-render intentionally\n const configOptions = useMemo(\n () => ({\n apiKey: options.apiKey,\n userId: options.userId,\n courseId: options.courseId,\n clipId: options.clipId,\n clipPlayHead: options.clipPlayHead,\n language: options.language,\n buttonId: options.buttonId,\n mode: options.mode,\n allowLayoutToggle: options.allowLayoutToggle,\n videoTarget: options.videoTarget,\n debug: options.debug,\n tapUrl: options.tapUrl,\n apiUrl: options.apiUrl,\n environment: options.environment,\n // Note: root removed - declarative API stays in declared position\n }),\n [\n options.apiKey,\n options.userId,\n options.courseId,\n options.clipId,\n options.clipPlayHead,\n options.language,\n options.buttonId,\n options.mode,\n options.allowLayoutToggle,\n options.videoTarget,\n options.debug,\n options.tapUrl,\n options.apiUrl,\n options.environment,\n ]\n );\n\n // Create stable handlers object for control\n // NOTE: This object is intentionally created once (empty deps) and appears to capture\n // stale values. However, the actual handler invocations in TapKit.tsx use handlersRef.current\n // which always has the latest handlers. This object is just a stable reference marker\n // that tells TapKit.tsx which handlers exist - not the actual handlers to call.\n const handlers = useMemo<TapKitEventHandlers>(\n () => ({\n onReady: handlersRef.current.onReady,\n onError: handlersRef.current.onError,\n onTimelineSeek: handlersRef.current.onTimelineSeek,\n onAlarmFadeIn: handlersRef.current.onAlarmFadeIn,\n }),\n []\n );\n\n // Create control object\n const control = useMemo<TapKitControl<TapKitOptions>>(\n () => ({\n setInstance,\n options: configOptions,\n handlers,\n isCdnLoaded,\n }),\n [setInstance, configOptions, handlers, isCdnLoaded]\n );\n\n // Methods\n const show = useCallback(() => {\n if (!elementRef.current) {\n console.warn(\"[useTapKit] Cannot show: element not mounted\");\n return;\n }\n elementRef.current.show();\n }, []);\n\n const hide = useCallback(() => {\n if (!elementRef.current) {\n console.warn(\"[useTapKit] Cannot hide: element not mounted\");\n return;\n }\n elementRef.current.hide();\n }, []);\n\n const setCourse = useCallback((course: { courseId: string; clipId: string; userId?: string; clipPlayHead?: number }) => {\n if (!elementRef.current) {\n console.warn(\"[useTapKit] Cannot setCourse: element not mounted\");\n return;\n }\n elementRef.current.setCourse(course);\n }, []);\n\n // Video adapter control\n const video = useMemo(\n () => ({\n bind: (adapter: VideoAdapter, clipId?: string) => {\n // If video controller is ready, bind immediately\n if (elementRef.current?.video) {\n elementRef.current.video.bind(adapter, clipId);\n return;\n }\n\n // Queue for later execution when ready\n pendingVideoBind.current = { adapter, clipId };\n },\n unbind: () => {\n // Clear pending request\n pendingVideoBind.current = null;\n elementRef.current?.video?.unbind();\n },\n }),\n []\n );\n\n return {\n control,\n isReady,\n error,\n show,\n hide,\n setCourse,\n video,\n };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coxwave/tap-kit",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "EduTAP SDK with React support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"README.md"
|
|
24
24
|
],
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@coxwave/tap-kit-types": "2.
|
|
26
|
+
"@coxwave/tap-kit-types": "2.9.1"
|
|
27
27
|
},
|
|
28
28
|
"peerDependencies": {
|
|
29
29
|
"react": "^18.0.0 || ^19.0.0"
|