@coxwave/tap-kit-types 1.0.3 → 2.0.1

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.ts CHANGED
@@ -1,17 +1,100 @@
1
+ import * as CSS from 'csstype';
1
2
  import * as v from 'valibot';
2
3
  import { ReactNode, CSSProperties } from 'react';
3
4
 
4
- type AlarmType = "quiz" | "pingMessage" | "callToAction" | "hourSpent" | "default";
5
- interface AlarmMessageInstanceType {
5
+ /**
6
+ * Alarm display duration (18 seconds total)
7
+ * 말풍선 발생 후, 12초 뒤에 서서히 사라짐 (전체 시간 18초)
8
+ */
9
+ declare const ALARM_DURATION = 18000;
10
+ /**
11
+ * Base style type for all elements
12
+ * Uses csstype for accurate CSS property types
13
+ */
14
+ type CSSStyle = CSS.Properties<string | number>;
15
+ /**
16
+ * Props for AlarmElement
17
+ * Supports common HTML attributes with type safety
18
+ */
19
+ interface AlarmElementProps {
20
+ style?: CSSStyle;
21
+ className?: string;
22
+ id?: string;
23
+ src?: string;
24
+ alt?: string;
25
+ href?: string;
26
+ target?: string;
27
+ type?: string;
28
+ placeholder?: string;
29
+ value?: string;
30
+ disabled?: boolean;
31
+ [key: string]: unknown;
32
+ }
33
+ /**
34
+ * Alarm click action payload
35
+ * Defines what happens when user clicks an alarm
36
+ */
37
+ type AlarmPayload = {
38
+ type: "startChat";
6
39
  message: string;
7
- question?: string;
8
40
  pingMessageId?: string;
41
+ };
42
+ /**
43
+ * Generalized UI element that can represent any HTML element
44
+ * Inspired by crel.js pattern for flexible DOM construction
45
+ *
46
+ * This replaces the previous hardcoded types (AlarmDivElement, AlarmTextElement, etc.)
47
+ * with a single flexible schema that supports all HTML elements.
48
+ *
49
+ * @example
50
+ * // Text node
51
+ * "Hello World"
52
+ *
53
+ * @example
54
+ * // Button with action
55
+ * {
56
+ * tag: "button",
57
+ * action: { id: "summarize", payload: {} },
58
+ * children: ["Summarize"]
59
+ * }
60
+ *
61
+ * @example
62
+ * // Div container
63
+ * {
64
+ * tag: "div",
65
+ * props: { className: "container", style: { padding: "16px" } },
66
+ * children: [
67
+ * { tag: "h1", children: ["Title"] },
68
+ * { tag: "p", children: ["Description"] }
69
+ * ]
70
+ * }
71
+ */
72
+ interface AlarmElement {
73
+ tag: string;
74
+ props?: AlarmElementProps;
75
+ children?: (AlarmElement | string)[];
76
+ payload?: AlarmPayload;
77
+ }
78
+ /**
79
+ * Alarm type classification with namespace pattern
80
+ * - basic:* - System-level alarms (welcome, default, etc.)
81
+ * - feat:* - Feature-specific alarms (quiz, guide, progress, etc.)
82
+ * - custom:* - Custom alarms (cheer, etc.)
83
+ */
84
+ type AlarmType = "basic:welcome" | "basic:default" | "feat:quiz" | "feat:guide" | "feat:progress" | "custom:cheer";
85
+ /**
86
+ * Complete alarm message definition
87
+ * UI is fully defined via JSON Component Descriptor
88
+ * Click behavior is simple: open chat + send message (if provided in content.payload)
89
+ * Duration is always 18 seconds (handled in useAlarmEffects)
90
+ */
91
+ interface AlarmMessageInstanceType {
9
92
  type: AlarmType;
10
- priority: number;
93
+ content: AlarmElement;
11
94
  }
12
95
 
13
- type TapMessageType = "tap:ready" | "tap:close" | "timeline:seek" | "alarm:click" | "alarm:fadeIn" | "popUp:open" | "popUp:close" | "container:expand" | "container:collapse" | "container:mode:change" | "container:mode:change:ack" | "container:layout:state:changed" | "viewport:resize" | "config:update" | "GA_EVENT";
14
- type TapMessage = TapReadyMessage | TapCloseMessage | TimelineSeekMessage | AlarmClickMessage | AlarmFadeInMessage | PopUpOpenMessage | PopUpCloseMessage | ContainerExpandMessage | ContainerCollapseMessage | ContainerModeChangeMessage | ContainerModeChangeAckMessage | ContainerLayoutStateChangedMessage | ViewportResizeMessage | ConfigUpdateMessage | GAEventMessage;
96
+ type TapMessageType = "tap:ready" | "tap:close" | "timeline:seek" | "alarm:click" | "alarm:fadeIn" | "popUp:open" | "popUp:close" | "material:view:open" | "material:view:close" | "material:view:error" | "container:mode:change" | "container:mode:change:ack" | "container:layout:state:changed" | "viewport:resize" | "config:update" | "GA_EVENT";
97
+ type TapMessage = TapReadyMessage | TapCloseMessage | TimelineSeekMessage | AlarmClickMessage | AlarmFadeInMessage | PopUpOpenMessage | PopUpCloseMessage | MaterialViewOpenMessage | MaterialViewCloseMessage | MaterialViewErrorMessage | ContainerModeChangeMessage | ContainerModeChangeAckMessage | ContainerLayoutStateChangedMessage | ViewportResizeMessage | ConfigUpdateMessage | GAEventMessage;
15
98
  interface TapReadyMessage {
16
99
  type: "tap:ready";
17
100
  gaId: string;
@@ -43,24 +126,35 @@ interface PopUpCloseMessage {
43
126
  type: "popUp:close";
44
127
  }
45
128
  /**
46
- * Container Expand Request (iframe → parent, call/handle pattern)
47
- * Requests parent to enlarge iframe container for PDF full-screen view
129
+ * Material View Open Request (iframe → parent, call/handle pattern)
130
+ * Requests parent to open material viewer overlay with presigned URL
48
131
  *
49
- * Response type (container:expand:ack) is auto-generated by iframe-messenger
132
+ * Response: { success: boolean } via call/handle pattern
50
133
  */
51
- interface ContainerExpandMessage {
52
- type: "container:expand";
134
+ interface MaterialViewOpenMessage {
135
+ type: "material:view:open";
136
+ materialId: string;
137
+ presignedUrl: string;
138
+ pageStart: number;
139
+ pageEnd: number;
53
140
  nonce?: string | number;
54
141
  }
55
142
  /**
56
- * Container Collapse Request (iframe → parent, call/handle pattern)
57
- * Requests parent to restore iframe container to normal size
58
- *
59
- * Response type (container:collapse:ack) is auto-generated by iframe-messenger
143
+ * Material View Close Request (iframe → parent, simple post pattern)
144
+ * Requests parent to close material viewer overlay
60
145
  */
61
- interface ContainerCollapseMessage {
62
- type: "container:collapse";
63
- nonce?: string | number;
146
+ interface MaterialViewCloseMessage {
147
+ type: "material:view:close";
148
+ }
149
+ /**
150
+ * Material View Error Notification (parent → iframe, stream pattern)
151
+ * Notifies iframe when material viewing fails (for user feedback)
152
+ */
153
+ interface MaterialViewErrorMessage {
154
+ type: "material:view:error";
155
+ materialId: string;
156
+ error: "fetch_failed" | "expired_url" | "extraction_failed" | "unknown";
157
+ message?: string;
64
158
  }
65
159
  /**
66
160
  * Container Mode Change Request (iframe → parent, call/handle pattern)
@@ -133,14 +227,21 @@ interface ConfigUpdateMessage {
133
227
  clipId?: string;
134
228
  clipPlayHead?: number;
135
229
  /**
136
- * Container configuration (mirrors ContainerConfig from @coxwave/tap-kit-types)
230
+ * Display mode
231
+ * - "inline": Container rendered at <tap-kit> position (embedded in page)
232
+ * - "floating": Container floats in document.body as compact widget
233
+ * - "sidebar": Container floats in document.body as full-height sidebar
234
+ * Default: "inline"
235
+ */
236
+ mode?: "inline" | "floating" | "sidebar";
237
+ /**
238
+ * Container configuration (styling for floating/sidebar modes)
137
239
  *
138
240
  * Structure:
139
- * - mode: "auto" (default) | "floating" | "sidebar"
140
241
  * - floatingConfig: { position, width, height, borderRadius }
141
242
  * - sidebarConfig: { maxWidth, minViewportWidth }
142
243
  *
143
- * @see ContainerConfig in @coxwave/tap-kit-types
244
+ * Note: containerMode ("auto" | "floating" | "sidebar") controls toggle button behavior
144
245
  */
145
246
  container?: {
146
247
  mode?: "auto" | "floating" | "sidebar";
@@ -167,16 +268,44 @@ interface ConfigUpdateMessage {
167
268
  */
168
269
  interface GAEventMessage {
169
270
  type: "GA_EVENT";
170
- payload: Record<string, any>;
271
+ payload: Record<string, unknown>;
171
272
  }
172
273
 
173
- declare const AlarmTypeSchema: v.UnionSchema<[v.LiteralSchema<"quiz", undefined>, v.LiteralSchema<"pingMessage", undefined>, v.LiteralSchema<"callToAction", undefined>, v.LiteralSchema<"hourSpent", undefined>, v.LiteralSchema<"default", undefined>], undefined>;
274
+ /**
275
+ * Schema for CSS style properties
276
+ * Allows any CSS property as string or number
277
+ */
278
+ declare const CSSStyleSchema: v.RecordSchema<v.StringSchema<undefined>, v.UnionSchema<[v.StringSchema<undefined>, v.NumberSchema<undefined>], undefined>, undefined>;
279
+ /**
280
+ * Schema for AlarmElementProps
281
+ * Supports common HTML attributes
282
+ */
283
+ declare const AlarmElementPropsSchema: v.ObjectSchema<{
284
+ readonly style: v.OptionalSchema<v.RecordSchema<v.StringSchema<undefined>, v.UnionSchema<[v.StringSchema<undefined>, v.NumberSchema<undefined>], undefined>, undefined>, undefined>;
285
+ readonly className: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
286
+ readonly id: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
287
+ readonly src: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
288
+ readonly alt: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
289
+ readonly href: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
290
+ readonly target: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
291
+ readonly type: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
292
+ readonly placeholder: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
293
+ readonly value: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
294
+ readonly disabled: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
295
+ }, undefined>;
296
+ /**
297
+ * Schema for AlarmElement (recursive)
298
+ * Supports nested structure with children and payload
299
+ */
300
+ declare const AlarmElementSchema: v.GenericSchema<any>;
301
+ /**
302
+ * Schema for AlarmMessageInstanceType (simplified)
303
+ * Uses JSON Component Descriptor pattern
304
+ * content includes UI + payload (for click handling)
305
+ */
174
306
  declare const AlarmMessageInstanceSchema: v.ObjectSchema<{
175
- readonly message: v.StringSchema<undefined>;
176
- readonly question: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
177
- readonly pingMessageId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
178
- readonly type: v.UnionSchema<[v.LiteralSchema<"quiz", undefined>, v.LiteralSchema<"pingMessage", undefined>, v.LiteralSchema<"callToAction", undefined>, v.LiteralSchema<"hourSpent", undefined>, v.LiteralSchema<"default", undefined>], undefined>;
179
- readonly priority: v.NumberSchema<undefined>;
307
+ readonly content: v.GenericSchema<any>;
308
+ readonly duration: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
180
309
  }, undefined>;
181
310
  declare const TapReadySchema: v.ObjectSchema<{
182
311
  readonly type: v.LiteralSchema<"tap:ready", undefined>;
@@ -193,21 +322,15 @@ declare const TimelineSeekSchema: v.ObjectSchema<{
193
322
  declare const AlarmClickSchema: v.ObjectSchema<{
194
323
  readonly type: v.LiteralSchema<"alarm:click", undefined>;
195
324
  readonly messageInfo: v.ObjectSchema<{
196
- readonly message: v.StringSchema<undefined>;
197
- readonly question: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
198
- readonly pingMessageId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
199
- readonly type: v.UnionSchema<[v.LiteralSchema<"quiz", undefined>, v.LiteralSchema<"pingMessage", undefined>, v.LiteralSchema<"callToAction", undefined>, v.LiteralSchema<"hourSpent", undefined>, v.LiteralSchema<"default", undefined>], undefined>;
200
- readonly priority: v.NumberSchema<undefined>;
325
+ readonly content: v.GenericSchema<any>;
326
+ readonly duration: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
201
327
  }, undefined>;
202
328
  }, undefined>;
203
329
  declare const AlarmFadeInSchema: v.ObjectSchema<{
204
330
  readonly type: v.LiteralSchema<"alarm:fadeIn", undefined>;
205
331
  readonly messageInfo: v.ObjectSchema<{
206
- readonly message: v.StringSchema<undefined>;
207
- readonly question: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
208
- readonly pingMessageId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
209
- readonly type: v.UnionSchema<[v.LiteralSchema<"quiz", undefined>, v.LiteralSchema<"pingMessage", undefined>, v.LiteralSchema<"callToAction", undefined>, v.LiteralSchema<"hourSpent", undefined>, v.LiteralSchema<"default", undefined>], undefined>;
210
- readonly priority: v.NumberSchema<undefined>;
332
+ readonly content: v.GenericSchema<any>;
333
+ readonly duration: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
211
334
  }, undefined>;
212
335
  }, undefined>;
213
336
  declare const PopUpOpenSchema: v.ObjectSchema<{
@@ -220,13 +343,22 @@ declare const PopUpOpenSchema: v.ObjectSchema<{
220
343
  declare const PopUpCloseSchema: v.ObjectSchema<{
221
344
  readonly type: v.LiteralSchema<"popUp:close", undefined>;
222
345
  }, undefined>;
223
- declare const ContainerExpandSchema: v.ObjectSchema<{
224
- readonly type: v.LiteralSchema<"container:expand", undefined>;
346
+ declare const MaterialViewOpenSchema: v.ObjectSchema<{
347
+ readonly type: v.LiteralSchema<"material:view:open", undefined>;
348
+ readonly materialId: v.StringSchema<undefined>;
349
+ readonly presignedUrl: v.StringSchema<undefined>;
350
+ readonly pageStart: v.NumberSchema<undefined>;
351
+ readonly pageEnd: v.NumberSchema<undefined>;
225
352
  readonly nonce: v.OptionalSchema<v.UnionSchema<[v.StringSchema<undefined>, v.NumberSchema<undefined>], undefined>, undefined>;
226
353
  }, undefined>;
227
- declare const ContainerCollapseSchema: v.ObjectSchema<{
228
- readonly type: v.LiteralSchema<"container:collapse", undefined>;
229
- readonly nonce: v.OptionalSchema<v.UnionSchema<[v.StringSchema<undefined>, v.NumberSchema<undefined>], undefined>, undefined>;
354
+ declare const MaterialViewCloseSchema: v.ObjectSchema<{
355
+ readonly type: v.LiteralSchema<"material:view:close", undefined>;
356
+ }, undefined>;
357
+ declare const MaterialViewErrorSchema: v.ObjectSchema<{
358
+ readonly type: v.LiteralSchema<"material:view:error", undefined>;
359
+ readonly materialId: v.StringSchema<undefined>;
360
+ readonly error: v.UnionSchema<[v.LiteralSchema<"fetch_failed", undefined>, v.LiteralSchema<"expired_url", undefined>, v.LiteralSchema<"extraction_failed", undefined>, v.LiteralSchema<"unknown", undefined>], undefined>;
361
+ readonly message: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
230
362
  }, undefined>;
231
363
  declare const ContainerModeChangeSchema: v.ObjectSchema<{
232
364
  readonly type: v.LiteralSchema<"container:mode:change", undefined>;
@@ -259,6 +391,7 @@ declare const ConfigUpdateSchema: v.ObjectSchema<{
259
391
  readonly courseId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
260
392
  readonly clipId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
261
393
  readonly clipPlayHead: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
394
+ readonly inline: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
262
395
  readonly container: v.OptionalSchema<v.ObjectSchema<{
263
396
  readonly mode: v.OptionalSchema<v.UnionSchema<[v.LiteralSchema<"auto", undefined>, v.LiteralSchema<"floating", undefined>, v.LiteralSchema<"sidebar", undefined>], undefined>, undefined>;
264
397
  readonly floatingConfig: v.OptionalSchema<v.ObjectSchema<{
@@ -294,20 +427,14 @@ declare const TapMessageSchema: v.UnionSchema<[v.ObjectSchema<{
294
427
  }, undefined>, v.ObjectSchema<{
295
428
  readonly type: v.LiteralSchema<"alarm:click", undefined>;
296
429
  readonly messageInfo: v.ObjectSchema<{
297
- readonly message: v.StringSchema<undefined>;
298
- readonly question: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
299
- readonly pingMessageId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
300
- readonly type: v.UnionSchema<[v.LiteralSchema<"quiz", undefined>, v.LiteralSchema<"pingMessage", undefined>, v.LiteralSchema<"callToAction", undefined>, v.LiteralSchema<"hourSpent", undefined>, v.LiteralSchema<"default", undefined>], undefined>;
301
- readonly priority: v.NumberSchema<undefined>;
430
+ readonly content: v.GenericSchema<any>;
431
+ readonly duration: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
302
432
  }, undefined>;
303
433
  }, undefined>, v.ObjectSchema<{
304
434
  readonly type: v.LiteralSchema<"alarm:fadeIn", undefined>;
305
435
  readonly messageInfo: v.ObjectSchema<{
306
- readonly message: v.StringSchema<undefined>;
307
- readonly question: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
308
- readonly pingMessageId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
309
- readonly type: v.UnionSchema<[v.LiteralSchema<"quiz", undefined>, v.LiteralSchema<"pingMessage", undefined>, v.LiteralSchema<"callToAction", undefined>, v.LiteralSchema<"hourSpent", undefined>, v.LiteralSchema<"default", undefined>], undefined>;
310
- readonly priority: v.NumberSchema<undefined>;
436
+ readonly content: v.GenericSchema<any>;
437
+ readonly duration: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
311
438
  }, undefined>;
312
439
  }, undefined>, v.ObjectSchema<{
313
440
  readonly type: v.LiteralSchema<"popUp:open", undefined>;
@@ -318,11 +445,19 @@ declare const TapMessageSchema: v.UnionSchema<[v.ObjectSchema<{
318
445
  }, undefined>, v.ObjectSchema<{
319
446
  readonly type: v.LiteralSchema<"popUp:close", undefined>;
320
447
  }, undefined>, v.ObjectSchema<{
321
- readonly type: v.LiteralSchema<"container:expand", undefined>;
448
+ readonly type: v.LiteralSchema<"material:view:open", undefined>;
449
+ readonly materialId: v.StringSchema<undefined>;
450
+ readonly presignedUrl: v.StringSchema<undefined>;
451
+ readonly pageStart: v.NumberSchema<undefined>;
452
+ readonly pageEnd: v.NumberSchema<undefined>;
322
453
  readonly nonce: v.OptionalSchema<v.UnionSchema<[v.StringSchema<undefined>, v.NumberSchema<undefined>], undefined>, undefined>;
323
454
  }, undefined>, v.ObjectSchema<{
324
- readonly type: v.LiteralSchema<"container:collapse", undefined>;
325
- readonly nonce: v.OptionalSchema<v.UnionSchema<[v.StringSchema<undefined>, v.NumberSchema<undefined>], undefined>, undefined>;
455
+ readonly type: v.LiteralSchema<"material:view:close", undefined>;
456
+ }, undefined>, v.ObjectSchema<{
457
+ readonly type: v.LiteralSchema<"material:view:error", undefined>;
458
+ readonly materialId: v.StringSchema<undefined>;
459
+ readonly error: v.UnionSchema<[v.LiteralSchema<"fetch_failed", undefined>, v.LiteralSchema<"expired_url", undefined>, v.LiteralSchema<"extraction_failed", undefined>, v.LiteralSchema<"unknown", undefined>], undefined>;
460
+ readonly message: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
326
461
  }, undefined>, v.ObjectSchema<{
327
462
  readonly type: v.LiteralSchema<"container:mode:change", undefined>;
328
463
  readonly mode: v.UnionSchema<[v.LiteralSchema<"floating", undefined>, v.LiteralSchema<"sidebar", undefined>], undefined>;
@@ -350,6 +485,7 @@ declare const TapMessageSchema: v.UnionSchema<[v.ObjectSchema<{
350
485
  readonly courseId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
351
486
  readonly clipId: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
352
487
  readonly clipPlayHead: v.OptionalSchema<v.NumberSchema<undefined>, undefined>;
488
+ readonly inline: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
353
489
  readonly container: v.OptionalSchema<v.ObjectSchema<{
354
490
  readonly mode: v.OptionalSchema<v.UnionSchema<[v.LiteralSchema<"auto", undefined>, v.LiteralSchema<"floating", undefined>, v.LiteralSchema<"sidebar", undefined>], undefined>, undefined>;
355
491
  readonly floatingConfig: v.OptionalSchema<v.ObjectSchema<{
@@ -420,6 +556,14 @@ type FloatingConfig = {
420
556
  height?: string;
421
557
  /** Border radius (default: "16px") */
422
558
  borderRadius?: string;
559
+ /** Expanded width for PDF viewer (default: "min(90vw, 1200px)") */
560
+ expandedWidth?: string;
561
+ /** Expanded height for PDF viewer (default: "90vh") */
562
+ expandedHeight?: string;
563
+ /** Expanded top position for PDF viewer (default: "5vh") */
564
+ expandedTop?: string;
565
+ /** Expanded right position for PDF viewer (default: "5vw") */
566
+ expandedRight?: string;
423
567
  };
424
568
  /**
425
569
  * Sidebar Configuration
@@ -427,6 +571,8 @@ type FloatingConfig = {
427
571
  type SidebarConfig = {
428
572
  /** Maximum width of the sidebar (default: "min(50%, 1000px)") */
429
573
  maxWidth?: string;
574
+ /** Expanded maximum width for PDF viewer (default: "min(70%, 1400px)") */
575
+ expandedMaxWidth?: string;
430
576
  /** Minimum viewport width to enable sidebar mode (default: 768) */
431
577
  minViewportWidth?: number;
432
578
  };
@@ -464,18 +610,21 @@ type SidebarConfig = {
464
610
  * mode: "sidebar",
465
611
  * sidebarConfig: { maxWidth: "500px" }
466
612
  * }
613
+ *
467
614
  */
468
615
  type ContainerConfig = {
469
616
  /**
470
- * Container mode controls toggle button visibility and behavior
617
+ * Container mode controls toggle button visibility and behavior (only for auto-created containers)
471
618
  * - "auto" (default): User can toggle between layouts, starts in floating, remembers preference via localStorage
472
619
  * - "floating": Fixed in floating layout, no toggle button
473
620
  * - "sidebar": Fixed in sidebar layout, no toggle button
621
+ *
622
+ * NOTE: When user provides their own <tap-container>, mode is ignored and container is fully user-controlled
474
623
  */
475
624
  mode?: ContainerMode;
476
- /** Floating layout configuration (applied when in floating layout) */
625
+ /** Floating layout configuration (applied when SDK auto-creates container in floating layout) */
477
626
  floatingConfig?: FloatingConfig;
478
- /** Sidebar layout configuration (applied when in sidebar layout) */
627
+ /** Sidebar layout configuration (applied when SDK auto-creates container in sidebar layout) */
479
628
  sidebarConfig?: SidebarConfig;
480
629
  };
481
630
 
@@ -530,6 +679,15 @@ type Course = {
530
679
  * 1. buttonId: Attach to your own button element (provide element ID)
531
680
  * 2. <tap-button>: Use the built-in Web Component (omit buttonId)
532
681
  *
682
+ * Container Mounting Options (Priority Resolution):
683
+ * 1. containerId: Use existing <tap-container id="..."> element (explicit)
684
+ * 2. container.parent: Mount iframe to custom parent with imperative API
685
+ * 3. Auto-detect: Find existing <tap-container> in DOM
686
+ * 4. Fallback: Auto-create container and mount to document.body (default)
687
+ *
688
+ * Debug Options:
689
+ * - debug: Enable debug logging (overrides environment-based suppression)
690
+ *
533
691
  * @example
534
692
  * ```typescript
535
693
  * const sdk = new TapSDK({ apiKey: "your-key" });
@@ -546,21 +704,32 @@ type Course = {
546
704
  * course: { userId: "user1", courseId: "course1", clipId: "clip1" }
547
705
  * });
548
706
  *
549
- * // Custom styled <tap-button>
550
- * // HTML: <tap-button position="bottom-left" size="large"></tap-button>
707
+ * // Option 3: Explicit container by ID
708
+ * // HTML: <tap-container id="my-chat-container"></tap-container>
551
709
  * await sdk.init({
710
+ * containerId: "my-chat-container",
552
711
  * course: { userId: "user1", courseId: "course1", clipId: "clip1" }
553
712
  * });
554
713
  *
714
+ * // Option 4: Custom parent element (imperative API)
715
+ * await sdk.init({
716
+ * course: { userId: "user1", courseId: "course1", clipId: "clip1" },
717
+ * container: {
718
+ * parent: document.querySelector('#my-container'),
719
+ * floatingConfig: { width: "100%", height: "100%" }
720
+ * }
721
+ * });
722
+ *
555
723
  * // Custom floating container
556
724
  * await sdk.init({
557
725
  * buttonId: "tap-button",
558
726
  * course: { userId: "user1", courseId: "course1", clipId: "clip1" },
559
727
  * container: {
560
728
  * mode: "floating",
561
- * position: { top: "80px", right: "32px" },
562
- * width: "400px",
563
- * disableSidebar: true // Prevent user from switching to sidebar
729
+ * floatingConfig: {
730
+ * position: { top: "80px", right: "32px" },
731
+ * width: "400px"
732
+ * }
564
733
  * }
565
734
  * });
566
735
  *
@@ -570,9 +739,15 @@ type Course = {
570
739
  * course: { userId: "user1", courseId: "course1", clipId: "clip1" },
571
740
  * container: {
572
741
  * mode: "sidebar",
573
- * sidebarConfig: { maxWidth: "800px", minViewportWidth: 1024 }
742
+ * sidebarConfig: { maxWidth: "800px" }
574
743
  * }
575
744
  * });
745
+ *
746
+ * // Enable debug logging (useful for production debugging)
747
+ * await sdk.init({
748
+ * course: { userId: "user1", courseId: "course1", clipId: "clip1" },
749
+ * debug: true
750
+ * });
576
751
  * ```
577
752
  */
578
753
  type TapKitInitParams = {
@@ -582,9 +757,23 @@ type TapKitInitParams = {
582
757
  * @see https://edutap-ai-docs.vercel.app/docs/sdk/button
583
758
  */
584
759
  buttonId?: string;
760
+ /**
761
+ * Element ID of existing <tap-container> element (optional)
762
+ * If provided, SDK will use this specific container instead of auto-detection
763
+ * Useful when you have multiple containers or need explicit control
764
+ * @see https://edutap-ai-docs.vercel.app/docs/sdk/container
765
+ */
766
+ containerId?: string;
585
767
  course: Course;
586
768
  /** Container configuration (optional, uses defaults if omitted) */
587
769
  container?: ContainerConfig;
770
+ /**
771
+ * Enable debug logging (optional)
772
+ * When true, forces debug logs to show even in production/demo environments
773
+ * Useful for troubleshooting user-reported issues
774
+ * @default false
775
+ */
776
+ debug?: boolean;
588
777
  };
589
778
 
590
779
  /**
@@ -643,6 +832,14 @@ declare class TapKitIframeError extends TapKitError {
643
832
  constructor(message: string, options?: TapErrorOptions);
644
833
  static fromPossibleFrameSafeError(error: any): TapKitIframeError | null;
645
834
  }
835
+ /**
836
+ * Error thrown when material viewer operations fail
837
+ */
838
+ declare class MaterialViewerError extends TapKitError {
839
+ materialId: string;
840
+ constructor(message: string, materialId: string, options?: TapErrorOptions);
841
+ static fromPossibleFrameSafeError(error: any): MaterialViewerError | null;
842
+ }
646
843
 
647
844
  /**
648
845
  * Event Types
@@ -709,13 +906,11 @@ interface VideoPlayerAdapter {
709
906
  type VideoPlayerConfig = HTMLVideoElement | VideoPlayerAdapter;
710
907
 
711
908
  /**
712
- * @internal
713
- * Type-only symbol reference for internal configuration method
714
- * Actual symbol value: Symbol.for("tapkit.config")
715
- * Use this type in interface definitions, create actual symbol in implementation packages
909
+ * TapKit instance interface - shared by tap-kit-core and tap-sdk
910
+ *
911
+ * Public API only - internal Symbol-based APIs are hidden from type definitions
912
+ * following React's pattern (e.g., $$typeof is not in types but exists at runtime)
716
913
  */
717
- declare const TAPKIT_CONFIG_SYMBOL: unique symbol;
718
- /** TapKit instance interface - shared by tap-kit-core and tap-sdk */
719
914
  interface TapKitInstance {
720
915
  events: {
721
916
  seekTimeline: (params: SeekTimelineParamsType) => void;
@@ -733,8 +928,22 @@ interface TapKitInstance {
733
928
  ready: Promise<void>;
734
929
  init(params: TapKitInitParams): Promise<() => void>;
735
930
  destroy(): void;
736
- /** @internal Advanced configuration API using Symbol.for("tapkit.config") */
737
- [TAPKIT_CONFIG_SYMBOL]?(options: TapKitConfigOptions): void;
931
+ /** Show chat container */
932
+ show(): void;
933
+ /** Hide chat container */
934
+ hide(): void;
935
+ /**
936
+ * Internal config update method (soft-private, discouraged)
937
+ *
938
+ * Accepts any config key-value pairs for advanced configuration.
939
+ * Type is intentionally generic to hide implementation details.
940
+ *
941
+ * @deprecated Use public APIs instead. This is kept for backward compatibility.
942
+ * Prefer using Symbol API via Symbol.for("tapkit.config") for advanced config.
943
+ *
944
+ * @internal Soft-private method (starts with _)
945
+ */
946
+ _updateConfig?(options: Record<string, unknown>): void;
738
947
  }
739
948
  /** TapKit constructor type */
740
949
  type TapKitConstructor = new (config: TapKitConfig) => TapKitInstance;
@@ -748,7 +957,67 @@ type TapKitConstructor = new (config: TapKitConfig) => TapKitInstance;
748
957
 
749
958
 
750
959
 
751
- declare global {
960
+ // ==================== Custom Events ====================
961
+
962
+ /**
963
+ * Event name constant for tap-button click
964
+ *
965
+ * Use this constant to ensure type-safety when listening to tap-button events.
966
+ * The event is dispatched with `bubbles: true` and `composed: true`, so it can
967
+ * be caught anywhere in the document.
968
+ *
969
+ * @example
970
+ * ```typescript
971
+ * import { TAP_BUTTON_CLICK_EVENT } from '@coxwave/tap-kit-types';
972
+ *
973
+ * // Type-safe event listening on document
974
+ * document.addEventListener(TAP_BUTTON_CLICK_EVENT, (e) => {
975
+ * console.log('Button clicked!');
976
+ * console.log('Original event:', e.detail.originalEvent);
977
+ * console.log('Button element:', e.detail.buttonElement);
978
+ * });
979
+ *
980
+ * // Or on specific element
981
+ * const btn = document.querySelector('tap-button');
982
+ * btn?.addEventListener(TAP_BUTTON_CLICK_EVENT, (e) => {
983
+ * console.log('Clicked:', e.detail);
984
+ * });
985
+ * ```
986
+ */
987
+ declare const TAP_BUTTON_CLICK_EVENT = "tap-button:click" as const;
988
+
989
+ /**
990
+ * Event detail payload for tap-button:click CustomEvent
991
+ *
992
+ * This interface defines the structure of the `detail` property
993
+ * in the CustomEvent dispatched by <tap-button> Web Component.
994
+ *
995
+ * @example
996
+ * ```typescript
997
+ * import { TAP_BUTTON_CLICK_EVENT, TapButtonClickEventDetail } from '@coxwave/tap-kit-types';
998
+ *
999
+ * document.addEventListener(TAP_BUTTON_CLICK_EVENT, (e: CustomEvent<TapButtonClickEventDetail>) => {
1000
+ * // Type-safe access to event detail
1001
+ * e.detail.originalEvent; // ✅ Event
1002
+ * e.detail.buttonElement; // ✅ HTMLButtonElement | null
1003
+ * });
1004
+ * ```
1005
+ */
1006
+ interface TapButtonClickEventDetail {
1007
+ /** Original DOM click event */
1008
+ originalEvent: Event;
1009
+ /** Reference to the button element inside Shadow DOM */
1010
+ buttonElement: HTMLButtonElement | null;
1011
+ }
1012
+
1013
+ // React 19 requires module augmentation based on tsconfig jsx setting
1014
+ // - "jsx": "react-jsx" → augment "react/jsx-runtime"
1015
+ // - "jsx": "react-jsxdev" → augment "react/jsx-dev-runtime"
1016
+ // - "jsx": "preserve" → augment "react" (Next.js default)
1017
+ // - "jsx": "react" → augment "react"
1018
+
1019
+ // For Next.js (jsx: preserve)
1020
+ declare module "react" {
752
1021
  namespace JSX {
753
1022
  interface IntrinsicElements {
754
1023
  "tap-button": TapButtonAttributes;
@@ -756,6 +1025,34 @@ declare global {
756
1025
  }
757
1026
  }
758
1027
 
1028
+ // For CRA, Vite (jsx: react-jsx)
1029
+ declare module "react/jsx-runtime" {
1030
+ namespace JSX {
1031
+ interface IntrinsicElements {
1032
+ "tap-button": TapButtonAttributes;
1033
+ }
1034
+ }
1035
+ }
1036
+
1037
+ // Global event maps for type-safe event listeners
1038
+ declare global {
1039
+ /**
1040
+ * Extend DocumentEventMap for type-safe addEventListener on document
1041
+ * Provides autocomplete and type checking for tap-button events
1042
+ */
1043
+ interface DocumentEventMap {
1044
+ [TAP_BUTTON_CLICK_EVENT]: CustomEvent<TapButtonClickEventDetail>;
1045
+ }
1046
+
1047
+ /**
1048
+ * Extend HTMLElementEventMap for type-safe addEventListener on elements
1049
+ * Provides autocomplete and type checking for tap-button events
1050
+ */
1051
+ interface HTMLElementEventMap {
1052
+ [TAP_BUTTON_CLICK_EVENT]: CustomEvent<TapButtonClickEventDetail>;
1053
+ }
1054
+ }
1055
+
759
1056
  /**
760
1057
  * Public API interface for TapButtonElement Web Component
761
1058
  *
@@ -775,6 +1072,16 @@ interface ITapButtonElement extends HTMLElement {
775
1072
  * Props/Attributes for <tap-button> Web Component
776
1073
  */
777
1074
  interface TapButtonAttributes {
1075
+ /**
1076
+ * Element ID attribute
1077
+ */
1078
+ id?: string;
1079
+
1080
+ /**
1081
+ * CSS class name(s)
1082
+ */
1083
+ className?: string;
1084
+
778
1085
  /**
779
1086
  * Enable floating mode (position: fixed)
780
1087
  * When true or omitted: button floats with fixed positioning
@@ -796,7 +1103,7 @@ interface TapButtonAttributes {
796
1103
 
797
1104
  /**
798
1105
  * Button size preset
799
- * @default "medium"
1106
+ * @default "large"
800
1107
  */
801
1108
  size?: "small" | "medium" | "large";
802
1109
 
@@ -814,6 +1121,637 @@ interface TapButtonAttributes {
814
1121
  style?: CSSProperties;
815
1122
  }
816
1123
 
1124
+ /**
1125
+ * Type declarations for <tap-container> Web Component
1126
+ *
1127
+ * Provides TypeScript support for using <tap-container> in JSX/TSX.
1128
+ * The actual Web Component is registered by tap-kit-core when loaded.
1129
+ */
1130
+
1131
+
1132
+
1133
+ // React 19 requires module augmentation based on tsconfig jsx setting
1134
+ // - "jsx": "react-jsx" → augment "react/jsx-runtime"
1135
+ // - "jsx": "react-jsxdev" → augment "react/jsx-dev-runtime"
1136
+ // - "jsx": "preserve" → augment "react" (Next.js default)
1137
+ // - "jsx": "react" → augment "react"
1138
+
1139
+ // For Next.js (jsx: preserve)
1140
+ declare module "react" {
1141
+ namespace JSX {
1142
+ interface IntrinsicElements {
1143
+ "tap-container": TapContainerAttributes;
1144
+ }
1145
+ }
1146
+ }
1147
+
1148
+ // For CRA, Vite (jsx: react-jsx)
1149
+ declare module "react/jsx-runtime" {
1150
+ namespace JSX {
1151
+ interface IntrinsicElements {
1152
+ "tap-container": TapContainerAttributes;
1153
+ }
1154
+ }
1155
+ }
1156
+
1157
+ /**
1158
+ * Public API interface for TapContainerElement Web Component
1159
+ *
1160
+ * This interface defines the contract that tap-kit-core's TapContainerElement must implement.
1161
+ * It provides type-safe access to the Web Component's public API.
1162
+ *
1163
+ * Note: TapContainerElement is now a pure container.
1164
+ * - No layout mode management (user controls via style/className)
1165
+ * - No iframe creation (ChatView injects iframe)
1166
+ * - Minimal API surface for maximum flexibility
1167
+ */
1168
+ interface ITapContainerElement extends HTMLElement {
1169
+ /**
1170
+ * Show the container (sets display to block)
1171
+ */
1172
+ show(): void;
1173
+
1174
+ /**
1175
+ * Hide the container (sets display to none)
1176
+ */
1177
+ hide(): void;
1178
+
1179
+ /**
1180
+ * Get the container element (returns self)
1181
+ * @returns The container HTMLElement (this)
1182
+ */
1183
+ getContainerElement(): HTMLElement;
1184
+
1185
+ /**
1186
+ * Check if container is visible
1187
+ * @returns true if visible (display !== "none"), false if hidden
1188
+ */
1189
+ isVisible(): boolean;
1190
+ }
1191
+
1192
+ /**
1193
+ * Props/Attributes for <tap-container> Web Component
1194
+ */
1195
+ interface TapContainerAttributes {
1196
+ /**
1197
+ * Element ID attribute
1198
+ */
1199
+ id?: string;
1200
+
1201
+ /**
1202
+ * CSS class name(s)
1203
+ */
1204
+ className?: string;
1205
+
1206
+ /**
1207
+ * Layout mode
1208
+ * @default "floating"
1209
+ */
1210
+ layout?: "floating" | "sidebar";
1211
+
1212
+ /**
1213
+ * iframe source URL
1214
+ * If provided, an iframe will be automatically created
1215
+ */
1216
+ src?: string;
1217
+
1218
+ /**
1219
+ * Initial visibility state
1220
+ * @default false
1221
+ */
1222
+ visible?: boolean;
1223
+
1224
+ /**
1225
+ * Child content (not typically used for tap-container)
1226
+ * The component automatically creates an iframe container
1227
+ */
1228
+ children?: ReactNode;
1229
+
1230
+ /**
1231
+ * Custom inline styles
1232
+ */
1233
+ style?: CSSProperties;
1234
+
1235
+ /**
1236
+ * Event handler for when container is shown
1237
+ */
1238
+ onShow?: () => void;
1239
+
1240
+ /**
1241
+ * Event handler for when container is hidden
1242
+ */
1243
+ onHide?: () => void;
1244
+
1245
+ /**
1246
+ * Event handler for when container is destroyed
1247
+ */
1248
+ onDestroy?: () => void;
1249
+ }
1250
+
1251
+ /**
1252
+ * TapKit Web Component Type Definitions
1253
+ *
1254
+ * Provides TypeScript types for <tap-kit> custom element
1255
+ */
1256
+
1257
+
1258
+
1259
+ /**
1260
+ * EventManager type (matches TapKitInstance.events)
1261
+ */
1262
+ interface EventManager {
1263
+ seekTimeline: (params: SeekTimelineParamsType) => void;
1264
+ onTimelineSeek: (
1265
+ callback: (clipPlayHead: number, clipId: string) => void,
1266
+ ) => () => void;
1267
+ onAlarmFadeIn: (
1268
+ handler: (messageInfo: AlarmMessageInstanceType) => void,
1269
+ ) => () => void;
1270
+ }
1271
+
1272
+ /**
1273
+ * VideoController type (matches TapKitInstance.video)
1274
+ */
1275
+ interface VideoController {
1276
+ bind: (config: VideoPlayerConfig, clipId: string) => void;
1277
+ unbind: () => void;
1278
+ }
1279
+
1280
+ /**
1281
+ * TapKit Web Component Interface
1282
+ *
1283
+ * Declarative HTML-based API for TapKit SDK
1284
+ *
1285
+ * @example
1286
+ * ```html
1287
+ * <tap-kit
1288
+ * api-key="your-key"
1289
+ * user-id="user-123"
1290
+ * course-id="course-456"
1291
+ * clip-id="clip-789"
1292
+ * ></tap-kit>
1293
+ * ```
1294
+ *
1295
+ * @example
1296
+ * ```typescript
1297
+ * const kit = document.querySelector('tap-kit');
1298
+ * await kit.ready;
1299
+ * kit.show();
1300
+ * kit.setCourse({ courseId: 'new', clipId: 'new-1' });
1301
+ * ```
1302
+ */
1303
+ interface TapKitElement extends HTMLElement {
1304
+ // ===== Attributes (Reactive Properties) =====
1305
+
1306
+ /** API Key (required, can be set asynchronously after element creation) */
1307
+ apiKey: string;
1308
+
1309
+ /** User ID (optional) */
1310
+ userId?: string;
1311
+
1312
+ /** Course ID (optional) */
1313
+ courseId?: string;
1314
+
1315
+ /** Clip ID (optional) */
1316
+ clipId?: string;
1317
+
1318
+ /** Clip Play Head in seconds (optional) */
1319
+ clipPlayHead?: number;
1320
+
1321
+ /** Language (optional, default: "ko") */
1322
+ language?: "ko" | "en";
1323
+
1324
+ /** TAP Frontend URL (optional, for testing) */
1325
+ tapUrl?: string;
1326
+
1327
+ /** API Backend URL (optional, for testing) */
1328
+ apiUrl?: string;
1329
+
1330
+ /** Environment (optional, for testing) */
1331
+ environment?: "dev" | "prod" | "staging" | "demo";
1332
+
1333
+ /** Custom button element ID (optional) */
1334
+ buttonId?: string;
1335
+
1336
+ /** Custom container element ID (optional) */
1337
+ containerId?: string;
1338
+
1339
+ /** Enable debug mode (optional, default: false) */
1340
+ debug: boolean;
1341
+
1342
+ /**
1343
+ * Display mode - controls container positioning and layout
1344
+ * - "inline" (default): Container renders at <tap-kit> element position (embedded)
1345
+ * - "floating": Container floats in document.body as compact widget
1346
+ * - "sidebar": Container floats in document.body as full-height sidebar
1347
+ */
1348
+ mode?: "inline" | "floating" | "sidebar";
1349
+
1350
+ /**
1351
+ * Auto-created flag (internal use only)
1352
+ * Indicates SDK-controlled element (used by legacy TapKit class)
1353
+ * @internal
1354
+ */
1355
+ autoCreated?: boolean;
1356
+
1357
+ // ===== Public Methods (Imperative API) =====
1358
+
1359
+ /**
1360
+ * Show chat container
1361
+ * @example kit.show()
1362
+ */
1363
+ show(): void;
1364
+
1365
+ /**
1366
+ * Hide chat container
1367
+ * @example kit.hide()
1368
+ */
1369
+ hide(): void;
1370
+
1371
+ /**
1372
+ * Update course information
1373
+ * @param course - Partial course information (courseId and clipId required)
1374
+ * @example kit.setCourse({ courseId: 'new', clipId: 'new-1' })
1375
+ */
1376
+ setCourse(
1377
+ course: Partial<Course> & { courseId: string; clipId: string },
1378
+ ): void;
1379
+
1380
+ // ===== Delegate Properties (Read-only) =====
1381
+
1382
+ /**
1383
+ * EventManager instance
1384
+ *
1385
+ * Returns a proxy that automatically waits for initialization.
1386
+ * Safe to access immediately - calls are queued until ready.
1387
+ *
1388
+ * @readonly
1389
+ * @example
1390
+ * // No need to wait for ready - proxy handles it
1391
+ * kit.events.seekTimeline({ clipPlayHead: 100, clipId: 'clip-1' });
1392
+ *
1393
+ * // Or with ready for guaranteed initialization
1394
+ * await kit.ready;
1395
+ * kit.events.seekTimeline({ clipPlayHead: 100, clipId: 'clip-1' });
1396
+ */
1397
+ readonly events: EventManager;
1398
+
1399
+ /**
1400
+ * VideoController instance
1401
+ *
1402
+ * Returns a proxy that automatically waits for initialization.
1403
+ * Safe to access immediately - calls are queued until ready.
1404
+ *
1405
+ * @readonly
1406
+ * @example
1407
+ * // No need to wait for ready - proxy handles it
1408
+ * kit.video.bind(videoConfig, 'clip-1');
1409
+ *
1410
+ * // Or with ready for guaranteed initialization
1411
+ * await kit.ready;
1412
+ * kit.video.bind(videoConfig, 'clip-1');
1413
+ */
1414
+ readonly video: VideoController;
1415
+
1416
+ /**
1417
+ * Check if chat is currently open
1418
+ * @readonly
1419
+ */
1420
+ readonly isOpen: boolean;
1421
+
1422
+ /**
1423
+ * Check if SDK is initialized
1424
+ * @readonly
1425
+ */
1426
+ readonly isInitialized: boolean;
1427
+
1428
+ /**
1429
+ * Promise that resolves when SDK is ready
1430
+ * @readonly
1431
+ * @example await kit.ready
1432
+ */
1433
+ readonly ready: Promise<void>;
1434
+ }
1435
+
1436
+ /**
1437
+ * Custom Events dispatched by <tap-kit>
1438
+ */
1439
+ interface TapKitElementEventMap {
1440
+ /**
1441
+ * Fired when TapKit SDK is ready
1442
+ * @example
1443
+ * ```typescript
1444
+ * kit.addEventListener('tap-kit:ready', () => {
1445
+ * console.log('TapKit is ready!');
1446
+ * });
1447
+ * ```
1448
+ */
1449
+ "tap-kit:ready": CustomEvent<void>;
1450
+
1451
+ /**
1452
+ * Fired when initialization fails
1453
+ * @example
1454
+ * ```typescript
1455
+ * kit.addEventListener('tap-kit:error', (e) => {
1456
+ * console.error('TapKit error:', e.detail.error);
1457
+ * });
1458
+ * ```
1459
+ */
1460
+ "tap-kit:error": CustomEvent<{ error: Error }>;
1461
+ }
1462
+
1463
+ /**
1464
+ * Props/Attributes for <tap-kit> Web Component (JSX/TSX usage)
1465
+ */
1466
+ interface TapKitAttributes {
1467
+ /** React key for list rendering */
1468
+ key?: string | number;
1469
+
1470
+ /** React ref for element access */
1471
+ ref?: React.Ref<TapKitElement>;
1472
+
1473
+ /** Element ID attribute */
1474
+ id?: string;
1475
+
1476
+ /** CSS class name(s) */
1477
+ className?: string;
1478
+
1479
+ /** API Key (required) */
1480
+ "api-key"?: string;
1481
+
1482
+ /** User ID */
1483
+ "user-id"?: string;
1484
+
1485
+ /** Course ID */
1486
+ "course-id"?: string;
1487
+
1488
+ /** Clip ID */
1489
+ "clip-id"?: string;
1490
+
1491
+ /** Clip playhead position in seconds */
1492
+ "clip-play-head"?: number;
1493
+
1494
+ /** Language setting */
1495
+ language?: "ko" | "en";
1496
+
1497
+ /** TAP Frontend URL (for testing) */
1498
+ "tap-url"?: string;
1499
+
1500
+ /** API Backend URL (for testing) */
1501
+ "api-url"?: string;
1502
+
1503
+ /** Environment (for testing) */
1504
+ environment?: "dev" | "prod" | "staging" | "demo";
1505
+
1506
+ /** Custom button element ID */
1507
+ "button-id"?: string;
1508
+
1509
+ /** Custom container element ID */
1510
+ "container-id"?: string;
1511
+
1512
+ /** Enable debug mode */
1513
+ debug?: boolean;
1514
+
1515
+ /**
1516
+ * Display mode - controls container positioning
1517
+ * - "inline" (default): Embedded at element position
1518
+ * - "floating": Compact widget in document.body
1519
+ * - "sidebar": Full-height sidebar in document.body
1520
+ */
1521
+ mode?: "inline" | "floating" | "sidebar";
1522
+
1523
+ /** Custom inline styles */
1524
+ style?: CSSProperties;
1525
+
1526
+ /** Child content (slot) */
1527
+ children?: ReactNode;
1528
+ }
1529
+
1530
+ // React 19 requires module augmentation based on tsconfig jsx setting
1531
+ // - "jsx": "react-jsx" → augment "react/jsx-runtime"
1532
+ // - "jsx": "react-jsxdev" → augment "react/jsx-dev-runtime"
1533
+ // - "jsx": "preserve" → augment "react" (Next.js default)
1534
+ // - "jsx": "react" → augment "react"
1535
+
1536
+ // For Next.js (jsx: preserve)
1537
+ declare module "react" {
1538
+ namespace JSX {
1539
+ interface IntrinsicElements {
1540
+ "tap-kit": TapKitAttributes;
1541
+ }
1542
+ }
1543
+ }
1544
+
1545
+ // For CRA, Vite (jsx: react-jsx)
1546
+ declare module "react/jsx-runtime" {
1547
+ namespace JSX {
1548
+ interface IntrinsicElements {
1549
+ "tap-kit": TapKitAttributes;
1550
+ }
1551
+ }
1552
+ }
1553
+
1554
+ /**
1555
+ * @deprecated Use TapKitElement instead
1556
+ */
1557
+ type ITapKitElement = TapKitElement;
1558
+
1559
+ /**
1560
+ * Global type augmentation for HTMLElementTagNameMap
1561
+ * Enables type-safe querySelector and createElement
1562
+ */
1563
+ declare global {
1564
+ interface HTMLElementTagNameMap {
1565
+ "tap-kit": TapKitElement;
1566
+ }
1567
+
1568
+ interface HTMLElementEventMap extends TapKitElementEventMap {}
1569
+ }
1570
+
1571
+ /**
1572
+ * Type declarations for <tap-material-viewer> Web Component
1573
+ *
1574
+ * Provides TypeScript support for using <tap-material-viewer> in JSX/TSX.
1575
+ * The actual Web Component is registered by tap-kit-core when loaded.
1576
+ */
1577
+
1578
+
1579
+
1580
+ // React 19 requires module augmentation based on tsconfig jsx setting
1581
+ // - "jsx": "react-jsx" → augment "react/jsx-runtime"
1582
+ // - "jsx": "react-jsxdev" → augment "react/jsx-dev-runtime"
1583
+ // - "jsx": "preserve" → augment "react" (Next.js default)
1584
+ // - "jsx": "react" → augment "react"
1585
+
1586
+ // For Next.js (jsx: preserve)
1587
+ declare module "react" {
1588
+ namespace JSX {
1589
+ interface IntrinsicElements {
1590
+ "tap-material-viewer": TapMaterialViewerAttributes;
1591
+ }
1592
+ }
1593
+ }
1594
+
1595
+ // For CRA, Vite (jsx: react-jsx)
1596
+ declare module "react/jsx-runtime" {
1597
+ namespace JSX {
1598
+ interface IntrinsicElements {
1599
+ "tap-material-viewer": TapMaterialViewerAttributes;
1600
+ }
1601
+ }
1602
+ }
1603
+
1604
+ /**
1605
+ * Configuration for opening material viewer
1606
+ */
1607
+ interface MaterialViewConfig {
1608
+ /**
1609
+ * Material identifier
1610
+ */
1611
+ materialId: string;
1612
+
1613
+ /**
1614
+ * Presigned URL for PDF download
1615
+ */
1616
+ presignedUrl: string;
1617
+
1618
+ /**
1619
+ * Starting page number (1-indexed)
1620
+ */
1621
+ pageStart: number;
1622
+
1623
+ /**
1624
+ * Ending page number (1-indexed)
1625
+ */
1626
+ pageEnd: number;
1627
+ }
1628
+
1629
+ /**
1630
+ * Public API interface for TapMaterialViewerElement Web Component
1631
+ *
1632
+ * This interface defines the contract that tap-kit-core's TapMaterialViewerElement must implement.
1633
+ * It provides type-safe access to the Web Component's public API.
1634
+ *
1635
+ * The material viewer manages PDF display with loading states, error handling,
1636
+ * and responsive positioning relative to a container element.
1637
+ */
1638
+ interface ITapMaterialViewerElement extends HTMLElement {
1639
+ /**
1640
+ * Container element reference for positioning
1641
+ * Must be set before calling open()
1642
+ */
1643
+ containerElement: HTMLElement | null;
1644
+
1645
+ /**
1646
+ * Open material viewer with PDF configuration
1647
+ *
1648
+ * Downloads PDF from presigned URL, extracts specified pages,
1649
+ * and displays in an overlay positioned relative to containerElement.
1650
+ *
1651
+ * @param config - Material view configuration
1652
+ * @throws {MaterialViewerError} If PDF fetch or processing fails
1653
+ * @fires error - When material loading fails
1654
+ * @fires open - When material is successfully displayed
1655
+ */
1656
+ open(config: MaterialViewConfig): Promise<void>;
1657
+
1658
+ /**
1659
+ * Close material viewer and cleanup resources
1660
+ *
1661
+ * Revokes blob URLs, resets state, and hides overlay.
1662
+ *
1663
+ * @fires close - When viewer is closed
1664
+ */
1665
+ close(): void;
1666
+
1667
+ /**
1668
+ * Update overlay position relative to container
1669
+ *
1670
+ * Automatically called on container resize/move.
1671
+ * Can be manually triggered if needed.
1672
+ */
1673
+ updatePosition(): void;
1674
+
1675
+ /**
1676
+ * Hide overlay without closing viewer
1677
+ *
1678
+ * Called when container is hidden. Preserves viewer state
1679
+ * so it can be shown again when container becomes visible.
1680
+ */
1681
+ hideOverlay(): void;
1682
+
1683
+ /**
1684
+ * Show overlay (if viewer has content)
1685
+ *
1686
+ * Called when container is shown. Only displays overlay
1687
+ * if viewer has content loaded (_isVisible is true).
1688
+ */
1689
+ showOverlay(): void;
1690
+
1691
+ /**
1692
+ * Check if viewer is currently visible
1693
+ *
1694
+ * @returns true if visible, false otherwise
1695
+ */
1696
+ readonly isVisible: boolean;
1697
+
1698
+ /**
1699
+ * Get current material ID
1700
+ *
1701
+ * @returns Material ID if open, null otherwise
1702
+ */
1703
+ readonly currentMaterialId: string | null;
1704
+ }
1705
+
1706
+ /**
1707
+ * Props/Attributes for <tap-material-viewer> Web Component
1708
+ */
1709
+ interface TapMaterialViewerAttributes {
1710
+ /**
1711
+ * Element ID attribute
1712
+ */
1713
+ id?: string;
1714
+
1715
+ /**
1716
+ * CSS class name(s)
1717
+ */
1718
+ className?: string;
1719
+
1720
+ /**
1721
+ * Container element reference for positioning
1722
+ * Required before calling open()
1723
+ */
1724
+ containerElement?: HTMLElement | null;
1725
+
1726
+ /**
1727
+ * Child content (not typically used for tap-material-viewer)
1728
+ * The component automatically creates overlay content
1729
+ */
1730
+ children?: ReactNode;
1731
+
1732
+ /**
1733
+ * Custom inline styles
1734
+ */
1735
+ style?: CSSProperties;
1736
+
1737
+ /**
1738
+ * Event handler for when material loading fails
1739
+ * @param event - CustomEvent with MaterialViewerError detail
1740
+ */
1741
+ onError?: (event: CustomEvent<MaterialViewerError>) => void;
1742
+
1743
+ /**
1744
+ * Event handler for when material is successfully displayed
1745
+ * @param event - CustomEvent with { materialId: string } detail
1746
+ */
1747
+ onOpen?: (event: CustomEvent<{ materialId: string }>) => void;
1748
+
1749
+ /**
1750
+ * Event handler for when viewer is closed
1751
+ */
1752
+ onClose?: () => void;
1753
+ }
1754
+
817
1755
  /**
818
1756
  * Global type definitions for browser environment
819
1757
  */
@@ -823,6 +1761,20 @@ interface TapButtonAttributes {
823
1761
  declare global {
824
1762
  interface Window {
825
1763
  TapKit?: TapKitConstructor;
1764
+ createTapKit?: (config: {
1765
+ apiKey: string;
1766
+ userId?: string;
1767
+ courseId?: string;
1768
+ clipId?: string;
1769
+ clipPlayHead?: number;
1770
+ language?: "ko" | "en";
1771
+ buttonId?: string;
1772
+ mode?: "inline" | "floating" | "sidebar";
1773
+ debug?: boolean;
1774
+ apiUrl?: string;
1775
+ tapUrl?: string;
1776
+ environment?: "dev" | "prod" | "staging" | "demo";
1777
+ }) => TapKitElement;
826
1778
  __TAP_KIT_LOADER_LOADED__?: boolean;
827
1779
  __TAP_KIT_LOADER_LOADING__?: Promise<void>;
828
1780
  __TAP_KIT_LOADER_URL__?: string; // Override CDN loader URL for local testing
@@ -846,4 +1798,4 @@ declare global {
846
1798
  function cancelIdleCallback(handle: number): void;
847
1799
  }
848
1800
 
849
- export { type AlarmClickMessage, AlarmClickSchema, type AlarmFadeInMessage, AlarmFadeInSchema, AlarmMessageInstanceSchema, type AlarmMessageInstanceType, type AlarmType, AlarmTypeSchema, type ConfigUpdateMessage, ConfigUpdateSchema, type ContainerCollapseMessage, ContainerCollapseSchema, type ContainerConfig, type ContainerExpandMessage, ContainerExpandSchema, type ContainerLayoutState, type ContainerLayoutStateChangedMessage, ContainerLayoutStateChangedSchema, type ContainerMode, type ContainerModeChangeAckMessage, ContainerModeChangeAckSchema, type ContainerModeChangeMessage, ContainerModeChangeSchema, type ContainerVisibility, type Course, type FloatingConfig, type GAEventMessage, GAEventSchema, type ITapButtonElement, TapKitInitializationError as InitializationError, type PopUpCloseMessage, PopUpCloseSchema, type PopUpOpenMessage, PopUpOpenSchema, type PositionType, type SeekTimelineParamsType, type ShortcutKeyPropertiesType, type SidebarConfig, TAPKIT_CONFIG_SYMBOL, TAP_ERROR_MARKER, type TapButtonAttributes, type TapCloseMessage, TapCloseSchema, type TapErrorOptions, type TapKitConfig, type TapKitConfigOptions, TapKitConfigurationError, type TapKitConstructor, TapKitError, TapKitIframeError, type TapKitInitParams, TapKitInitializationError, type TapKitInstance, TapKitLoaderError, TapKitMessageError, type TapKitRuntimeConfig, type TapMessage, type TapMessageRecord, TapMessageSchema, type TapMessageType, type TapReadyMessage, TapReadySchema, type TimelineSeekMessage, TimelineSeekSchema, type VideoPlayerAdapter, type VideoPlayerConfig, type ViewportResizeMessage, ViewportResizeSchema };
1801
+ export { ALARM_DURATION, type AlarmClickMessage, AlarmClickSchema, type AlarmElement, type AlarmElementProps, AlarmElementPropsSchema, AlarmElementSchema, type AlarmFadeInMessage, AlarmFadeInSchema, AlarmMessageInstanceSchema, type AlarmMessageInstanceType, type AlarmPayload, type AlarmType, type CSSStyle, CSSStyleSchema, type ConfigUpdateMessage, ConfigUpdateSchema, type ContainerConfig, type ContainerLayoutState, type ContainerLayoutStateChangedMessage, ContainerLayoutStateChangedSchema, type ContainerMode, type ContainerModeChangeAckMessage, ContainerModeChangeAckSchema, type ContainerModeChangeMessage, ContainerModeChangeSchema, type ContainerVisibility, type Course, type EventManager, type FloatingConfig, type GAEventMessage, GAEventSchema, type ITapButtonElement, type ITapContainerElement, type ITapKitElement, type ITapMaterialViewerElement, TapKitInitializationError as InitializationError, type MaterialViewCloseMessage, MaterialViewCloseSchema, type MaterialViewConfig, type MaterialViewErrorMessage, MaterialViewErrorSchema, type MaterialViewOpenMessage, MaterialViewOpenSchema, MaterialViewerError, type PopUpCloseMessage, PopUpCloseSchema, type PopUpOpenMessage, PopUpOpenSchema, type PositionType, type SeekTimelineParamsType, type ShortcutKeyPropertiesType, type SidebarConfig, TAP_BUTTON_CLICK_EVENT, TAP_ERROR_MARKER, type TapButtonAttributes, type TapButtonClickEventDetail, type TapCloseMessage, TapCloseSchema, type TapContainerAttributes, type TapErrorOptions, type TapKitConfig, type TapKitConfigOptions, TapKitConfigurationError, type TapKitConstructor, type TapKitElement, type TapKitElementEventMap, TapKitError, TapKitIframeError, type TapKitInitParams, TapKitInitializationError, type TapKitInstance, TapKitLoaderError, TapKitMessageError, type TapKitRuntimeConfig, type TapMaterialViewerAttributes, type TapMessage, type TapMessageRecord, TapMessageSchema, type TapMessageType, type TapReadyMessage, TapReadySchema, type TimelineSeekMessage, TimelineSeekSchema, type VideoController, type VideoPlayerAdapter, type VideoPlayerConfig, type ViewportResizeMessage, ViewportResizeSchema };