@nuraly/lumenui 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4175,6 +4175,7 @@ function un(e){if(!e)return"";const t=e instanceof Date?e:new Date(e);if(Number.
4175
4175
  size="small"
4176
4176
  color="#9ca3af"
4177
4177
  class="message__copy"
4178
+ part="message-copy"
4178
4179
  @click=${()=>t.onCopy(e)}
4179
4180
  @keydown=${i=>t.onCopyKeydown(i,e)}
4180
4181
  title="${i.messages.copyMessageLabel}"
@@ -4201,7 +4202,7 @@ function un(e){if(!e)return"";const t=e instanceof Date?e:new Date(e);if(Number.
4201
4202
  ${0===e.length?function(e){return q`
4202
4203
  <div class="empty-state" part="empty-state">
4203
4204
  <slot name="empty-state">
4204
- <div class="empty-state__content">
4205
+ <div class="empty-state__content" part="empty-state-content">
4205
4206
  ${e.messages.startConversationLabel}
4206
4207
  </div>
4207
4208
  </slot>
@@ -4261,6 +4262,7 @@ function Dn(e,t,i,n){const s=(e,t)=>{const i=e.lastIndexOf(".");if(i>=0&&i<e.len
4261
4262
  <div
4262
4263
  slot="trigger"
4263
4264
  class="file-thumb ${e.isUploading?"file-thumb--uploading":""}"
4265
+ part="file-thumb"
4264
4266
  role="button"
4265
4267
  tabindex="0"
4266
4268
  title="${e.name}"
@@ -4269,22 +4271,24 @@ function Dn(e,t,i,n){const s=(e,t)=>{const i=e.lastIndexOf(".");if(i>=0&&i<e.len
4269
4271
  ${a(e.mimeType)&&(e.previewUrl||e.url)?q`
4270
4272
  <img
4271
4273
  class="file-thumb__image"
4274
+ part="file-thumb-image"
4272
4275
  src="${e.previewUrl||e.url}"
4273
4276
  alt="${e.name}"
4274
4277
  />
4275
4278
  `:q`
4276
- <div class="file-thumb__ext" data-ext="${s(e.name,e.mimeType)}">
4277
- <span class="file-thumb__ext-label">${s(e.name,e.mimeType)}</span>
4279
+ <div class="file-thumb__ext" part="file-thumb-ext" data-ext="${s(e.name,e.mimeType)}">
4280
+ <span class="file-thumb__ext-label" part="file-thumb-ext-label">${s(e.name,e.mimeType)}</span>
4278
4281
  </div>
4279
4282
  `}
4280
4283
  ${e.isUploading?q`
4281
- <div class="file-thumb__spinner" aria-label="${i.input.uploadingLabel}">
4284
+ <div class="file-thumb__spinner" part="file-thumb-spinner" aria-label="${i.input.uploadingLabel}">
4282
4285
  <span class="file-thumb__spinner-ring"></span>
4283
4286
  </div>
4284
4287
  `:""}
4285
4288
  <button
4286
4289
  type="button"
4287
4290
  class="file-thumb__remove"
4291
+ part="file-thumb-remove"
4288
4292
  aria-label="${i.input.removeFileLabel}"
4289
4293
  title="${i.input.removeFileLabel}"
4290
4294
  @click=${i=>{i.stopPropagation(),t(e.id)}}
@@ -4320,8 +4324,8 @@ function Dn(e,t,i,n){const s=(e,t)=>{const i=e.lastIndexOf(".");if(i>=0&&i<e.len
4320
4324
  `)}
4321
4325
  </div>
4322
4326
  `}function zn(e,t){return q`
4323
- <div class="action-buttons-row">
4324
- <div class="action-buttons-left">
4327
+ <div class="action-buttons-row" part="actions">
4328
+ <div class="action-buttons-left" part="actions-left">
4325
4329
  ${e.enableFileUpload?function(e,t){return q`
4326
4330
  <nr-dropdown
4327
4331
  .items=${e.fileUploadItems}
@@ -4369,11 +4373,12 @@ function Dn(e,t,i,n){const s=(e,t)=>{const i=e.lastIndexOf(".");if(i>=0&&i<e.len
4369
4373
  `}(e,t):K}
4370
4374
  </div>
4371
4375
 
4372
- <div class="action-buttons-right">
4376
+ <div class="action-buttons-right" part="actions-right">
4373
4377
  ${e.showAudioButton&&!e.isQueryRunning?q`
4374
4378
  <!-- Speech-to-text: mic + keyboard indicator -->
4375
4379
  <button
4376
4380
  class="audio-mic-btn"
4381
+ part="audio-mic-button audio-mic-transcribe"
4377
4382
  title="${e.i18n.audio.recordSpeechLabel}"
4378
4383
  ?disabled=${e.disabled}
4379
4384
  @click=${()=>{var e;return null===(e=t.onAudioStart)||void 0===e?void 0:e.call(t,"transcribe")}}
@@ -4392,6 +4397,7 @@ function Dn(e,t,i,n){const s=(e,t)=>{const i=e.lastIndexOf(".");if(i>=0&&i<e.len
4392
4397
  <!-- Voice message: mic + waveform indicator -->
4393
4398
  <button
4394
4399
  class="audio-mic-btn"
4400
+ part="audio-mic-button audio-mic-voice"
4395
4401
  title="${e.i18n.audio.sendVoiceMessageLabel}"
4396
4402
  ?disabled=${e.disabled}
4397
4403
  @click=${()=>{var e;return null===(e=t.onAudioStart)||void 0===e?void 0:e.call(t,"message")}}
@@ -4433,9 +4439,10 @@ function Dn(e,t,i,n){const s=(e,t)=>{const i=e.lastIndexOf(".");if(i>=0&&i<e.len
4433
4439
  </svg>`:q`<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
4434
4440
  <path d="m22 2-7 20-4-9-9-4Z"/><path d="M22 2 11 13"/>
4435
4441
  </svg>`;return q`
4436
- <div class="audio-recording-bar">
4442
+ <div class="audio-recording-bar" part="audio-recording-bar">
4437
4443
  <button
4438
4444
  class="audio-rec-cancel"
4445
+ part="audio-cancel-button"
4439
4446
  title="${e.i18n.audio.cancelRecordingLabel}"
4440
4447
  @click=${t.onAudioCancel}
4441
4448
  aria-label="${e.i18n.audio.cancelRecordingLabel}"
@@ -4447,22 +4454,23 @@ function Dn(e,t,i,n){const s=(e,t)=>{const i=e.lastIndexOf(".");if(i>=0&&i<e.len
4447
4454
  </svg>
4448
4455
  </button>
4449
4456
 
4450
- <div class="audio-rec-indicator">
4451
- <span class="audio-rec-dot"></span>
4452
- <div class="audio-rec-wave">
4457
+ <div class="audio-rec-indicator" part="audio-indicator">
4458
+ <span class="audio-rec-dot" part="audio-dot"></span>
4459
+ <div class="audio-rec-wave" part="audio-wave">
4453
4460
  ${n.map(e=>q`
4454
- <div class="audio-rec-bar" style=${Be({height:`${Math.round(24*e)}px`})}></div>
4461
+ <div class="audio-rec-bar" part="audio-bar" style=${Be({height:`${Math.round(24*e)}px`})}></div>
4455
4462
  `)}
4456
4463
  </div>
4457
- <span class="audio-rec-time">${i}</span>
4464
+ <span class="audio-rec-time" part="audio-time">${i}</span>
4458
4465
  </div>
4459
4466
 
4460
- <span class="audio-rec-mode-label">
4467
+ <span class="audio-rec-mode-label" part="audio-mode-label">
4461
4468
  ${s?e.i18n.audio.speechToTextLabel:e.i18n.audio.voiceMessageLabel}
4462
4469
  </span>
4463
4470
 
4464
4471
  <button
4465
4472
  class="audio-rec-send ${s?"audio-rec-send--transcribe":""}"
4473
+ part="audio-send-button"
4466
4474
  title="${a}"
4467
4475
  @click=${t.onAudioSend}
4468
4476
  aria-label="${a}"
@@ -4480,7 +4488,7 @@ function Dn(e,t,i,n){const s=(e,t)=>{const i=e.lastIndexOf(".");if(i>=0&&i<e.len
4480
4488
  ${e.uploadedFiles.length>0?Dn(e.uploadedFiles,t.onFileRemove,e.i18n,t.onFileClick):K}
4481
4489
 
4482
4490
  <!-- Input area -->
4483
- <div class="input-row">
4491
+ <div class="input-row" part="input-row">
4484
4492
  <div
4485
4493
  class="input-box__input"
4486
4494
  part="input"
@@ -4515,6 +4523,7 @@ function Dn(e,t,i,n){const s=(e,t)=>{const i=e.lastIndexOf(".");if(i>=0&&i<e.len
4515
4523
  ${t.editingThreadId===e.id&&i.onRenameThread?q`
4516
4524
  <input
4517
4525
  class="thread-item__rename-input"
4526
+ part="thread-rename-input"
4518
4527
  type="text"
4519
4528
  .value=${e.title||""}
4520
4529
  @click=${e=>e.stopPropagation()}
@@ -4522,9 +4531,9 @@ function Dn(e,t,i,n){const s=(e,t)=>{const i=e.lastIndexOf(".");if(i>=0&&i<e.len
4522
4531
  @blur=${t=>{const n=t.target.value.trim();n&&n!==e.title?i.onRenameThread(e.id,n):i.onRenameThread(e.id,e.title||"")}}
4523
4532
  />
4524
4533
  `:q`
4525
- <div class="thread-item__title">${e.title||t.i18n.threads.newChatTitle}</div>
4534
+ <div class="thread-item__title" part="thread-title">${e.title||t.i18n.threads.newChatTitle}</div>
4526
4535
  `}
4527
- <div class="thread-item__actions">
4536
+ <div class="thread-item__actions" part="thread-actions">
4528
4537
  ${i.onBookmarkThread&&e.bookmarked?q`
4529
4538
  <button
4530
4539
  class="thread-item__action-btn thread-item__bookmark--active"
@@ -4558,10 +4567,10 @@ function Dn(e,t,i,n){const s=(e,t)=>{const i=e.lastIndexOf(".");if(i>=0&&i<e.len
4558
4567
  `:""}
4559
4568
  </div>
4560
4569
  </div>
4561
- <div class="thread-item__preview">
4570
+ <div class="thread-item__preview" part="thread-preview">
4562
4571
  ${s}
4563
4572
  </div>
4564
- <div class="thread-item__timestamp">${un(e.updatedAt)}</div>
4573
+ <div class="thread-item__timestamp" part="thread-timestamp">${un(e.updatedAt)}</div>
4565
4574
  </div>
4566
4575
  `}function On(e,t){if(!e.isOpen||!e.file)return K;const i=e.file,n=i.mimeType.startsWith("image/"),s="application/pdf"===i.mimeType||i.name.toLowerCase().endsWith(".pdf");return q`
4567
4576
  <nr-modal
@@ -4621,15 +4630,15 @@ function(e){if(0===e)return"0 Bytes";const t=Math.floor(Math.log(e)/Math.log(102
4621
4630
  */function In(e,t){var i;if(!e.isOpen||!e.artifact)return K;const n=e.artifact,s=bn(n.language);return q`
4622
4631
  <div class="artifact-panel" part="artifact-panel">
4623
4632
  <div class="artifact-panel__resize-handle" part="artifact-panel-resize-handle">
4624
- <div class="artifact-panel__resize-bar"></div>
4633
+ <div class="artifact-panel__resize-bar" part="artifact-panel-resize-bar"></div>
4625
4634
  </div>
4626
- <div class="artifact-panel__body">
4627
- <div class="artifact-panel__header">
4628
- <div class="artifact-panel__header-info">
4629
- <nr-tag size="small" class="artifact-panel__lang-badge">${s}</nr-tag>
4630
- <span class="artifact-panel__title">${n.title}</span>
4635
+ <div class="artifact-panel__body" part="artifact-panel-body">
4636
+ <div class="artifact-panel__header" part="artifact-panel-header">
4637
+ <div class="artifact-panel__header-info" part="artifact-panel-header-info">
4638
+ <nr-tag size="small" class="artifact-panel__lang-badge" part="artifact-panel-lang">${s}</nr-tag>
4639
+ <span class="artifact-panel__title" part="artifact-panel-title">${n.title}</span>
4631
4640
  </div>
4632
- <div class="artifact-panel__actions">
4641
+ <div class="artifact-panel__actions" part="artifact-panel-actions">
4633
4642
  <nr-button
4634
4643
  type="text"
4635
4644
  size="small"
@@ -4648,8 +4657,8 @@ function(e){if(0===e)return"0 Bytes";const t=Math.floor(Math.log(e)/Math.log(102
4648
4657
  ></nr-button>
4649
4658
  </div>
4650
4659
  </div>
4651
- <div class="artifact-panel__content">
4652
- ${(null===(i=e.renderContent)||void 0===i?void 0:i.call(e,n))||function(e){switch(e.language){case"json":{let t;try{t=JSON.stringify(JSON.parse(e.content),null,2)}catch(i){t=e.content}return q`<pre class="artifact-panel__code"><code>${t}</code></pre>`}case"md":case"markdown":return q`<div class="artifact-panel__rendered-md">${Jt(gn(e.content))}</div>`;case"html":return q`<div class="artifact-panel__rendered-html">${Jt(e.content)}</div>`;case"text":case"txt":return q`<div class="artifact-panel__rendered-text">${e.content}</div>`;default:return q`<pre class="artifact-panel__code"><code>${e.content}</code></pre>`}}(n)}
4660
+ <div class="artifact-panel__content" part="artifact-panel-content">
4661
+ ${(null===(i=e.renderContent)||void 0===i?void 0:i.call(e,n))||function(e){switch(e.language){case"json":{let t;try{t=JSON.stringify(JSON.parse(e.content),null,2)}catch(i){t=e.content}return q`<pre class="artifact-panel__code" part="artifact-panel-code"><code>${t}</code></pre>`}case"md":case"markdown":return q`<div class="artifact-panel__rendered-md" part="artifact-panel-md">${Jt(gn(e.content))}</div>`;case"html":return q`<div class="artifact-panel__rendered-html" part="artifact-panel-html">${Jt(e.content)}</div>`;case"text":case"txt":return q`<div class="artifact-panel__rendered-text" part="artifact-panel-text">${e.content}</div>`;default:return q`<pre class="artifact-panel__code" part="artifact-panel-code"><code>${e.content}</code></pre>`}}(n)}
4653
4662
  </div>
4654
4663
  </div>
4655
4664
  </div>
@@ -4661,16 +4670,16 @@ function(e){if(0===e)return"0 Bytes";const t=Math.floor(Math.log(e)/Math.log(102
4661
4670
  */function jn(e,t){return!1!==e.showMessages?q`
4662
4671
  <div class="chatbot-content" part="content">
4663
4672
  ${xn(e.messages,wn(e.chatStarted,e.suggestions,t.suggestion,e.i18n),e.isTyping?function(e,t,i){if(!e)return K;const n=t===Qi.Dots?q`
4664
- <div class="dots">
4673
+ <div class="dots" part="typing-dots">
4665
4674
  <span></span>
4666
4675
  <span></span>
4667
4676
  <span></span>
4668
4677
  </div>
4669
- `:q`<div class="spinner"></div>`;return q`
4678
+ `:q`<div class="spinner" part="typing-spinner"></div>`;return q`
4670
4679
  <div class="message bot loading" part="typing-indicator">
4671
- <div class="message__content">
4680
+ <div class="message__content" part="typing-content">
4672
4681
  ${n}
4673
- ${i?q`<span class="loading-text">${i.split("").map((e,t)=>q`<span class="loading-text__char" style="animation-delay:${.04*t}s">${" "===e?" ":e}</span>`)}</span>`:K}
4682
+ ${i?q`<span class="loading-text" part="typing-text">${i.split("").map((e,t)=>q`<span class="loading-text__char" style="animation-delay:${.04*t}s">${" "===e?" ":e}</span>`)}</span>`:K}
4674
4683
  </div>
4675
4684
  </div>
4676
4685
  `}(e.isTyping,e.loadingIndicator||Qi.Spinner,e.loadingText):K,t.message,e.i18n)}
@@ -4690,14 +4699,14 @@ function(e){if(0===e)return"0 Bytes";const t=Math.floor(Math.log(e)/Math.log(102
4690
4699
 
4691
4700
  ${e.enableThreads&&e.isThreadSidebarOpen&&e.threadSidebar&&t.threadSidebar?function(e,t){const i=e.threads.filter(e=>e.bookmarked),n=e.threads.filter(e=>!e.bookmarked);return q`
4692
4701
  <div class="thread-sidebar" part="thread-sidebar">
4693
- <div class="thread-sidebar__header">
4694
- <h3>${e.i18n.threads.conversationsTitle}</h3>
4702
+ <div class="thread-sidebar__header" part="thread-sidebar-header">
4703
+ <h3 part="thread-sidebar-title">${e.i18n.threads.conversationsTitle}</h3>
4695
4704
  </div>
4696
4705
 
4697
- <div class="thread-list">
4706
+ <div class="thread-list" part="thread-list">
4698
4707
  ${i.length>0?q`
4699
- <div class="thread-section" part="thread-section-bookmarks">
4700
- <div class="thread-section__label">
4708
+ <div class="thread-section" part="thread-section thread-section-bookmarks">
4709
+ <div class="thread-section__label" part="thread-section-label">
4701
4710
  <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg>
4702
4711
  ${e.i18n.threads.bookmarksLabel}
4703
4712
  </div>
@@ -4706,11 +4715,11 @@ function(e){if(0===e)return"0 Bytes";const t=Math.floor(Math.log(e)/Math.log(102
4706
4715
  `:K}
4707
4716
  ${n.length>0||0===i.length?q`
4708
4717
  ${i.length>0?q`
4709
- <div class="thread-section__label">${e.i18n.threads.allConversationsLabel}</div>
4718
+ <div class="thread-section__label" part="thread-section-label">${e.i18n.threads.allConversationsLabel}</div>
4710
4719
  `:K}
4711
4720
  ${Cn(n,e=>e.id,i=>An(i,e,t))}
4712
4721
  ${0===n.length&&0===i.length?q`
4713
- <p class="empty-msg">${e.i18n.threads.noConversationsLabel}</p>
4722
+ <p class="empty-msg" part="thread-empty">${e.i18n.threads.noConversationsLabel}</p>
4714
4723
  `:K}
4715
4724
  `:K}
4716
4725
  </div>
@@ -4764,9 +4773,9 @@ function(e){if(0===e)return"0 Bytes";const t=Math.floor(Math.log(e)/Math.log(102
4764
4773
  class="file-upload-area ${e.isDragging?"file-upload-area--dragging":""}"
4765
4774
  part="file-upload-area"
4766
4775
  >
4767
- <div class="file-upload-area__content">
4768
- <nr-icon name="upload" size="xlarge"></nr-icon>
4769
- <div class="file-upload-area__text">
4776
+ <div class="file-upload-area__content" part="file-upload-area-content">
4777
+ <nr-icon name="upload" size="xlarge" part="file-upload-area-icon"></nr-icon>
4778
+ <div class="file-upload-area__text" part="file-upload-area-text">
4770
4779
  ${e.label}
4771
4780
  </div>
4772
4781
  </div>
Binary file
@@ -3029,6 +3029,7 @@ function(e){const t=e.match(/\[ERROR_START\]\[ERROR_TITLE_START\]([\s\S]*?)\[ERR
3029
3029
  size="small"
3030
3030
  color="#9ca3af"
3031
3031
  class="message__copy"
3032
+ part="message-copy"
3032
3033
  @click=${()=>t.onCopy(e)}
3033
3034
  @keydown=${i=>t.onCopyKeydown(i,e)}
3034
3035
  title="${a.messages.copyMessageLabel}"
@@ -3055,7 +3056,7 @@ function(e){const t=e.match(/\[ERROR_START\]\[ERROR_TITLE_START\]([\s\S]*?)\[ERR
3055
3056
  ${0===e.length?function(e){return i`
3056
3057
  <div class="empty-state" part="empty-state">
3057
3058
  <slot name="empty-state">
3058
- <div class="empty-state__content">
3059
+ <div class="empty-state__content" part="empty-state-content">
3059
3060
  ${e.messages.startConversationLabel}
3060
3061
  </div>
3061
3062
  </slot>
@@ -3104,6 +3105,7 @@ function(e){const t=e.match(/\[ERROR_START\]\[ERROR_TITLE_START\]([\s\S]*?)\[ERR
3104
3105
  <div
3105
3106
  slot="trigger"
3106
3107
  class="file-thumb ${e.isUploading?"file-thumb--uploading":""}"
3108
+ part="file-thumb"
3107
3109
  role="button"
3108
3110
  tabindex="0"
3109
3111
  title="${e.name}"
@@ -3112,22 +3114,24 @@ function(e){const t=e.match(/\[ERROR_START\]\[ERROR_TITLE_START\]([\s\S]*?)\[ERR
3112
3114
  ${s(e.mimeType)&&(e.previewUrl||e.url)?i`
3113
3115
  <img
3114
3116
  class="file-thumb__image"
3117
+ part="file-thumb-image"
3115
3118
  src="${e.previewUrl||e.url}"
3116
3119
  alt="${e.name}"
3117
3120
  />
3118
3121
  `:i`
3119
- <div class="file-thumb__ext" data-ext="${o(e.name,e.mimeType)}">
3120
- <span class="file-thumb__ext-label">${o(e.name,e.mimeType)}</span>
3122
+ <div class="file-thumb__ext" part="file-thumb-ext" data-ext="${o(e.name,e.mimeType)}">
3123
+ <span class="file-thumb__ext-label" part="file-thumb-ext-label">${o(e.name,e.mimeType)}</span>
3121
3124
  </div>
3122
3125
  `}
3123
3126
  ${e.isUploading?i`
3124
- <div class="file-thumb__spinner" aria-label="${n.input.uploadingLabel}">
3127
+ <div class="file-thumb__spinner" part="file-thumb-spinner" aria-label="${n.input.uploadingLabel}">
3125
3128
  <span class="file-thumb__spinner-ring"></span>
3126
3129
  </div>
3127
3130
  `:""}
3128
3131
  <button
3129
3132
  type="button"
3130
3133
  class="file-thumb__remove"
3134
+ part="file-thumb-remove"
3131
3135
  aria-label="${n.input.removeFileLabel}"
3132
3136
  title="${n.input.removeFileLabel}"
3133
3137
  @click=${i=>{i.stopPropagation(),t(e.id)}}
@@ -3163,8 +3167,8 @@ function(e){const t=e.match(/\[ERROR_START\]\[ERROR_TITLE_START\]([\s\S]*?)\[ERR
3163
3167
  `)}
3164
3168
  </div>
3165
3169
  `}function Ut(e,t){return i`
3166
- <div class="action-buttons-row">
3167
- <div class="action-buttons-left">
3170
+ <div class="action-buttons-row" part="actions">
3171
+ <div class="action-buttons-left" part="actions-left">
3168
3172
  ${e.enableFileUpload?function(e,t){return i`
3169
3173
  <nr-dropdown
3170
3174
  .items=${e.fileUploadItems}
@@ -3212,11 +3216,12 @@ function(e){const t=e.match(/\[ERROR_START\]\[ERROR_TITLE_START\]([\s\S]*?)\[ERR
3212
3216
  `}(e,t):n}
3213
3217
  </div>
3214
3218
 
3215
- <div class="action-buttons-right">
3219
+ <div class="action-buttons-right" part="actions-right">
3216
3220
  ${e.showAudioButton&&!e.isQueryRunning?i`
3217
3221
  <!-- Speech-to-text: mic + keyboard indicator -->
3218
3222
  <button
3219
3223
  class="audio-mic-btn"
3224
+ part="audio-mic-button audio-mic-transcribe"
3220
3225
  title="${e.i18n.audio.recordSpeechLabel}"
3221
3226
  ?disabled=${e.disabled}
3222
3227
  @click=${()=>{var e;return null===(e=t.onAudioStart)||void 0===e?void 0:e.call(t,"transcribe")}}
@@ -3235,6 +3240,7 @@ function(e){const t=e.match(/\[ERROR_START\]\[ERROR_TITLE_START\]([\s\S]*?)\[ERR
3235
3240
  <!-- Voice message: mic + waveform indicator -->
3236
3241
  <button
3237
3242
  class="audio-mic-btn"
3243
+ part="audio-mic-button audio-mic-voice"
3238
3244
  title="${e.i18n.audio.sendVoiceMessageLabel}"
3239
3245
  ?disabled=${e.disabled}
3240
3246
  @click=${()=>{var e;return null===(e=t.onAudioStart)||void 0===e?void 0:e.call(t,"message")}}
@@ -3276,9 +3282,10 @@ function(e){const t=e.match(/\[ERROR_START\]\[ERROR_TITLE_START\]([\s\S]*?)\[ERR
3276
3282
  </svg>`:i`<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
3277
3283
  <path d="m22 2-7 20-4-9-9-4Z"/><path d="M22 2 11 13"/>
3278
3284
  </svg>`;return i`
3279
- <div class="audio-recording-bar">
3285
+ <div class="audio-recording-bar" part="audio-recording-bar">
3280
3286
  <button
3281
3287
  class="audio-rec-cancel"
3288
+ part="audio-cancel-button"
3282
3289
  title="${e.i18n.audio.cancelRecordingLabel}"
3283
3290
  @click=${t.onAudioCancel}
3284
3291
  aria-label="${e.i18n.audio.cancelRecordingLabel}"
@@ -3290,22 +3297,23 @@ function(e){const t=e.match(/\[ERROR_START\]\[ERROR_TITLE_START\]([\s\S]*?)\[ERR
3290
3297
  </svg>
3291
3298
  </button>
3292
3299
 
3293
- <div class="audio-rec-indicator">
3294
- <span class="audio-rec-dot"></span>
3295
- <div class="audio-rec-wave">
3300
+ <div class="audio-rec-indicator" part="audio-indicator">
3301
+ <span class="audio-rec-dot" part="audio-dot"></span>
3302
+ <div class="audio-rec-wave" part="audio-wave">
3296
3303
  ${a.map(e=>i`
3297
- <div class="audio-rec-bar" style=${d({height:`${Math.round(24*e)}px`})}></div>
3304
+ <div class="audio-rec-bar" part="audio-bar" style=${d({height:`${Math.round(24*e)}px`})}></div>
3298
3305
  `)}
3299
3306
  </div>
3300
- <span class="audio-rec-time">${n}</span>
3307
+ <span class="audio-rec-time" part="audio-time">${n}</span>
3301
3308
  </div>
3302
3309
 
3303
- <span class="audio-rec-mode-label">
3310
+ <span class="audio-rec-mode-label" part="audio-mode-label">
3304
3311
  ${o?e.i18n.audio.speechToTextLabel:e.i18n.audio.voiceMessageLabel}
3305
3312
  </span>
3306
3313
 
3307
3314
  <button
3308
3315
  class="audio-rec-send ${o?"audio-rec-send--transcribe":""}"
3316
+ part="audio-send-button"
3309
3317
  title="${s}"
3310
3318
  @click=${t.onAudioSend}
3311
3319
  aria-label="${s}"
@@ -3323,7 +3331,7 @@ function(e){const t=e.match(/\[ERROR_START\]\[ERROR_TITLE_START\]([\s\S]*?)\[ERR
3323
3331
  ${e.uploadedFiles.length>0?Bt(e.uploadedFiles,t.onFileRemove,e.i18n,t.onFileClick):n}
3324
3332
 
3325
3333
  <!-- Input area -->
3326
- <div class="input-row">
3334
+ <div class="input-row" part="input-row">
3327
3335
  <div
3328
3336
  class="input-box__input"
3329
3337
  part="input"
@@ -3358,6 +3366,7 @@ function(e){const t=e.match(/\[ERROR_START\]\[ERROR_TITLE_START\]([\s\S]*?)\[ERR
3358
3366
  ${t.editingThreadId===e.id&&n.onRenameThread?i`
3359
3367
  <input
3360
3368
  class="thread-item__rename-input"
3369
+ part="thread-rename-input"
3361
3370
  type="text"
3362
3371
  .value=${e.title||""}
3363
3372
  @click=${e=>e.stopPropagation()}
@@ -3365,9 +3374,9 @@ function(e){const t=e.match(/\[ERROR_START\]\[ERROR_TITLE_START\]([\s\S]*?)\[ERR
3365
3374
  @blur=${t=>{const i=t.target.value.trim();i&&i!==e.title?n.onRenameThread(e.id,i):n.onRenameThread(e.id,e.title||"")}}
3366
3375
  />
3367
3376
  `:i`
3368
- <div class="thread-item__title">${e.title||t.i18n.threads.newChatTitle}</div>
3377
+ <div class="thread-item__title" part="thread-title">${e.title||t.i18n.threads.newChatTitle}</div>
3369
3378
  `}
3370
- <div class="thread-item__actions">
3379
+ <div class="thread-item__actions" part="thread-actions">
3371
3380
  ${n.onBookmarkThread&&e.bookmarked?i`
3372
3381
  <button
3373
3382
  class="thread-item__action-btn thread-item__bookmark--active"
@@ -3401,10 +3410,10 @@ function(e){const t=e.match(/\[ERROR_START\]\[ERROR_TITLE_START\]([\s\S]*?)\[ERR
3401
3410
  `:""}
3402
3411
  </div>
3403
3412
  </div>
3404
- <div class="thread-item__preview">
3413
+ <div class="thread-item__preview" part="thread-preview">
3405
3414
  ${o}
3406
3415
  </div>
3407
- <div class="thread-item__timestamp">${_t(e.updatedAt)}</div>
3416
+ <div class="thread-item__timestamp" part="thread-timestamp">${_t(e.updatedAt)}</div>
3408
3417
  </div>
3409
3418
  `}function qt(e,t){if(!e.isOpen||!e.file)return n;const a=e.file,o=a.mimeType.startsWith("image/"),s="application/pdf"===a.mimeType||a.name.toLowerCase().endsWith(".pdf");return i`
3410
3419
  <nr-modal
@@ -3464,15 +3473,15 @@ function(e){if(0===e)return"0 Bytes";const t=Math.floor(Math.log(e)/Math.log(102
3464
3473
  */function Gt(e,t){var a;if(!e.isOpen||!e.artifact)return n;const o=e.artifact,s=(r=o.language,Rt[r]||r.charAt(0).toUpperCase()+r.slice(1));var r;return i`
3465
3474
  <div class="artifact-panel" part="artifact-panel">
3466
3475
  <div class="artifact-panel__resize-handle" part="artifact-panel-resize-handle">
3467
- <div class="artifact-panel__resize-bar"></div>
3476
+ <div class="artifact-panel__resize-bar" part="artifact-panel-resize-bar"></div>
3468
3477
  </div>
3469
- <div class="artifact-panel__body">
3470
- <div class="artifact-panel__header">
3471
- <div class="artifact-panel__header-info">
3472
- <nr-tag size="small" class="artifact-panel__lang-badge">${s}</nr-tag>
3473
- <span class="artifact-panel__title">${o.title}</span>
3478
+ <div class="artifact-panel__body" part="artifact-panel-body">
3479
+ <div class="artifact-panel__header" part="artifact-panel-header">
3480
+ <div class="artifact-panel__header-info" part="artifact-panel-header-info">
3481
+ <nr-tag size="small" class="artifact-panel__lang-badge" part="artifact-panel-lang">${s}</nr-tag>
3482
+ <span class="artifact-panel__title" part="artifact-panel-title">${o.title}</span>
3474
3483
  </div>
3475
- <div class="artifact-panel__actions">
3484
+ <div class="artifact-panel__actions" part="artifact-panel-actions">
3476
3485
  <nr-button
3477
3486
  type="text"
3478
3487
  size="small"
@@ -3491,8 +3500,8 @@ function(e){if(0===e)return"0 Bytes";const t=Math.floor(Math.log(e)/Math.log(102
3491
3500
  ></nr-button>
3492
3501
  </div>
3493
3502
  </div>
3494
- <div class="artifact-panel__content">
3495
- ${(null===(a=e.renderContent)||void 0===a?void 0:a.call(e,o))||function(e){switch(e.language){case"json":{let t;try{t=JSON.stringify(JSON.parse(e.content),null,2)}catch(i){t=e.content}return i`<pre class="artifact-panel__code"><code>${t}</code></pre>`}case"md":case"markdown":return i`<div class="artifact-panel__rendered-md">${u(function(e){let t=function(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;")}(e);return t=t.replaceAll(/```([\s\S]*?)```/g,'<pre class="md-code"><code>$1</code></pre>'),t=t.replaceAll(/`([^`]+)`/g,'<code class="md-inline-code">$1</code>'),t=t.replaceAll(/^###[^\S\n]+(.+)$/gm,"<h3>$1</h3>"),t=t.replaceAll(/^##[^\S\n]+(.+)$/gm,"<h2>$1</h2>"),t=t.replaceAll(/^#[^\S\n]+(.+)$/gm,"<h1>$1</h1>"),t=t.replaceAll(/\*\*([^*]+)\*\*/g,"<strong>$1</strong>"),t=t.replaceAll(/\*([^*]+)\*/g,"<em>$1</em>"),t=t.replaceAll(/\[([^\]]+)\]\(([^)]+)\)/g,'<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>'),t=t.replaceAll(/(?:^|\n)-\s+(.+)(?=\n|$)/g,(e,t)=>`\n<ul><li>${t}</li></ul>`),t=t.replaceAll(/<ul>\s*<li>([\s\S]*?)<\/li>\s*<\/ul>\n<ul>/g,"<ul><li>$1</li>"),t=t.split(/\n\n+/).map(e=>/^(<h\d|<pre|<ul|<ol|<blockquote)/.test(e.trim())?e:`<p>${e.replaceAll("\n","<br/>")}</p>`).join("\n"),t}(e.content))}</div>`;case"html":return i`<div class="artifact-panel__rendered-html">${u(e.content)}</div>`;case"text":case"txt":return i`<div class="artifact-panel__rendered-text">${e.content}</div>`;default:return i`<pre class="artifact-panel__code"><code>${e.content}</code></pre>`}}(o)}
3503
+ <div class="artifact-panel__content" part="artifact-panel-content">
3504
+ ${(null===(a=e.renderContent)||void 0===a?void 0:a.call(e,o))||function(e){switch(e.language){case"json":{let t;try{t=JSON.stringify(JSON.parse(e.content),null,2)}catch(i){t=e.content}return i`<pre class="artifact-panel__code" part="artifact-panel-code"><code>${t}</code></pre>`}case"md":case"markdown":return i`<div class="artifact-panel__rendered-md" part="artifact-panel-md">${u(function(e){let t=function(e){return e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;")}(e);return t=t.replaceAll(/```([\s\S]*?)```/g,'<pre class="md-code"><code>$1</code></pre>'),t=t.replaceAll(/`([^`]+)`/g,'<code class="md-inline-code">$1</code>'),t=t.replaceAll(/^###[^\S\n]+(.+)$/gm,"<h3>$1</h3>"),t=t.replaceAll(/^##[^\S\n]+(.+)$/gm,"<h2>$1</h2>"),t=t.replaceAll(/^#[^\S\n]+(.+)$/gm,"<h1>$1</h1>"),t=t.replaceAll(/\*\*([^*]+)\*\*/g,"<strong>$1</strong>"),t=t.replaceAll(/\*([^*]+)\*/g,"<em>$1</em>"),t=t.replaceAll(/\[([^\]]+)\]\(([^)]+)\)/g,'<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>'),t=t.replaceAll(/(?:^|\n)-\s+(.+)(?=\n|$)/g,(e,t)=>`\n<ul><li>${t}</li></ul>`),t=t.replaceAll(/<ul>\s*<li>([\s\S]*?)<\/li>\s*<\/ul>\n<ul>/g,"<ul><li>$1</li>"),t=t.split(/\n\n+/).map(e=>/^(<h\d|<pre|<ul|<ol|<blockquote)/.test(e.trim())?e:`<p>${e.replaceAll("\n","<br/>")}</p>`).join("\n"),t}(e.content))}</div>`;case"html":return i`<div class="artifact-panel__rendered-html" part="artifact-panel-html">${u(e.content)}</div>`;case"text":case"txt":return i`<div class="artifact-panel__rendered-text" part="artifact-panel-text">${e.content}</div>`;default:return i`<pre class="artifact-panel__code" part="artifact-panel-code"><code>${e.content}</code></pre>`}}(o)}
3496
3505
  </div>
3497
3506
  </div>
3498
3507
  </div>
@@ -3504,16 +3513,16 @@ function(e){if(0===e)return"0 Bytes";const t=Math.floor(Math.log(e)/Math.log(102
3504
3513
  */function Wt(e,t){return!1!==e.showMessages?i`
3505
3514
  <div class="chatbot-content" part="content">
3506
3515
  ${jt(e.messages,Ft(e.chatStarted,e.suggestions,t.suggestion,e.i18n),e.isTyping?function(e,t,a){if(!e)return n;const o=t===Mt.Dots?i`
3507
- <div class="dots">
3516
+ <div class="dots" part="typing-dots">
3508
3517
  <span></span>
3509
3518
  <span></span>
3510
3519
  <span></span>
3511
3520
  </div>
3512
- `:i`<div class="spinner"></div>`;return i`
3521
+ `:i`<div class="spinner" part="typing-spinner"></div>`;return i`
3513
3522
  <div class="message bot loading" part="typing-indicator">
3514
- <div class="message__content">
3523
+ <div class="message__content" part="typing-content">
3515
3524
  ${o}
3516
- ${a?i`<span class="loading-text">${a.split("").map((e,t)=>i`<span class="loading-text__char" style="animation-delay:${.04*t}s">${" "===e?" ":e}</span>`)}</span>`:n}
3525
+ ${a?i`<span class="loading-text" part="typing-text">${a.split("").map((e,t)=>i`<span class="loading-text__char" style="animation-delay:${.04*t}s">${" "===e?" ":e}</span>`)}</span>`:n}
3517
3526
  </div>
3518
3527
  </div>
3519
3528
  `}(e.isTyping,e.loadingIndicator||Mt.Spinner,e.loadingText):n,t.message,e.i18n)}
@@ -3533,14 +3542,14 @@ function(e){if(0===e)return"0 Bytes";const t=Math.floor(Math.log(e)/Math.log(102
3533
3542
 
3534
3543
  ${e.enableThreads&&e.isThreadSidebarOpen&&e.threadSidebar&&t.threadSidebar?function(e,t){const a=e.threads.filter(e=>e.bookmarked),o=e.threads.filter(e=>!e.bookmarked);return i`
3535
3544
  <div class="thread-sidebar" part="thread-sidebar">
3536
- <div class="thread-sidebar__header">
3537
- <h3>${e.i18n.threads.conversationsTitle}</h3>
3545
+ <div class="thread-sidebar__header" part="thread-sidebar-header">
3546
+ <h3 part="thread-sidebar-title">${e.i18n.threads.conversationsTitle}</h3>
3538
3547
  </div>
3539
3548
 
3540
- <div class="thread-list">
3549
+ <div class="thread-list" part="thread-list">
3541
3550
  ${a.length>0?i`
3542
- <div class="thread-section" part="thread-section-bookmarks">
3543
- <div class="thread-section__label">
3551
+ <div class="thread-section" part="thread-section thread-section-bookmarks">
3552
+ <div class="thread-section__label" part="thread-section-label">
3544
3553
  <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg>
3545
3554
  ${e.i18n.threads.bookmarksLabel}
3546
3555
  </div>
@@ -3549,11 +3558,11 @@ function(e){if(0===e)return"0 Bytes";const t=Math.floor(Math.log(e)/Math.log(102
3549
3558
  `:n}
3550
3559
  ${o.length>0||0===a.length?i`
3551
3560
  ${a.length>0?i`
3552
- <div class="thread-section__label">${e.i18n.threads.allConversationsLabel}</div>
3561
+ <div class="thread-section__label" part="thread-section-label">${e.i18n.threads.allConversationsLabel}</div>
3553
3562
  `:n}
3554
3563
  ${b(o,e=>e.id,i=>Ht(i,e,t))}
3555
3564
  ${0===o.length&&0===a.length?i`
3556
- <p class="empty-msg">${e.i18n.threads.noConversationsLabel}</p>
3565
+ <p class="empty-msg" part="thread-empty">${e.i18n.threads.noConversationsLabel}</p>
3557
3566
  `:n}
3558
3567
  `:n}
3559
3568
  </div>
@@ -3607,9 +3616,9 @@ function(e){if(0===e)return"0 Bytes";const t=Math.floor(Math.log(e)/Math.log(102
3607
3616
  class="file-upload-area ${e.isDragging?"file-upload-area--dragging":""}"
3608
3617
  part="file-upload-area"
3609
3618
  >
3610
- <div class="file-upload-area__content">
3611
- <nr-icon name="upload" size="xlarge"></nr-icon>
3612
- <div class="file-upload-area__text">
3619
+ <div class="file-upload-area__content" part="file-upload-area-content">
3620
+ <nr-icon name="upload" size="xlarge" part="file-upload-area-icon"></nr-icon>
3621
+ <div class="file-upload-area__text" part="file-upload-area-text">
3613
3622
  ${e.label}
3614
3623
  </div>
3615
3624
  </div>
@@ -2062,6 +2062,7 @@ function rt(t){if(!t)return"";const e=t instanceof Date?t:new Date(t);if(Number.
2062
2062
  size="small"
2063
2063
  color="#9ca3af"
2064
2064
  class="message__copy"
2065
+ part="message-copy"
2065
2066
  @click=${()=>n.onCopy(t)}
2066
2067
  @keydown=${e=>n.onCopyKeydown(e,t)}
2067
2068
  title="${o.messages.copyMessageLabel}"
@@ -2088,7 +2089,7 @@ function rt(t){if(!t)return"";const e=t instanceof Date?t:new Date(t);if(Number.
2088
2089
  ${0===t.length?function(t){return i`
2089
2090
  <div class="empty-state" part="empty-state">
2090
2091
  <slot name="empty-state">
2091
- <div class="empty-state__content">
2092
+ <div class="empty-state__content" part="empty-state-content">
2092
2093
  ${t.messages.startConversationLabel}
2093
2094
  </div>
2094
2095
  </slot>
@@ -2137,6 +2138,7 @@ function rt(t){if(!t)return"";const e=t instanceof Date?t:new Date(t);if(Number.
2137
2138
  <div
2138
2139
  slot="trigger"
2139
2140
  class="file-thumb ${t.isUploading?"file-thumb--uploading":""}"
2141
+ part="file-thumb"
2140
2142
  role="button"
2141
2143
  tabindex="0"
2142
2144
  title="${t.name}"
@@ -2145,22 +2147,24 @@ function rt(t){if(!t)return"";const e=t instanceof Date?t:new Date(t);if(Number.
2145
2147
  ${r(t.mimeType)&&(t.previewUrl||t.url)?i`
2146
2148
  <img
2147
2149
  class="file-thumb__image"
2150
+ part="file-thumb-image"
2148
2151
  src="${t.previewUrl||t.url}"
2149
2152
  alt="${t.name}"
2150
2153
  />
2151
2154
  `:i`
2152
- <div class="file-thumb__ext" data-ext="${s(t.name,t.mimeType)}">
2153
- <span class="file-thumb__ext-label">${s(t.name,t.mimeType)}</span>
2155
+ <div class="file-thumb__ext" part="file-thumb-ext" data-ext="${s(t.name,t.mimeType)}">
2156
+ <span class="file-thumb__ext-label" part="file-thumb-ext-label">${s(t.name,t.mimeType)}</span>
2154
2157
  </div>
2155
2158
  `}
2156
2159
  ${t.isUploading?i`
2157
- <div class="file-thumb__spinner" aria-label="${n.input.uploadingLabel}">
2160
+ <div class="file-thumb__spinner" part="file-thumb-spinner" aria-label="${n.input.uploadingLabel}">
2158
2161
  <span class="file-thumb__spinner-ring"></span>
2159
2162
  </div>
2160
2163
  `:""}
2161
2164
  <button
2162
2165
  type="button"
2163
2166
  class="file-thumb__remove"
2167
+ part="file-thumb-remove"
2164
2168
  aria-label="${n.input.removeFileLabel}"
2165
2169
  title="${n.input.removeFileLabel}"
2166
2170
  @click=${i=>{i.stopPropagation(),e(t.id)}}
@@ -2196,8 +2200,8 @@ function rt(t){if(!t)return"";const e=t instanceof Date?t:new Date(t);if(Number.
2196
2200
  `)}
2197
2201
  </div>
2198
2202
  `}function vt(t,n){return i`
2199
- <div class="action-buttons-row">
2200
- <div class="action-buttons-left">
2203
+ <div class="action-buttons-row" part="actions">
2204
+ <div class="action-buttons-left" part="actions-left">
2201
2205
  ${t.enableFileUpload?function(t,e){return i`
2202
2206
  <nr-dropdown
2203
2207
  .items=${t.fileUploadItems}
@@ -2245,11 +2249,12 @@ function rt(t){if(!t)return"";const e=t instanceof Date?t:new Date(t);if(Number.
2245
2249
  `}(t,n):e}
2246
2250
  </div>
2247
2251
 
2248
- <div class="action-buttons-right">
2252
+ <div class="action-buttons-right" part="actions-right">
2249
2253
  ${t.showAudioButton&&!t.isQueryRunning?i`
2250
2254
  <!-- Speech-to-text: mic + keyboard indicator -->
2251
2255
  <button
2252
2256
  class="audio-mic-btn"
2257
+ part="audio-mic-button audio-mic-transcribe"
2253
2258
  title="${t.i18n.audio.recordSpeechLabel}"
2254
2259
  ?disabled=${t.disabled}
2255
2260
  @click=${()=>{var t;return null===(t=n.onAudioStart)||void 0===t?void 0:t.call(n,"transcribe")}}
@@ -2268,6 +2273,7 @@ function rt(t){if(!t)return"";const e=t instanceof Date?t:new Date(t);if(Number.
2268
2273
  <!-- Voice message: mic + waveform indicator -->
2269
2274
  <button
2270
2275
  class="audio-mic-btn"
2276
+ part="audio-mic-button audio-mic-voice"
2271
2277
  title="${t.i18n.audio.sendVoiceMessageLabel}"
2272
2278
  ?disabled=${t.disabled}
2273
2279
  @click=${()=>{var t;return null===(t=n.onAudioStart)||void 0===t?void 0:t.call(n,"message")}}
@@ -2309,9 +2315,10 @@ function rt(t){if(!t)return"";const e=t instanceof Date?t:new Date(t);if(Number.
2309
2315
  </svg>`:i`<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
2310
2316
  <path d="m22 2-7 20-4-9-9-4Z"/><path d="M22 2 11 13"/>
2311
2317
  </svg>`;return i`
2312
- <div class="audio-recording-bar">
2318
+ <div class="audio-recording-bar" part="audio-recording-bar">
2313
2319
  <button
2314
2320
  class="audio-rec-cancel"
2321
+ part="audio-cancel-button"
2315
2322
  title="${t.i18n.audio.cancelRecordingLabel}"
2316
2323
  @click=${e.onAudioCancel}
2317
2324
  aria-label="${t.i18n.audio.cancelRecordingLabel}"
@@ -2323,22 +2330,23 @@ function rt(t){if(!t)return"";const e=t instanceof Date?t:new Date(t);if(Number.
2323
2330
  </svg>
2324
2331
  </button>
2325
2332
 
2326
- <div class="audio-rec-indicator">
2327
- <span class="audio-rec-dot"></span>
2328
- <div class="audio-rec-wave">
2333
+ <div class="audio-rec-indicator" part="audio-indicator">
2334
+ <span class="audio-rec-dot" part="audio-dot"></span>
2335
+ <div class="audio-rec-wave" part="audio-wave">
2329
2336
  ${o.map(t=>i`
2330
- <div class="audio-rec-bar" style=${c({height:`${Math.round(24*t)}px`})}></div>
2337
+ <div class="audio-rec-bar" part="audio-bar" style=${c({height:`${Math.round(24*t)}px`})}></div>
2331
2338
  `)}
2332
2339
  </div>
2333
- <span class="audio-rec-time">${n}</span>
2340
+ <span class="audio-rec-time" part="audio-time">${n}</span>
2334
2341
  </div>
2335
2342
 
2336
- <span class="audio-rec-mode-label">
2343
+ <span class="audio-rec-mode-label" part="audio-mode-label">
2337
2344
  ${s?t.i18n.audio.speechToTextLabel:t.i18n.audio.voiceMessageLabel}
2338
2345
  </span>
2339
2346
 
2340
2347
  <button
2341
2348
  class="audio-rec-send ${s?"audio-rec-send--transcribe":""}"
2349
+ part="audio-send-button"
2342
2350
  title="${r}"
2343
2351
  @click=${e.onAudioSend}
2344
2352
  aria-label="${r}"
@@ -2356,7 +2364,7 @@ function rt(t){if(!t)return"";const e=t instanceof Date?t:new Date(t);if(Number.
2356
2364
  ${t.uploadedFiles.length>0?gt(t.uploadedFiles,n.onFileRemove,t.i18n,n.onFileClick):e}
2357
2365
 
2358
2366
  <!-- Input area -->
2359
- <div class="input-row">
2367
+ <div class="input-row" part="input-row">
2360
2368
  <div
2361
2369
  class="input-box__input"
2362
2370
  part="input"
@@ -2391,6 +2399,7 @@ function rt(t){if(!t)return"";const e=t instanceof Date?t:new Date(t);if(Number.
2391
2399
  ${e.editingThreadId===t.id&&n.onRenameThread?i`
2392
2400
  <input
2393
2401
  class="thread-item__rename-input"
2402
+ part="thread-rename-input"
2394
2403
  type="text"
2395
2404
  .value=${t.title||""}
2396
2405
  @click=${t=>t.stopPropagation()}
@@ -2398,9 +2407,9 @@ function rt(t){if(!t)return"";const e=t instanceof Date?t:new Date(t);if(Number.
2398
2407
  @blur=${e=>{const i=e.target.value.trim();i&&i!==t.title?n.onRenameThread(t.id,i):n.onRenameThread(t.id,t.title||"")}}
2399
2408
  />
2400
2409
  `:i`
2401
- <div class="thread-item__title">${t.title||e.i18n.threads.newChatTitle}</div>
2410
+ <div class="thread-item__title" part="thread-title">${t.title||e.i18n.threads.newChatTitle}</div>
2402
2411
  `}
2403
- <div class="thread-item__actions">
2412
+ <div class="thread-item__actions" part="thread-actions">
2404
2413
  ${n.onBookmarkThread&&t.bookmarked?i`
2405
2414
  <button
2406
2415
  class="thread-item__action-btn thread-item__bookmark--active"
@@ -2434,10 +2443,10 @@ function rt(t){if(!t)return"";const e=t instanceof Date?t:new Date(t);if(Number.
2434
2443
  `:""}
2435
2444
  </div>
2436
2445
  </div>
2437
- <div class="thread-item__preview">
2446
+ <div class="thread-item__preview" part="thread-preview">
2438
2447
  ${s}
2439
2448
  </div>
2440
- <div class="thread-item__timestamp">${rt(t.updatedAt)}</div>
2449
+ <div class="thread-item__timestamp" part="thread-timestamp">${rt(t.updatedAt)}</div>
2441
2450
  </div>
2442
2451
  `}function xt(t,n){if(!t.isOpen||!t.file)return e;const o=t.file,s=o.mimeType.startsWith("image/"),r="application/pdf"===o.mimeType||o.name.toLowerCase().endsWith(".pdf");return i`
2443
2452
  <nr-modal
@@ -2497,15 +2506,15 @@ function(t){if(0===t)return"0 Bytes";const e=Math.floor(Math.log(t)/Math.log(102
2497
2506
  */function wt(t,n){var o;if(!t.isOpen||!t.artifact)return e;const s=t.artifact,r=dt(s.language);return i`
2498
2507
  <div class="artifact-panel" part="artifact-panel">
2499
2508
  <div class="artifact-panel__resize-handle" part="artifact-panel-resize-handle">
2500
- <div class="artifact-panel__resize-bar"></div>
2509
+ <div class="artifact-panel__resize-bar" part="artifact-panel-resize-bar"></div>
2501
2510
  </div>
2502
- <div class="artifact-panel__body">
2503
- <div class="artifact-panel__header">
2504
- <div class="artifact-panel__header-info">
2505
- <nr-tag size="small" class="artifact-panel__lang-badge">${r}</nr-tag>
2506
- <span class="artifact-panel__title">${s.title}</span>
2511
+ <div class="artifact-panel__body" part="artifact-panel-body">
2512
+ <div class="artifact-panel__header" part="artifact-panel-header">
2513
+ <div class="artifact-panel__header-info" part="artifact-panel-header-info">
2514
+ <nr-tag size="small" class="artifact-panel__lang-badge" part="artifact-panel-lang">${r}</nr-tag>
2515
+ <span class="artifact-panel__title" part="artifact-panel-title">${s.title}</span>
2507
2516
  </div>
2508
- <div class="artifact-panel__actions">
2517
+ <div class="artifact-panel__actions" part="artifact-panel-actions">
2509
2518
  <nr-button
2510
2519
  type="text"
2511
2520
  size="small"
@@ -2524,8 +2533,8 @@ function(t){if(0===t)return"0 Bytes";const e=Math.floor(Math.log(t)/Math.log(102
2524
2533
  ></nr-button>
2525
2534
  </div>
2526
2535
  </div>
2527
- <div class="artifact-panel__content">
2528
- ${(null===(o=t.renderContent)||void 0===o?void 0:o.call(t,s))||function(t){switch(t.language){case"json":{let e;try{e=JSON.stringify(JSON.parse(t.content),null,2)}catch(i){e=t.content}return i`<pre class="artifact-panel__code"><code>${e}</code></pre>`}case"md":case"markdown":return i`<div class="artifact-panel__rendered-md">${l(ct(t.content))}</div>`;case"html":return i`<div class="artifact-panel__rendered-html">${l(t.content)}</div>`;case"text":case"txt":return i`<div class="artifact-panel__rendered-text">${t.content}</div>`;default:return i`<pre class="artifact-panel__code"><code>${t.content}</code></pre>`}}(s)}
2536
+ <div class="artifact-panel__content" part="artifact-panel-content">
2537
+ ${(null===(o=t.renderContent)||void 0===o?void 0:o.call(t,s))||function(t){switch(t.language){case"json":{let e;try{e=JSON.stringify(JSON.parse(t.content),null,2)}catch(i){e=t.content}return i`<pre class="artifact-panel__code" part="artifact-panel-code"><code>${e}</code></pre>`}case"md":case"markdown":return i`<div class="artifact-panel__rendered-md" part="artifact-panel-md">${l(ct(t.content))}</div>`;case"html":return i`<div class="artifact-panel__rendered-html" part="artifact-panel-html">${l(t.content)}</div>`;case"text":case"txt":return i`<div class="artifact-panel__rendered-text" part="artifact-panel-text">${t.content}</div>`;default:return i`<pre class="artifact-panel__code" part="artifact-panel-code"><code>${t.content}</code></pre>`}}(s)}
2529
2538
  </div>
2530
2539
  </div>
2531
2540
  </div>
@@ -2537,16 +2546,16 @@ function(t){if(0===t)return"0 Bytes";const e=Math.floor(Math.log(t)/Math.log(102
2537
2546
  */function _t(t,n){return!1!==t.showMessages?i`
2538
2547
  <div class="chatbot-content" part="content">
2539
2548
  ${ft(t.messages,mt(t.chatStarted,t.suggestions,n.suggestion,t.i18n),t.isTyping?function(t,n,o){if(!t)return e;const s=n===V.Dots?i`
2540
- <div class="dots">
2549
+ <div class="dots" part="typing-dots">
2541
2550
  <span></span>
2542
2551
  <span></span>
2543
2552
  <span></span>
2544
2553
  </div>
2545
- `:i`<div class="spinner"></div>`;return i`
2554
+ `:i`<div class="spinner" part="typing-spinner"></div>`;return i`
2546
2555
  <div class="message bot loading" part="typing-indicator">
2547
- <div class="message__content">
2556
+ <div class="message__content" part="typing-content">
2548
2557
  ${s}
2549
- ${o?i`<span class="loading-text">${o.split("").map((t,e)=>i`<span class="loading-text__char" style="animation-delay:${.04*e}s">${" "===t?" ":t}</span>`)}</span>`:e}
2558
+ ${o?i`<span class="loading-text" part="typing-text">${o.split("").map((t,e)=>i`<span class="loading-text__char" style="animation-delay:${.04*e}s">${" "===t?" ":t}</span>`)}</span>`:e}
2550
2559
  </div>
2551
2560
  </div>
2552
2561
  `}(t.isTyping,t.loadingIndicator||V.Spinner,t.loadingText):e,n.message,t.i18n)}
@@ -2566,14 +2575,14 @@ function(t){if(0===t)return"0 Bytes";const e=Math.floor(Math.log(t)/Math.log(102
2566
2575
 
2567
2576
  ${t.enableThreads&&t.isThreadSidebarOpen&&t.threadSidebar&&n.threadSidebar?function(t,n){const o=t.threads.filter(t=>t.bookmarked),s=t.threads.filter(t=>!t.bookmarked);return i`
2568
2577
  <div class="thread-sidebar" part="thread-sidebar">
2569
- <div class="thread-sidebar__header">
2570
- <h3>${t.i18n.threads.conversationsTitle}</h3>
2578
+ <div class="thread-sidebar__header" part="thread-sidebar-header">
2579
+ <h3 part="thread-sidebar-title">${t.i18n.threads.conversationsTitle}</h3>
2571
2580
  </div>
2572
2581
 
2573
- <div class="thread-list">
2582
+ <div class="thread-list" part="thread-list">
2574
2583
  ${o.length>0?i`
2575
- <div class="thread-section" part="thread-section-bookmarks">
2576
- <div class="thread-section__label">
2584
+ <div class="thread-section" part="thread-section thread-section-bookmarks">
2585
+ <div class="thread-section__label" part="thread-section-label">
2577
2586
  <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg>
2578
2587
  ${t.i18n.threads.bookmarksLabel}
2579
2588
  </div>
@@ -2582,11 +2591,11 @@ function(t){if(0===t)return"0 Bytes";const e=Math.floor(Math.log(t)/Math.log(102
2582
2591
  `:e}
2583
2592
  ${s.length>0||0===o.length?i`
2584
2593
  ${o.length>0?i`
2585
- <div class="thread-section__label">${t.i18n.threads.allConversationsLabel}</div>
2594
+ <div class="thread-section__label" part="thread-section-label">${t.i18n.threads.allConversationsLabel}</div>
2586
2595
  `:e}
2587
2596
  ${d(s,t=>t.id,e=>yt(e,t,n))}
2588
2597
  ${0===s.length&&0===o.length?i`
2589
- <p class="empty-msg">${t.i18n.threads.noConversationsLabel}</p>
2598
+ <p class="empty-msg" part="thread-empty">${t.i18n.threads.noConversationsLabel}</p>
2590
2599
  `:e}
2591
2600
  `:e}
2592
2601
  </div>
@@ -2640,9 +2649,9 @@ function(t){if(0===t)return"0 Bytes";const e=Math.floor(Math.log(t)/Math.log(102
2640
2649
  class="file-upload-area ${t.isDragging?"file-upload-area--dragging":""}"
2641
2650
  part="file-upload-area"
2642
2651
  >
2643
- <div class="file-upload-area__content">
2644
- <nr-icon name="upload" size="xlarge"></nr-icon>
2645
- <div class="file-upload-area__text">
2652
+ <div class="file-upload-area__content" part="file-upload-area-content">
2653
+ <nr-icon name="upload" size="xlarge" part="file-upload-area-icon"></nr-icon>
2654
+ <div class="file-upload-area__text" part="file-upload-area-text">
2646
2655
  ${t.label}
2647
2656
  </div>
2648
2657
  </div>
@@ -17,18 +17,18 @@ function renderArtifactContent(artifact) {
17
17
  catch (_a) {
18
18
  prettyContent = artifact.content;
19
19
  }
20
- return html `<pre class="artifact-panel__code"><code>${prettyContent}</code></pre>`;
20
+ return html `<pre class="artifact-panel__code" part="artifact-panel-code"><code>${prettyContent}</code></pre>`;
21
21
  }
22
22
  case 'md':
23
23
  case 'markdown':
24
- return html `<div class="artifact-panel__rendered-md">${unsafeHTML(renderMarkdown(artifact.content))}</div>`;
24
+ return html `<div class="artifact-panel__rendered-md" part="artifact-panel-md">${unsafeHTML(renderMarkdown(artifact.content))}</div>`;
25
25
  case 'html':
26
- return html `<div class="artifact-panel__rendered-html">${unsafeHTML(artifact.content)}</div>`;
26
+ return html `<div class="artifact-panel__rendered-html" part="artifact-panel-html">${unsafeHTML(artifact.content)}</div>`;
27
27
  case 'text':
28
28
  case 'txt':
29
- return html `<div class="artifact-panel__rendered-text">${artifact.content}</div>`;
29
+ return html `<div class="artifact-panel__rendered-text" part="artifact-panel-text">${artifact.content}</div>`;
30
30
  default:
31
- return html `<pre class="artifact-panel__code"><code>${artifact.content}</code></pre>`;
31
+ return html `<pre class="artifact-panel__code" part="artifact-panel-code"><code>${artifact.content}</code></pre>`;
32
32
  }
33
33
  }
34
34
  /**
@@ -43,15 +43,15 @@ export function renderArtifactPanel(data, handlers) {
43
43
  return html `
44
44
  <div class="artifact-panel" part="artifact-panel">
45
45
  <div class="artifact-panel__resize-handle" part="artifact-panel-resize-handle">
46
- <div class="artifact-panel__resize-bar"></div>
46
+ <div class="artifact-panel__resize-bar" part="artifact-panel-resize-bar"></div>
47
47
  </div>
48
- <div class="artifact-panel__body">
49
- <div class="artifact-panel__header">
50
- <div class="artifact-panel__header-info">
51
- <nr-tag size="small" class="artifact-panel__lang-badge">${langLabel}</nr-tag>
52
- <span class="artifact-panel__title">${artifact.title}</span>
48
+ <div class="artifact-panel__body" part="artifact-panel-body">
49
+ <div class="artifact-panel__header" part="artifact-panel-header">
50
+ <div class="artifact-panel__header-info" part="artifact-panel-header-info">
51
+ <nr-tag size="small" class="artifact-panel__lang-badge" part="artifact-panel-lang">${langLabel}</nr-tag>
52
+ <span class="artifact-panel__title" part="artifact-panel-title">${artifact.title}</span>
53
53
  </div>
54
- <div class="artifact-panel__actions">
54
+ <div class="artifact-panel__actions" part="artifact-panel-actions">
55
55
  <nr-button
56
56
  type="text"
57
57
  size="small"
@@ -70,7 +70,7 @@ export function renderArtifactPanel(data, handlers) {
70
70
  ></nr-button>
71
71
  </div>
72
72
  </div>
73
- <div class="artifact-panel__content">
73
+ <div class="artifact-panel__content" part="artifact-panel-content">
74
74
  ${((_a = data.renderContent) === null || _a === void 0 ? void 0 : _a.call(data, artifact)) || renderArtifactContent(artifact)}
75
75
  </div>
76
76
  </div>
@@ -10,9 +10,9 @@ export function renderFileUploadArea(data) {
10
10
  class="file-upload-area ${data.isDragging ? 'file-upload-area--dragging' : ''}"
11
11
  part="file-upload-area"
12
12
  >
13
- <div class="file-upload-area__content">
14
- <nr-icon name="upload" size="xlarge"></nr-icon>
15
- <div class="file-upload-area__text">
13
+ <div class="file-upload-area__content" part="file-upload-area-content">
14
+ <nr-icon name="upload" size="xlarge" part="file-upload-area-icon"></nr-icon>
15
+ <div class="file-upload-area__text" part="file-upload-area-text">
16
16
  ${data.label}
17
17
  </div>
18
18
  </div>
@@ -45,6 +45,7 @@ function renderContextTags(files, onRemove, i18n, onFileClick) {
45
45
  <div
46
46
  slot="trigger"
47
47
  class="file-thumb ${f.isUploading ? 'file-thumb--uploading' : ''}"
48
+ part="file-thumb"
48
49
  role="button"
49
50
  tabindex="0"
50
51
  title="${f.name}"
@@ -53,22 +54,24 @@ function renderContextTags(files, onRemove, i18n, onFileClick) {
53
54
  ${isImage(f.mimeType) && (f.previewUrl || f.url) ? html `
54
55
  <img
55
56
  class="file-thumb__image"
57
+ part="file-thumb-image"
56
58
  src="${f.previewUrl || f.url}"
57
59
  alt="${f.name}"
58
60
  />
59
61
  ` : html `
60
- <div class="file-thumb__ext" data-ext="${getExtension(f.name, f.mimeType)}">
61
- <span class="file-thumb__ext-label">${getExtension(f.name, f.mimeType)}</span>
62
+ <div class="file-thumb__ext" part="file-thumb-ext" data-ext="${getExtension(f.name, f.mimeType)}">
63
+ <span class="file-thumb__ext-label" part="file-thumb-ext-label">${getExtension(f.name, f.mimeType)}</span>
62
64
  </div>
63
65
  `}
64
66
  ${f.isUploading ? html `
65
- <div class="file-thumb__spinner" aria-label="${i18n.input.uploadingLabel}">
67
+ <div class="file-thumb__spinner" part="file-thumb-spinner" aria-label="${i18n.input.uploadingLabel}">
66
68
  <span class="file-thumb__spinner-ring"></span>
67
69
  </div>
68
70
  ` : ''}
69
71
  <button
70
72
  type="button"
71
73
  class="file-thumb__remove"
74
+ part="file-thumb-remove"
72
75
  aria-label="${i18n.input.removeFileLabel}"
73
76
  title="${i18n.input.removeFileLabel}"
74
77
  @click=${(e) => { e.stopPropagation(); onRemove(f.id); }}
@@ -199,9 +202,10 @@ function renderRecordingBar(data, handlers) {
199
202
  <path d="m22 2-7 20-4-9-9-4Z"/><path d="M22 2 11 13"/>
200
203
  </svg>`;
201
204
  return html `
202
- <div class="audio-recording-bar">
205
+ <div class="audio-recording-bar" part="audio-recording-bar">
203
206
  <button
204
207
  class="audio-rec-cancel"
208
+ part="audio-cancel-button"
205
209
  title="${data.i18n.audio.cancelRecordingLabel}"
206
210
  @click=${handlers.onAudioCancel}
207
211
  aria-label="${data.i18n.audio.cancelRecordingLabel}"
@@ -213,22 +217,23 @@ function renderRecordingBar(data, handlers) {
213
217
  </svg>
214
218
  </button>
215
219
 
216
- <div class="audio-rec-indicator">
217
- <span class="audio-rec-dot"></span>
218
- <div class="audio-rec-wave">
220
+ <div class="audio-rec-indicator" part="audio-indicator">
221
+ <span class="audio-rec-dot" part="audio-dot"></span>
222
+ <div class="audio-rec-wave" part="audio-wave">
219
223
  ${bars.map(v => html `
220
- <div class="audio-rec-bar" style=${styleMap({ height: `${Math.round(v * 24)}px` })}></div>
224
+ <div class="audio-rec-bar" part="audio-bar" style=${styleMap({ height: `${Math.round(v * 24)}px` })}></div>
221
225
  `)}
222
226
  </div>
223
- <span class="audio-rec-time">${duration}</span>
227
+ <span class="audio-rec-time" part="audio-time">${duration}</span>
224
228
  </div>
225
229
 
226
- <span class="audio-rec-mode-label">
230
+ <span class="audio-rec-mode-label" part="audio-mode-label">
227
231
  ${isTranscribe ? data.i18n.audio.speechToTextLabel : data.i18n.audio.voiceMessageLabel}
228
232
  </span>
229
233
 
230
234
  <button
231
235
  class="audio-rec-send ${isTranscribe ? 'audio-rec-send--transcribe' : ''}"
236
+ part="audio-send-button"
232
237
  title="${sendTitle}"
233
238
  @click=${handlers.onAudioSend}
234
239
  aria-label="${sendTitle}"
@@ -243,19 +248,20 @@ function renderRecordingBar(data, handlers) {
243
248
  */
244
249
  function renderActionButtons(data, handlers) {
245
250
  return html `
246
- <div class="action-buttons-row">
247
- <div class="action-buttons-left">
251
+ <div class="action-buttons-row" part="actions">
252
+ <div class="action-buttons-left" part="actions-left">
248
253
  ${data.enableFileUpload ? renderFileUploadButton(data, handlers) : nothing}
249
254
  ${data.enableModuleSelection && data.moduleOptions.length > 0
250
255
  ? renderModuleSelector(data, handlers)
251
256
  : nothing}
252
257
  </div>
253
258
 
254
- <div class="action-buttons-right">
259
+ <div class="action-buttons-right" part="actions-right">
255
260
  ${data.showAudioButton && !data.isQueryRunning ? html `
256
261
  <!-- Speech-to-text: mic + keyboard indicator -->
257
262
  <button
258
263
  class="audio-mic-btn"
264
+ part="audio-mic-button audio-mic-transcribe"
259
265
  title="${data.i18n.audio.recordSpeechLabel}"
260
266
  ?disabled=${data.disabled}
261
267
  @click=${() => { var _a; return (_a = handlers.onAudioStart) === null || _a === void 0 ? void 0 : _a.call(handlers, 'transcribe'); }}
@@ -274,6 +280,7 @@ function renderActionButtons(data, handlers) {
274
280
  <!-- Voice message: mic + waveform indicator -->
275
281
  <button
276
282
  class="audio-mic-btn"
283
+ part="audio-mic-button audio-mic-voice"
277
284
  title="${data.i18n.audio.sendVoiceMessageLabel}"
278
285
  ?disabled=${data.disabled}
279
286
  @click=${() => { var _a; return (_a = handlers.onAudioStart) === null || _a === void 0 ? void 0 : _a.call(handlers, 'message'); }}
@@ -319,7 +326,7 @@ export function renderInputBox(data, handlers) {
319
326
  : nothing}
320
327
 
321
328
  <!-- Input area -->
322
- <div class="input-row">
329
+ <div class="input-row" part="input-row">
323
330
  <div
324
331
  class="input-box__input"
325
332
  part="input"
@@ -144,6 +144,7 @@ export function renderMessage(message, handlers, i18n) {
144
144
  size="small"
145
145
  color="#9ca3af"
146
146
  class="message__copy"
147
+ part="message-copy"
147
148
  @click=${() => handlers.onCopy(message)}
148
149
  @keydown=${(e) => handlers.onCopyKeydown(e, message)}
149
150
  title="${i18n.messages.copyMessageLabel}"
@@ -177,18 +178,18 @@ export function renderBotTypingIndicator(isTyping, loadingIndicator, loadingText
177
178
  return nothing;
178
179
  const indicatorContent = loadingIndicator === ChatbotLoadingType.Dots
179
180
  ? html `
180
- <div class="dots">
181
+ <div class="dots" part="typing-dots">
181
182
  <span></span>
182
183
  <span></span>
183
184
  <span></span>
184
185
  </div>
185
186
  `
186
- : html `<div class="spinner"></div>`;
187
+ : html `<div class="spinner" part="typing-spinner"></div>`;
187
188
  return html `
188
189
  <div class="message bot loading" part="typing-indicator">
189
- <div class="message__content">
190
+ <div class="message__content" part="typing-content">
190
191
  ${indicatorContent}
191
- ${loadingText ? html `<span class="loading-text">${loadingText.split('').map((char, i) => html `<span class="loading-text__char" style="animation-delay:${i * 0.04}s">${char === ' ' ? '\u00A0' : char}</span>`)}</span>` : nothing}
192
+ ${loadingText ? html `<span class="loading-text" part="typing-text">${loadingText.split('').map((char, i) => html `<span class="loading-text__char" style="animation-delay:${i * 0.04}s">${char === ' ' ? '\u00A0' : char}</span>`)}</span>` : nothing}
192
193
  </div>
193
194
  </div>
194
195
  `;
@@ -200,7 +201,7 @@ export function renderEmptyState(i18n) {
200
201
  return html `
201
202
  <div class="empty-state" part="empty-state">
202
203
  <slot name="empty-state">
203
- <div class="empty-state__content">
204
+ <div class="empty-state__content" part="empty-state-content">
204
205
  ${i18n.messages.startConversationLabel}
205
206
  </div>
206
207
  </slot>
@@ -26,6 +26,7 @@ function renderThreadItem(thread, data, handlers) {
26
26
  ${data.editingThreadId === thread.id && handlers.onRenameThread ? html `
27
27
  <input
28
28
  class="thread-item__rename-input"
29
+ part="thread-rename-input"
29
30
  type="text"
30
31
  .value=${thread.title || ''}
31
32
  @click=${(e) => e.stopPropagation()}
@@ -52,9 +53,9 @@ function renderThreadItem(thread, data, handlers) {
52
53
  }}
53
54
  />
54
55
  ` : html `
55
- <div class="thread-item__title">${thread.title || data.i18n.threads.newChatTitle}</div>
56
+ <div class="thread-item__title" part="thread-title">${thread.title || data.i18n.threads.newChatTitle}</div>
56
57
  `}
57
- <div class="thread-item__actions">
58
+ <div class="thread-item__actions" part="thread-actions">
58
59
  ${handlers.onBookmarkThread && thread.bookmarked ? html `
59
60
  <button
60
61
  class="thread-item__action-btn thread-item__bookmark--active"
@@ -111,10 +112,10 @@ function renderThreadItem(thread, data, handlers) {
111
112
  ` : ''}
112
113
  </div>
113
114
  </div>
114
- <div class="thread-item__preview">
115
+ <div class="thread-item__preview" part="thread-preview">
115
116
  ${previewText}
116
117
  </div>
117
- <div class="thread-item__timestamp">${formatTimestamp(thread.updatedAt)}</div>
118
+ <div class="thread-item__timestamp" part="thread-timestamp">${formatTimestamp(thread.updatedAt)}</div>
118
119
  </div>
119
120
  `;
120
121
  }
@@ -126,14 +127,14 @@ export function renderThreadSidebar(data, handlers) {
126
127
  const regularThreads = data.threads.filter(t => !t.bookmarked);
127
128
  return html `
128
129
  <div class="thread-sidebar" part="thread-sidebar">
129
- <div class="thread-sidebar__header">
130
- <h3>${data.i18n.threads.conversationsTitle}</h3>
130
+ <div class="thread-sidebar__header" part="thread-sidebar-header">
131
+ <h3 part="thread-sidebar-title">${data.i18n.threads.conversationsTitle}</h3>
131
132
  </div>
132
133
 
133
- <div class="thread-list">
134
+ <div class="thread-list" part="thread-list">
134
135
  ${bookmarkedThreads.length > 0 ? html `
135
- <div class="thread-section" part="thread-section-bookmarks">
136
- <div class="thread-section__label">
136
+ <div class="thread-section" part="thread-section thread-section-bookmarks">
137
+ <div class="thread-section__label" part="thread-section-label">
137
138
  <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg>
138
139
  ${data.i18n.threads.bookmarksLabel}
139
140
  </div>
@@ -142,11 +143,11 @@ export function renderThreadSidebar(data, handlers) {
142
143
  ` : nothing}
143
144
  ${regularThreads.length > 0 || bookmarkedThreads.length === 0 ? html `
144
145
  ${bookmarkedThreads.length > 0 ? html `
145
- <div class="thread-section__label">${data.i18n.threads.allConversationsLabel}</div>
146
+ <div class="thread-section__label" part="thread-section-label">${data.i18n.threads.allConversationsLabel}</div>
146
147
  ` : nothing}
147
148
  ${repeat(regularThreads, t => t.id, t => renderThreadItem(t, data, handlers))}
148
149
  ${regularThreads.length === 0 && bookmarkedThreads.length === 0 ? html `
149
- <p class="empty-msg">${data.i18n.threads.noConversationsLabel}</p>
150
+ <p class="empty-msg" part="thread-empty">${data.i18n.threads.noConversationsLabel}</p>
150
151
  ` : nothing}
151
152
  ` : nothing}
152
153
  </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuraly/lumenui",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "A comprehensive collection of enterprise-class web components built with Lit and TypeScript",
5
5
  "type": "module",
6
6
  "main": "dist/nuralyui.bundle.js",