@coxwave/tap-kit 3.2.3 → 3.2.5

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.d.cts CHANGED
@@ -90,7 +90,7 @@ interface CreateTapKitOptions extends FactoryEventHandlers {
90
90
  /** Chat interaction mode (staging feature: per-message override) */
91
91
  chatMode?: ChatMode;
92
92
  /** Language */
93
- language?: "ko" | "en";
93
+ language?: "ko" | "en" | "ja";
94
94
  /**
95
95
  * Allow user to toggle between floating and sidebar layouts
96
96
  * Only applies when mode is "floating" or "sidebar"
package/dist/index.d.ts CHANGED
@@ -90,7 +90,7 @@ interface CreateTapKitOptions extends FactoryEventHandlers {
90
90
  /** Chat interaction mode (staging feature: per-message override) */
91
91
  chatMode?: ChatMode;
92
92
  /** Language */
93
- language?: "ko" | "en";
93
+ language?: "ko" | "en" | "ja";
94
94
  /**
95
95
  * Allow user to toggle between floating and sidebar layouts
96
96
  * Only applies when mode is "floating" or "sidebar"
package/dist/react.d.cts CHANGED
@@ -131,7 +131,7 @@ interface UseTapKitOptions extends TapKitEventHandlers {
131
131
  /** Clip playhead position */
132
132
  clipPlayHead?: number;
133
133
  /** Language */
134
- language?: "ko" | "en";
134
+ language?: "ko" | "en" | "ja";
135
135
  /** Custom button element ID */
136
136
  buttonId?: string;
137
137
  /** Display mode */
package/dist/react.d.ts CHANGED
@@ -131,7 +131,7 @@ interface UseTapKitOptions extends TapKitEventHandlers {
131
131
  /** Clip playhead position */
132
132
  clipPlayHead?: number;
133
133
  /** Language */
134
- language?: "ko" | "en";
134
+ language?: "ko" | "en" | "ja";
135
135
  /** Custom button element ID */
136
136
  buttonId?: string;
137
137
  /** Display mode */
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":"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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 { ChatMode, 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 /** Chat interaction mode (staging feature: per-message override) */\n chatMode?: ChatMode;\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 chatMode: options.chatMode,\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.chatMode,\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"]}
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 { ChatMode, 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\" | \"ja\";\n /** Custom button element ID */\n buttonId?: string;\n /** Display mode */\n mode?: \"inline\" | \"floating\" | \"sidebar\";\n /** Chat interaction mode (staging feature: per-message override) */\n chatMode?: ChatMode;\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 chatMode: options.chatMode,\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.chatMode,\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"]}
@@ -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({ 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 { ChatMode, 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 /** Chat interaction mode (staging feature: per-message override) */\n chatMode?: ChatMode;\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 chatMode: options.chatMode,\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.chatMode,\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"]}
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({ 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 { ChatMode, 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\" | \"ja\";\n /** Custom button element ID */\n buttonId?: string;\n /** Display mode */\n mode?: \"inline\" | \"floating\" | \"sidebar\";\n /** Chat interaction mode (staging feature: per-message override) */\n chatMode?: ChatMode;\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 chatMode: options.chatMode,\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.chatMode,\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.2.3",
3
+ "version": "3.2.5",
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.10.2"
26
+ "@coxwave/tap-kit-types": "2.10.3"
27
27
  },
28
28
  "peerDependencies": {
29
29
  "react": "^18.0.0 || ^19.0.0"