@aparte/core 0.5.0-alpha.0 → 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.
package/dist/index.js CHANGED
@@ -312,8 +312,23 @@ class AparteSelect extends HTMLElement {
312
312
  _setupMutationObserver() {
313
313
  this._observer = new MutationObserver(() => {
314
314
  this._updateDropdownContent();
315
+ this._restoreActive();
315
316
  });
316
- this._observer.observe(this, { childList: true });
317
+ this._observer.observe(this, { childList: true, subtree: true });
318
+ }
319
+ /**
320
+ * Put the roving highlight back after the options changed underneath it.
321
+ *
322
+ * `data-active` lives on an option ELEMENT, so replacing the list throws it
323
+ * away while `_activeIndex` still claims a position: the highlight disappeared,
324
+ * `aria-activedescendant` kept pointing at an id no longer in the document, and
325
+ * the next ArrowDown moved from the stale index — skipping an option. Only when
326
+ * open and only when a position was held, so a refresh never invents one.
327
+ */
328
+ _restoreActive() {
329
+ if (!this._isOpen || this._activeIndex < 0) return;
330
+ if (this.querySelector("aparte-option[data-active]")) return;
331
+ this._setActive(this._activeIndex);
317
332
  }
318
333
  _updateDropdownContent() {
319
334
  if (!this._trigger || !this.contains(this._trigger)) {
@@ -335,9 +350,10 @@ class AparteSelect extends HTMLElement {
335
350
  optionsContainer.appendChild(child);
336
351
  });
337
352
  if (this.isConnected) {
338
- this._observer?.observe(this, { childList: true });
353
+ this._observer?.observe(this, { childList: true, subtree: true });
339
354
  }
340
355
  this._updateTriggerLabel();
356
+ this._restoreActive();
341
357
  }
342
358
  _setupEventListeners() {
343
359
  this._trigger?.addEventListener("click", () => this._toggle());
@@ -772,7 +788,23 @@ class AparteChatBubble extends HTMLElement {
772
788
  const detail = e2.detail;
773
789
  if (detail?.config && detail.config !== this._cfg) return;
774
790
  this._updateActionBar();
791
+ this._updateName();
792
+ this._updateLocalizedLabels();
793
+ this._updateWaiting();
775
794
  };
795
+ /**
796
+ * Re-apply the locale strings written straight into the markup by `_render()` —
797
+ * the accessible names a screen reader reads, which nothing else refreshes.
798
+ */
799
+ _updateLocalizedLabels() {
800
+ const locale = this._cfg.getLocale();
801
+ const set = (selector, label) => {
802
+ this.querySelector(selector)?.setAttribute("aria-label", label);
803
+ };
804
+ set(".aparte-branch-prev", locale.previousResponse ?? "Previous response");
805
+ set(".aparte-branch-next", locale.nextResponse ?? "Next response");
806
+ set(".aparte-action-bar", locale.messageActions ?? "Message actions");
807
+ }
776
808
  /**
777
809
  * Config governing this bubble: the instance config of the nearest
778
810
  * `[data-aparte-host]` boundary, else the global singleton. Resolved live
@@ -1797,8 +1829,28 @@ class AparteChatViewport extends HTMLElement {
1797
1829
  this._setupObservers();
1798
1830
  this._boundResetHandler = () => this.clearAll();
1799
1831
  window.addEventListener("aparte-reset", this._boundResetHandler);
1832
+ window.addEventListener("aparte-config-change", this._onConfigChange);
1833
+ }
1834
+ /**
1835
+ * A locale switch changes the reading direction, and `dir` was applied once at
1836
+ * render — so a chat already on screen never flipped to RTL until a reload.
1837
+ * Only OUR config: an instance-scoped change elsewhere must not touch us.
1838
+ */
1839
+ _onConfigChange = (e2) => {
1840
+ const detail = e2.detail;
1841
+ if (detail?.config && detail.config !== resolveConfig(this)) return;
1842
+ this._applyDirection();
1843
+ };
1844
+ /** Mirror `locale.direction` onto the scroll container. */
1845
+ _applyDirection() {
1846
+ const container = this.querySelector(".aparte-viewport-container");
1847
+ if (!container) return;
1848
+ const direction = resolveConfig(this).getLocale().direction;
1849
+ if (direction) container.setAttribute("dir", direction);
1850
+ else container.removeAttribute("dir");
1800
1851
  }
1801
1852
  disconnectedCallback() {
1853
+ window.removeEventListener("aparte-config-change", this._onConfigChange);
1802
1854
  if (this._boundResetHandler) {
1803
1855
  window.removeEventListener("aparte-reset", this._boundResetHandler);
1804
1856
  this._boundResetHandler = null;
@@ -3609,6 +3661,10 @@ class AparteConversationList extends HTMLElement {
3609
3661
  this.setAttribute("role", "navigation");
3610
3662
  }
3611
3663
  this._render();
3664
+ window.addEventListener("aparte-config-change", this._onConfigChange);
3665
+ }
3666
+ disconnectedCallback() {
3667
+ window.removeEventListener("aparte-config-change", this._onConfigChange);
3612
3668
  }
3613
3669
  attributeChangedCallback(name, oldValue, newValue) {
3614
3670
  if (oldValue === newValue) return;
@@ -3617,6 +3673,16 @@ class AparteConversationList extends HTMLElement {
3617
3673
  this._updateActiveState();
3618
3674
  }
3619
3675
  }
3676
+ /**
3677
+ * Re-render on a locale switch: every row's title fallback and both button
3678
+ * labels come from the locale, so without this the list stayed in the previous
3679
+ * language until something else happened to re-render it. Only OUR config.
3680
+ */
3681
+ _onConfigChange = (e2) => {
3682
+ const detail = e2.detail;
3683
+ if (detail?.config && detail.config !== resolveConfig(this)) return;
3684
+ this._render();
3685
+ };
3620
3686
  // ─── Public API ───────────────────────────────────────────────────────
3621
3687
  /** Set the list of conversations to display. Triggers a re-render. */
3622
3688
  set conversations(items) {