@designbasekorea/ui-wc 0.5.6 → 0.6.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.
Files changed (193) hide show
  1. package/dist/chunks/lottie-35a9b0f5.js +1 -0
  2. package/dist/esm/components/animation-background/canvas-controller.js +1 -121
  3. package/dist/esm/components/animation-background/controls.js +1 -180
  4. package/dist/esm/components/animation-background/graphite-controller.js +1 -196
  5. package/dist/esm/components/animation-background/graphite-presets.js +1 -42
  6. package/dist/esm/components/animation-background/graphite-shader.js +1 -111
  7. package/dist/esm/components/animation-background/layers.js +1 -369
  8. package/dist/esm/components/animation-background/lumina-presets.js +1 -53
  9. package/dist/esm/components/animation-background/lumina-shaders.js +1 -181
  10. package/dist/esm/components/animation-background/lumina-utils.js +1 -60
  11. package/dist/esm/components/animation-background/particle-defaults.js +1 -36
  12. package/dist/esm/components/animation-background/renderers.js +1 -167
  13. package/dist/esm/components/animation-background/shader-controller.js +1 -154
  14. package/dist/esm/components/color-picker-utils.js +1 -134
  15. package/dist/esm/components/date-picker-utils.js +1 -140
  16. package/dist/esm/components/db-accordion.js +1 -304
  17. package/dist/esm/components/db-ad-banner.js +1 -455
  18. package/dist/esm/components/db-ai-chat-list.d.ts +43 -0
  19. package/dist/esm/components/db-ai-chat-list.js +1 -0
  20. package/dist/esm/components/db-ai-chat.d.ts +159 -0
  21. package/dist/esm/components/db-ai-chat.js +1 -0
  22. package/dist/esm/components/db-alert.js +1 -149
  23. package/dist/esm/components/db-animation-background.js +1 -486
  24. package/dist/esm/components/db-animation-text.js +1 -601
  25. package/dist/esm/components/db-app-badge.js +1 -88
  26. package/dist/esm/components/db-audio-player.js +1 -752
  27. package/dist/esm/components/db-avatar.js +1 -188
  28. package/dist/esm/components/db-backdrop.js +1 -139
  29. package/dist/esm/components/db-badge.js +1 -91
  30. package/dist/esm/components/db-banner.js +1 -188
  31. package/dist/esm/components/db-bottom-navigation.js +1 -146
  32. package/dist/esm/components/db-bottom-sheet.js +1 -481
  33. package/dist/esm/components/db-breadcrumbs.js +1 -329
  34. package/dist/esm/components/db-button.js +1 -316
  35. package/dist/esm/components/db-calendar.js +1 -675
  36. package/dist/esm/components/db-card.js +1 -265
  37. package/dist/esm/components/db-carousel.js +1 -264
  38. package/dist/esm/components/db-chat-composer.d.ts +134 -0
  39. package/dist/esm/components/db-chat-composer.js +1 -0
  40. package/dist/esm/components/db-chat-list.d.ts +47 -0
  41. package/dist/esm/components/db-chat-list.js +1 -0
  42. package/dist/esm/components/db-chat-message.d.ts +39 -0
  43. package/dist/esm/components/db-chat-message.js +1 -0
  44. package/dist/esm/components/db-chat-orb.d.ts +99 -0
  45. package/dist/esm/components/db-chat-orb.js +1 -0
  46. package/dist/esm/components/db-chat-room.d.ts +71 -0
  47. package/dist/esm/components/db-chat-room.js +1 -0
  48. package/dist/esm/components/db-chat-suggestions.d.ts +44 -0
  49. package/dist/esm/components/db-chat-suggestions.js +1 -0
  50. package/dist/esm/components/db-chat-typing.d.ts +22 -0
  51. package/dist/esm/components/db-chat-typing.js +1 -0
  52. package/dist/esm/components/db-chat-welcome.d.ts +32 -0
  53. package/dist/esm/components/db-chat-welcome.js +1 -0
  54. package/dist/esm/components/db-chat-widget.d.ts +146 -0
  55. package/dist/esm/components/db-chat-widget.js +1 -0
  56. package/dist/esm/components/db-checkbox.js +1 -202
  57. package/dist/esm/components/db-chip.js +1 -104
  58. package/dist/esm/components/db-code-block.js +1 -304
  59. package/dist/esm/components/db-color-picker.js +1 -746
  60. package/dist/esm/components/db-confirm.js +1 -191
  61. package/dist/esm/components/db-container.js +1 -39
  62. package/dist/esm/components/db-context-menu.js +1 -265
  63. package/dist/esm/components/db-countdown.js +1 -146
  64. package/dist/esm/components/db-cursor-follower.js +1 -173
  65. package/dist/esm/components/db-date-picker.js +1 -837
  66. package/dist/esm/components/db-divider.js +1 -66
  67. package/dist/esm/components/db-download-progress.d.ts +89 -0
  68. package/dist/esm/components/db-download-progress.js +1 -0
  69. package/dist/esm/components/db-drawer.js +1 -245
  70. package/dist/esm/components/db-dropdown.js +1 -493
  71. package/dist/esm/components/db-dropzone.js +1 -266
  72. package/dist/esm/components/db-email-verify.d.ts +64 -0
  73. package/dist/esm/components/db-email-verify.js +1 -0
  74. package/dist/esm/components/db-empty-state.js +1 -97
  75. package/dist/esm/components/db-file-uploader.js +1 -256
  76. package/dist/esm/components/db-floating-action-button.js +1 -139
  77. package/dist/esm/components/db-footer.js +1 -385
  78. package/dist/esm/components/db-form.js +1 -566
  79. package/dist/esm/components/db-gantt.js +1 -882
  80. package/dist/esm/components/db-gradient.js +1 -133
  81. package/dist/esm/components/db-grid-background.js +1 -909
  82. package/dist/esm/components/db-grid.js +1 -73
  83. package/dist/esm/components/db-icons.d.ts +3 -0
  84. package/dist/esm/components/db-icons.js +1 -82
  85. package/dist/esm/components/db-image-list.js +1 -214
  86. package/dist/esm/components/db-image-placeholder.js +1 -96
  87. package/dist/esm/components/db-image.js +1 -289
  88. package/dist/esm/components/db-indicator.js +1 -517
  89. package/dist/esm/components/db-input.js +1 -420
  90. package/dist/esm/components/db-kanban.js +1 -538
  91. package/dist/esm/components/db-label.js +1 -66
  92. package/dist/esm/components/db-lightbox.js +1 -372
  93. package/dist/esm/components/db-list.js +1 -340
  94. package/dist/esm/components/db-logo-svg.js +1 -72
  95. package/dist/esm/components/db-logo.js +1 -106
  96. package/dist/esm/components/db-lottie.js +1 -109
  97. package/dist/esm/components/db-markdown-editor.js +1 -458
  98. package/dist/esm/components/db-marquee.js +1 -402
  99. package/dist/esm/components/db-masonry.js +1 -94
  100. package/dist/esm/components/db-menu-item.js +1 -211
  101. package/dist/esm/components/db-modal.js +1 -522
  102. package/dist/esm/components/db-navbar.js +1 -680
  103. package/dist/esm/components/db-onboarding-modal.js +1 -322
  104. package/dist/esm/components/db-otp-input.d.ts +53 -0
  105. package/dist/esm/components/db-otp-input.js +1 -0
  106. package/dist/esm/components/db-page-header.js +1 -312
  107. package/dist/esm/components/db-pagination.js +1 -271
  108. package/dist/esm/components/db-phone-verify.d.ts +63 -0
  109. package/dist/esm/components/db-phone-verify.js +1 -0
  110. package/dist/esm/components/db-popover.js +1 -580
  111. package/dist/esm/components/db-progress-step.js +1 -166
  112. package/dist/esm/components/db-progress.js +1 -171
  113. package/dist/esm/components/db-progressbar.js +1 -72
  114. package/dist/esm/components/db-radio.js +1 -172
  115. package/dist/esm/components/db-random-gradient.js +1 -121
  116. package/dist/esm/components/db-range-slider.js +1 -811
  117. package/dist/esm/components/db-rating.js +1 -325
  118. package/dist/esm/components/db-reorder.js +1 -286
  119. package/dist/esm/components/db-resizable-panels.js +1 -282
  120. package/dist/esm/components/db-scroll-area.js +1 -126
  121. package/dist/esm/components/db-search-bar.js +1 -668
  122. package/dist/esm/components/db-section-app-download.js +1 -101
  123. package/dist/esm/components/db-section-contact.js +1 -102
  124. package/dist/esm/components/db-section-cta.js +1 -150
  125. package/dist/esm/components/db-section-faq.js +1 -120
  126. package/dist/esm/components/db-section-feature-grid.js +1 -137
  127. package/dist/esm/components/db-section-feature.js +1 -179
  128. package/dist/esm/components/db-section-hero.js +1 -243
  129. package/dist/esm/components/db-section-logo-cloud.js +1 -138
  130. package/dist/esm/components/db-section-pricing.js +1 -166
  131. package/dist/esm/components/db-section-stats.js +1 -129
  132. package/dist/esm/components/db-section-testimonials.js +1 -252
  133. package/dist/esm/components/db-section.js +1 -385
  134. package/dist/esm/components/db-segment-control.js +1 -231
  135. package/dist/esm/components/db-select.js +1 -571
  136. package/dist/esm/components/db-share-icons.js +1 -40
  137. package/dist/esm/components/db-share.js +1 -489
  138. package/dist/esm/components/db-sidebar.d.ts +20 -0
  139. package/dist/esm/components/db-sidebar.js +1 -1068
  140. package/dist/esm/components/db-skeleton.js +1 -94
  141. package/dist/esm/components/db-spinner.js +1 -102
  142. package/dist/esm/components/db-split-view.js +1 -297
  143. package/dist/esm/components/db-stack.js +1 -46
  144. package/dist/esm/components/db-stat.js +1 -218
  145. package/dist/esm/components/db-stepper.js +1 -398
  146. package/dist/esm/components/db-table.js +1 -412
  147. package/dist/esm/components/db-tabs.js +1 -335
  148. package/dist/esm/components/db-testimonial.d.ts +2 -0
  149. package/dist/esm/components/db-testimonial.js +1 -162
  150. package/dist/esm/components/db-textarea.js +1 -233
  151. package/dist/esm/components/db-time-picker.js +1 -523
  152. package/dist/esm/components/db-timeline.js +1 -174
  153. package/dist/esm/components/db-toast.js +1 -158
  154. package/dist/esm/components/db-toggle.js +1 -162
  155. package/dist/esm/components/db-toolbar.js +1 -94
  156. package/dist/esm/components/db-tooltip.js +1 -440
  157. package/dist/esm/components/db-top-banner.js +1 -121
  158. package/dist/esm/components/db-tutorial.js +1 -324
  159. package/dist/esm/components/db-two-factor.d.ts +71 -0
  160. package/dist/esm/components/db-two-factor.js +1 -0
  161. package/dist/esm/components/db-video-player.js +1 -503
  162. package/dist/esm/components/db-youtube-player.js +1 -194
  163. package/dist/esm/components/highlight-code.js +1 -65
  164. package/dist/esm/components/input-validation.js +1 -50
  165. package/dist/esm/components/marketing-block-host-utils.js +1 -9
  166. package/dist/esm/components/number-scrub.js +1 -117
  167. package/dist/esm/components/random-gradient-palettes.js +1 -67
  168. package/dist/esm/components/section-block-header-utils.js +1 -48
  169. package/dist/esm/helpers/confirm.js +1 -52
  170. package/dist/esm/helpers/toast.js +1 -73
  171. package/dist/esm/index.d.ts +16 -0
  172. package/dist/esm/index.js +1 -111
  173. package/dist/esm/node_modules/prismjs/components/prism-bash.js +1 -235
  174. package/dist/esm/node_modules/prismjs/components/prism-clike.js +1 -31
  175. package/dist/esm/node_modules/prismjs/components/prism-css.js +1 -64
  176. package/dist/esm/node_modules/prismjs/components/prism-javascript.js +1 -172
  177. package/dist/esm/node_modules/prismjs/components/prism-json.js +1 -27
  178. package/dist/esm/node_modules/prismjs/components/prism-jsx.js +1 -143
  179. package/dist/esm/node_modules/prismjs/components/prism-markup.js +1 -186
  180. package/dist/esm/node_modules/prismjs/components/prism-scss.js +1 -81
  181. package/dist/esm/node_modules/prismjs/components/prism-tsx.js +1 -15
  182. package/dist/esm/node_modules/prismjs/components/prism-typescript.js +1 -60
  183. package/dist/index.cjs +1 -55414
  184. package/dist/index.css +1 -1
  185. package/dist/index.d.ts +1125 -2
  186. package/dist/index.esm.js +1 -38284
  187. package/dist/react/chunks/lottie-14e7addb.js +1 -0
  188. package/dist/react/index.cjs +1 -56503
  189. package/dist/react/index.d.ts +1637 -21
  190. package/dist/react/index.esm.js +1 -39373
  191. package/package.json +2 -2
  192. package/dist/chunks/lottie-e273b936.js +0 -17032
  193. package/dist/react/chunks/lottie-72a4c6d4.js +0 -17032
@@ -0,0 +1,134 @@
1
+ import { DbIconName } from './db-icons.js';
2
+ import './db-button.js';
3
+ import './db-chip.js';
4
+
5
+ interface DbChatTool {
6
+ /** 고유 id (이벤트 detail 로 전달) */
7
+ id: string;
8
+ /** 표시 라벨 */
9
+ label: string;
10
+ /** 시작 아이콘 (DbIconName) */
11
+ icon?: DbIconName;
12
+ /** 활성 여부 */
13
+ active?: boolean;
14
+ }
15
+ interface DbChatAttachOption {
16
+ /** 고유 id (예: 'image', 'file', 'camera') */
17
+ id: string;
18
+ /** 표시 라벨 */
19
+ label: string;
20
+ /** 메뉴 아이콘 (DbIconName) */
21
+ icon?: DbIconName;
22
+ /** 파일 선택 시 accept 값. 지정하면 파일 선택창을 엽니다. 없으면 db-attach 이벤트만 발행 */
23
+ accept?: string;
24
+ /** 카메라/캡처 (모바일). 'user' | 'environment' */
25
+ capture?: 'user' | 'environment';
26
+ /** 다중 선택 허용 */
27
+ multiple?: boolean;
28
+ /** 보조 설명 (선택) */
29
+ description?: string;
30
+ }
31
+ interface DbChatAttachment {
32
+ id: string;
33
+ name: string;
34
+ /** 바이트 크기 */
35
+ size: number;
36
+ /** MIME 타입 */
37
+ mime: string;
38
+ /** 표시 종류 (아이콘 결정) */
39
+ kind: 'image' | 'video' | 'file';
40
+ /** 원본 File (파일 선택 시) */
41
+ file?: File;
42
+ /** 이미지 미리보기 objectURL */
43
+ preview?: string;
44
+ }
45
+ /**
46
+ * AI 채팅 입력 컴포저. 채팅 시작 전/중 모두에서 사용합니다.
47
+ * 자동 높이 textarea + 툴바(첨부·툴 토글·음성·전송)로 구성되며,
48
+ * 전송 버튼은 `db-button`, 툴 토글·첨부 미리보기는 `db-chip` 을 재사용합니다.
49
+ *
50
+ * - 툴 토글: `db-chip` 의 `default`↔`primary` variant 로 선택 상태를 표현합니다.
51
+ * - 첨부: 첨부 버튼을 누르면 드롭다운이 열려 이미지·동영상·파일·카메라 등
52
+ * 여러 종류를 선택할 수 있고, 고른 파일은 삭제 가능한 칩으로 미리보기됩니다.
53
+ * - 전송 중(`streaming`)에는 전송 버튼이 정지 버튼으로 바뀝니다.
54
+ *
55
+ * @example
56
+ * const c = document.querySelector('db-chat-composer');
57
+ * c.tools = [{ id:'reason', label:'추론', icon:'bulb' }];
58
+ * c.addEventListener('db-send', (e) => console.log(e.detail.value, e.detail.attachments));
59
+ */
60
+ declare class DbChatComposer extends HTMLElement {
61
+ static get observedAttributes(): string[];
62
+ private built;
63
+ private _tools;
64
+ private _attachOptions;
65
+ private _attachments;
66
+ private attachMenuOpen;
67
+ private field;
68
+ private attachmentsEl;
69
+ private toolsEl;
70
+ private attachWrap;
71
+ private attachBtn;
72
+ private attachMenu;
73
+ private fileInput;
74
+ private micBtn;
75
+ private sendBtn;
76
+ get value(): string;
77
+ set value(v: string);
78
+ get streaming(): boolean;
79
+ set streaming(v: boolean);
80
+ get disabled(): boolean;
81
+ set disabled(v: boolean);
82
+ /** 툴 토글 목록 (JSON 프로퍼티) */
83
+ get tools(): DbChatTool[];
84
+ set tools(list: DbChatTool[]);
85
+ /** 첨부 드롭다운 옵션 목록 */
86
+ get attachOptions(): DbChatAttachOption[];
87
+ set attachOptions(list: DbChatAttachOption[]);
88
+ /** 현재 첨부 목록 (읽기 전용 복사본) */
89
+ get attachments(): DbChatAttachment[];
90
+ connectedCallback(): void;
91
+ disconnectedCallback(): void;
92
+ attributeChangedCallback(name: string): void;
93
+ private build;
94
+ private makeIconButton;
95
+ private sync;
96
+ private renderTools;
97
+ private renderAttachMenu;
98
+ private renderAttachments;
99
+ private renderSendButton;
100
+ private syncSendState;
101
+ private autoGrow;
102
+ private handleAttachClick;
103
+ private openAttachMenu;
104
+ private closeAttachMenu;
105
+ private handleOutsideClick;
106
+ private chooseAttachOption;
107
+ private handleFilePicked;
108
+ private addFile;
109
+ private attachmentMeta;
110
+ private handleInput;
111
+ private handleKeyDown;
112
+ private handleSendClick;
113
+ private submit;
114
+ /** 입력 필드를 비웁니다. */
115
+ clear(): void;
116
+ /** 첨부를 하나 제거합니다. */
117
+ removeAttachment(id: string): void;
118
+ /** 모든 첨부를 제거합니다. */
119
+ clearAttachments(): void;
120
+ /** 프로그램적으로 첨부를 추가합니다(파일 또는 메타). */
121
+ addAttachment(input: File | Omit<DbChatAttachment, 'id' | 'kind'>): void;
122
+ /** 입력 필드에 포커스합니다. */
123
+ focusInput(): void;
124
+ /** 전송 중(정지 버튼) 상태를 설정합니다. */
125
+ setStreaming(streaming: boolean): void;
126
+ }
127
+ declare global {
128
+ interface HTMLElementTagNameMap {
129
+ 'db-chat-composer': DbChatComposer;
130
+ }
131
+ }
132
+
133
+ export { DbChatComposer };
134
+ export type { DbChatAttachOption, DbChatAttachment, DbChatTool };
@@ -0,0 +1 @@
1
+ import{isDbIconName as t,createDbIcon as e}from"./db-icons.js";import"./db-button.js";import"./db-chip.js";const s=[{id:"image",label:"사진·이미지",icon:"gallery",accept:"image/*",multiple:!0},{id:"video",label:"동영상",icon:"video",accept:"video/*",multiple:!0},{id:"file",label:"문서·파일",icon:"file-new",accept:"",multiple:!0},{id:"camera",label:"카메라 촬영",icon:"camera",accept:"image/*",capture:"environment"}];function kindFromMime(t){return t.startsWith("image/")?"image":t.startsWith("video/")?"video":"file"}const i={image:"gallery",video:"video",file:"file-new"};let a=0;class DbChatComposer extends HTMLElement{constructor(){super(...arguments),this.built=!1,this._tools=[],this._attachOptions=s,this._attachments=[],this.attachMenuOpen=!1,this.handleAttachClick=()=>{this.disabled||(this.attachMenuOpen?this.closeAttachMenu():this.openAttachMenu())},this.handleOutsideClick=t=>{this.attachMenuOpen&&(this.attachWrap.contains(t.target)||this.closeAttachMenu())},this.handleFilePicked=()=>{const t=Array.from(this.fileInput.files||[]);if(!t.length)return;const e=this.fileInput.dataset.type||"file";t.forEach(t=>this.addFile(t)),this.dispatchEvent(new CustomEvent("db-attach-files",{detail:{type:e,files:t},bubbles:!0,composed:!0}))},this.handleInput=()=>{this.autoGrow(),this.syncSendState(),this.dispatchEvent(new CustomEvent("db-input",{detail:{value:this.field.value},bubbles:!0,composed:!0}))},this.handleKeyDown=t=>{if("Escape"===t.key&&this.attachMenuOpen)return t.preventDefault(),void this.closeAttachMenu();"Enter"!==t.key||t.shiftKey||t.isComposing||(t.preventDefault(),this.submit())},this.handleSendClick=()=>{this.disabled||(this.streaming?this.dispatchEvent(new CustomEvent("db-stop",{bubbles:!0,composed:!0})):this.submit())}}static get observedAttributes(){return["placeholder","value","disabled","streaming","voice","attach","max-length","send-label"]}get value(){return this.field?.value??this.getAttribute("value")??""}set value(t){this.field&&(this.field.value=t,this.autoGrow(),this.syncSendState()),t?this.setAttribute("value",t):this.removeAttribute("value")}get streaming(){return this.hasAttribute("streaming")}set streaming(t){this.toggleAttribute("streaming",t)}get disabled(){return this.hasAttribute("disabled")}set disabled(t){this.toggleAttribute("disabled",t)}get tools(){return this._tools}set tools(t){this._tools=Array.isArray(t)?t:[],this.built&&this.renderTools()}get attachOptions(){return this._attachOptions}set attachOptions(t){this._attachOptions=Array.isArray(t)&&t.length?t:s,this.built&&this.renderAttachMenu()}get attachments(){return this._attachments.map(t=>({...t}))}connectedCallback(){this.built||this.build(),this.sync(),requestAnimationFrame(()=>this.autoGrow()),document.addEventListener("click",this.handleOutsideClick,!0)}disconnectedCallback(){document.removeEventListener("click",this.handleOutsideClick,!0),this._attachments.forEach(t=>t.preview&&URL.revokeObjectURL(t.preview))}attributeChangedCallback(t){if(this.built){if("value"===t){const t=this.getAttribute("value")||"";return this.field.value!==t&&(this.field.value=t,this.autoGrow()),void this.syncSendState()}"streaming"!==t?this.sync():this.renderSendButton()}}build(){this.built=!0,this.className="designbase-wc-chat-composer",this.replaceChildren();const t=document.createElement("div");t.className="designbase-wc-chat-composer__surface",this.attachmentsEl=document.createElement("div"),this.attachmentsEl.className="designbase-wc-chat-composer__attachments",this.attachmentsEl.hidden=!0;const e=document.createElement("div");e.className="designbase-wc-chat-composer__field-row",this.field=document.createElement("textarea"),this.field.className="designbase-wc-chat-composer__field",this.field.rows=1,this.field.addEventListener("input",this.handleInput),this.field.addEventListener("keydown",this.handleKeyDown),e.append(this.field);const s=document.createElement("div");s.className="designbase-wc-chat-composer__toolbar";const i=document.createElement("div");i.className="designbase-wc-chat-composer__leading",this.attachWrap=document.createElement("div"),this.attachWrap.className="designbase-wc-chat-composer__attach-wrap",this.attachBtn=document.createElement("db-button"),this.attachBtn.className="designbase-wc-chat-composer__attach",this.attachBtn.setAttribute("variant","ghost"),this.attachBtn.setAttribute("icon-only",""),this.attachBtn.setAttribute("radius","pill"),this.attachBtn.setAttribute("size","m"),this.attachBtn.setAttribute("start-icon","paperclip"),this.attachBtn.setAttribute("aria-label","첨부"),this.attachBtn.setAttribute("aria-haspopup","menu"),this.attachBtn.addEventListener("db-click",this.handleAttachClick),this.attachMenu=document.createElement("div"),this.attachMenu.className="designbase-wc-chat-composer__attach-menu",this.attachMenu.setAttribute("role","menu"),this.attachMenu.hidden=!0,this.attachWrap.append(this.attachBtn,this.attachMenu),this.fileInput=document.createElement("input"),this.fileInput.type="file",this.fileInput.className="designbase-wc-chat-composer__file-input",this.fileInput.hidden=!0,this.fileInput.addEventListener("change",this.handleFilePicked),this.toolsEl=document.createElement("div"),this.toolsEl.className="designbase-wc-chat-composer__tools",i.append(this.attachWrap,this.toolsEl);const a=document.createElement("div");a.className="designbase-wc-chat-composer__trailing",this.micBtn=this.makeIconButton("microphone","음성 입력","db-voice"),this.micBtn.classList.add("designbase-wc-chat-composer__mic"),this.sendBtn=document.createElement("db-button"),this.sendBtn.className="designbase-wc-chat-composer__send",this.sendBtn.setAttribute("icon-only",""),this.sendBtn.setAttribute("radius","pill"),this.sendBtn.setAttribute("size","m"),this.sendBtn.addEventListener("db-click",this.handleSendClick),a.append(this.micBtn,this.sendBtn),s.append(i,a),t.append(this.attachmentsEl,e,s),this.append(t,this.fileInput),this.renderTools(),this.renderAttachMenu(),this.renderSendButton()}makeIconButton(t,e,s){const i=document.createElement("db-button");return i.setAttribute("variant","ghost"),i.setAttribute("icon-only",""),i.setAttribute("radius","pill"),i.setAttribute("size","m"),i.setAttribute("start-icon",t),i.setAttribute("aria-label",e),i.addEventListener("db-click",()=>{this.disabled||this.dispatchEvent(new CustomEvent(s,{bubbles:!0,composed:!0}))}),i}sync(){this.classList.toggle("designbase-wc-chat-composer--disabled",this.disabled),this.classList.toggle("designbase-wc-chat-composer--streaming",this.streaming),this.field.placeholder=this.getAttribute("placeholder")||"메시지를 입력하세요…",this.field.disabled=this.disabled;const t=this.getAttribute("max-length");t?this.field.maxLength=Number(t):this.field.removeAttribute("maxlength"),this.attachWrap.hidden=!this.hasAttribute("attach"),this.micBtn.hidden=!this.hasAttribute("voice"),this.hasAttribute("attach")||this.closeAttachMenu(),this.autoGrow(),this.syncSendState()}renderTools(){this.toolsEl.replaceChildren();for(const e of this._tools){const s=document.createElement("db-chip");s.setAttribute("label",e.label),s.setAttribute("size","s"),s.setAttribute("clickable",""),s.setAttribute("variant",e.active?"primary":"default"),e.icon&&t(e.icon)&&s.setAttribute("start-icon",e.icon),s.setAttribute("aria-pressed",String(!!e.active)),s.addEventListener("db-click",()=>{this.disabled||(e.active=!e.active,s.setAttribute("variant",e.active?"primary":"default"),s.setAttribute("aria-pressed",String(!!e.active)),this.dispatchEvent(new CustomEvent("db-tool-toggle",{detail:{id:e.id,active:!!e.active},bubbles:!0,composed:!0})))}),this.toolsEl.append(s)}}renderAttachMenu(){this.attachMenu.replaceChildren();for(const s of this._attachOptions){const i=document.createElement("button");i.type="button",i.className="designbase-wc-chat-composer__attach-item",i.setAttribute("role","menuitem");const a=document.createElement("span");a.className="designbase-wc-chat-composer__attach-item-icon",s.icon&&t(s.icon)&&a.append(e(s.icon,18));const n=document.createElement("span");n.className="designbase-wc-chat-composer__attach-item-text";const c=document.createElement("span");if(c.className="designbase-wc-chat-composer__attach-item-label",c.textContent=s.label,n.append(c),s.description){const t=document.createElement("span");t.className="designbase-wc-chat-composer__attach-item-desc",t.textContent=s.description,n.append(t)}i.append(a,n),i.addEventListener("click",()=>this.chooseAttachOption(s)),this.attachMenu.append(i)}}renderAttachments(){this.attachmentsEl.replaceChildren(),this.attachmentsEl.hidden=0===this._attachments.length;for(const t of this._attachments){const e=document.createElement("db-chip");e.setAttribute("label",t.name),e.setAttribute("size","s"),e.setAttribute("variant","neutral"),e.setAttribute("deletable",""),e.setAttribute("start-icon",i[t.kind]),e.addEventListener("db-delete",()=>this.removeAttachment(t.id)),this.attachmentsEl.append(e)}this.syncSendState()}renderSendButton(){const t=this.streaming;this.sendBtn.setAttribute("variant",t?"secondary":"primary"),this.sendBtn.setAttribute("start-icon",t?"stop":"send"),this.sendBtn.setAttribute("aria-label",t?"생성 중지":"전송"),this.syncSendState()}syncSendState(){if(!this.sendBtn)return;const t=0===this.value.trim().length&&0===this._attachments.length,e=this.disabled||!this.streaming&&t;this.sendBtn.toggleAttribute("disabled",e)}autoGrow(){this.field&&0!==this.field.clientWidth&&(this.field.style.height="auto",this.field.style.height=`${Math.min(this.field.scrollHeight,200)}px`)}openAttachMenu(){this.attachMenuOpen=!0,this.attachMenu.hidden=!1,this.attachBtn.setAttribute("aria-expanded","true"),this.classList.add("designbase-wc-chat-composer--attach-open"),requestAnimationFrame(()=>this.attachMenu.classList.add("designbase-wc-chat-composer__attach-menu--visible"))}closeAttachMenu(){this.attachMenuOpen&&(this.attachMenuOpen=!1,this.attachMenu.hidden=!0,this.attachMenu.classList.remove("designbase-wc-chat-composer__attach-menu--visible"),this.attachBtn.setAttribute("aria-expanded","false"),this.classList.remove("designbase-wc-chat-composer--attach-open"))}chooseAttachOption(t){this.closeAttachMenu(),this.dispatchEvent(new CustomEvent("db-attach",{detail:{type:t.id},bubbles:!0,composed:!0})),void 0!==t.accept&&(this.fileInput.value="",this.fileInput.accept=t.accept,this.fileInput.multiple=!!t.multiple,t.capture?this.fileInput.setAttribute("capture",t.capture):this.fileInput.removeAttribute("capture"),this.fileInput.dataset.type=t.id,this.fileInput.click())}addFile(t){const e=kindFromMime(t.type),s={id:`att-${a+=1}`,name:t.name,size:t.size,mime:t.type,kind:e,file:t,preview:"image"===e?URL.createObjectURL(t):void 0};this._attachments.push(s),this.renderAttachments(),this.dispatchEvent(new CustomEvent("db-attachment-add",{detail:{attachment:this.attachmentMeta(s)},bubbles:!0,composed:!0}))}attachmentMeta(t){return{id:t.id,name:t.name,size:t.size,mime:t.mime,kind:t.kind,file:t.file}}submit(){const t=this.field.value.trim();if(!t&&0===this._attachments.length||this.disabled||this.streaming)return;const e=new CustomEvent("db-send",{detail:{value:t,attachments:this._attachments.map(t=>this.attachmentMeta(t))},bubbles:!0,composed:!0,cancelable:!0});this.dispatchEvent(e)&&(this.clear(),this.clearAttachments())}clear(){this.value=""}removeAttachment(t){const e=this._attachments.find(e=>e.id===t);e&&(e.preview&&URL.revokeObjectURL(e.preview),this._attachments=this._attachments.filter(e=>e.id!==t),this.renderAttachments(),this.dispatchEvent(new CustomEvent("db-attachment-remove",{detail:{id:t},bubbles:!0,composed:!0})))}clearAttachments(){this._attachments.forEach(t=>t.preview&&URL.revokeObjectURL(t.preview)),this._attachments=[],this.renderAttachments()}addAttachment(t){if(t instanceof File)return void this.addFile(t);const e=kindFromMime(t.mime);this._attachments.push({id:`att-${a+=1}`,kind:e,...t}),this.renderAttachments()}focusInput(){this.field?.focus()}setStreaming(t){this.streaming=t}}customElements.get("db-chat-composer")||customElements.define("db-chat-composer",DbChatComposer);export{DbChatComposer};
@@ -0,0 +1,47 @@
1
+ import './db-avatar.js';
2
+
3
+ interface DbChatListItem {
4
+ id: string;
5
+ /** 상대(대화방) 이름 */
6
+ name: string;
7
+ avatarSrc?: string;
8
+ avatarInitials?: string;
9
+ /** 마지막 메시지 미리보기 */
10
+ preview?: string;
11
+ /** 시간 라벨 (예: '방금', '어제') */
12
+ time?: string;
13
+ /** 안 읽은 수 */
14
+ unread?: number;
15
+ /** 온라인 표시 */
16
+ online?: boolean;
17
+ }
18
+ /**
19
+ * 사람 간 대화(메신저) 목록. 아바타 + 이름 + 미리보기 + 시간 + 안읽음 배지의 행 리스트로,
20
+ * 아바타는 `db-avatar` 를 재사용합니다. AI 대화 이력은 `db-ai-chat-list` 를 쓰세요.
21
+ *
22
+ * @fires db-select - 대화 선택 (detail: { id })
23
+ *
24
+ * @example
25
+ * const list = document.querySelector('db-chat-list');
26
+ * list.items = [{ id:'1', name:'디지털 상담원', preview:'무엇을 도와드릴까요?', time:'방금', unread:1, online:true }];
27
+ * list.addEventListener('db-select', (e) => console.log(e.detail.id));
28
+ */
29
+ declare class DbChatList extends HTMLElement {
30
+ static get observedAttributes(): string[];
31
+ private _items;
32
+ get items(): DbChatListItem[];
33
+ set items(v: DbChatListItem[]);
34
+ get selectedId(): string;
35
+ set selectedId(v: string);
36
+ connectedCallback(): void;
37
+ attributeChangedCallback(): void;
38
+ private render;
39
+ }
40
+ declare global {
41
+ interface HTMLElementTagNameMap {
42
+ 'db-chat-list': DbChatList;
43
+ }
44
+ }
45
+
46
+ export { DbChatList };
47
+ export type { DbChatListItem };
@@ -0,0 +1 @@
1
+ import"./db-avatar.js";class DbChatList extends HTMLElement{constructor(){super(...arguments),this._items=[]}static get observedAttributes(){return["selected-id"]}get items(){return this._items}set items(t){this._items=Array.isArray(t)?t:[],this.render()}get selectedId(){return this.getAttribute("selected-id")||""}set selectedId(t){t?this.setAttribute("selected-id",t):this.removeAttribute("selected-id")}connectedCallback(){if(!this._items.length&&this.hasAttribute("items"))try{this._items=JSON.parse(this.getAttribute("items")||"[]")}catch{this._items=[]}this.render()}attributeChangedCallback(){this.render()}render(){this.className="designbase-wc-chat-list",this.setAttribute("role","list"),this.replaceChildren();const t=this.selectedId;for(const e of this._items){const s=document.createElement("button");s.type="button",s.className=["designbase-wc-chat-list__item",e.id===t&&"designbase-wc-chat-list__item--selected"].filter(Boolean).join(" "),s.setAttribute("role","listitem");const a=document.createElement("db-avatar");a.className="designbase-wc-chat-list__avatar",a.setAttribute("size","l"),a.setAttribute("variant","primary"),e.avatarSrc?a.setAttribute("src",e.avatarSrc):e.avatarInitials&&a.setAttribute("initials",e.avatarInitials),e.online&&a.setAttribute("status","online");const i=document.createElement("span");i.className="designbase-wc-chat-list__body";const n=document.createElement("span");n.className="designbase-wc-chat-list__top";const c=document.createElement("span");if(c.className="designbase-wc-chat-list__name",c.textContent=e.name,n.append(c),e.time){const t=document.createElement("span");t.className="designbase-wc-chat-list__time",t.textContent=e.time,n.append(t)}const r=document.createElement("span");r.className="designbase-wc-chat-list__bottom";const d=document.createElement("span");if(d.className="designbase-wc-chat-list__preview",d.textContent=e.preview||"",r.append(d),e.unread&&e.unread>0){const t=document.createElement("span");t.className="designbase-wc-chat-list__unread",t.textContent=e.unread>99?"99+":String(e.unread),r.append(t)}i.append(n,r),s.append(a,i),s.addEventListener("click",()=>{this.selectedId=e.id,this.dispatchEvent(new CustomEvent("db-select",{detail:{id:e.id},bubbles:!0,composed:!0}))}),this.append(s)}}}customElements.get("db-chat-list")||customElements.define("db-chat-list",DbChatList);export{DbChatList};
@@ -0,0 +1,39 @@
1
+ import './db-avatar.js';
2
+
3
+ declare const ROLES: readonly ["user", "assistant", "system"];
4
+ type Role = (typeof ROLES)[number];
5
+ /**
6
+ * 채팅 메시지 한 줄. 본문은 라이트 DOM 자식으로 전달하며 컴포넌트가 감쌉니다.
7
+ * 아바타는 `db-avatar` 를 재사용합니다.
8
+ *
9
+ * @example
10
+ * <db-chat-message role="assistant" name="Orion" actions="copy,regenerate,like,dislike">
11
+ * 안녕하세요! 무엇을 도와드릴까요?
12
+ * </db-chat-message>
13
+ */
14
+ declare class DbChatMessage extends HTMLElement {
15
+ static get observedAttributes(): string[];
16
+ private built;
17
+ private contentEl;
18
+ private avatarWrap;
19
+ private headerEl;
20
+ private nameEl;
21
+ private timeEl;
22
+ private actionsEl;
23
+ get role(): Role;
24
+ set role(v: Role);
25
+ connectedCallback(): void;
26
+ attributeChangedCallback(name: string): void;
27
+ private build;
28
+ private sync;
29
+ private renderAvatar;
30
+ private renderActions;
31
+ private handleAction;
32
+ }
33
+ declare global {
34
+ interface HTMLElementTagNameMap {
35
+ 'db-chat-message': DbChatMessage;
36
+ }
37
+ }
38
+
39
+ export { DbChatMessage };
@@ -0,0 +1 @@
1
+ import{createDbIcon as t,isDbIconName as e}from"./db-icons.js";import"./db-avatar.js";const s=["user","assistant","system"],a={copy:"copy",regenerate:"refresh",like:"thumb-up",dislike:"thumb-down",edit:"edit"},i={copy:"복사",regenerate:"다시 생성",like:"좋아요",dislike:"싫어요",edit:"편집"};class DbChatMessage extends HTMLElement{constructor(){super(...arguments),this.built=!1}static get observedAttributes(){return["role","name","avatar-src","avatar-initials","timestamp","status","actions","variant"]}get role(){return s.includes(this.getAttribute("role"))?this.getAttribute("role"):"assistant"}set role(t){this.setAttribute("role",t)}connectedCallback(){this.built||this.build(),this.sync()}attributeChangedCallback(t){this.built&&("actions"===t&&this.renderActions(),this.sync())}build(){for(this.built=!0,this.contentEl=document.createElement("div"),this.contentEl.className="designbase-wc-chat-message__content";this.firstChild;)this.contentEl.append(this.firstChild);this.avatarWrap=document.createElement("div"),this.avatarWrap.className="designbase-wc-chat-message__avatar";const t=document.createElement("div");t.className="designbase-wc-chat-message__bubble",this.headerEl=document.createElement("div"),this.headerEl.className="designbase-wc-chat-message__header",this.nameEl=document.createElement("span"),this.nameEl.className="designbase-wc-chat-message__name",this.timeEl=document.createElement("span"),this.timeEl.className="designbase-wc-chat-message__time",this.headerEl.append(this.nameEl,this.timeEl),this.actionsEl=document.createElement("div"),this.actionsEl.className="designbase-wc-chat-message__actions",t.append(this.headerEl,this.contentEl,this.actionsEl),this.append(this.avatarWrap,t),this.renderAvatar(),this.renderActions()}sync(){this.className=["designbase-wc-chat-message",`designbase-wc-chat-message--${this.role}`,`designbase-wc-chat-message--${this.getAttribute("variant")||"bubble"}`,this.getAttribute("status")&&`designbase-wc-chat-message--${this.getAttribute("status")}`].filter(Boolean).join(" ");const t=this.getAttribute("name")||"";this.nameEl.textContent=t,this.nameEl.hidden=!t;const e=this.getAttribute("timestamp")||"";this.timeEl.textContent=e,this.timeEl.hidden=!e,this.headerEl.hidden=!t&&!e,this.renderAvatar()}renderAvatar(){const t=this.getAttribute("avatar-src"),e=this.getAttribute("avatar-initials"),s="assistant"===this.role||Boolean(t||e);if(this.avatarWrap.hidden=!s,!s)return void this.avatarWrap.replaceChildren();let a=this.avatarWrap.querySelector("db-avatar");a||(a=document.createElement("db-avatar"),this.avatarWrap.append(a)),a.setAttribute("size","s"),t?a.setAttribute("src",t):a.removeAttribute("src"),e?a.setAttribute("initials",e):a.removeAttribute("initials"),"assistant"!==this.role||t||e||a.setAttribute("variant","primary")}renderActions(){this.actionsEl.replaceChildren();const s=(this.getAttribute("actions")||"").split(",").map(t=>t.trim()).filter(Boolean);for(const n of s){const s=document.createElement("button");s.type="button",s.className="designbase-wc-chat-message__action",s.setAttribute("aria-label",i[n]||n);const r=a[n];s.append(t(r&&e(r)?r:"sparkle",16)),s.addEventListener("click",()=>this.handleAction(n,s)),this.actionsEl.append(s)}this.actionsEl.hidden=0===s.length}handleAction(t,e){if("copy"===t){const t=this.contentEl.textContent||"";navigator.clipboard?.writeText(t).catch(()=>{})}"like"!==t&&"dislike"!==t||e.classList.toggle("designbase-wc-chat-message__action--active"),this.dispatchEvent(new CustomEvent("db-action",{detail:{action:t,text:this.contentEl.textContent||""},bubbles:!0,composed:!0}))}}customElements.get("db-chat-message")||customElements.define("db-chat-message",DbChatMessage);export{DbChatMessage};
@@ -0,0 +1,99 @@
1
+ declare const SIZES: readonly ["xs", "s", "m", "l", "xl"];
2
+ declare const VARIANTS: readonly ["aurora", "ocean", "emerald", "violet", "mono"];
3
+ declare const STATES: readonly ["idle", "thinking", "listening"];
4
+ declare const THEMES: readonly ["auto", "light", "dark"];
5
+ type OrbSize = (typeof SIZES)[number];
6
+ type OrbVariant = (typeof VARIANTS)[number];
7
+ type OrbState = (typeof STATES)[number];
8
+ type OrbTheme = (typeof THEMES)[number];
9
+ /**
10
+ * AI 어시스턴트를 상징하는 캔버스 기반 애니메이션 오브(구체).
11
+ * 광택 있는 3D 구체 위에 유체처럼 흐르는 내부 광원 + 프레넬 림 라이트 + 스페큘러 하이라이트로
12
+ * 고품질 오브를 렌더링합니다. `state` 에 따라 모양(진폭·난류)·색(밝기)·인터랙션(리플)이 바뀌며,
13
+ * 포인터를 올리면 하이라이트와 내부 광원이 살짝 따라옵니다. 순수 장식이라 `aria-hidden` 입니다.
14
+ *
15
+ * @example
16
+ * <db-chat-orb variant="aurora" state="thinking"></db-chat-orb>
17
+ *
18
+ * @example
19
+ * const orb = document.querySelector('db-chat-orb');
20
+ * orb.state = 'listening';
21
+ * orb.minScale = 0.86;
22
+ * orb.maxScale = 1.2;
23
+ * orb.audioStream = await navigator.mediaDevices.getUserMedia({ audio: true });
24
+ */
25
+ declare class DbChatOrb extends HTMLElement {
26
+ static get observedAttributes(): string[];
27
+ private canvas;
28
+ private ctx;
29
+ private dpr;
30
+ private cssW;
31
+ private cssH;
32
+ private t0;
33
+ private lastFrame;
34
+ private resizeObs;
35
+ private io;
36
+ private themeObserver;
37
+ private themeMedia;
38
+ private visible;
39
+ private hover;
40
+ private ripples;
41
+ private lastAutoRipple;
42
+ private lastVoiceRipple;
43
+ private previousVoiceLevel;
44
+ private displayedVoiceLevel;
45
+ private inputStream;
46
+ private audioContext;
47
+ private audioSource;
48
+ private analyser;
49
+ private analyserData;
50
+ get size(): OrbSize;
51
+ set size(v: OrbSize);
52
+ get variant(): OrbVariant;
53
+ set variant(v: OrbVariant);
54
+ get state(): OrbState;
55
+ set state(v: OrbState);
56
+ get theme(): OrbTheme;
57
+ set theme(v: OrbTheme);
58
+ /** 외부 오디오 분석기가 전달하는 정규화 음성 레벨(0–1). */
59
+ get voiceLevel(): number;
60
+ set voiceLevel(v: number);
61
+ /** listening 무음 구간의 최소 구체 배율. */
62
+ get minScale(): number;
63
+ set minScale(v: number);
64
+ /** listening 최대 음량 구간의 최대 구체 배율. */
65
+ get maxScale(): number;
66
+ set maxScale(v: number);
67
+ /**
68
+ * 실제 마이크/통화 MediaStream. 내부 AnalyserNode로 RMS를 계산하며 오디오를 재생하거나
69
+ * 전달받은 트랙을 stop()하지 않습니다. null을 할당하면 분석만 해제합니다.
70
+ */
71
+ get audioStream(): MediaStream | null;
72
+ set audioStream(stream: MediaStream | null);
73
+ connectedCallback(): void;
74
+ disconnectedCallback(): void;
75
+ attributeChangedCallback(name: string): void;
76
+ private setVisible;
77
+ private syncClasses;
78
+ private build;
79
+ private onPointerMove;
80
+ private onPointerLeave;
81
+ private onPointerDown;
82
+ private addRipple;
83
+ private observeTheme;
84
+ private onThemeChange;
85
+ private isDarkContext;
86
+ private startAudioAnalysis;
87
+ private stopAudioAnalysis;
88
+ private sampleVoiceLevel;
89
+ private resize;
90
+ /** 공유 틱커가 호출 — 한 프레임 렌더 */
91
+ frame(now: number): void;
92
+ }
93
+ declare global {
94
+ interface HTMLElementTagNameMap {
95
+ 'db-chat-orb': DbChatOrb;
96
+ }
97
+ }
98
+
99
+ export { DbChatOrb };
@@ -0,0 +1 @@
1
+ const t=["xs","s","m","l","xl"],e=["aurora","ocean","emerald","violet","mono"],i=["idle","thinking","listening"],s=["auto","light","dark"];function pick(t,e,i){return t.includes(e)?e:i}const a={aurora:{core:[22,20,52],lights:[[236,92,220],[70,140,250],[42,208,158]],rim:[199,210,254],glow:[120,110,240]},ocean:{core:[10,32,62],lights:[[56,189,248],[34,211,238],[96,165,250]],rim:[165,243,252],glow:[59,130,246]},emerald:{core:[6,42,38],lights:[[52,211,153],[74,222,128],[163,230,53]],rim:[187,247,208],glow:[34,197,94]},violet:{core:[40,12,82],lights:[[192,132,252],[168,85,247],[240,171,252]],rim:[245,208,254],glow:[139,92,246]},mono:{core:[30,34,44],lights:[[229,231,235],[156,163,175],[249,250,251]],rim:[255,255,255],glow:[156,163,175]}},r={aurora:{core:[240,240,250],lights:[[235,96,224],[74,144,246],[26,196,150]],rim:[150,130,210],glow:[138,128,244]},ocean:{core:[226,248,255],lights:[[14,165,233],[34,211,238],[96,165,250]],rim:[64,150,181],glow:[56,189,248]},emerald:{core:[228,250,240],lights:[[16,185,129],[74,222,128],[163,230,53]],rim:[57,145,111],glow:[52,211,153]},violet:{core:[246,233,255],lights:[[168,85,247],[217,70,239],[240,171,252]],rim:[143,91,183],glow:[192,132,252]},mono:{core:[243,246,250],lights:[[148,163,184],[203,213,225],[255,255,255]],rim:[100,116,139],glow:[148,163,184]}},n={idle:{speed:1.55,amp:.38,turb:.95,glow:1.15,breatheSpeed:1.9,breatheAmt:.055,lightAlpha:.95,autoRipple:0},thinking:{speed:3.2,amp:.5,turb:1.45,glow:1.85,breatheSpeed:4.2,breatheAmt:.075,lightAlpha:1.35,autoRipple:0},listening:{speed:1.45,amp:.32,turb:.9,glow:1.2,breatheSpeed:2.1,breatheAmt:.035,lightAlpha:.95,autoRipple:1.7}};function rgba(t,e){return`rgba(${t[0]},${t[1]},${t[2]},${e})`}function mix(t,e,i){return[t[0]+(e[0]-t[0])*i,t[1]+(e[1]-t[1])*i,t[2]+(e[2]-t[2])*i]}function clamp(t,e,i){return Math.min(i,Math.max(e,t))}function numberAttribute(t,e,i,s,a){const r=t.getAttribute(e);if(null===r||""===r.trim())return i;const n=Number(r);return Number.isFinite(n)?clamp(n,s,a):i}const h=2*Math.PI,o="undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia("(prefers-reduced-motion: reduce)").matches,l=new Set;let c=0;function tick(t){l.forEach(e=>e.frame(t)),c=l.size?requestAnimationFrame(tick):0}class DbChatOrb extends HTMLElement{constructor(){super(...arguments),this.dpr=1,this.cssW=0,this.cssH=0,this.t0=0,this.lastFrame=0,this.resizeObs=null,this.io=null,this.themeObserver=null,this.themeMedia=null,this.visible=!1,this.hover={x:0,y:0,tx:0,ty:0},this.ripples=[],this.lastAutoRipple=0,this.lastVoiceRipple=0,this.previousVoiceLevel=0,this.displayedVoiceLevel=0,this.inputStream=null,this.audioContext=null,this.audioSource=null,this.analyser=null,this.analyserData=null,this.onPointerMove=t=>{const e=this.getBoundingClientRect();e.width&&(this.hover.tx=Math.max(-1,Math.min(1,2*((t.clientX-e.left)/e.width-.5))),this.hover.ty=Math.max(-1,Math.min(1,2*((t.clientY-e.top)/e.height-.5))))},this.onPointerLeave=()=>{this.hover.tx=0,this.hover.ty=0},this.onPointerDown=()=>{this.addRipple(performance.now(),1)},this.onThemeChange=()=>{o&&this.frame(performance.now())}}static get observedAttributes(){return["size","variant","state","theme","voice-level","min-scale","max-scale"]}get size(){return pick(t,this.getAttribute("size"),"l")}set size(t){this.setAttribute("size",t)}get variant(){return pick(e,this.getAttribute("variant"),"aurora")}set variant(t){this.setAttribute("variant",t)}get state(){return pick(i,this.getAttribute("state"),"idle")}set state(t){this.setAttribute("state",t)}get theme(){return pick(s,this.getAttribute("theme"),"auto")}set theme(t){this.setAttribute("theme",t)}get voiceLevel(){return numberAttribute(this,"voice-level",0,0,1)}set voiceLevel(t){const e=Number(t);this.setAttribute("voice-level",String(clamp(Number.isFinite(e)?e:0,0,1)))}get minScale(){return numberAttribute(this,"min-scale",.88,.5,1.5)}set minScale(t){const e=Number(t);this.setAttribute("min-scale",String(clamp(Number.isFinite(e)?e:.88,.5,1.5)))}get maxScale(){return Math.max(this.minScale,numberAttribute(this,"max-scale",1.18,.5,1.7))}set maxScale(t){const e=Number(t);this.setAttribute("max-scale",String(clamp(Number.isFinite(e)?e:1.18,.5,1.7)))}get audioStream(){return this.inputStream}set audioStream(t){(t!==this.inputStream||null!==t&&!this.analyser)&&(this.stopAudioAnalysis(),this.inputStream=t&&"function"==typeof t.getAudioTracks?t:null,this.isConnected&&this.inputStream&&this.startAudioAnalysis(this.inputStream))}connectedCallback(){this.syncClasses(),this.setAttribute("aria-hidden","true"),this.canvas||this.build(),this.t0=performance.now(),this.lastFrame=0,this.observeTheme(),this.inputStream&&this.startAudioAnalysis(this.inputStream),"undefined"!=typeof ResizeObserver&&(this.resizeObs=new ResizeObserver(()=>this.resize()),this.resizeObs.observe(this)),this.resize(),o?this.frame(this.t0+1600):"undefined"!=typeof IntersectionObserver?(this.io=new IntersectionObserver(t=>{for(const e of t)this.setVisible(e.isIntersecting)},{threshold:.01}),this.io.observe(this)):this.setVisible(!0)}disconnectedCallback(){this.setVisible(!1),this.resizeObs?.disconnect(),this.resizeObs=null,this.io?.disconnect(),this.io=null,this.themeObserver?.disconnect(),this.themeObserver=null,this.themeMedia?.removeEventListener("change",this.onThemeChange),this.themeMedia=null,this.stopAudioAnalysis()}attributeChangedCallback(t){this.canvas&&("size"!==t&&"variant"!==t&&"state"!==t||this.syncClasses(),o&&this.frame(performance.now()))}setVisible(t){t!==this.visible&&(this.visible=t,t?(l.add(this),function ensureTicking(){c||!l.size||o||(c=requestAnimationFrame(tick))}()):l.delete(this))}syncClasses(){this.className=["designbase-wc-chat-orb",`designbase-wc-chat-orb--${this.size}`,`designbase-wc-chat-orb--${this.variant}`,`designbase-wc-chat-orb--${this.state}`].join(" ")}build(){this.replaceChildren(),this.canvas=document.createElement("canvas"),this.canvas.className="designbase-wc-chat-orb__canvas",this.ctx=this.canvas.getContext("2d"),this.append(this.canvas),this.addEventListener("pointermove",this.onPointerMove),this.addEventListener("pointerleave",this.onPointerLeave),this.addEventListener("pointerdown",this.onPointerDown)}addRipple(t,e){this.ripples.push({start:t,strength:clamp(e,.2,1)})}observeTheme(){"undefined"==typeof MutationObserver||this.themeObserver||(this.themeObserver=new MutationObserver(this.onThemeChange),this.themeObserver.observe(document.documentElement,{attributes:!0,attributeFilter:["data-theme","data-color-mode","class"]})),"function"!=typeof window.matchMedia||this.themeMedia||(this.themeMedia=window.matchMedia("(prefers-color-scheme: dark)"),this.themeMedia.addEventListener("change",this.onThemeChange))}isDarkContext(){if("dark"===this.theme)return!0;if("light"===this.theme)return!1;const t=this.parentElement?.closest("[data-theme], [data-color-mode]"),e=t?.getAttribute("data-color-mode");if("dark"===e)return!0;if("light"===e)return!1;const i=t?.getAttribute("data-theme");if("dark"===i)return!0;if("light"===i)return!1;const s=document.documentElement,a=s.getAttribute("data-color-mode");if("dark"===a)return!0;if("light"===a)return!1;const r=s.getAttribute("data-theme");return"dark"===r||"light"!==r&&(this.themeMedia?.matches??!1)}startAudioAnalysis(t){if(this.analyser)return;const e=window.AudioContext??window.webkitAudioContext;if(e)try{const i=new e,s=i.createAnalyser();s.fftSize=512,s.smoothingTimeConstant=.58;const a=i.createMediaStreamSource(t);a.connect(s),this.audioContext=i,this.audioSource=a,this.analyser=s,this.analyserData=new Float32Array(s.fftSize),"suspended"===i.state&&i.resume().catch(()=>{})}catch{this.stopAudioAnalysis()}}stopAudioAnalysis(){try{this.audioSource?.disconnect(),this.analyser?.disconnect()}catch{}this.audioContext&&this.audioContext.close().catch(()=>{}),this.audioContext=null,this.audioSource=null,this.analyser=null,this.analyserData=null}sampleVoiceLevel(t){if(this.analyser&&this.analyserData){this.analyser.getFloatTimeDomainData(this.analyserData);let t=0;for(const e of this.analyserData)t+=e*e;return{level:clamp((Math.sqrt(t/this.analyserData.length)-.01)/.11,0,1),hasInput:!0}}if(this.hasAttribute("voice-level"))return{level:this.voiceLevel,hasInput:!0};return{level:clamp(.16+.12*(Math.sin(2.05*t)+1)+.42*Math.pow(Math.max(0,Math.sin(.86*t+.7)),5),0,1),hasInput:!1}}resize(){this.canvas&&(this.dpr=Math.min(2,window.devicePixelRatio||1),this.cssW=this.clientWidth||72,this.cssH=this.clientHeight||this.cssW,this.canvas.width=Math.round(this.cssW*this.dpr),this.canvas.height=Math.round(this.cssH*this.dpr),o&&this.frame(this.t0+1600))}frame(t){const e=this.ctx;if(!e||!this.cssW)return;const i=this.lastFrame?Math.min((t-this.lastFrame)/1e3,.1):1/60;this.lastFrame=t;const s=(t-this.t0)/1e3,o=this.cssW,l=this.cssH,c=o/2,d=l/2,m=Math.min(o,l)/2,u=this.isDarkContext(),p=u?a[this.variant]:r[this.variant],g=n[this.state];this.hover.x+=.08*(this.hover.tx-this.hover.x),this.hover.y+=.08*(this.hover.ty-this.hover.y);const b="listening"===this.state?this.sampleVoiceLevel(s):{level:0,hasInput:!1},v=b.level>this.displayedVoiceLevel?19:7;this.displayedVoiceLevel+=(b.level-this.displayedVoiceLevel)*(1-Math.exp(-v*i)),"listening"!==this.state&&this.displayedVoiceLevel<.001&&(this.displayedVoiceLevel=0);const f=Math.pow(clamp(this.displayedVoiceLevel,0,1),.72),y="listening"===this.state?this.minScale+(this.maxScale-this.minScale)*f:1,w=g.breatheAmt*("listening"===this.state?.55:1),A=1+Math.sin(s*g.breatheSpeed)*w,S="listening"===this.state?f:"thinking"===this.state?1:.55,M="thinking"===this.state?1.38:1,C=.68*m*A*y*("thinking"===this.state?1.1:1),x=.99*m;e.setTransform(this.dpr,0,0,this.dpr,0,0),e.clearRect(0,0,o,l),e.globalCompositeOperation="source-over",e.filter="none";const k="thinking"===this.state?u?.42:.22:u?.28:.5,O=u?mix(p.core,p.lights[0],k):"thinking"===this.state?mix(p.core,p.lights[0],.35):mix(p.core,[255,255,255],.5),z=mix(p.core,p.lights[1],u?"thinking"===this.state?.35:0:"thinking"===this.state?.28:.12),L=e.createRadialGradient(c,d-.12*C,.1*C,c,d,C);L.addColorStop(0,rgba(O,u?1:.96)),L.addColorStop(.72,rgba(z,u?1:.9)),L.addColorStop(.93,rgba(z,u?.72:.5)),L.addColorStop(1,rgba(z,0)),e.fillStyle=L,e.beginPath(),e.arc(c,d,C,0,h),e.fill(),e.globalCompositeOperation=u?"lighter":"source-over";const R=p.lights.length;for(let t=0;t<R;t+=1){const i=2.1*t,a=g.turb*(.85+.55*S),r=Math.sin(s*g.speed+i)*g.amp+Math.sin(s*g.speed*1.7+1.4*i)*g.amp*a*.75+Math.sin(s*g.speed*.45+.7*i)*g.amp*.35,n=Math.cos(s*g.speed*.9+1.3*i)*g.amp+Math.cos(s*g.speed*2+i)*g.amp*a*.75+Math.cos(s*g.speed*.55+.9*i)*g.amp*.35,o="thinking"===this.state?.42:.28,l=t/R*h-.5*Math.PI+s*g.speed*o,m=C*("thinking"===this.state?.52:.44),b=c+Math.cos(l)*m+r*C+this.hover.x*C*.12,v=d+Math.sin(l)*m+n*C+this.hover.y*C*.12,f=C*M*(.92+.28*S+.12*Math.sin(s*g.speed*1.3+i)),y="thinking"===this.state?mix(p.lights[t],u?[255,255,255]:p.core,u?.1:.18):p.lights[t],w=(u?.62:.78)*g.lightAlpha*(.85+.28*S)*("thinking"===this.state?1.22:1);e.save(),e.filter=`blur(${C*(u?.15:.18)*("thinking"===this.state?.92:1)}px)`;const A=e.createRadialGradient(b,v,0,b,v,f);A.addColorStop(0,rgba(y,Math.min(1,w))),A.addColorStop(.48,rgba(y,Math.min(1,.5*w))),A.addColorStop(1,rgba(y,0)),e.fillStyle=A,e.beginPath(),e.arc(b,v,f,0,h),e.fill(),e.restore()}e.filter="none",e.globalCompositeOperation="destination-in";const D=e.createRadialGradient(c,d,.6*C,c,d,C);D.addColorStop(0,"rgba(0,0,0,1)"),D.addColorStop(.86,"rgba(0,0,0,1)"),D.addColorStop(1,"rgba(0,0,0,0)"),e.fillStyle=D,e.beginPath(),e.arc(c,d,C,0,h),e.fill(),e.globalCompositeOperation="source-atop";const V=c-.22*C+this.hover.x*C*.1,F=d-.28*C+this.hover.y*C*.1,E=e.createRadialGradient(V,F,0,V,F,.95*C);E.addColorStop(0,`rgba(255,255,255,${u?.12:.2})`),E.addColorStop(1,"rgba(255,255,255,0)"),e.fillStyle=E,e.fillRect(c-C,d-C,2*C,2*C),e.globalCompositeOperation="destination-over";const P=e.createRadialGradient(c,d,.7*C,c,d,x),T=(u?.32:.14)*g.glow*(.85+.28*S);P.addColorStop(0,rgba(p.glow,T)),P.addColorStop(1,rgba(p.glow,0)),e.fillStyle=P,e.beginPath(),e.arc(c,d,x,0,h),e.fill(),e.globalCompositeOperation="source-over",e.filter="none"}}customElements.get("db-chat-orb")||customElements.define("db-chat-orb",DbChatOrb);export{DbChatOrb};
@@ -0,0 +1,71 @@
1
+ import './db-avatar.js';
2
+ import './db-chat-message.js';
3
+ import './db-chat-typing.js';
4
+ import { DbChatComposer } from './db-chat-composer.js';
5
+
6
+ interface DbChatRoomMessage {
7
+ id?: string;
8
+ /** 내가 보낸 메시지면 true(우측 정렬). 아니면 상대(좌측) */
9
+ mine?: boolean;
10
+ text?: string;
11
+ html?: string;
12
+ time?: string;
13
+ /** 상대 이름(그룹 채팅에서 유용) */
14
+ name?: string;
15
+ avatarSrc?: string;
16
+ avatarInitials?: string;
17
+ }
18
+ /**
19
+ * 사람 간 1:1/그룹 대화 화면. 상단 헤더(상대 정보) + 메시지 스레드 + 하단 컴포저.
20
+ * 메시지·아바타·입력창은 `db-chat-message` · `db-avatar` · `db-chat-composer` 를 재사용합니다.
21
+ * AI 대화 화면은 `db-ai-chat` 을 쓰세요.
22
+ *
23
+ * @fires db-send - 전송 (detail: { value, attachments })
24
+ * @fires db-back - 뒤로가기
25
+ *
26
+ * @example
27
+ * const room = document.querySelector('db-chat-room');
28
+ * room.setAttribute('peer-name', '디지털 상담원');
29
+ * room.messages = [{ mine:false, text:'무엇을 도와드릴까요?', time:'오후 2:31' }];
30
+ * room.addEventListener('db-send', (e) => room.addMessage({ mine:true, text:e.detail.value }));
31
+ */
32
+ declare class DbChatRoom extends HTMLElement {
33
+ static get observedAttributes(): string[];
34
+ private built;
35
+ private typing;
36
+ private _messages;
37
+ private headerEl;
38
+ private scrollEl;
39
+ private threadEl;
40
+ private dockEl;
41
+ private composer;
42
+ set messages(v: DbChatRoomMessage[]);
43
+ get messages(): DbChatRoomMessage[];
44
+ connectedCallback(): void;
45
+ attributeChangedCallback(): void;
46
+ private build;
47
+ private sync;
48
+ private renderHeader;
49
+ private renderThread;
50
+ private buildMessage;
51
+ private buildTyping;
52
+ private scrollToEnd;
53
+ /** 메시지 추가. 추가된 id 반환 */
54
+ addMessage(msg: DbChatRoomMessage): string;
55
+ /** 전체 메시지 교체 */
56
+ setMessages(list: DbChatRoomMessage[]): void;
57
+ /** 상대 입력중(…) 표시 토글 */
58
+ setTyping(on: boolean): void;
59
+ /** 입력창 포커스 */
60
+ focusInput(): void;
61
+ /** 컴포저 인스턴스 접근 */
62
+ getComposer(): DbChatComposer;
63
+ }
64
+ declare global {
65
+ interface HTMLElementTagNameMap {
66
+ 'db-chat-room': DbChatRoom;
67
+ }
68
+ }
69
+
70
+ export { DbChatRoom };
71
+ export type { DbChatRoomMessage };
@@ -0,0 +1 @@
1
+ import{createDbIcon as t}from"./db-icons.js";import"./db-avatar.js";import"./db-chat-message.js";import"./db-chat-typing.js";import"./db-chat-composer.js";const e="designbase-wc-chat-room";let s=0;function el(t,e,s){const i=document.createElement(t);return e&&(i.className=e),null!=s&&(i.textContent=s),i}class DbChatRoom extends HTMLElement{constructor(){super(...arguments),this.built=!1,this.typing=!1,this._messages=[]}static get observedAttributes(){return["peer-name","peer-avatar","peer-initials","peer-status","placeholder","online","show-back"]}set messages(t){this._messages=Array.isArray(t)?t.map(t=>({id:`r${s+=1}`,...t})):[],this.built&&this.renderThread()}get messages(){return this._messages}connectedCallback(){this.built||this.build(),this.sync()}attributeChangedCallback(){this.built&&this.sync()}build(){this.built=!0,this.className=e,this.replaceChildren(),this.headerEl=el("div",`${e}__header`),this.scrollEl=el("div",`${e}__scroll`),this.threadEl=el("div",`${e}__thread`),this.scrollEl.append(this.threadEl),this.dockEl=el("div",`${e}__dock`),this.composer=document.createElement("db-chat-composer"),this.composer.setAttribute("attach",""),this.composer.setAttribute("placeholder",this.getAttribute("placeholder")||"메시지 보내기…"),this.composer.addEventListener("db-send",t=>{t.stopPropagation();const e=t.detail;this.dispatchEvent(new CustomEvent("db-send",{detail:e,bubbles:!0,composed:!0}))}),this.dockEl.append(this.composer),this.append(this.headerEl,this.scrollEl,this.dockEl),this.renderThread()}sync(){this.renderHeader(),this.composer.setAttribute("placeholder",this.getAttribute("placeholder")||"메시지 보내기…")}renderHeader(){if(this.headerEl.replaceChildren(),this.hasAttribute("show-back")){const s=el("button",`${e}__back`);s.type="button",s.setAttribute("aria-label","뒤로"),s.append(t("chevron-left",20)),s.addEventListener("click",()=>this.dispatchEvent(new CustomEvent("db-back",{bubbles:!0,composed:!0}))),this.headerEl.append(s)}const s=document.createElement("db-avatar");s.className=`${e}__avatar`,s.setAttribute("size","m"),s.setAttribute("variant","primary");const i=this.getAttribute("peer-avatar"),a=this.getAttribute("peer-initials");i?s.setAttribute("src",i):a&&s.setAttribute("initials",a),this.hasAttribute("online")&&s.setAttribute("status","online");const r=el("div",`${e}__meta`);r.append(el("span",`${e}__name`,this.getAttribute("peer-name")||"대화"));const n=this.getAttribute("peer-status")||(this.hasAttribute("online")?"온라인":"");n&&r.append(el("span",`${e}__status`,n)),this.headerEl.append(s,r)}renderThread(){this.threadEl.replaceChildren(),this._messages.forEach(t=>this.threadEl.append(this.buildMessage(t))),this.typing&&this.threadEl.append(this.buildTyping()),requestAnimationFrame(()=>this.scrollToEnd())}buildMessage(t){const e=el("db-chat-message");return e.dataset.id=t.id||"",e.setAttribute("role",t.mine?"user":"assistant"),!t.mine&&t.name&&e.setAttribute("name",t.name),t.time&&e.setAttribute("timestamp",t.time),!t.mine&&t.avatarSrc&&e.setAttribute("avatar-src",t.avatarSrc),!t.mine&&t.avatarInitials&&e.setAttribute("avatar-initials",t.avatarInitials),null!=t.html?e.innerHTML=t.html:e.textContent=t.text||"",e}buildTyping(){const t=el("db-chat-typing");return t.setAttribute("name",this.getAttribute("peer-name")||""),t.setAttribute("avatar-initials",this.getAttribute("peer-initials")||""),t}scrollToEnd(){this.scrollEl.scrollTop=this.scrollEl.scrollHeight}addMessage(t){const e={id:`r${s+=1}`,...t};this._messages.push(e);const i=this.threadEl.querySelector("db-chat-typing"),a=this.buildMessage(e);return i?this.threadEl.insertBefore(a,i):this.threadEl.append(a),requestAnimationFrame(()=>this.scrollToEnd()),e.id}setMessages(t){this.messages=t}setTyping(t){if(this.typing=Boolean(t),!this.built)return;const e=this.threadEl.querySelector("db-chat-typing");this.typing&&!e?(this.threadEl.append(this.buildTyping()),requestAnimationFrame(()=>this.scrollToEnd())):!this.typing&&e&&e.remove()}focusInput(){this.composer?.focusInput()}getComposer(){return this.composer}}customElements.get("db-chat-room")||customElements.define("db-chat-room",DbChatRoom);export{DbChatRoom};
@@ -0,0 +1,44 @@
1
+ import { DbIconName } from './db-icons.js';
2
+ import './db-chip.js';
3
+
4
+ interface DbChatSuggestion {
5
+ /** 고유 id */
6
+ id?: string;
7
+ /** 제목 (카드/칩 라벨) */
8
+ title: string;
9
+ /** 설명 (카드 레이아웃에서만 표시) */
10
+ description?: string;
11
+ /** 아이콘 (DbIconName) */
12
+ icon?: DbIconName;
13
+ /** 선택 시 전달될 값 (기본값: title) */
14
+ value?: string;
15
+ }
16
+ /**
17
+ * 채팅 시작 화면의 추천 프롬프트 목록.
18
+ * `layout="card"` 는 아이콘·제목·설명이 있는 카드 그리드,
19
+ * `layout="chip"` 은 클릭 가능한 칩(db-chip 재사용) 행으로 렌더링합니다.
20
+ *
21
+ * @example
22
+ * const s = document.querySelector('db-chat-suggestions');
23
+ * s.items = [{ title:'Write Copy', description:'...', icon:'edit' }];
24
+ * s.addEventListener('db-select', (e) => console.log(e.detail.value));
25
+ */
26
+ declare class DbChatSuggestions extends HTMLElement {
27
+ static get observedAttributes(): string[];
28
+ private _items;
29
+ get items(): DbChatSuggestion[];
30
+ set items(list: DbChatSuggestion[]);
31
+ get layout(): string;
32
+ connectedCallback(): void;
33
+ attributeChangedCallback(): void;
34
+ private select;
35
+ private render;
36
+ }
37
+ declare global {
38
+ interface HTMLElementTagNameMap {
39
+ 'db-chat-suggestions': DbChatSuggestions;
40
+ }
41
+ }
42
+
43
+ export { DbChatSuggestions };
44
+ export type { DbChatSuggestion };
@@ -0,0 +1 @@
1
+ import{isDbIconName as t,createDbIcon as e}from"./db-icons.js";import"./db-chip.js";const s=["card","chip"];class DbChatSuggestions extends HTMLElement{constructor(){super(...arguments),this._items=[]}static get observedAttributes(){return["layout","columns"]}get items(){return this._items}set items(t){this._items=Array.isArray(t)?t:[],this.render()}get layout(){return s.includes(this.getAttribute("layout"))?this.getAttribute("layout"):"card"}connectedCallback(){if(!this._items.length&&this.hasAttribute("items"))try{this._items=JSON.parse(this.getAttribute("items")||"[]")}catch{this._items=[]}this.render()}attributeChangedCallback(){this.render()}select(t){this.dispatchEvent(new CustomEvent("db-select",{detail:{id:t.id??t.title,title:t.title,value:t.value??t.title},bubbles:!0,composed:!0}))}render(){const s=this.layout,i=Number(this.getAttribute("columns")||3);if(this.className=`designbase-wc-chat-suggestions designbase-wc-chat-suggestions--${s}`,this.style.setProperty("--dbcs-columns",String(i)),this.replaceChildren(),"chip"!==s)for(const s of this._items){const i=document.createElement("button");i.type="button",i.className="designbase-wc-chat-suggestions__card";const n=document.createElement("span");n.className="designbase-wc-chat-suggestions__icon",n.setAttribute("aria-hidden","true"),s.icon&&t(s.icon)&&n.append(e(s.icon,20));const c=document.createElement("span");c.className="designbase-wc-chat-suggestions__body";const a=document.createElement("span");if(a.className="designbase-wc-chat-suggestions__title",a.textContent=s.title,c.append(a),s.description){const t=document.createElement("span");t.className="designbase-wc-chat-suggestions__desc",t.textContent=s.description,c.append(t)}i.append(n,c),i.addEventListener("click",()=>this.select(s)),this.append(i)}else for(const e of this._items){const s=document.createElement("db-chip");s.className="designbase-wc-chat-suggestions__chip",s.setAttribute("label",e.title),s.setAttribute("size","m"),s.setAttribute("clickable",""),e.icon&&t(e.icon)&&s.setAttribute("start-icon",e.icon),s.addEventListener("db-click",()=>this.select(e)),this.append(s)}}}customElements.get("db-chat-suggestions")||customElements.define("db-chat-suggestions",DbChatSuggestions);export{DbChatSuggestions};
@@ -0,0 +1,22 @@
1
+ import './db-avatar.js';
2
+
3
+ /**
4
+ * AI 응답 대기(생성 중) 표시. 3점 바운스 인디케이터.
5
+ * 아바타는 `db-avatar` 를 재사용합니다.
6
+ *
7
+ * @example
8
+ * <db-chat-typing name="Orion" label="생각 중…"></db-chat-typing>
9
+ */
10
+ declare class DbChatTyping extends HTMLElement {
11
+ static get observedAttributes(): string[];
12
+ connectedCallback(): void;
13
+ attributeChangedCallback(): void;
14
+ private render;
15
+ }
16
+ declare global {
17
+ interface HTMLElementTagNameMap {
18
+ 'db-chat-typing': DbChatTyping;
19
+ }
20
+ }
21
+
22
+ export { DbChatTyping };
@@ -0,0 +1 @@
1
+ import"./db-avatar.js";class DbChatTyping extends HTMLElement{static get observedAttributes(){return["name","avatar-src","avatar-initials","label"]}connectedCallback(){this.render()}attributeChangedCallback(){this.render()}render(){this.className="designbase-wc-chat-typing",this.setAttribute("role","status"),this.setAttribute("aria-live","polite"),this.setAttribute("aria-label",this.getAttribute("label")||"AI가 응답을 생성하고 있습니다"),this.replaceChildren();const t=this.getAttribute("avatar-src"),e=this.getAttribute("avatar-initials"),a=document.createElement("div");a.className="designbase-wc-chat-typing__avatar";const s=document.createElement("db-avatar");s.setAttribute("size","s"),s.setAttribute("variant","primary"),t&&s.setAttribute("src",t),e&&s.setAttribute("initials",e),a.append(s);const i=document.createElement("div");i.className="designbase-wc-chat-typing__bubble";const n=this.getAttribute("label");if(n){const t=document.createElement("span");t.className="designbase-wc-chat-typing__label",t.textContent=n,i.append(t)}const r=document.createElement("span");r.className="designbase-wc-chat-typing__dots",r.setAttribute("aria-hidden","true");for(let t=0;t<3;t+=1){const t=document.createElement("span");t.className="designbase-wc-chat-typing__dot",r.append(t)}i.append(r),this.append(a,i)}}customElements.get("db-chat-typing")||customElements.define("db-chat-typing",DbChatTyping);export{DbChatTyping};
@@ -0,0 +1,32 @@
1
+ import './db-chat-orb.js';
2
+
3
+ /**
4
+ * 채팅 시작 전 화면의 히어로 블록.
5
+ * 오브(또는 로고 이미지) + 인사말 + 타이틀 + 서브타이틀을 렌더링합니다.
6
+ * 타이틀의 `highlight` 부분은 그라디언트로 강조됩니다.
7
+ *
8
+ * 컴포저(`db-chat-composer`)와 추천(`db-chat-suggestions`)은 형제 요소로 배치합니다.
9
+ *
10
+ * @example
11
+ * <db-chat-welcome
12
+ * greeting="안녕하세요"
13
+ * title="오늘은 무엇을 도와드릴까요?"
14
+ * highlight="도와드릴까요"
15
+ * orb-variant="aurora">
16
+ * </db-chat-welcome>
17
+ */
18
+ declare class DbChatWelcome extends HTMLElement {
19
+ static get observedAttributes(): string[];
20
+ connectedCallback(): void;
21
+ attributeChangedCallback(): void;
22
+ private render;
23
+ /** 타이틀에서 highlight 구간만 그라디언트 span 으로 감싼다. */
24
+ private appendTitle;
25
+ }
26
+ declare global {
27
+ interface HTMLElementTagNameMap {
28
+ 'db-chat-welcome': DbChatWelcome;
29
+ }
30
+ }
31
+
32
+ export { DbChatWelcome };
@@ -0,0 +1 @@
1
+ import"./db-chat-orb.js";const e=["center","left"];class DbChatWelcome extends HTMLElement{static get observedAttributes(){return["greeting","title","highlight","subtitle","align","orb-variant","orb-state","orb-size","hide-orb","avatar-src"]}connectedCallback(){this.render()}attributeChangedCallback(){this.render()}render(){const t=this.getAttribute("greeting")||"",a=this.getAttribute("title")||"",s=this.getAttribute("highlight")||"",c=this.getAttribute("subtitle")||"",i=e.includes(this.getAttribute("align"))?this.getAttribute("align"):"center",n=this.getAttribute("avatar-src"),r=this.hasAttribute("hide-orb");if(this.className=`designbase-wc-chat-welcome designbase-wc-chat-welcome--${i}`,this.replaceChildren(),n){const e=document.createElement("div");e.className="designbase-wc-chat-welcome__media";const t=document.createElement("img");t.className="designbase-wc-chat-welcome__logo",t.src=n,t.alt="",t.setAttribute("aria-hidden","true"),e.append(t),this.append(e)}else if(!r){const e=document.createElement("db-chat-orb");e.className="designbase-wc-chat-welcome__orb",e.setAttribute("size",this.getAttribute("orb-size")||"l"),e.setAttribute("variant",this.getAttribute("orb-variant")||"aurora"),e.setAttribute("state",this.getAttribute("orb-state")||"idle"),this.append(e)}const l=document.createElement("h1");if(l.className="designbase-wc-chat-welcome__heading",t){const e=document.createElement("span");e.className="designbase-wc-chat-welcome__greeting",e.textContent=t,l.append(e)}if(a){const e=document.createElement("span");e.className="designbase-wc-chat-welcome__title",this.appendTitle(e,a,s),l.append(e)}if(this.append(l),c){const e=document.createElement("p");e.className="designbase-wc-chat-welcome__subtitle",e.textContent=c,this.append(e)}}appendTitle(e,t,a){const s=a?t.indexOf(a):-1;if(-1===s)return void(e.textContent=t);const c=t.slice(0,s),i=t.slice(s+a.length);c&&e.append(document.createTextNode(c));const n=document.createElement("span");n.className="designbase-wc-chat-welcome__highlight",n.textContent=a,e.append(n),i&&e.append(document.createTextNode(i))}}customElements.get("db-chat-welcome")||customElements.define("db-chat-welcome",DbChatWelcome);export{DbChatWelcome};