@newheap/platform-ai-chat 0.2.0 → 0.3.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.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, inject, DestroyRef, Injectable, EnvironmentInjector, runInInjectionContext, signal, computed, Injector, afterNextRender, makeEnvironmentProviders, provideEnvironmentInitializer, input, ChangeDetectionStrategy, Component, effect, untracked, ChangeDetectorRef, Pipe, output, ElementRef, viewChild, HostListener, ViewContainerRef } from '@angular/core';
2
+ import { InjectionToken, inject, DestroyRef, Injectable, EnvironmentInjector, runInInjectionContext, signal, computed, Injector, effect, untracked, afterNextRender, makeEnvironmentProviders, provideEnvironmentInitializer, input, ChangeDetectionStrategy, Component, ChangeDetectorRef, Pipe, output, ElementRef, viewChild, HostListener, ViewContainerRef } from '@angular/core';
3
3
  import { TranslateService, TranslatePipe } from '@ngx-translate/core';
4
4
  import { merge, Observable, firstValueFrom, isObservable, Subscription } from 'rxjs';
5
5
  import { BreakpointObserver } from '@angular/cdk/layout';
@@ -1633,6 +1633,91 @@ function isRecord(value) {
1633
1633
  return !!value && typeof value === 'object' && !Array.isArray(value);
1634
1634
  }
1635
1635
 
1636
+ const emptyState = { agentId: null, conversationId: null, panelOpen: false };
1637
+ const storagePrefix = 'nh-assistant-ui:v1:';
1638
+ /** Browser-only UI pointers. Messages, drafts, tokens and page context are never stored. */
1639
+ class NhAssistantUiState {
1640
+ constructor() {
1641
+ this.config = inject(NH_ASSISTANT_CONFIG);
1642
+ this.injector = inject(EnvironmentInjector);
1643
+ this.key = null;
1644
+ this.state = { ...emptyState };
1645
+ this.activated = false;
1646
+ this.activationRevision = 0;
1647
+ }
1648
+ async activate() {
1649
+ const revision = ++this.activationRevision;
1650
+ let scope = null;
1651
+ try {
1652
+ scope = await runInInjectionContext(this.injector, () => this.config.getStateScope?.() ?? null);
1653
+ }
1654
+ catch {
1655
+ scope = null;
1656
+ }
1657
+ if (revision !== this.activationRevision) {
1658
+ return { changed: false, state: this.state };
1659
+ }
1660
+ const normalized = typeof scope === 'string' ? scope.trim() : '';
1661
+ const key = normalized.length > 0 && normalized.length <= 256
1662
+ ? `${storagePrefix}${encodeURIComponent(this.config.apiBaseUrl)}:${encodeURIComponent(normalized)}`
1663
+ : null;
1664
+ if (this.activated && key === this.key) {
1665
+ return { changed: false, state: this.state };
1666
+ }
1667
+ this.activated = true;
1668
+ this.key = key;
1669
+ this.state = this.read();
1670
+ return { changed: true, state: this.state };
1671
+ }
1672
+ deactivate() {
1673
+ this.activationRevision++;
1674
+ this.activated = false;
1675
+ this.key = null;
1676
+ this.state = { ...emptyState };
1677
+ }
1678
+ update(patch) {
1679
+ this.state = { ...this.state, ...patch };
1680
+ if (!this.key) {
1681
+ return;
1682
+ }
1683
+ try {
1684
+ globalThis.localStorage?.setItem(this.key, JSON.stringify(this.state));
1685
+ }
1686
+ catch {
1687
+ // Storage can be unavailable or full; the live assistant remains usable.
1688
+ }
1689
+ }
1690
+ read() {
1691
+ if (!this.key) {
1692
+ return { ...emptyState };
1693
+ }
1694
+ try {
1695
+ const raw = globalThis.localStorage?.getItem(this.key);
1696
+ const value = raw ? JSON.parse(raw) : null;
1697
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
1698
+ return { ...emptyState };
1699
+ }
1700
+ const saved = value;
1701
+ return {
1702
+ agentId: validId(saved['agentId']),
1703
+ conversationId: validId(saved['conversationId']),
1704
+ panelOpen: saved['panelOpen'] === true
1705
+ };
1706
+ }
1707
+ catch {
1708
+ return { ...emptyState };
1709
+ }
1710
+ }
1711
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhAssistantUiState, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1712
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhAssistantUiState }); }
1713
+ }
1714
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhAssistantUiState, decorators: [{
1715
+ type: Injectable
1716
+ }] });
1717
+ function validId(value) {
1718
+ return typeof value === 'string' && value.length > 0 && value.length <= 256 ? value : null;
1719
+ }
1720
+
1636
1721
  const conversationPageSize = 50;
1637
1722
  const cancelGracePeriodMs = 5_000;
1638
1723
  /**
@@ -1645,6 +1730,7 @@ class NhAssistantStore {
1645
1730
  this.config = inject(NH_ASSISTANT_CONFIG);
1646
1731
  this.injector = inject(EnvironmentInjector);
1647
1732
  this.accessPolicy = inject(NH_ASSISTANT_ACCESS_POLICY);
1733
+ this.uiState = inject(NhAssistantUiState);
1648
1734
  this.accessGrantedState = signal(null, ...(ngDevMode ? [{ debugName: "accessGrantedState" }] : []));
1649
1735
  this.statusState = signal(null, ...(ngDevMode ? [{ debugName: "statusState" }] : []));
1650
1736
  this.statusLoadingState = signal(false, ...(ngDevMode ? [{ debugName: "statusLoadingState" }] : []));
@@ -1661,6 +1747,9 @@ class NhAssistantStore {
1661
1747
  this.restoredDraftState = signal(null, ...(ngDevMode ? [{ debugName: "restoredDraftState" }] : []));
1662
1748
  this.pageContextState = signal(null, ...(ngDevMode ? [{ debugName: "pageContextState" }] : []));
1663
1749
  this.pageContextExcludedState = signal(false, ...(ngDevMode ? [{ debugName: "pageContextExcludedState" }] : []));
1750
+ this.restorePanelOpenState = signal(false, ...(ngDevMode ? [{ debugName: "restorePanelOpenState" }] : []));
1751
+ this.statusRevision = 0;
1752
+ this.accountRevision = 0;
1664
1753
  /** True when the access policy allows the user and the server reports the assistant as enabled. */
1665
1754
  this.enabled = computed(() => this.accessGrantedState() === true && this.statusState()?.enabled === true, ...(ngDevMode ? [{ debugName: "enabled" }] : []));
1666
1755
  this.accessGranted = this.accessGrantedState.asReadonly();
@@ -1687,11 +1776,13 @@ class NhAssistantStore {
1687
1776
  this.pageContext = this.pageContextState.asReadonly();
1688
1777
  /** True when the user left the page context out of the next message. */
1689
1778
  this.pageContextExcluded = this.pageContextExcludedState.asReadonly();
1779
+ /** Whether the drawer was open when this account last left the application. */
1780
+ this.restorePanelOpen = this.restorePanelOpenState.asReadonly();
1690
1781
  /** True while the user can send: enabled, an agent is chosen and no turn runs or waits. */
1691
1782
  this.canSend = computed(() => {
1692
1783
  const conversation = this.activeConversationState();
1693
1784
  const blocked = conversation !== null && conversation.status !== 'idle';
1694
- return this.enabled() && this.selectedAgentIdState() !== null && !this.streamingState() && !blocked;
1785
+ return this.enabled() && this.selectedAgentIdState() !== null && !this.streamingState() && !this.conversationLoadingState() && !blocked;
1695
1786
  }, ...(ngDevMode ? [{ debugName: "canSend" }] : []));
1696
1787
  inject(DestroyRef).onDestroy(() => {
1697
1788
  this.accessSubscription?.unsubscribe();
@@ -1706,39 +1797,95 @@ class NhAssistantStore {
1706
1797
  }
1707
1798
  /** Reloads the server status, for example after the host changed the signed-in user. */
1708
1799
  async reloadStatus() {
1800
+ const revision = ++this.statusRevision;
1709
1801
  if (this.accessGrantedState() !== true) {
1802
+ this.accountRevision++;
1803
+ this.stopLocalTurn();
1710
1804
  this.statusState.set(null);
1805
+ this.activeConversationState.set(null);
1806
+ this.selectedAgentIdState.set(null);
1807
+ this.conversationsState.set([]);
1808
+ this.conversationsTotalState.set(0);
1809
+ this.conversationLoadingState.set(false);
1810
+ this.conversationsLoadingState.set(false);
1811
+ this.pageContextState.set(null);
1812
+ this.pageContextExcludedState.set(false);
1813
+ this.restoredDraftState.set(null);
1814
+ this.errorState.set(null);
1815
+ this.lastUsageState.set(null);
1816
+ this.restorePanelOpenState.set(false);
1817
+ this.uiState.deactivate();
1711
1818
  return;
1712
1819
  }
1713
1820
  this.statusLoadingState.set(true);
1714
1821
  try {
1822
+ const restored = await this.uiState.activate();
1823
+ if (revision !== this.statusRevision) {
1824
+ return;
1825
+ }
1826
+ if (restored.changed) {
1827
+ this.accountRevision++;
1828
+ this.stopLocalTurn();
1829
+ this.statusState.set(null);
1830
+ this.activeConversationState.set(null);
1831
+ this.conversationsState.set([]);
1832
+ this.conversationsTotalState.set(0);
1833
+ this.conversationLoadingState.set(false);
1834
+ this.conversationsLoadingState.set(false);
1835
+ this.pageContextState.set(null);
1836
+ this.pageContextExcludedState.set(false);
1837
+ this.restoredDraftState.set(null);
1838
+ this.errorState.set(null);
1839
+ this.lastUsageState.set(null);
1840
+ this.selectedAgentIdState.set(restored.state.agentId);
1841
+ this.restorePanelOpenState.set(restored.state.panelOpen);
1842
+ }
1715
1843
  const status = await firstValueFrom(this.api.status());
1844
+ if (revision !== this.statusRevision) {
1845
+ return;
1846
+ }
1716
1847
  this.statusState.set(status);
1717
- this.selectInitialAgent(status.agents);
1848
+ if (status.enabled) {
1849
+ this.selectInitialAgent(status.agents);
1850
+ if (!this.activeConversationState() && restored.state.conversationId) {
1851
+ await this.loadConversation(restored.state.conversationId, true);
1852
+ }
1853
+ }
1718
1854
  }
1719
1855
  catch {
1720
1856
  // An unavailable assistant endpoint hides the assistant instead of showing an error.
1721
- this.statusState.set(null);
1857
+ if (revision === this.statusRevision) {
1858
+ this.statusState.set(null);
1859
+ }
1722
1860
  }
1723
1861
  finally {
1724
- this.statusLoadingState.set(false);
1862
+ if (revision === this.statusRevision) {
1863
+ this.statusLoadingState.set(false);
1864
+ }
1725
1865
  }
1726
1866
  }
1727
1867
  async refreshConversations() {
1728
1868
  if (!this.enabled()) {
1729
1869
  return;
1730
1870
  }
1871
+ const revision = this.accountRevision;
1731
1872
  this.conversationsLoadingState.set(true);
1732
1873
  try {
1733
1874
  const page = await firstValueFrom(this.api.listConversations(1, conversationPageSize));
1734
- this.conversationsState.set(page.items);
1735
- this.conversationsTotalState.set(page.total);
1875
+ if (revision === this.accountRevision) {
1876
+ this.conversationsState.set(page.items);
1877
+ this.conversationsTotalState.set(page.total);
1878
+ }
1736
1879
  }
1737
1880
  catch (error) {
1738
- this.setError(error);
1881
+ if (revision === this.accountRevision) {
1882
+ this.setError(error);
1883
+ }
1739
1884
  }
1740
1885
  finally {
1741
- this.conversationsLoadingState.set(false);
1886
+ if (revision === this.accountRevision) {
1887
+ this.conversationsLoadingState.set(false);
1888
+ }
1742
1889
  }
1743
1890
  }
1744
1891
  /** Chooses the agent for the next new conversation. Switching away from the active conversation's agent starts a new one. */
@@ -1747,6 +1894,7 @@ class NhAssistantStore {
1747
1894
  return;
1748
1895
  }
1749
1896
  this.selectedAgentIdState.set(agentId);
1897
+ this.uiState.update({ agentId });
1750
1898
  const active = this.activeConversationState();
1751
1899
  if (active && active.agentId !== agentId && !this.streamingState()) {
1752
1900
  this.startNewConversation();
@@ -1758,43 +1906,71 @@ class NhAssistantStore {
1758
1906
  return;
1759
1907
  }
1760
1908
  this.activeConversationState.set(null);
1909
+ this.uiState.update({ conversationId: null });
1761
1910
  this.errorState.set(null);
1762
1911
  this.lastUsageState.set(null);
1763
1912
  }
1764
1913
  async openConversation(conversationId) {
1914
+ await this.loadConversation(conversationId, false);
1915
+ }
1916
+ async loadConversation(conversationId, restoring) {
1765
1917
  if (this.streamingState() || this.activeConversationState()?.id === conversationId) {
1766
1918
  return;
1767
1919
  }
1920
+ const revision = this.accountRevision;
1768
1921
  this.conversationLoadingState.set(true);
1769
1922
  this.errorState.set(null);
1770
1923
  try {
1771
1924
  const conversation = await firstValueFrom(this.api.getConversation(conversationId));
1772
- this.activeConversationState.set(conversation);
1773
- if (this.agents().some(agent => agent.id === conversation.agentId)) {
1774
- this.selectedAgentIdState.set(conversation.agentId);
1925
+ if (revision !== this.accountRevision) {
1926
+ return;
1927
+ }
1928
+ if (!this.agents().some(agent => agent.id === conversation.agentId)) {
1929
+ this.uiState.update({ conversationId: null });
1930
+ return;
1775
1931
  }
1932
+ this.activeConversationState.set(conversation);
1933
+ this.selectedAgentIdState.set(conversation.agentId);
1934
+ this.uiState.update({ conversationId, agentId: conversation.agentId });
1776
1935
  }
1777
1936
  catch (error) {
1778
- this.setError(error);
1937
+ if (revision !== this.accountRevision) {
1938
+ return;
1939
+ }
1940
+ if (error instanceof NhAssistantApiError && (error.status === 403 || error.status === 404)) {
1941
+ this.uiState.update({ conversationId: null });
1942
+ }
1943
+ if (!restoring) {
1944
+ this.setError(error);
1945
+ }
1779
1946
  }
1780
1947
  finally {
1781
- this.conversationLoadingState.set(false);
1948
+ if (revision === this.accountRevision) {
1949
+ this.conversationLoadingState.set(false);
1950
+ }
1782
1951
  }
1783
1952
  }
1784
1953
  async deleteConversation(conversationId) {
1785
1954
  if (this.streamingState() && this.activeConversationState()?.id === conversationId) {
1786
1955
  return;
1787
1956
  }
1957
+ const revision = this.accountRevision;
1788
1958
  try {
1789
1959
  await firstValueFrom(this.api.deleteConversation(conversationId), { defaultValue: undefined });
1960
+ if (revision !== this.accountRevision) {
1961
+ return;
1962
+ }
1790
1963
  this.conversationsState.update(items => items.filter(item => item.id !== conversationId));
1791
1964
  this.conversationsTotalState.update(total => Math.max(0, total - 1));
1792
1965
  if (this.activeConversationState()?.id === conversationId) {
1793
1966
  this.activeConversationState.set(null);
1967
+ this.uiState.update({ conversationId: null });
1794
1968
  }
1795
1969
  }
1796
1970
  catch (error) {
1797
- this.setError(error);
1971
+ if (revision === this.accountRevision) {
1972
+ this.setError(error);
1973
+ }
1798
1974
  }
1799
1975
  }
1800
1976
  /**
@@ -1804,6 +1980,8 @@ class NhAssistantStore {
1804
1980
  */
1805
1981
  async send(text) {
1806
1982
  const trimmed = text.trim();
1983
+ await this.initialize();
1984
+ await this.reloadStatus();
1807
1985
  const maxChars = this.limits()?.maxMessageChars ?? Number.MAX_SAFE_INTEGER;
1808
1986
  if (trimmed.length === 0 || trimmed.length > maxChars || !this.canSend()) {
1809
1987
  return false;
@@ -1811,16 +1989,22 @@ class NhAssistantStore {
1811
1989
  this.errorState.set(null);
1812
1990
  this.restoredDraftState.set(null);
1813
1991
  this.streamingState.set(true);
1992
+ const accountRevision = this.accountRevision;
1814
1993
  let conversation = this.activeConversationState();
1815
1994
  if (!conversation) {
1816
1995
  conversation = await this.createConversation();
1817
1996
  if (!conversation) {
1818
- this.streamingState.set(false);
1819
- this.restoredDraftState.set(text);
1997
+ if (accountRevision === this.accountRevision) {
1998
+ this.streamingState.set(false);
1999
+ this.restoredDraftState.set(text);
2000
+ }
1820
2001
  return false;
1821
2002
  }
1822
2003
  }
1823
2004
  const clientContext = await this.contextForMessage();
2005
+ if (accountRevision !== this.accountRevision) {
2006
+ return false;
2007
+ }
1824
2008
  const clientMessageId = createClientMessageId();
1825
2009
  const previousStatus = conversation.status;
1826
2010
  this.activeConversationState.set({
@@ -1833,6 +2017,7 @@ class NhAssistantStore {
1833
2017
  });
1834
2018
  const conversationId = conversation.id;
1835
2019
  return new Promise(resolve => {
2020
+ this.pendingSendResolve = resolve;
1836
2021
  let started = false;
1837
2022
  let failedAfterStart = false;
1838
2023
  this.runStream(this.api.sendMessage(conversationId, {
@@ -1858,6 +2043,7 @@ class NhAssistantStore {
1858
2043
  this.applyEvent(event, clientMessageId);
1859
2044
  },
1860
2045
  complete: () => {
2046
+ this.pendingSendResolve = undefined;
1861
2047
  resolve(started);
1862
2048
  this.afterTurn(conversationId, failedAfterStart);
1863
2049
  }
@@ -1904,11 +2090,17 @@ class NhAssistantStore {
1904
2090
  if (!conversation || (conversation.status === 'idle' && !this.streamingState())) {
1905
2091
  return;
1906
2092
  }
2093
+ const revision = this.accountRevision;
1907
2094
  try {
1908
2095
  await firstValueFrom(this.api.cancel(conversation.id), { defaultValue: undefined });
1909
2096
  }
1910
2097
  catch (error) {
1911
- this.setError(error);
2098
+ if (revision === this.accountRevision) {
2099
+ this.setError(error);
2100
+ }
2101
+ return;
2102
+ }
2103
+ if (revision !== this.accountRevision) {
1912
2104
  return;
1913
2105
  }
1914
2106
  if (!this.streamingState()) {
@@ -1929,9 +2121,18 @@ class NhAssistantStore {
1929
2121
  clearError() {
1930
2122
  this.errorState.set(null);
1931
2123
  }
2124
+ /** Persists only drawer visibility, never the page context or a message draft. */
2125
+ setPanelOpen(open) {
2126
+ this.restorePanelOpenState.set(open);
2127
+ this.uiState.update({ panelOpen: open });
2128
+ }
1932
2129
  /** Reads the host's page context again so the chip shows what the next message sends. */
1933
2130
  async refreshPageContext() {
1934
- this.pageContextState.set((await this.readPageContext()) ?? null);
2131
+ const revision = this.accountRevision;
2132
+ const context = await this.readPageContext();
2133
+ if (revision === this.accountRevision) {
2134
+ this.pageContextState.set(context ?? null);
2135
+ }
1935
2136
  }
1936
2137
  /** Leaves the page context out of the next message only, or includes it again. */
1937
2138
  setPageContextExcluded(excluded) {
@@ -1955,8 +2156,11 @@ class NhAssistantStore {
1955
2156
  this.pageContextExcludedState.set(false);
1956
2157
  return null;
1957
2158
  }
2159
+ const revision = this.accountRevision;
1958
2160
  const context = await this.readPageContext();
1959
- this.pageContextState.set(context ?? null);
2161
+ if (revision === this.accountRevision) {
2162
+ this.pageContextState.set(context ?? null);
2163
+ }
1960
2164
  return context;
1961
2165
  }
1962
2166
  async readPageContext() {
@@ -1986,14 +2190,8 @@ class NhAssistantStore {
1986
2190
  await new Promise(resolve => {
1987
2191
  this.accessSubscription = decision.subscribe({
1988
2192
  next: granted => {
1989
- const changed = this.accessGrantedState() !== granted;
1990
2193
  this.accessGrantedState.set(granted);
1991
- if (changed) {
1992
- void this.reloadStatus().finally(() => resolve());
1993
- }
1994
- else {
1995
- resolve();
1996
- }
2194
+ void this.reloadStatus().finally(() => resolve());
1997
2195
  },
1998
2196
  error: () => {
1999
2197
  this.accessGrantedState.set(false);
@@ -2008,15 +2206,22 @@ class NhAssistantStore {
2008
2206
  if (!agentId) {
2009
2207
  return null;
2010
2208
  }
2209
+ const revision = this.accountRevision;
2011
2210
  try {
2012
2211
  const conversation = await firstValueFrom(this.api.createConversation({ agentId }));
2212
+ if (revision !== this.accountRevision) {
2213
+ return null;
2214
+ }
2013
2215
  this.activeConversationState.set(conversation);
2216
+ this.uiState.update({ conversationId: conversation.id });
2014
2217
  this.conversationsState.update(items => [toSummary(conversation), ...items.filter(item => item.id !== conversation.id)]);
2015
2218
  this.conversationsTotalState.update(total => total + 1);
2016
2219
  return conversation;
2017
2220
  }
2018
2221
  catch (error) {
2019
- this.setError(error);
2222
+ if (revision === this.accountRevision) {
2223
+ this.setError(error);
2224
+ }
2020
2225
  return null;
2021
2226
  }
2022
2227
  }
@@ -2037,6 +2242,14 @@ class NhAssistantStore {
2037
2242
  complete: finish
2038
2243
  });
2039
2244
  }
2245
+ stopLocalTurn() {
2246
+ this.streamSubscription?.unsubscribe();
2247
+ this.pendingSendResolve?.(false);
2248
+ this.pendingSendResolve = undefined;
2249
+ clearTimeout(this.cancelTimer);
2250
+ this.streamingState.set(false);
2251
+ this.decidingState.set(false);
2252
+ }
2040
2253
  applyEvent(event, clientMessageId) {
2041
2254
  this.updateActive(conversation => applyNhAssistantEvent(conversation, event, clientMessageId));
2042
2255
  if (event.type === 'error') {
@@ -2063,9 +2276,10 @@ class NhAssistantStore {
2063
2276
  }
2064
2277
  }
2065
2278
  async reloadActiveConversation(conversationId) {
2279
+ const revision = this.accountRevision;
2066
2280
  try {
2067
2281
  const conversation = await firstValueFrom(this.api.getConversation(conversationId));
2068
- if (this.activeConversationState()?.id === conversationId && !this.streamingState()) {
2282
+ if (revision === this.accountRevision && this.activeConversationState()?.id === conversationId && !this.streamingState()) {
2069
2283
  this.activeConversationState.set(conversation);
2070
2284
  }
2071
2285
  }
@@ -2093,6 +2307,7 @@ class NhAssistantStore {
2093
2307
  }
2094
2308
  const preferred = agents.find(agent => agent.id === this.config.defaultAgentId) ?? agents[0] ?? null;
2095
2309
  this.selectedAgentIdState.set(preferred?.id ?? null);
2310
+ this.uiState.update({ agentId: preferred?.id ?? null });
2096
2311
  }
2097
2312
  setError(error) {
2098
2313
  if (error instanceof NhAssistantApiError) {
@@ -2161,12 +2376,31 @@ class NhAssistantPanelService {
2161
2376
  this.returnFocusTo = null;
2162
2377
  this.isOpen = this.openState.asReadonly();
2163
2378
  inject(DestroyRef).onDestroy(() => this.dispose());
2379
+ effect(() => {
2380
+ if (this.store.enabled() && this.store.restorePanelOpen() && this.portal && !this.openState()) {
2381
+ untracked(() => queueMicrotask(() => {
2382
+ if (this.portal && this.store.enabled() && this.store.restorePanelOpen() && !this.openState()) {
2383
+ this.showPanel();
2384
+ }
2385
+ }));
2386
+ }
2387
+ });
2164
2388
  }
2165
2389
  open(conversationId) {
2166
- void this.store.initialize();
2167
- if (conversationId) {
2168
- void this.store.openConversation(conversationId);
2390
+ if (this.openState() && !conversationId) {
2391
+ return;
2169
2392
  }
2393
+ this.showPanel();
2394
+ void this.store.initialize().then(() => this.store.reloadStatus()).then(() => {
2395
+ if (this.openState()) {
2396
+ this.store.setPanelOpen(true);
2397
+ }
2398
+ if (conversationId) {
2399
+ void this.store.openConversation(conversationId);
2400
+ }
2401
+ });
2402
+ }
2403
+ showPanel() {
2170
2404
  if (this.openState()) {
2171
2405
  return;
2172
2406
  }
@@ -2180,6 +2414,7 @@ class NhAssistantPanelService {
2180
2414
  return;
2181
2415
  }
2182
2416
  this.openState.set(false);
2417
+ this.store.setPanelOpen(false);
2183
2418
  this.overlayRef?.detach();
2184
2419
  const target = this.returnFocusTo;
2185
2420
  this.returnFocusTo = null;
@@ -2201,6 +2436,13 @@ class NhAssistantPanelService {
2201
2436
  if (this.openState()) {
2202
2437
  this.attach();
2203
2438
  }
2439
+ else {
2440
+ void this.store.initialize().then(() => {
2441
+ if (this.portal === portal && this.store.enabled() && this.store.restorePanelOpen()) {
2442
+ this.showPanel();
2443
+ }
2444
+ });
2445
+ }
2204
2446
  }
2205
2447
  unregisterPanel(portal) {
2206
2448
  if (this.portal !== portal) {
@@ -2274,6 +2516,7 @@ function provideNhAssistant(config) {
2274
2516
  NhAssistantApiService,
2275
2517
  NhAssistantAdminApiService,
2276
2518
  NhAssistantStore,
2519
+ NhAssistantUiState,
2277
2520
  NhAssistantPanelService,
2278
2521
  ...(config.translations === 'host'
2279
2522
  ? []