@newheap/platform-ai-chat 0.2.0 → 0.4.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.
- package/fesm2022/newheap-platform-ai-chat.mjs +333 -35
- package/fesm2022/newheap-platform-ai-chat.mjs.map +1 -1
- package/i18n/en.json +3 -0
- package/i18n/nl.json +3 -0
- package/index.d.ts +39 -2
- package/package.json +1 -1
|
@@ -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,
|
|
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';
|
|
@@ -233,6 +233,9 @@ var en = {
|
|
|
233
233
|
"assistant-turn-failed": "The assistant could not finish the answer. Try again.",
|
|
234
234
|
"assistant-turn-timeout": "The answer took too long and was stopped. Try again or ask a narrower question."
|
|
235
235
|
},
|
|
236
|
+
notices: {
|
|
237
|
+
"no-answer": "The assistant finished without an answer. Try again or rephrase your question."
|
|
238
|
+
},
|
|
236
239
|
preferences: {
|
|
237
240
|
title: "Your preferences",
|
|
238
241
|
description: "Choose how the assistant answers you. Preferences change the style only; they never change what the assistant may do or which actions need your approval.",
|
|
@@ -618,6 +621,9 @@ var nl = {
|
|
|
618
621
|
"assistant-turn-failed": "De assistent kon het antwoord niet afmaken. Probeer het opnieuw.",
|
|
619
622
|
"assistant-turn-timeout": "Het antwoord duurde te lang en is gestopt. Probeer het opnieuw of stel een gerichtere vraag."
|
|
620
623
|
},
|
|
624
|
+
notices: {
|
|
625
|
+
"no-answer": "De assistent is klaar zonder antwoord. Probeer het opnieuw of stel je vraag anders."
|
|
626
|
+
},
|
|
621
627
|
preferences: {
|
|
622
628
|
title: "Je voorkeuren",
|
|
623
629
|
description: "Kies hoe de assistent je antwoordt. Voorkeuren veranderen alleen de stijl; ze veranderen nooit wat de assistent mag of welke acties je goedkeuring nodig hebben.",
|
|
@@ -1463,6 +1469,22 @@ function applyNhAssistantApprovalDecision(conversation, approvalId, decision) {
|
|
|
1463
1469
|
}
|
|
1464
1470
|
return updated;
|
|
1465
1471
|
}
|
|
1472
|
+
/**
|
|
1473
|
+
* True when an assistant message after the latest user message contains non-blank text,
|
|
1474
|
+
* so the latest turn produced a visible answer. Tool calls and approvals alone do not count.
|
|
1475
|
+
*/
|
|
1476
|
+
function nhAssistantLatestTurnHasText(conversation) {
|
|
1477
|
+
for (let position = conversation.messages.length - 1; position >= 0; position--) {
|
|
1478
|
+
const message = conversation.messages[position];
|
|
1479
|
+
if (message.role === 'user') {
|
|
1480
|
+
return false;
|
|
1481
|
+
}
|
|
1482
|
+
if (message.role === 'assistant' && message.parts.some(part => part.type === 'text' && part.text.trim().length > 0)) {
|
|
1483
|
+
return true;
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
return false;
|
|
1487
|
+
}
|
|
1466
1488
|
function conversationStatusFor(status) {
|
|
1467
1489
|
switch (status) {
|
|
1468
1490
|
case 'completed':
|
|
@@ -1633,6 +1655,93 @@ function isRecord(value) {
|
|
|
1633
1655
|
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
1634
1656
|
}
|
|
1635
1657
|
|
|
1658
|
+
const emptyState = { agentId: null, conversationId: null, panelOpen: false };
|
|
1659
|
+
const storagePrefix = 'nh-assistant-ui:v1:';
|
|
1660
|
+
/** Browser-only UI pointers. Messages, drafts, tokens and page context are never stored. */
|
|
1661
|
+
class NhAssistantUiState {
|
|
1662
|
+
constructor() {
|
|
1663
|
+
this.config = inject(NH_ASSISTANT_CONFIG);
|
|
1664
|
+
this.injector = inject(EnvironmentInjector);
|
|
1665
|
+
this.key = null;
|
|
1666
|
+
this.state = { ...emptyState };
|
|
1667
|
+
this.activated = false;
|
|
1668
|
+
this.activationRevision = 0;
|
|
1669
|
+
}
|
|
1670
|
+
async activate() {
|
|
1671
|
+
const revision = ++this.activationRevision;
|
|
1672
|
+
let scope = null;
|
|
1673
|
+
try {
|
|
1674
|
+
scope = await runInInjectionContext(this.injector, () => this.config.getStateScope?.() ?? null);
|
|
1675
|
+
}
|
|
1676
|
+
catch {
|
|
1677
|
+
scope = null;
|
|
1678
|
+
}
|
|
1679
|
+
if (revision !== this.activationRevision) {
|
|
1680
|
+
return { changed: false, state: this.state };
|
|
1681
|
+
}
|
|
1682
|
+
const normalized = typeof scope === 'string' ? scope.trim() : '';
|
|
1683
|
+
const key = normalized.length > 0 && normalized.length <= 256
|
|
1684
|
+
? `${storagePrefix}${encodeURIComponent(this.config.apiBaseUrl)}:${encodeURIComponent(normalized)}`
|
|
1685
|
+
: null;
|
|
1686
|
+
if (this.activated && key === this.key) {
|
|
1687
|
+
return { changed: false, state: this.state };
|
|
1688
|
+
}
|
|
1689
|
+
this.activated = true;
|
|
1690
|
+
this.key = key;
|
|
1691
|
+
this.state = this.read();
|
|
1692
|
+
return { changed: true, state: this.state };
|
|
1693
|
+
}
|
|
1694
|
+
deactivate() {
|
|
1695
|
+
this.activationRevision++;
|
|
1696
|
+
this.activated = false;
|
|
1697
|
+
this.key = null;
|
|
1698
|
+
this.state = { ...emptyState };
|
|
1699
|
+
}
|
|
1700
|
+
update(patch) {
|
|
1701
|
+
this.state = { ...this.state, ...patch };
|
|
1702
|
+
if (!this.key) {
|
|
1703
|
+
return;
|
|
1704
|
+
}
|
|
1705
|
+
try {
|
|
1706
|
+
globalThis.localStorage?.setItem(this.key, JSON.stringify(this.state));
|
|
1707
|
+
}
|
|
1708
|
+
catch {
|
|
1709
|
+
// Storage can be unavailable or full; the live assistant remains usable.
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
read() {
|
|
1713
|
+
if (!this.key) {
|
|
1714
|
+
return { ...emptyState };
|
|
1715
|
+
}
|
|
1716
|
+
try {
|
|
1717
|
+
const raw = globalThis.localStorage?.getItem(this.key);
|
|
1718
|
+
const value = raw ? JSON.parse(raw) : null;
|
|
1719
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
1720
|
+
return { ...emptyState };
|
|
1721
|
+
}
|
|
1722
|
+
const saved = value;
|
|
1723
|
+
return {
|
|
1724
|
+
agentId: validId(saved['agentId']),
|
|
1725
|
+
conversationId: validId(saved['conversationId']),
|
|
1726
|
+
panelOpen: saved['panelOpen'] === true
|
|
1727
|
+
};
|
|
1728
|
+
}
|
|
1729
|
+
catch {
|
|
1730
|
+
return { ...emptyState };
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhAssistantUiState, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1734
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhAssistantUiState }); }
|
|
1735
|
+
}
|
|
1736
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhAssistantUiState, decorators: [{
|
|
1737
|
+
type: Injectable
|
|
1738
|
+
}] });
|
|
1739
|
+
function validId(value) {
|
|
1740
|
+
return typeof value === 'string' && value.length > 0 && value.length <= 256 ? value : null;
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
/** Notice code for a completed turn that produced no answer text and no server code. */
|
|
1744
|
+
const NH_ASSISTANT_NO_ANSWER_NOTICE_CODE = 'assistant-no-answer';
|
|
1636
1745
|
const conversationPageSize = 50;
|
|
1637
1746
|
const cancelGracePeriodMs = 5_000;
|
|
1638
1747
|
/**
|
|
@@ -1645,6 +1754,7 @@ class NhAssistantStore {
|
|
|
1645
1754
|
this.config = inject(NH_ASSISTANT_CONFIG);
|
|
1646
1755
|
this.injector = inject(EnvironmentInjector);
|
|
1647
1756
|
this.accessPolicy = inject(NH_ASSISTANT_ACCESS_POLICY);
|
|
1757
|
+
this.uiState = inject(NhAssistantUiState);
|
|
1648
1758
|
this.accessGrantedState = signal(null, ...(ngDevMode ? [{ debugName: "accessGrantedState" }] : []));
|
|
1649
1759
|
this.statusState = signal(null, ...(ngDevMode ? [{ debugName: "statusState" }] : []));
|
|
1650
1760
|
this.statusLoadingState = signal(false, ...(ngDevMode ? [{ debugName: "statusLoadingState" }] : []));
|
|
@@ -1657,10 +1767,14 @@ class NhAssistantStore {
|
|
|
1657
1767
|
this.streamingState = signal(false, ...(ngDevMode ? [{ debugName: "streamingState" }] : []));
|
|
1658
1768
|
this.decidingState = signal(false, ...(ngDevMode ? [{ debugName: "decidingState" }] : []));
|
|
1659
1769
|
this.errorState = signal(null, ...(ngDevMode ? [{ debugName: "errorState" }] : []));
|
|
1770
|
+
this.noticeState = signal(null, ...(ngDevMode ? [{ debugName: "noticeState" }] : []));
|
|
1660
1771
|
this.lastUsageState = signal(null, ...(ngDevMode ? [{ debugName: "lastUsageState" }] : []));
|
|
1661
1772
|
this.restoredDraftState = signal(null, ...(ngDevMode ? [{ debugName: "restoredDraftState" }] : []));
|
|
1662
1773
|
this.pageContextState = signal(null, ...(ngDevMode ? [{ debugName: "pageContextState" }] : []));
|
|
1663
1774
|
this.pageContextExcludedState = signal(false, ...(ngDevMode ? [{ debugName: "pageContextExcludedState" }] : []));
|
|
1775
|
+
this.restorePanelOpenState = signal(false, ...(ngDevMode ? [{ debugName: "restorePanelOpenState" }] : []));
|
|
1776
|
+
this.statusRevision = 0;
|
|
1777
|
+
this.accountRevision = 0;
|
|
1664
1778
|
/** True when the access policy allows the user and the server reports the assistant as enabled. */
|
|
1665
1779
|
this.enabled = computed(() => this.accessGrantedState() === true && this.statusState()?.enabled === true, ...(ngDevMode ? [{ debugName: "enabled" }] : []));
|
|
1666
1780
|
this.accessGranted = this.accessGrantedState.asReadonly();
|
|
@@ -1680,6 +1794,8 @@ class NhAssistantStore {
|
|
|
1680
1794
|
this.pendingApproval = computed(() => this.activeConversationState()?.pendingApproval ?? null, ...(ngDevMode ? [{ debugName: "pendingApproval" }] : []));
|
|
1681
1795
|
this.deciding = this.decidingState.asReadonly();
|
|
1682
1796
|
this.error = this.errorState.asReadonly();
|
|
1797
|
+
/** Non-blocking remark about the latest completed turn; shown only while no error is shown. */
|
|
1798
|
+
this.notice = this.noticeState.asReadonly();
|
|
1683
1799
|
this.lastUsage = this.lastUsageState.asReadonly();
|
|
1684
1800
|
/** Text of a message the server did not accept, so the composer can offer it again. */
|
|
1685
1801
|
this.restoredDraft = this.restoredDraftState.asReadonly();
|
|
@@ -1687,11 +1803,13 @@ class NhAssistantStore {
|
|
|
1687
1803
|
this.pageContext = this.pageContextState.asReadonly();
|
|
1688
1804
|
/** True when the user left the page context out of the next message. */
|
|
1689
1805
|
this.pageContextExcluded = this.pageContextExcludedState.asReadonly();
|
|
1806
|
+
/** Whether the drawer was open when this account last left the application. */
|
|
1807
|
+
this.restorePanelOpen = this.restorePanelOpenState.asReadonly();
|
|
1690
1808
|
/** True while the user can send: enabled, an agent is chosen and no turn runs or waits. */
|
|
1691
1809
|
this.canSend = computed(() => {
|
|
1692
1810
|
const conversation = this.activeConversationState();
|
|
1693
1811
|
const blocked = conversation !== null && conversation.status !== 'idle';
|
|
1694
|
-
return this.enabled() && this.selectedAgentIdState() !== null && !this.streamingState() && !blocked;
|
|
1812
|
+
return this.enabled() && this.selectedAgentIdState() !== null && !this.streamingState() && !this.conversationLoadingState() && !blocked;
|
|
1695
1813
|
}, ...(ngDevMode ? [{ debugName: "canSend" }] : []));
|
|
1696
1814
|
inject(DestroyRef).onDestroy(() => {
|
|
1697
1815
|
this.accessSubscription?.unsubscribe();
|
|
@@ -1706,39 +1824,97 @@ class NhAssistantStore {
|
|
|
1706
1824
|
}
|
|
1707
1825
|
/** Reloads the server status, for example after the host changed the signed-in user. */
|
|
1708
1826
|
async reloadStatus() {
|
|
1827
|
+
const revision = ++this.statusRevision;
|
|
1709
1828
|
if (this.accessGrantedState() !== true) {
|
|
1829
|
+
this.accountRevision++;
|
|
1830
|
+
this.stopLocalTurn();
|
|
1710
1831
|
this.statusState.set(null);
|
|
1832
|
+
this.activeConversationState.set(null);
|
|
1833
|
+
this.selectedAgentIdState.set(null);
|
|
1834
|
+
this.conversationsState.set([]);
|
|
1835
|
+
this.conversationsTotalState.set(0);
|
|
1836
|
+
this.conversationLoadingState.set(false);
|
|
1837
|
+
this.conversationsLoadingState.set(false);
|
|
1838
|
+
this.pageContextState.set(null);
|
|
1839
|
+
this.pageContextExcludedState.set(false);
|
|
1840
|
+
this.restoredDraftState.set(null);
|
|
1841
|
+
this.errorState.set(null);
|
|
1842
|
+
this.noticeState.set(null);
|
|
1843
|
+
this.lastUsageState.set(null);
|
|
1844
|
+
this.restorePanelOpenState.set(false);
|
|
1845
|
+
this.uiState.deactivate();
|
|
1711
1846
|
return;
|
|
1712
1847
|
}
|
|
1713
1848
|
this.statusLoadingState.set(true);
|
|
1714
1849
|
try {
|
|
1850
|
+
const restored = await this.uiState.activate();
|
|
1851
|
+
if (revision !== this.statusRevision) {
|
|
1852
|
+
return;
|
|
1853
|
+
}
|
|
1854
|
+
if (restored.changed) {
|
|
1855
|
+
this.accountRevision++;
|
|
1856
|
+
this.stopLocalTurn();
|
|
1857
|
+
this.statusState.set(null);
|
|
1858
|
+
this.activeConversationState.set(null);
|
|
1859
|
+
this.conversationsState.set([]);
|
|
1860
|
+
this.conversationsTotalState.set(0);
|
|
1861
|
+
this.conversationLoadingState.set(false);
|
|
1862
|
+
this.conversationsLoadingState.set(false);
|
|
1863
|
+
this.pageContextState.set(null);
|
|
1864
|
+
this.pageContextExcludedState.set(false);
|
|
1865
|
+
this.restoredDraftState.set(null);
|
|
1866
|
+
this.errorState.set(null);
|
|
1867
|
+
this.noticeState.set(null);
|
|
1868
|
+
this.lastUsageState.set(null);
|
|
1869
|
+
this.selectedAgentIdState.set(restored.state.agentId);
|
|
1870
|
+
this.restorePanelOpenState.set(restored.state.panelOpen);
|
|
1871
|
+
}
|
|
1715
1872
|
const status = await firstValueFrom(this.api.status());
|
|
1873
|
+
if (revision !== this.statusRevision) {
|
|
1874
|
+
return;
|
|
1875
|
+
}
|
|
1716
1876
|
this.statusState.set(status);
|
|
1717
|
-
|
|
1877
|
+
if (status.enabled) {
|
|
1878
|
+
this.selectInitialAgent(status.agents);
|
|
1879
|
+
if (!this.activeConversationState() && restored.state.conversationId) {
|
|
1880
|
+
await this.loadConversation(restored.state.conversationId, true);
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1718
1883
|
}
|
|
1719
1884
|
catch {
|
|
1720
1885
|
// An unavailable assistant endpoint hides the assistant instead of showing an error.
|
|
1721
|
-
this.
|
|
1886
|
+
if (revision === this.statusRevision) {
|
|
1887
|
+
this.statusState.set(null);
|
|
1888
|
+
}
|
|
1722
1889
|
}
|
|
1723
1890
|
finally {
|
|
1724
|
-
this.
|
|
1891
|
+
if (revision === this.statusRevision) {
|
|
1892
|
+
this.statusLoadingState.set(false);
|
|
1893
|
+
}
|
|
1725
1894
|
}
|
|
1726
1895
|
}
|
|
1727
1896
|
async refreshConversations() {
|
|
1728
1897
|
if (!this.enabled()) {
|
|
1729
1898
|
return;
|
|
1730
1899
|
}
|
|
1900
|
+
const revision = this.accountRevision;
|
|
1731
1901
|
this.conversationsLoadingState.set(true);
|
|
1732
1902
|
try {
|
|
1733
1903
|
const page = await firstValueFrom(this.api.listConversations(1, conversationPageSize));
|
|
1734
|
-
this.
|
|
1735
|
-
|
|
1904
|
+
if (revision === this.accountRevision) {
|
|
1905
|
+
this.conversationsState.set(page.items);
|
|
1906
|
+
this.conversationsTotalState.set(page.total);
|
|
1907
|
+
}
|
|
1736
1908
|
}
|
|
1737
1909
|
catch (error) {
|
|
1738
|
-
this.
|
|
1910
|
+
if (revision === this.accountRevision) {
|
|
1911
|
+
this.setError(error);
|
|
1912
|
+
}
|
|
1739
1913
|
}
|
|
1740
1914
|
finally {
|
|
1741
|
-
this.
|
|
1915
|
+
if (revision === this.accountRevision) {
|
|
1916
|
+
this.conversationsLoadingState.set(false);
|
|
1917
|
+
}
|
|
1742
1918
|
}
|
|
1743
1919
|
}
|
|
1744
1920
|
/** Chooses the agent for the next new conversation. Switching away from the active conversation's agent starts a new one. */
|
|
@@ -1747,6 +1923,7 @@ class NhAssistantStore {
|
|
|
1747
1923
|
return;
|
|
1748
1924
|
}
|
|
1749
1925
|
this.selectedAgentIdState.set(agentId);
|
|
1926
|
+
this.uiState.update({ agentId });
|
|
1750
1927
|
const active = this.activeConversationState();
|
|
1751
1928
|
if (active && active.agentId !== agentId && !this.streamingState()) {
|
|
1752
1929
|
this.startNewConversation();
|
|
@@ -1758,43 +1935,74 @@ class NhAssistantStore {
|
|
|
1758
1935
|
return;
|
|
1759
1936
|
}
|
|
1760
1937
|
this.activeConversationState.set(null);
|
|
1938
|
+
this.uiState.update({ conversationId: null });
|
|
1761
1939
|
this.errorState.set(null);
|
|
1940
|
+
this.noticeState.set(null);
|
|
1762
1941
|
this.lastUsageState.set(null);
|
|
1763
1942
|
}
|
|
1764
1943
|
async openConversation(conversationId) {
|
|
1944
|
+
await this.loadConversation(conversationId, false);
|
|
1945
|
+
}
|
|
1946
|
+
async loadConversation(conversationId, restoring) {
|
|
1765
1947
|
if (this.streamingState() || this.activeConversationState()?.id === conversationId) {
|
|
1766
1948
|
return;
|
|
1767
1949
|
}
|
|
1950
|
+
const revision = this.accountRevision;
|
|
1768
1951
|
this.conversationLoadingState.set(true);
|
|
1769
1952
|
this.errorState.set(null);
|
|
1953
|
+
this.noticeState.set(null);
|
|
1770
1954
|
try {
|
|
1771
1955
|
const conversation = await firstValueFrom(this.api.getConversation(conversationId));
|
|
1772
|
-
this.
|
|
1773
|
-
|
|
1774
|
-
this.selectedAgentIdState.set(conversation.agentId);
|
|
1956
|
+
if (revision !== this.accountRevision) {
|
|
1957
|
+
return;
|
|
1775
1958
|
}
|
|
1959
|
+
if (!this.agents().some(agent => agent.id === conversation.agentId)) {
|
|
1960
|
+
this.uiState.update({ conversationId: null });
|
|
1961
|
+
return;
|
|
1962
|
+
}
|
|
1963
|
+
this.activeConversationState.set(conversation);
|
|
1964
|
+
this.selectedAgentIdState.set(conversation.agentId);
|
|
1965
|
+
this.uiState.update({ conversationId, agentId: conversation.agentId });
|
|
1776
1966
|
}
|
|
1777
1967
|
catch (error) {
|
|
1778
|
-
this.
|
|
1968
|
+
if (revision !== this.accountRevision) {
|
|
1969
|
+
return;
|
|
1970
|
+
}
|
|
1971
|
+
if (error instanceof NhAssistantApiError && (error.status === 403 || error.status === 404)) {
|
|
1972
|
+
this.uiState.update({ conversationId: null });
|
|
1973
|
+
}
|
|
1974
|
+
if (!restoring) {
|
|
1975
|
+
this.setError(error);
|
|
1976
|
+
}
|
|
1779
1977
|
}
|
|
1780
1978
|
finally {
|
|
1781
|
-
this.
|
|
1979
|
+
if (revision === this.accountRevision) {
|
|
1980
|
+
this.conversationLoadingState.set(false);
|
|
1981
|
+
}
|
|
1782
1982
|
}
|
|
1783
1983
|
}
|
|
1784
1984
|
async deleteConversation(conversationId) {
|
|
1785
1985
|
if (this.streamingState() && this.activeConversationState()?.id === conversationId) {
|
|
1786
1986
|
return;
|
|
1787
1987
|
}
|
|
1988
|
+
const revision = this.accountRevision;
|
|
1788
1989
|
try {
|
|
1789
1990
|
await firstValueFrom(this.api.deleteConversation(conversationId), { defaultValue: undefined });
|
|
1991
|
+
if (revision !== this.accountRevision) {
|
|
1992
|
+
return;
|
|
1993
|
+
}
|
|
1790
1994
|
this.conversationsState.update(items => items.filter(item => item.id !== conversationId));
|
|
1791
1995
|
this.conversationsTotalState.update(total => Math.max(0, total - 1));
|
|
1792
1996
|
if (this.activeConversationState()?.id === conversationId) {
|
|
1793
1997
|
this.activeConversationState.set(null);
|
|
1998
|
+
this.noticeState.set(null);
|
|
1999
|
+
this.uiState.update({ conversationId: null });
|
|
1794
2000
|
}
|
|
1795
2001
|
}
|
|
1796
2002
|
catch (error) {
|
|
1797
|
-
this.
|
|
2003
|
+
if (revision === this.accountRevision) {
|
|
2004
|
+
this.setError(error);
|
|
2005
|
+
}
|
|
1798
2006
|
}
|
|
1799
2007
|
}
|
|
1800
2008
|
/**
|
|
@@ -1804,23 +2012,32 @@ class NhAssistantStore {
|
|
|
1804
2012
|
*/
|
|
1805
2013
|
async send(text) {
|
|
1806
2014
|
const trimmed = text.trim();
|
|
2015
|
+
await this.initialize();
|
|
2016
|
+
await this.reloadStatus();
|
|
1807
2017
|
const maxChars = this.limits()?.maxMessageChars ?? Number.MAX_SAFE_INTEGER;
|
|
1808
2018
|
if (trimmed.length === 0 || trimmed.length > maxChars || !this.canSend()) {
|
|
1809
2019
|
return false;
|
|
1810
2020
|
}
|
|
1811
2021
|
this.errorState.set(null);
|
|
2022
|
+
this.noticeState.set(null);
|
|
1812
2023
|
this.restoredDraftState.set(null);
|
|
1813
2024
|
this.streamingState.set(true);
|
|
2025
|
+
const accountRevision = this.accountRevision;
|
|
1814
2026
|
let conversation = this.activeConversationState();
|
|
1815
2027
|
if (!conversation) {
|
|
1816
2028
|
conversation = await this.createConversation();
|
|
1817
2029
|
if (!conversation) {
|
|
1818
|
-
this.
|
|
1819
|
-
|
|
2030
|
+
if (accountRevision === this.accountRevision) {
|
|
2031
|
+
this.streamingState.set(false);
|
|
2032
|
+
this.restoredDraftState.set(text);
|
|
2033
|
+
}
|
|
1820
2034
|
return false;
|
|
1821
2035
|
}
|
|
1822
2036
|
}
|
|
1823
2037
|
const clientContext = await this.contextForMessage();
|
|
2038
|
+
if (accountRevision !== this.accountRevision) {
|
|
2039
|
+
return false;
|
|
2040
|
+
}
|
|
1824
2041
|
const clientMessageId = createClientMessageId();
|
|
1825
2042
|
const previousStatus = conversation.status;
|
|
1826
2043
|
this.activeConversationState.set({
|
|
@@ -1833,6 +2050,7 @@ class NhAssistantStore {
|
|
|
1833
2050
|
});
|
|
1834
2051
|
const conversationId = conversation.id;
|
|
1835
2052
|
return new Promise(resolve => {
|
|
2053
|
+
this.pendingSendResolve = resolve;
|
|
1836
2054
|
let started = false;
|
|
1837
2055
|
let failedAfterStart = false;
|
|
1838
2056
|
this.runStream(this.api.sendMessage(conversationId, {
|
|
@@ -1858,6 +2076,7 @@ class NhAssistantStore {
|
|
|
1858
2076
|
this.applyEvent(event, clientMessageId);
|
|
1859
2077
|
},
|
|
1860
2078
|
complete: () => {
|
|
2079
|
+
this.pendingSendResolve = undefined;
|
|
1861
2080
|
resolve(started);
|
|
1862
2081
|
this.afterTurn(conversationId, failedAfterStart);
|
|
1863
2082
|
}
|
|
@@ -1874,6 +2093,7 @@ class NhAssistantStore {
|
|
|
1874
2093
|
this.decidingState.set(true);
|
|
1875
2094
|
this.streamingState.set(true);
|
|
1876
2095
|
this.errorState.set(null);
|
|
2096
|
+
this.noticeState.set(null);
|
|
1877
2097
|
let accepted = false;
|
|
1878
2098
|
let failed = false;
|
|
1879
2099
|
const request = { decision, expectedProposalHash: approval.proposalHash, ...(reason ? { reason } : {}) };
|
|
@@ -1904,11 +2124,17 @@ class NhAssistantStore {
|
|
|
1904
2124
|
if (!conversation || (conversation.status === 'idle' && !this.streamingState())) {
|
|
1905
2125
|
return;
|
|
1906
2126
|
}
|
|
2127
|
+
const revision = this.accountRevision;
|
|
1907
2128
|
try {
|
|
1908
2129
|
await firstValueFrom(this.api.cancel(conversation.id), { defaultValue: undefined });
|
|
1909
2130
|
}
|
|
1910
2131
|
catch (error) {
|
|
1911
|
-
this.
|
|
2132
|
+
if (revision === this.accountRevision) {
|
|
2133
|
+
this.setError(error);
|
|
2134
|
+
}
|
|
2135
|
+
return;
|
|
2136
|
+
}
|
|
2137
|
+
if (revision !== this.accountRevision) {
|
|
1912
2138
|
return;
|
|
1913
2139
|
}
|
|
1914
2140
|
if (!this.streamingState()) {
|
|
@@ -1929,9 +2155,21 @@ class NhAssistantStore {
|
|
|
1929
2155
|
clearError() {
|
|
1930
2156
|
this.errorState.set(null);
|
|
1931
2157
|
}
|
|
2158
|
+
clearNotice() {
|
|
2159
|
+
this.noticeState.set(null);
|
|
2160
|
+
}
|
|
2161
|
+
/** Persists only drawer visibility, never the page context or a message draft. */
|
|
2162
|
+
setPanelOpen(open) {
|
|
2163
|
+
this.restorePanelOpenState.set(open);
|
|
2164
|
+
this.uiState.update({ panelOpen: open });
|
|
2165
|
+
}
|
|
1932
2166
|
/** Reads the host's page context again so the chip shows what the next message sends. */
|
|
1933
2167
|
async refreshPageContext() {
|
|
1934
|
-
|
|
2168
|
+
const revision = this.accountRevision;
|
|
2169
|
+
const context = await this.readPageContext();
|
|
2170
|
+
if (revision === this.accountRevision) {
|
|
2171
|
+
this.pageContextState.set(context ?? null);
|
|
2172
|
+
}
|
|
1935
2173
|
}
|
|
1936
2174
|
/** Leaves the page context out of the next message only, or includes it again. */
|
|
1937
2175
|
setPageContextExcluded(excluded) {
|
|
@@ -1955,8 +2193,11 @@ class NhAssistantStore {
|
|
|
1955
2193
|
this.pageContextExcludedState.set(false);
|
|
1956
2194
|
return null;
|
|
1957
2195
|
}
|
|
2196
|
+
const revision = this.accountRevision;
|
|
1958
2197
|
const context = await this.readPageContext();
|
|
1959
|
-
|
|
2198
|
+
if (revision === this.accountRevision) {
|
|
2199
|
+
this.pageContextState.set(context ?? null);
|
|
2200
|
+
}
|
|
1960
2201
|
return context;
|
|
1961
2202
|
}
|
|
1962
2203
|
async readPageContext() {
|
|
@@ -1986,14 +2227,8 @@ class NhAssistantStore {
|
|
|
1986
2227
|
await new Promise(resolve => {
|
|
1987
2228
|
this.accessSubscription = decision.subscribe({
|
|
1988
2229
|
next: granted => {
|
|
1989
|
-
const changed = this.accessGrantedState() !== granted;
|
|
1990
2230
|
this.accessGrantedState.set(granted);
|
|
1991
|
-
|
|
1992
|
-
void this.reloadStatus().finally(() => resolve());
|
|
1993
|
-
}
|
|
1994
|
-
else {
|
|
1995
|
-
resolve();
|
|
1996
|
-
}
|
|
2231
|
+
void this.reloadStatus().finally(() => resolve());
|
|
1997
2232
|
},
|
|
1998
2233
|
error: () => {
|
|
1999
2234
|
this.accessGrantedState.set(false);
|
|
@@ -2008,15 +2243,22 @@ class NhAssistantStore {
|
|
|
2008
2243
|
if (!agentId) {
|
|
2009
2244
|
return null;
|
|
2010
2245
|
}
|
|
2246
|
+
const revision = this.accountRevision;
|
|
2011
2247
|
try {
|
|
2012
2248
|
const conversation = await firstValueFrom(this.api.createConversation({ agentId }));
|
|
2249
|
+
if (revision !== this.accountRevision) {
|
|
2250
|
+
return null;
|
|
2251
|
+
}
|
|
2013
2252
|
this.activeConversationState.set(conversation);
|
|
2253
|
+
this.uiState.update({ conversationId: conversation.id });
|
|
2014
2254
|
this.conversationsState.update(items => [toSummary(conversation), ...items.filter(item => item.id !== conversation.id)]);
|
|
2015
2255
|
this.conversationsTotalState.update(total => total + 1);
|
|
2016
2256
|
return conversation;
|
|
2017
2257
|
}
|
|
2018
2258
|
catch (error) {
|
|
2019
|
-
this.
|
|
2259
|
+
if (revision === this.accountRevision) {
|
|
2260
|
+
this.setError(error);
|
|
2261
|
+
}
|
|
2020
2262
|
return null;
|
|
2021
2263
|
}
|
|
2022
2264
|
}
|
|
@@ -2037,6 +2279,14 @@ class NhAssistantStore {
|
|
|
2037
2279
|
complete: finish
|
|
2038
2280
|
});
|
|
2039
2281
|
}
|
|
2282
|
+
stopLocalTurn() {
|
|
2283
|
+
this.streamSubscription?.unsubscribe();
|
|
2284
|
+
this.pendingSendResolve?.(false);
|
|
2285
|
+
this.pendingSendResolve = undefined;
|
|
2286
|
+
clearTimeout(this.cancelTimer);
|
|
2287
|
+
this.streamingState.set(false);
|
|
2288
|
+
this.decidingState.set(false);
|
|
2289
|
+
}
|
|
2040
2290
|
applyEvent(event, clientMessageId) {
|
|
2041
2291
|
this.updateActive(conversation => applyNhAssistantEvent(conversation, event, clientMessageId));
|
|
2042
2292
|
if (event.type === 'error') {
|
|
@@ -2048,8 +2298,22 @@ class NhAssistantStore {
|
|
|
2048
2298
|
const code = event.data.errorCode ?? NhAssistantClientErrorCodes.server;
|
|
2049
2299
|
this.errorState.set(clientError(code));
|
|
2050
2300
|
}
|
|
2301
|
+
else if (event.data.status === 'completed') {
|
|
2302
|
+
this.noticeState.set(this.completionNotice(event.data.errorCode));
|
|
2303
|
+
}
|
|
2051
2304
|
}
|
|
2052
2305
|
}
|
|
2306
|
+
/** A completed turn with a server code, or without any answer text, explains itself with a notice. */
|
|
2307
|
+
completionNotice(errorCode) {
|
|
2308
|
+
if (errorCode) {
|
|
2309
|
+
return clientError(errorCode);
|
|
2310
|
+
}
|
|
2311
|
+
const conversation = this.activeConversationState();
|
|
2312
|
+
if (conversation && !nhAssistantLatestTurnHasText(conversation)) {
|
|
2313
|
+
return { code: NH_ASSISTANT_NO_ANSWER_NOTICE_CODE, messageKey: 'nh-assistant.notices.no-answer' };
|
|
2314
|
+
}
|
|
2315
|
+
return null;
|
|
2316
|
+
}
|
|
2053
2317
|
afterTurn(conversationId, reload) {
|
|
2054
2318
|
const active = this.activeConversationState();
|
|
2055
2319
|
if (active?.id === conversationId) {
|
|
@@ -2063,9 +2327,10 @@ class NhAssistantStore {
|
|
|
2063
2327
|
}
|
|
2064
2328
|
}
|
|
2065
2329
|
async reloadActiveConversation(conversationId) {
|
|
2330
|
+
const revision = this.accountRevision;
|
|
2066
2331
|
try {
|
|
2067
2332
|
const conversation = await firstValueFrom(this.api.getConversation(conversationId));
|
|
2068
|
-
if (this.activeConversationState()?.id === conversationId && !this.streamingState()) {
|
|
2333
|
+
if (revision === this.accountRevision && this.activeConversationState()?.id === conversationId && !this.streamingState()) {
|
|
2069
2334
|
this.activeConversationState.set(conversation);
|
|
2070
2335
|
}
|
|
2071
2336
|
}
|
|
@@ -2093,6 +2358,7 @@ class NhAssistantStore {
|
|
|
2093
2358
|
}
|
|
2094
2359
|
const preferred = agents.find(agent => agent.id === this.config.defaultAgentId) ?? agents[0] ?? null;
|
|
2095
2360
|
this.selectedAgentIdState.set(preferred?.id ?? null);
|
|
2361
|
+
this.uiState.update({ agentId: preferred?.id ?? null });
|
|
2096
2362
|
}
|
|
2097
2363
|
setError(error) {
|
|
2098
2364
|
if (error instanceof NhAssistantApiError) {
|
|
@@ -2161,12 +2427,31 @@ class NhAssistantPanelService {
|
|
|
2161
2427
|
this.returnFocusTo = null;
|
|
2162
2428
|
this.isOpen = this.openState.asReadonly();
|
|
2163
2429
|
inject(DestroyRef).onDestroy(() => this.dispose());
|
|
2430
|
+
effect(() => {
|
|
2431
|
+
if (this.store.enabled() && this.store.restorePanelOpen() && this.portal && !this.openState()) {
|
|
2432
|
+
untracked(() => queueMicrotask(() => {
|
|
2433
|
+
if (this.portal && this.store.enabled() && this.store.restorePanelOpen() && !this.openState()) {
|
|
2434
|
+
this.showPanel();
|
|
2435
|
+
}
|
|
2436
|
+
}));
|
|
2437
|
+
}
|
|
2438
|
+
});
|
|
2164
2439
|
}
|
|
2165
2440
|
open(conversationId) {
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
void this.store.openConversation(conversationId);
|
|
2441
|
+
if (this.openState() && !conversationId) {
|
|
2442
|
+
return;
|
|
2169
2443
|
}
|
|
2444
|
+
this.showPanel();
|
|
2445
|
+
void this.store.initialize().then(() => this.store.reloadStatus()).then(() => {
|
|
2446
|
+
if (this.openState()) {
|
|
2447
|
+
this.store.setPanelOpen(true);
|
|
2448
|
+
}
|
|
2449
|
+
if (conversationId) {
|
|
2450
|
+
void this.store.openConversation(conversationId);
|
|
2451
|
+
}
|
|
2452
|
+
});
|
|
2453
|
+
}
|
|
2454
|
+
showPanel() {
|
|
2170
2455
|
if (this.openState()) {
|
|
2171
2456
|
return;
|
|
2172
2457
|
}
|
|
@@ -2180,6 +2465,7 @@ class NhAssistantPanelService {
|
|
|
2180
2465
|
return;
|
|
2181
2466
|
}
|
|
2182
2467
|
this.openState.set(false);
|
|
2468
|
+
this.store.setPanelOpen(false);
|
|
2183
2469
|
this.overlayRef?.detach();
|
|
2184
2470
|
const target = this.returnFocusTo;
|
|
2185
2471
|
this.returnFocusTo = null;
|
|
@@ -2201,6 +2487,13 @@ class NhAssistantPanelService {
|
|
|
2201
2487
|
if (this.openState()) {
|
|
2202
2488
|
this.attach();
|
|
2203
2489
|
}
|
|
2490
|
+
else {
|
|
2491
|
+
void this.store.initialize().then(() => {
|
|
2492
|
+
if (this.portal === portal && this.store.enabled() && this.store.restorePanelOpen()) {
|
|
2493
|
+
this.showPanel();
|
|
2494
|
+
}
|
|
2495
|
+
});
|
|
2496
|
+
}
|
|
2204
2497
|
}
|
|
2205
2498
|
unregisterPanel(portal) {
|
|
2206
2499
|
if (this.portal !== portal) {
|
|
@@ -2274,6 +2567,7 @@ function provideNhAssistant(config) {
|
|
|
2274
2567
|
NhAssistantApiService,
|
|
2275
2568
|
NhAssistantAdminApiService,
|
|
2276
2569
|
NhAssistantStore,
|
|
2570
|
+
NhAssistantUiState,
|
|
2277
2571
|
NhAssistantPanelService,
|
|
2278
2572
|
...(config.translations === 'host'
|
|
2279
2573
|
? []
|
|
@@ -3060,6 +3354,10 @@ class NhAssistantPanelComponent {
|
|
|
3060
3354
|
const error = this.store.error();
|
|
3061
3355
|
return error ? [error.messageKey, `nh-assistant.errors.${error.code}`, 'nh-assistant.errors.generic'] : [];
|
|
3062
3356
|
}, ...(ngDevMode ? [{ debugName: "errorKeys" }] : []));
|
|
3357
|
+
this.noticeKeys = computed(() => {
|
|
3358
|
+
const notice = this.store.notice();
|
|
3359
|
+
return notice ? [notice.messageKey, `nh-assistant.errors.${notice.code}`, 'nh-assistant.errors.generic'] : [];
|
|
3360
|
+
}, ...(ngDevMode ? [{ debugName: "noticeKeys" }] : []));
|
|
3063
3361
|
// Opening, creating or leaving a conversation returns from the list to the thread.
|
|
3064
3362
|
effect(() => {
|
|
3065
3363
|
if (this.conversationId() !== undefined) {
|
|
@@ -3131,7 +3429,7 @@ class NhAssistantPanelComponent {
|
|
|
3131
3429
|
void this.store.cancel();
|
|
3132
3430
|
}
|
|
3133
3431
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhAssistantPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3134
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: NhAssistantPanelComponent, isStandalone: true, selector: "nh-assistant-panel", viewQueries: [{ propertyName: "drawer", first: true, predicate: ["drawer"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template #drawer>\n <section class=\"drawer\" role=\"dialog\" aria-modal=\"false\" [attr.aria-labelledby]=\"titleId\">\n <header class=\"header\">\n <div class=\"heading\">\n <h2 [id]=\"titleId\">{{ 'nh-assistant.panel.title' | translate }}</h2>\n @if (store.enabled()) {\n <nh-assistant-agent-picker\n [agents]=\"store.agents()\"\n [selectedAgentId]=\"store.selectedAgentId()\"\n [disabled]=\"store.streaming()\"\n (agentChange)=\"store.selectAgent($event)\" />\n }\n </div>\n <div class=\"header-actions\">\n @if (store.enabled()) {\n <button\n class=\"icon-button\"\n type=\"button\"\n [class.active]=\"showConversations()\"\n [attr.aria-expanded]=\"showConversations()\"\n [attr.aria-label]=\"(showConversations() ? 'nh-assistant.panel.hide-conversations' : 'nh-assistant.panel.show-conversations') | translate\"\n [attr.title]=\"(showConversations() ? 'nh-assistant.panel.hide-conversations' : 'nh-assistant.panel.show-conversations') | translate\"\n (click)=\"toggleConversations()\">\n <nh-assistant-icon name=\"list\" />\n </button>\n <button\n class=\"icon-button\"\n type=\"button\"\n [class.active]=\"showPreferences()\"\n [attr.aria-expanded]=\"showPreferences()\"\n [attr.aria-label]=\"'nh-assistant.panel.open-preferences' | translate\"\n [attr.title]=\"'nh-assistant.panel.open-preferences' | translate\"\n (click)=\"togglePreferences()\">\n <nh-assistant-icon name=\"settings\" />\n </button>\n @if (adminRoute && store.canAdminister()) {\n <a\n class=\"icon-button\"\n [routerLink]=\"adminRoute\"\n [attr.aria-label]=\"'nh-assistant.panel.open-admin' | translate\"\n [attr.title]=\"'nh-assistant.panel.open-admin' | translate\"\n (click)=\"panel.close()\">\n <nh-assistant-icon name=\"admin\" />\n </a>\n }\n <button\n class=\"icon-button\"\n type=\"button\"\n [disabled]=\"store.streaming()\"\n [attr.aria-label]=\"'nh-assistant.panel.new-conversation' | translate\"\n [attr.title]=\"'nh-assistant.panel.new-conversation' | translate\"\n (click)=\"newConversation()\">\n <nh-assistant-icon name=\"plus\" />\n </button>\n }\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.panel.close' | translate\"\n [attr.title]=\"'nh-assistant.panel.close' | translate\"\n (click)=\"panel.close()\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </div>\n </header>\n\n <div class=\"body\">\n @if (store.statusLoading() && !store.enabled()) {\n <div class=\"state\" role=\"status\">\n <p class=\"state-title\">{{ 'nh-assistant.panel.loading' | translate }}</p>\n </div>\n } @else if (!store.enabled()) {\n <div class=\"state\">\n <nh-assistant-icon class=\"state-icon\" name=\"warning\" />\n <p class=\"state-title\">{{ 'nh-assistant.panel.unavailable-title' | translate }}</p>\n <p>{{ 'nh-assistant.panel.unavailable-description' | translate }}</p>\n </div>\n } @else {\n @if (showPreferences()) {\n <nh-assistant-preferences class=\"overlay-view\" (closed)=\"showPreferences.set(false)\" />\n }\n\n @if (showConversations()) {\n <nh-assistant-conversation-list\n class=\"overlay-view\"\n [conversations]=\"store.conversations()\"\n [total]=\"store.conversationsTotal()\"\n [activeConversationId]=\"conversation()?.id ?? null\"\n [loading]=\"store.conversationsLoading()\"\n [disabled]=\"store.streaming()\"\n (conversationSelect)=\"openConversation($event)\"\n (conversationDelete)=\"deleteConversation($event)\"\n (conversationCreate)=\"newConversation()\" />\n }\n\n @if (store.conversationLoading()) {\n <div class=\"state\" role=\"status\">\n <p class=\"state-title\">{{ 'nh-assistant.thread.loading' | translate }}</p>\n </div>\n } @else if (messages().length === 0 && !store.streaming()) {\n <div class=\"state\">\n <nh-assistant-icon class=\"state-icon\" name=\"assistant\" />\n <p class=\"state-title\">{{ 'nh-assistant.panel.empty-title' | translate }}</p>\n <p>{{ 'nh-assistant.panel.empty-description' | translate }}</p>\n </div>\n } @else {\n <nh-assistant-thread\n class=\"thread\"\n [messages]=\"messages()\"\n [streaming]=\"store.streaming()\"\n [deciding]=\"store.deciding()\"\n [markdown]=\"markdown\"\n (approvalDecision)=\"decide($event)\" />\n }\n }\n </div>\n\n @if (store.error(); as error) {\n <div class=\"error\" role=\"alert\">\n <nh-assistant-icon name=\"warning\" />\n <p>{{ errorKeys() | nhAssistantTranslate: error.code }}</p>\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.panel.dismiss-error' | translate\"\n [attr.title]=\"'nh-assistant.panel.dismiss-error' | translate\"\n (click)=\"store.clearError()\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </div>\n }\n\n @if (store.enabled() && !showPreferences()) {\n <footer class=\"footer\" (focusin)=\"refreshPageContext()\">\n @if (closed()) {\n <p class=\"notice\">{{ 'nh-assistant.panel.conversation-closed' | translate }}</p>\n }\n @if (store.pageContext(); as context) {\n <div class=\"context-chip\" [class.excluded]=\"store.pageContextExcluded()\" [attr.title]=\"context.route\">\n <nh-assistant-icon name=\"page\" />\n <span class=\"context-text\">\n {{ (store.pageContextExcluded() ? 'nh-assistant.page-context.excluded' : 'nh-assistant.page-context.included') | translate: { page: describeContext(context) } }}\n </span>\n <button\n class=\"icon-button small\"\n type=\"button\"\n [attr.aria-pressed]=\"store.pageContextExcluded()\"\n [attr.aria-label]=\"(store.pageContextExcluded() ? 'nh-assistant.page-context.include' : 'nh-assistant.page-context.exclude') | translate: { page: describeContext(context) }\"\n [attr.title]=\"(store.pageContextExcluded() ? 'nh-assistant.page-context.include' : 'nh-assistant.page-context.exclude') | translate: { page: describeContext(context) }\"\n (click)=\"store.setPageContextExcluded(!store.pageContextExcluded())\">\n <nh-assistant-icon [name]=\"store.pageContextExcluded() ? 'plus' : 'x'\" />\n </button>\n </div>\n }\n <nh-assistant-composer\n [disabled]=\"closed()\"\n [sendDisabled]=\"composerDisabled()\"\n [busy]=\"busy()\"\n [status]=\"composerStatus()\"\n [maxLength]=\"store.limits()?.maxMessageChars ?? null\"\n [restore]=\"store.restoredDraft()\"\n (restored)=\"store.consumeRestoredDraft()\"\n (send)=\"send($event)\"\n (cancel)=\"cancel()\" />\n </footer>\n }\n </section>\n</ng-template>\n", styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}.drawer{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){.drawer{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:contents}.drawer{position:relative;display:flex;flex-direction:column;box-sizing:border-box;width:100%;height:100%;border-left:1px solid var(--nha-border);color:var(--nha-text);background:var(--nha-background);box-shadow:var(--nha-shadow);font-family:var(--nha-font);font-size:14px}.drawer *,.drawer *:before,.drawer *:after{box-sizing:border-box}.header{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:6px 12px;border-bottom:1px solid var(--nha-border);padding:12px 12px 12px 16px}.heading{display:contents}nh-assistant-agent-picker{grid-column:1/-1;grid-row:2}h2{grid-row:1;min-width:0;margin:0;overflow:hidden;font-size:16px;font-weight:750;letter-spacing:-.01em;text-overflow:ellipsis;white-space:nowrap}.header-actions{display:flex;flex:none;grid-column:2;grid-row:1;gap:4px}.icon-button{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border:1px solid transparent;border-radius:var(--nha-radius-control);color:var(--nha-text-muted);background:transparent;cursor:pointer;text-decoration:none}.icon-button:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.icon-button:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}.icon-button:disabled{cursor:not-allowed;opacity:.5}.icon-button.active{color:var(--nha-highlight);background:var(--nha-surface-strong)}.body{position:relative;display:flex;flex:1;flex-direction:column;min-height:0;background:var(--nha-background)}.thread{flex:1;min-height:0}.overlay-view{position:absolute;z-index:1;inset:0;border-bottom:1px solid var(--nha-border);background:var(--nha-background)}.state{display:flex;flex:1;flex-direction:column;align-items:center;justify-content:center;gap:4px;padding:24px;color:var(--nha-text-muted);text-align:center}.state p{max-width:32ch;margin:0;font-size:13px}.state-icon{margin-bottom:8px;color:var(--nha-highlight);font-size:28px}.state .state-title{color:var(--nha-text);font-size:15px;font-weight:700}.error{display:flex;align-items:center;gap:8px;margin:0 12px 8px;border:1px solid var(--nha-danger);border-radius:var(--nha-radius-control);padding:4px 4px 4px 12px;color:var(--nha-danger);background:var(--nha-danger-soft)}.error p{flex:1;margin:0;color:var(--nha-text);font-size:13px}.footer{border-top:1px solid var(--nha-border);padding:12px 16px 14px}.context-chip{display:flex;align-items:center;gap:6px;max-width:100%;margin:0 0 8px;border:1px solid var(--nha-border);border-radius:999px;padding:2px 2px 2px 10px;width:fit-content;color:var(--nha-text-muted);background:var(--nha-surface);font-size:12px}.context-chip.excluded .context-text{text-decoration:line-through}.context-text{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.icon-button.small{flex:none;width:26px;height:26px;border-radius:999px}.notice{margin:0 0 8px;color:var(--nha-text-muted);font-size:12px}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }, { kind: "component", type: NhAssistantAgentPickerComponent, selector: "nh-assistant-agent-picker", inputs: ["agents", "selectedAgentId", "disabled"], outputs: ["agentChange"] }, { kind: "component", type: NhAssistantComposerComponent, selector: "nh-assistant-composer", inputs: ["disabled", "sendDisabled", "busy", "status", "maxLength", "restore"], outputs: ["send", "cancel", "restored"] }, { kind: "component", type: NhAssistantConversationListComponent, selector: "nh-assistant-conversation-list", inputs: ["conversations", "total", "activeConversationId", "loading", "disabled"], outputs: ["conversationSelect", "conversationDelete", "conversationCreate"] }, { kind: "component", type: NhAssistantPreferencesComponent, selector: "nh-assistant-preferences", outputs: ["closed", "saved"] }, { kind: "component", type: NhAssistantThreadComponent, selector: "nh-assistant-thread", inputs: ["messages", "streaming", "deciding", "markdown"], outputs: ["approvalDecision"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: NhAssistantTranslatePipe, name: "nhAssistantTranslate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3432
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: NhAssistantPanelComponent, isStandalone: true, selector: "nh-assistant-panel", viewQueries: [{ propertyName: "drawer", first: true, predicate: ["drawer"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template #drawer>\n <section class=\"drawer\" role=\"dialog\" aria-modal=\"false\" [attr.aria-labelledby]=\"titleId\">\n <header class=\"header\">\n <div class=\"heading\">\n <h2 [id]=\"titleId\">{{ 'nh-assistant.panel.title' | translate }}</h2>\n @if (store.enabled()) {\n <nh-assistant-agent-picker\n [agents]=\"store.agents()\"\n [selectedAgentId]=\"store.selectedAgentId()\"\n [disabled]=\"store.streaming()\"\n (agentChange)=\"store.selectAgent($event)\" />\n }\n </div>\n <div class=\"header-actions\">\n @if (store.enabled()) {\n <button\n class=\"icon-button\"\n type=\"button\"\n [class.active]=\"showConversations()\"\n [attr.aria-expanded]=\"showConversations()\"\n [attr.aria-label]=\"(showConversations() ? 'nh-assistant.panel.hide-conversations' : 'nh-assistant.panel.show-conversations') | translate\"\n [attr.title]=\"(showConversations() ? 'nh-assistant.panel.hide-conversations' : 'nh-assistant.panel.show-conversations') | translate\"\n (click)=\"toggleConversations()\">\n <nh-assistant-icon name=\"list\" />\n </button>\n <button\n class=\"icon-button\"\n type=\"button\"\n [class.active]=\"showPreferences()\"\n [attr.aria-expanded]=\"showPreferences()\"\n [attr.aria-label]=\"'nh-assistant.panel.open-preferences' | translate\"\n [attr.title]=\"'nh-assistant.panel.open-preferences' | translate\"\n (click)=\"togglePreferences()\">\n <nh-assistant-icon name=\"settings\" />\n </button>\n @if (adminRoute && store.canAdminister()) {\n <a\n class=\"icon-button\"\n [routerLink]=\"adminRoute\"\n [attr.aria-label]=\"'nh-assistant.panel.open-admin' | translate\"\n [attr.title]=\"'nh-assistant.panel.open-admin' | translate\"\n (click)=\"panel.close()\">\n <nh-assistant-icon name=\"admin\" />\n </a>\n }\n <button\n class=\"icon-button\"\n type=\"button\"\n [disabled]=\"store.streaming()\"\n [attr.aria-label]=\"'nh-assistant.panel.new-conversation' | translate\"\n [attr.title]=\"'nh-assistant.panel.new-conversation' | translate\"\n (click)=\"newConversation()\">\n <nh-assistant-icon name=\"plus\" />\n </button>\n }\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.panel.close' | translate\"\n [attr.title]=\"'nh-assistant.panel.close' | translate\"\n (click)=\"panel.close()\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </div>\n </header>\n\n <div class=\"body\">\n @if (store.statusLoading() && !store.enabled()) {\n <div class=\"state\" role=\"status\">\n <p class=\"state-title\">{{ 'nh-assistant.panel.loading' | translate }}</p>\n </div>\n } @else if (!store.enabled()) {\n <div class=\"state\">\n <nh-assistant-icon class=\"state-icon\" name=\"warning\" />\n <p class=\"state-title\">{{ 'nh-assistant.panel.unavailable-title' | translate }}</p>\n <p>{{ 'nh-assistant.panel.unavailable-description' | translate }}</p>\n </div>\n } @else {\n @if (showPreferences()) {\n <nh-assistant-preferences class=\"overlay-view\" (closed)=\"showPreferences.set(false)\" />\n }\n\n @if (showConversations()) {\n <nh-assistant-conversation-list\n class=\"overlay-view\"\n [conversations]=\"store.conversations()\"\n [total]=\"store.conversationsTotal()\"\n [activeConversationId]=\"conversation()?.id ?? null\"\n [loading]=\"store.conversationsLoading()\"\n [disabled]=\"store.streaming()\"\n (conversationSelect)=\"openConversation($event)\"\n (conversationDelete)=\"deleteConversation($event)\"\n (conversationCreate)=\"newConversation()\" />\n }\n\n @if (store.conversationLoading()) {\n <div class=\"state\" role=\"status\">\n <p class=\"state-title\">{{ 'nh-assistant.thread.loading' | translate }}</p>\n </div>\n } @else if (messages().length === 0 && !store.streaming()) {\n <div class=\"state\">\n <nh-assistant-icon class=\"state-icon\" name=\"assistant\" />\n <p class=\"state-title\">{{ 'nh-assistant.panel.empty-title' | translate }}</p>\n <p>{{ 'nh-assistant.panel.empty-description' | translate }}</p>\n </div>\n } @else {\n <nh-assistant-thread\n class=\"thread\"\n [messages]=\"messages()\"\n [streaming]=\"store.streaming()\"\n [deciding]=\"store.deciding()\"\n [markdown]=\"markdown\"\n (approvalDecision)=\"decide($event)\" />\n }\n }\n </div>\n\n @if (store.error(); as error) {\n <div class=\"error\" role=\"alert\">\n <nh-assistant-icon name=\"warning\" />\n <p>{{ errorKeys() | nhAssistantTranslate: error.code }}</p>\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.panel.dismiss-error' | translate\"\n [attr.title]=\"'nh-assistant.panel.dismiss-error' | translate\"\n (click)=\"store.clearError()\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </div>\n } @else if (store.notice(); as notice) {\n <div class=\"turn-notice\" role=\"status\">\n <nh-assistant-icon name=\"warning\" />\n <p>{{ noticeKeys() | nhAssistantTranslate: notice.code }}</p>\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.panel.dismiss-error' | translate\"\n [attr.title]=\"'nh-assistant.panel.dismiss-error' | translate\"\n (click)=\"store.clearNotice()\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </div>\n }\n\n @if (store.enabled() && !showPreferences()) {\n <footer class=\"footer\" (focusin)=\"refreshPageContext()\">\n @if (closed()) {\n <p class=\"notice\">{{ 'nh-assistant.panel.conversation-closed' | translate }}</p>\n }\n @if (store.pageContext(); as context) {\n <div class=\"context-chip\" [class.excluded]=\"store.pageContextExcluded()\" [attr.title]=\"context.route\">\n <nh-assistant-icon name=\"page\" />\n <span class=\"context-text\">\n {{ (store.pageContextExcluded() ? 'nh-assistant.page-context.excluded' : 'nh-assistant.page-context.included') | translate: { page: describeContext(context) } }}\n </span>\n <button\n class=\"icon-button small\"\n type=\"button\"\n [attr.aria-pressed]=\"store.pageContextExcluded()\"\n [attr.aria-label]=\"(store.pageContextExcluded() ? 'nh-assistant.page-context.include' : 'nh-assistant.page-context.exclude') | translate: { page: describeContext(context) }\"\n [attr.title]=\"(store.pageContextExcluded() ? 'nh-assistant.page-context.include' : 'nh-assistant.page-context.exclude') | translate: { page: describeContext(context) }\"\n (click)=\"store.setPageContextExcluded(!store.pageContextExcluded())\">\n <nh-assistant-icon [name]=\"store.pageContextExcluded() ? 'plus' : 'x'\" />\n </button>\n </div>\n }\n <nh-assistant-composer\n [disabled]=\"closed()\"\n [sendDisabled]=\"composerDisabled()\"\n [busy]=\"busy()\"\n [status]=\"composerStatus()\"\n [maxLength]=\"store.limits()?.maxMessageChars ?? null\"\n [restore]=\"store.restoredDraft()\"\n (restored)=\"store.consumeRestoredDraft()\"\n (send)=\"send($event)\"\n (cancel)=\"cancel()\" />\n </footer>\n }\n </section>\n</ng-template>\n", styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}.drawer{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){.drawer{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:contents}.drawer{position:relative;display:flex;flex-direction:column;box-sizing:border-box;width:100%;height:100%;border-left:1px solid var(--nha-border);color:var(--nha-text);background:var(--nha-background);box-shadow:var(--nha-shadow);font-family:var(--nha-font);font-size:14px}.drawer *,.drawer *:before,.drawer *:after{box-sizing:border-box}.header{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:6px 12px;border-bottom:1px solid var(--nha-border);padding:12px 12px 12px 16px}.heading{display:contents}nh-assistant-agent-picker{grid-column:1/-1;grid-row:2}h2{grid-row:1;min-width:0;margin:0;overflow:hidden;font-size:16px;font-weight:750;letter-spacing:-.01em;text-overflow:ellipsis;white-space:nowrap}.header-actions{display:flex;flex:none;grid-column:2;grid-row:1;gap:4px}.icon-button{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border:1px solid transparent;border-radius:var(--nha-radius-control);color:var(--nha-text-muted);background:transparent;cursor:pointer;text-decoration:none}.icon-button:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.icon-button:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}.icon-button:disabled{cursor:not-allowed;opacity:.5}.icon-button.active{color:var(--nha-highlight);background:var(--nha-surface-strong)}.body{position:relative;display:flex;flex:1;flex-direction:column;min-height:0;background:var(--nha-background)}.thread{flex:1;min-height:0}.overlay-view{position:absolute;z-index:1;inset:0;border-bottom:1px solid var(--nha-border);background:var(--nha-background)}.state{display:flex;flex:1;flex-direction:column;align-items:center;justify-content:center;gap:4px;padding:24px;color:var(--nha-text-muted);text-align:center}.state p{max-width:32ch;margin:0;font-size:13px}.state-icon{margin-bottom:8px;color:var(--nha-highlight);font-size:28px}.state .state-title{color:var(--nha-text);font-size:15px;font-weight:700}.error{display:flex;align-items:center;gap:8px;margin:0 12px 8px;border:1px solid var(--nha-danger);border-radius:var(--nha-radius-control);padding:4px 4px 4px 12px;color:var(--nha-danger);background:var(--nha-danger-soft)}.error p{flex:1;margin:0;color:var(--nha-text);font-size:13px}.turn-notice{display:flex;align-items:center;gap:8px;margin:0 12px 8px;border:1px solid var(--nha-warning);border-radius:var(--nha-radius-control);padding:4px 4px 4px 12px;color:var(--nha-warning);background:var(--nha-warning-soft)}.turn-notice p{flex:1;margin:0;color:var(--nha-text);font-size:13px}.footer{border-top:1px solid var(--nha-border);padding:12px 16px 14px}.context-chip{display:flex;align-items:center;gap:6px;max-width:100%;margin:0 0 8px;border:1px solid var(--nha-border);border-radius:999px;padding:2px 2px 2px 10px;width:fit-content;color:var(--nha-text-muted);background:var(--nha-surface);font-size:12px}.context-chip.excluded .context-text{text-decoration:line-through}.context-text{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.icon-button.small{flex:none;width:26px;height:26px;border-radius:999px}.notice{margin:0 0 8px;color:var(--nha-text-muted);font-size:12px}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }, { kind: "component", type: NhAssistantAgentPickerComponent, selector: "nh-assistant-agent-picker", inputs: ["agents", "selectedAgentId", "disabled"], outputs: ["agentChange"] }, { kind: "component", type: NhAssistantComposerComponent, selector: "nh-assistant-composer", inputs: ["disabled", "sendDisabled", "busy", "status", "maxLength", "restore"], outputs: ["send", "cancel", "restored"] }, { kind: "component", type: NhAssistantConversationListComponent, selector: "nh-assistant-conversation-list", inputs: ["conversations", "total", "activeConversationId", "loading", "disabled"], outputs: ["conversationSelect", "conversationDelete", "conversationCreate"] }, { kind: "component", type: NhAssistantPreferencesComponent, selector: "nh-assistant-preferences", outputs: ["closed", "saved"] }, { kind: "component", type: NhAssistantThreadComponent, selector: "nh-assistant-thread", inputs: ["messages", "streaming", "deciding", "markdown"], outputs: ["approvalDecision"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: NhAssistantTranslatePipe, name: "nhAssistantTranslate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3135
3433
|
}
|
|
3136
3434
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhAssistantPanelComponent, decorators: [{
|
|
3137
3435
|
type: Component,
|
|
@@ -3145,7 +3443,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImpo
|
|
|
3145
3443
|
NhAssistantConversationListComponent,
|
|
3146
3444
|
NhAssistantPreferencesComponent,
|
|
3147
3445
|
NhAssistantThreadComponent
|
|
3148
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-template #drawer>\n <section class=\"drawer\" role=\"dialog\" aria-modal=\"false\" [attr.aria-labelledby]=\"titleId\">\n <header class=\"header\">\n <div class=\"heading\">\n <h2 [id]=\"titleId\">{{ 'nh-assistant.panel.title' | translate }}</h2>\n @if (store.enabled()) {\n <nh-assistant-agent-picker\n [agents]=\"store.agents()\"\n [selectedAgentId]=\"store.selectedAgentId()\"\n [disabled]=\"store.streaming()\"\n (agentChange)=\"store.selectAgent($event)\" />\n }\n </div>\n <div class=\"header-actions\">\n @if (store.enabled()) {\n <button\n class=\"icon-button\"\n type=\"button\"\n [class.active]=\"showConversations()\"\n [attr.aria-expanded]=\"showConversations()\"\n [attr.aria-label]=\"(showConversations() ? 'nh-assistant.panel.hide-conversations' : 'nh-assistant.panel.show-conversations') | translate\"\n [attr.title]=\"(showConversations() ? 'nh-assistant.panel.hide-conversations' : 'nh-assistant.panel.show-conversations') | translate\"\n (click)=\"toggleConversations()\">\n <nh-assistant-icon name=\"list\" />\n </button>\n <button\n class=\"icon-button\"\n type=\"button\"\n [class.active]=\"showPreferences()\"\n [attr.aria-expanded]=\"showPreferences()\"\n [attr.aria-label]=\"'nh-assistant.panel.open-preferences' | translate\"\n [attr.title]=\"'nh-assistant.panel.open-preferences' | translate\"\n (click)=\"togglePreferences()\">\n <nh-assistant-icon name=\"settings\" />\n </button>\n @if (adminRoute && store.canAdminister()) {\n <a\n class=\"icon-button\"\n [routerLink]=\"adminRoute\"\n [attr.aria-label]=\"'nh-assistant.panel.open-admin' | translate\"\n [attr.title]=\"'nh-assistant.panel.open-admin' | translate\"\n (click)=\"panel.close()\">\n <nh-assistant-icon name=\"admin\" />\n </a>\n }\n <button\n class=\"icon-button\"\n type=\"button\"\n [disabled]=\"store.streaming()\"\n [attr.aria-label]=\"'nh-assistant.panel.new-conversation' | translate\"\n [attr.title]=\"'nh-assistant.panel.new-conversation' | translate\"\n (click)=\"newConversation()\">\n <nh-assistant-icon name=\"plus\" />\n </button>\n }\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.panel.close' | translate\"\n [attr.title]=\"'nh-assistant.panel.close' | translate\"\n (click)=\"panel.close()\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </div>\n </header>\n\n <div class=\"body\">\n @if (store.statusLoading() && !store.enabled()) {\n <div class=\"state\" role=\"status\">\n <p class=\"state-title\">{{ 'nh-assistant.panel.loading' | translate }}</p>\n </div>\n } @else if (!store.enabled()) {\n <div class=\"state\">\n <nh-assistant-icon class=\"state-icon\" name=\"warning\" />\n <p class=\"state-title\">{{ 'nh-assistant.panel.unavailable-title' | translate }}</p>\n <p>{{ 'nh-assistant.panel.unavailable-description' | translate }}</p>\n </div>\n } @else {\n @if (showPreferences()) {\n <nh-assistant-preferences class=\"overlay-view\" (closed)=\"showPreferences.set(false)\" />\n }\n\n @if (showConversations()) {\n <nh-assistant-conversation-list\n class=\"overlay-view\"\n [conversations]=\"store.conversations()\"\n [total]=\"store.conversationsTotal()\"\n [activeConversationId]=\"conversation()?.id ?? null\"\n [loading]=\"store.conversationsLoading()\"\n [disabled]=\"store.streaming()\"\n (conversationSelect)=\"openConversation($event)\"\n (conversationDelete)=\"deleteConversation($event)\"\n (conversationCreate)=\"newConversation()\" />\n }\n\n @if (store.conversationLoading()) {\n <div class=\"state\" role=\"status\">\n <p class=\"state-title\">{{ 'nh-assistant.thread.loading' | translate }}</p>\n </div>\n } @else if (messages().length === 0 && !store.streaming()) {\n <div class=\"state\">\n <nh-assistant-icon class=\"state-icon\" name=\"assistant\" />\n <p class=\"state-title\">{{ 'nh-assistant.panel.empty-title' | translate }}</p>\n <p>{{ 'nh-assistant.panel.empty-description' | translate }}</p>\n </div>\n } @else {\n <nh-assistant-thread\n class=\"thread\"\n [messages]=\"messages()\"\n [streaming]=\"store.streaming()\"\n [deciding]=\"store.deciding()\"\n [markdown]=\"markdown\"\n (approvalDecision)=\"decide($event)\" />\n }\n }\n </div>\n\n @if (store.error(); as error) {\n <div class=\"error\" role=\"alert\">\n <nh-assistant-icon name=\"warning\" />\n <p>{{ errorKeys() | nhAssistantTranslate: error.code }}</p>\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.panel.dismiss-error' | translate\"\n [attr.title]=\"'nh-assistant.panel.dismiss-error' | translate\"\n (click)=\"store.clearError()\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </div>\n }\n\n @if (store.enabled() && !showPreferences()) {\n <footer class=\"footer\" (focusin)=\"refreshPageContext()\">\n @if (closed()) {\n <p class=\"notice\">{{ 'nh-assistant.panel.conversation-closed' | translate }}</p>\n }\n @if (store.pageContext(); as context) {\n <div class=\"context-chip\" [class.excluded]=\"store.pageContextExcluded()\" [attr.title]=\"context.route\">\n <nh-assistant-icon name=\"page\" />\n <span class=\"context-text\">\n {{ (store.pageContextExcluded() ? 'nh-assistant.page-context.excluded' : 'nh-assistant.page-context.included') | translate: { page: describeContext(context) } }}\n </span>\n <button\n class=\"icon-button small\"\n type=\"button\"\n [attr.aria-pressed]=\"store.pageContextExcluded()\"\n [attr.aria-label]=\"(store.pageContextExcluded() ? 'nh-assistant.page-context.include' : 'nh-assistant.page-context.exclude') | translate: { page: describeContext(context) }\"\n [attr.title]=\"(store.pageContextExcluded() ? 'nh-assistant.page-context.include' : 'nh-assistant.page-context.exclude') | translate: { page: describeContext(context) }\"\n (click)=\"store.setPageContextExcluded(!store.pageContextExcluded())\">\n <nh-assistant-icon [name]=\"store.pageContextExcluded() ? 'plus' : 'x'\" />\n </button>\n </div>\n }\n <nh-assistant-composer\n [disabled]=\"closed()\"\n [sendDisabled]=\"composerDisabled()\"\n [busy]=\"busy()\"\n [status]=\"composerStatus()\"\n [maxLength]=\"store.limits()?.maxMessageChars ?? null\"\n [restore]=\"store.restoredDraft()\"\n (restored)=\"store.consumeRestoredDraft()\"\n (send)=\"send($event)\"\n (cancel)=\"cancel()\" />\n </footer>\n }\n </section>\n</ng-template>\n", styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}.drawer{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){.drawer{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:contents}.drawer{position:relative;display:flex;flex-direction:column;box-sizing:border-box;width:100%;height:100%;border-left:1px solid var(--nha-border);color:var(--nha-text);background:var(--nha-background);box-shadow:var(--nha-shadow);font-family:var(--nha-font);font-size:14px}.drawer *,.drawer *:before,.drawer *:after{box-sizing:border-box}.header{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:6px 12px;border-bottom:1px solid var(--nha-border);padding:12px 12px 12px 16px}.heading{display:contents}nh-assistant-agent-picker{grid-column:1/-1;grid-row:2}h2{grid-row:1;min-width:0;margin:0;overflow:hidden;font-size:16px;font-weight:750;letter-spacing:-.01em;text-overflow:ellipsis;white-space:nowrap}.header-actions{display:flex;flex:none;grid-column:2;grid-row:1;gap:4px}.icon-button{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border:1px solid transparent;border-radius:var(--nha-radius-control);color:var(--nha-text-muted);background:transparent;cursor:pointer;text-decoration:none}.icon-button:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.icon-button:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}.icon-button:disabled{cursor:not-allowed;opacity:.5}.icon-button.active{color:var(--nha-highlight);background:var(--nha-surface-strong)}.body{position:relative;display:flex;flex:1;flex-direction:column;min-height:0;background:var(--nha-background)}.thread{flex:1;min-height:0}.overlay-view{position:absolute;z-index:1;inset:0;border-bottom:1px solid var(--nha-border);background:var(--nha-background)}.state{display:flex;flex:1;flex-direction:column;align-items:center;justify-content:center;gap:4px;padding:24px;color:var(--nha-text-muted);text-align:center}.state p{max-width:32ch;margin:0;font-size:13px}.state-icon{margin-bottom:8px;color:var(--nha-highlight);font-size:28px}.state .state-title{color:var(--nha-text);font-size:15px;font-weight:700}.error{display:flex;align-items:center;gap:8px;margin:0 12px 8px;border:1px solid var(--nha-danger);border-radius:var(--nha-radius-control);padding:4px 4px 4px 12px;color:var(--nha-danger);background:var(--nha-danger-soft)}.error p{flex:1;margin:0;color:var(--nha-text);font-size:13px}.footer{border-top:1px solid var(--nha-border);padding:12px 16px 14px}.context-chip{display:flex;align-items:center;gap:6px;max-width:100%;margin:0 0 8px;border:1px solid var(--nha-border);border-radius:999px;padding:2px 2px 2px 10px;width:fit-content;color:var(--nha-text-muted);background:var(--nha-surface);font-size:12px}.context-chip.excluded .context-text{text-decoration:line-through}.context-text{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.icon-button.small{flex:none;width:26px;height:26px;border-radius:999px}.notice{margin:0 0 8px;color:var(--nha-text-muted);font-size:12px}\n"] }]
|
|
3446
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-template #drawer>\n <section class=\"drawer\" role=\"dialog\" aria-modal=\"false\" [attr.aria-labelledby]=\"titleId\">\n <header class=\"header\">\n <div class=\"heading\">\n <h2 [id]=\"titleId\">{{ 'nh-assistant.panel.title' | translate }}</h2>\n @if (store.enabled()) {\n <nh-assistant-agent-picker\n [agents]=\"store.agents()\"\n [selectedAgentId]=\"store.selectedAgentId()\"\n [disabled]=\"store.streaming()\"\n (agentChange)=\"store.selectAgent($event)\" />\n }\n </div>\n <div class=\"header-actions\">\n @if (store.enabled()) {\n <button\n class=\"icon-button\"\n type=\"button\"\n [class.active]=\"showConversations()\"\n [attr.aria-expanded]=\"showConversations()\"\n [attr.aria-label]=\"(showConversations() ? 'nh-assistant.panel.hide-conversations' : 'nh-assistant.panel.show-conversations') | translate\"\n [attr.title]=\"(showConversations() ? 'nh-assistant.panel.hide-conversations' : 'nh-assistant.panel.show-conversations') | translate\"\n (click)=\"toggleConversations()\">\n <nh-assistant-icon name=\"list\" />\n </button>\n <button\n class=\"icon-button\"\n type=\"button\"\n [class.active]=\"showPreferences()\"\n [attr.aria-expanded]=\"showPreferences()\"\n [attr.aria-label]=\"'nh-assistant.panel.open-preferences' | translate\"\n [attr.title]=\"'nh-assistant.panel.open-preferences' | translate\"\n (click)=\"togglePreferences()\">\n <nh-assistant-icon name=\"settings\" />\n </button>\n @if (adminRoute && store.canAdminister()) {\n <a\n class=\"icon-button\"\n [routerLink]=\"adminRoute\"\n [attr.aria-label]=\"'nh-assistant.panel.open-admin' | translate\"\n [attr.title]=\"'nh-assistant.panel.open-admin' | translate\"\n (click)=\"panel.close()\">\n <nh-assistant-icon name=\"admin\" />\n </a>\n }\n <button\n class=\"icon-button\"\n type=\"button\"\n [disabled]=\"store.streaming()\"\n [attr.aria-label]=\"'nh-assistant.panel.new-conversation' | translate\"\n [attr.title]=\"'nh-assistant.panel.new-conversation' | translate\"\n (click)=\"newConversation()\">\n <nh-assistant-icon name=\"plus\" />\n </button>\n }\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.panel.close' | translate\"\n [attr.title]=\"'nh-assistant.panel.close' | translate\"\n (click)=\"panel.close()\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </div>\n </header>\n\n <div class=\"body\">\n @if (store.statusLoading() && !store.enabled()) {\n <div class=\"state\" role=\"status\">\n <p class=\"state-title\">{{ 'nh-assistant.panel.loading' | translate }}</p>\n </div>\n } @else if (!store.enabled()) {\n <div class=\"state\">\n <nh-assistant-icon class=\"state-icon\" name=\"warning\" />\n <p class=\"state-title\">{{ 'nh-assistant.panel.unavailable-title' | translate }}</p>\n <p>{{ 'nh-assistant.panel.unavailable-description' | translate }}</p>\n </div>\n } @else {\n @if (showPreferences()) {\n <nh-assistant-preferences class=\"overlay-view\" (closed)=\"showPreferences.set(false)\" />\n }\n\n @if (showConversations()) {\n <nh-assistant-conversation-list\n class=\"overlay-view\"\n [conversations]=\"store.conversations()\"\n [total]=\"store.conversationsTotal()\"\n [activeConversationId]=\"conversation()?.id ?? null\"\n [loading]=\"store.conversationsLoading()\"\n [disabled]=\"store.streaming()\"\n (conversationSelect)=\"openConversation($event)\"\n (conversationDelete)=\"deleteConversation($event)\"\n (conversationCreate)=\"newConversation()\" />\n }\n\n @if (store.conversationLoading()) {\n <div class=\"state\" role=\"status\">\n <p class=\"state-title\">{{ 'nh-assistant.thread.loading' | translate }}</p>\n </div>\n } @else if (messages().length === 0 && !store.streaming()) {\n <div class=\"state\">\n <nh-assistant-icon class=\"state-icon\" name=\"assistant\" />\n <p class=\"state-title\">{{ 'nh-assistant.panel.empty-title' | translate }}</p>\n <p>{{ 'nh-assistant.panel.empty-description' | translate }}</p>\n </div>\n } @else {\n <nh-assistant-thread\n class=\"thread\"\n [messages]=\"messages()\"\n [streaming]=\"store.streaming()\"\n [deciding]=\"store.deciding()\"\n [markdown]=\"markdown\"\n (approvalDecision)=\"decide($event)\" />\n }\n }\n </div>\n\n @if (store.error(); as error) {\n <div class=\"error\" role=\"alert\">\n <nh-assistant-icon name=\"warning\" />\n <p>{{ errorKeys() | nhAssistantTranslate: error.code }}</p>\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.panel.dismiss-error' | translate\"\n [attr.title]=\"'nh-assistant.panel.dismiss-error' | translate\"\n (click)=\"store.clearError()\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </div>\n } @else if (store.notice(); as notice) {\n <div class=\"turn-notice\" role=\"status\">\n <nh-assistant-icon name=\"warning\" />\n <p>{{ noticeKeys() | nhAssistantTranslate: notice.code }}</p>\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.panel.dismiss-error' | translate\"\n [attr.title]=\"'nh-assistant.panel.dismiss-error' | translate\"\n (click)=\"store.clearNotice()\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </div>\n }\n\n @if (store.enabled() && !showPreferences()) {\n <footer class=\"footer\" (focusin)=\"refreshPageContext()\">\n @if (closed()) {\n <p class=\"notice\">{{ 'nh-assistant.panel.conversation-closed' | translate }}</p>\n }\n @if (store.pageContext(); as context) {\n <div class=\"context-chip\" [class.excluded]=\"store.pageContextExcluded()\" [attr.title]=\"context.route\">\n <nh-assistant-icon name=\"page\" />\n <span class=\"context-text\">\n {{ (store.pageContextExcluded() ? 'nh-assistant.page-context.excluded' : 'nh-assistant.page-context.included') | translate: { page: describeContext(context) } }}\n </span>\n <button\n class=\"icon-button small\"\n type=\"button\"\n [attr.aria-pressed]=\"store.pageContextExcluded()\"\n [attr.aria-label]=\"(store.pageContextExcluded() ? 'nh-assistant.page-context.include' : 'nh-assistant.page-context.exclude') | translate: { page: describeContext(context) }\"\n [attr.title]=\"(store.pageContextExcluded() ? 'nh-assistant.page-context.include' : 'nh-assistant.page-context.exclude') | translate: { page: describeContext(context) }\"\n (click)=\"store.setPageContextExcluded(!store.pageContextExcluded())\">\n <nh-assistant-icon [name]=\"store.pageContextExcluded() ? 'plus' : 'x'\" />\n </button>\n </div>\n }\n <nh-assistant-composer\n [disabled]=\"closed()\"\n [sendDisabled]=\"composerDisabled()\"\n [busy]=\"busy()\"\n [status]=\"composerStatus()\"\n [maxLength]=\"store.limits()?.maxMessageChars ?? null\"\n [restore]=\"store.restoredDraft()\"\n (restored)=\"store.consumeRestoredDraft()\"\n (send)=\"send($event)\"\n (cancel)=\"cancel()\" />\n </footer>\n }\n </section>\n</ng-template>\n", styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}.drawer{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){.drawer{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:contents}.drawer{position:relative;display:flex;flex-direction:column;box-sizing:border-box;width:100%;height:100%;border-left:1px solid var(--nha-border);color:var(--nha-text);background:var(--nha-background);box-shadow:var(--nha-shadow);font-family:var(--nha-font);font-size:14px}.drawer *,.drawer *:before,.drawer *:after{box-sizing:border-box}.header{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:6px 12px;border-bottom:1px solid var(--nha-border);padding:12px 12px 12px 16px}.heading{display:contents}nh-assistant-agent-picker{grid-column:1/-1;grid-row:2}h2{grid-row:1;min-width:0;margin:0;overflow:hidden;font-size:16px;font-weight:750;letter-spacing:-.01em;text-overflow:ellipsis;white-space:nowrap}.header-actions{display:flex;flex:none;grid-column:2;grid-row:1;gap:4px}.icon-button{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border:1px solid transparent;border-radius:var(--nha-radius-control);color:var(--nha-text-muted);background:transparent;cursor:pointer;text-decoration:none}.icon-button:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.icon-button:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}.icon-button:disabled{cursor:not-allowed;opacity:.5}.icon-button.active{color:var(--nha-highlight);background:var(--nha-surface-strong)}.body{position:relative;display:flex;flex:1;flex-direction:column;min-height:0;background:var(--nha-background)}.thread{flex:1;min-height:0}.overlay-view{position:absolute;z-index:1;inset:0;border-bottom:1px solid var(--nha-border);background:var(--nha-background)}.state{display:flex;flex:1;flex-direction:column;align-items:center;justify-content:center;gap:4px;padding:24px;color:var(--nha-text-muted);text-align:center}.state p{max-width:32ch;margin:0;font-size:13px}.state-icon{margin-bottom:8px;color:var(--nha-highlight);font-size:28px}.state .state-title{color:var(--nha-text);font-size:15px;font-weight:700}.error{display:flex;align-items:center;gap:8px;margin:0 12px 8px;border:1px solid var(--nha-danger);border-radius:var(--nha-radius-control);padding:4px 4px 4px 12px;color:var(--nha-danger);background:var(--nha-danger-soft)}.error p{flex:1;margin:0;color:var(--nha-text);font-size:13px}.turn-notice{display:flex;align-items:center;gap:8px;margin:0 12px 8px;border:1px solid var(--nha-warning);border-radius:var(--nha-radius-control);padding:4px 4px 4px 12px;color:var(--nha-warning);background:var(--nha-warning-soft)}.turn-notice p{flex:1;margin:0;color:var(--nha-text);font-size:13px}.footer{border-top:1px solid var(--nha-border);padding:12px 16px 14px}.context-chip{display:flex;align-items:center;gap:6px;max-width:100%;margin:0 0 8px;border:1px solid var(--nha-border);border-radius:999px;padding:2px 2px 2px 10px;width:fit-content;color:var(--nha-text-muted);background:var(--nha-surface);font-size:12px}.context-chip.excluded .context-text{text-decoration:line-through}.context-text{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.icon-button.small{flex:none;width:26px;height:26px;border-radius:999px}.notice{margin:0 0 8px;color:var(--nha-text-muted);font-size:12px}\n"] }]
|
|
3149
3447
|
}], ctorParameters: () => [], propDecorators: { drawer: [{ type: i0.ViewChild, args: ['drawer', { isSignal: true }] }] } });
|
|
3150
3448
|
|
|
3151
3449
|
/** Converts any failure into a user-facing code and translation key, never raw text. */
|
|
@@ -3170,5 +3468,5 @@ const NH_ASSISTANT_DASH_CASE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
|
3170
3468
|
* Generated bundle index. Do not edit.
|
|
3171
3469
|
*/
|
|
3172
3470
|
|
|
3173
|
-
export { NH_ASSISTANT_ACCESS_POLICY, NH_ASSISTANT_CONFIG, NH_ASSISTANT_FETCH, NH_ASSISTANT_ICONS, NH_ASSISTANT_MAX_CUSTOM_INSTRUCTIONS, NH_ASSISTANT_PAGE_CONTEXT_LIMITS, NH_ASSISTANT_PANEL_MOBILE_QUERY, NH_ASSISTANT_PANEL_WIDTH, NH_ASSISTANT_SSE_EVENT_TYPES, NH_ASSISTANT_TRANSLATIONS, NH_ASSISTANT_VIRTUAL_SCROLL_THRESHOLD, NhAssistantAdminApiService, NhAssistantAgentPickerComponent, NhAssistantAllowAllAccessPolicy, NhAssistantApiError, NhAssistantApiService, NhAssistantApprovalCardComponent, NhAssistantClientErrorCodes, NhAssistantComposerComponent, NhAssistantConversationListComponent, NhAssistantLauncherComponent, NhAssistantPanelComponent, NhAssistantPanelService, NhAssistantPreferencesComponent, NhAssistantSseParser, NhAssistantStore, NhAssistantThreadComponent, NhAssistantToolCallCardComponent, NhAssistantTranslationMerger, applyNhAssistantApprovalDecision, applyNhAssistantEvent, describeNhAssistantClientContext, formatNhAssistantCountdown, nhAssistantCodeForStatus, nhAssistantErrorMessageKey, normalizeNhAssistantClientContext, provideNhAssistant, NH_ASSISTANT_DASH_CASE as ɵNH_ASSISTANT_DASH_CASE, NhAssistantIconComponent as ɵNhAssistantIconComponent, NhAssistantTranslatePipe as ɵNhAssistantTranslatePipe, nhAssistantErrorKeys as ɵnhAssistantErrorKeys, toNhAssistantError as ɵtoNhAssistantError };
|
|
3471
|
+
export { NH_ASSISTANT_ACCESS_POLICY, NH_ASSISTANT_CONFIG, NH_ASSISTANT_FETCH, NH_ASSISTANT_ICONS, NH_ASSISTANT_MAX_CUSTOM_INSTRUCTIONS, NH_ASSISTANT_NO_ANSWER_NOTICE_CODE, NH_ASSISTANT_PAGE_CONTEXT_LIMITS, NH_ASSISTANT_PANEL_MOBILE_QUERY, NH_ASSISTANT_PANEL_WIDTH, NH_ASSISTANT_SSE_EVENT_TYPES, NH_ASSISTANT_TRANSLATIONS, NH_ASSISTANT_VIRTUAL_SCROLL_THRESHOLD, NhAssistantAdminApiService, NhAssistantAgentPickerComponent, NhAssistantAllowAllAccessPolicy, NhAssistantApiError, NhAssistantApiService, NhAssistantApprovalCardComponent, NhAssistantClientErrorCodes, NhAssistantComposerComponent, NhAssistantConversationListComponent, NhAssistantLauncherComponent, NhAssistantPanelComponent, NhAssistantPanelService, NhAssistantPreferencesComponent, NhAssistantSseParser, NhAssistantStore, NhAssistantThreadComponent, NhAssistantToolCallCardComponent, NhAssistantTranslationMerger, applyNhAssistantApprovalDecision, applyNhAssistantEvent, describeNhAssistantClientContext, formatNhAssistantCountdown, nhAssistantCodeForStatus, nhAssistantErrorMessageKey, nhAssistantLatestTurnHasText, normalizeNhAssistantClientContext, provideNhAssistant, NH_ASSISTANT_DASH_CASE as ɵNH_ASSISTANT_DASH_CASE, NhAssistantIconComponent as ɵNhAssistantIconComponent, NhAssistantTranslatePipe as ɵNhAssistantTranslatePipe, nhAssistantErrorKeys as ɵnhAssistantErrorKeys, toNhAssistantError as ɵtoNhAssistantError };
|
|
3174
3472
|
//# sourceMappingURL=newheap-platform-ai-chat.mjs.map
|