@newheap/platform-ai-chat 0.5.0 → 0.7.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/README.md +20 -2
- package/fesm2022/newheap-platform-ai-chat-admin.mjs +21 -21
- package/fesm2022/newheap-platform-ai-chat-testing.mjs +323 -18
- package/fesm2022/newheap-platform-ai-chat-testing.mjs.map +1 -1
- package/fesm2022/newheap-platform-ai-chat.mjs +1863 -374
- package/fesm2022/newheap-platform-ai-chat.mjs.map +1 -1
- package/i18n/en.json +84 -9
- package/i18n/nl.json +84 -9
- package/index.d.ts +522 -37
- package/package.json +5 -1
- package/push/nh-assistant-push-worker.js +70 -0
- package/testing/index.d.ts +102 -11
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, inject, DestroyRef, Injectable, EnvironmentInjector, runInInjectionContext, signal, computed,
|
|
2
|
+
import { InjectionToken, inject, DestroyRef, Injectable, EnvironmentInjector, runInInjectionContext, PLATFORM_ID, signal, computed, effect, untracked, Injector, 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
|
-
import { merge, Observable,
|
|
4
|
+
import { merge, Observable, Subject, firstValueFrom, Subscription, isObservable } from 'rxjs';
|
|
5
|
+
import { isPlatformBrowser, DOCUMENT, DatePipe, NgTemplateOutlet } from '@angular/common';
|
|
5
6
|
import { BreakpointObserver } from '@angular/cdk/layout';
|
|
6
7
|
import { Overlay } from '@angular/cdk/overlay';
|
|
7
|
-
import { DOCUMENT, DatePipe, NgTemplateOutlet } from '@angular/common';
|
|
8
8
|
import { TemplatePortal } from '@angular/cdk/portal';
|
|
9
9
|
import { Router, NavigationEnd, RouterLink } from '@angular/router';
|
|
10
10
|
import * as i1 from '@angular/cdk/scrolling';
|
|
@@ -43,7 +43,9 @@ const NhAssistantClientErrorCodes = {
|
|
|
43
43
|
versionConflict: 'assistant-version-conflict',
|
|
44
44
|
invalidResponse: 'assistant-invalid-response',
|
|
45
45
|
streamInterrupted: 'assistant-stream-interrupted',
|
|
46
|
-
server: 'assistant-server'
|
|
46
|
+
server: 'assistant-server',
|
|
47
|
+
/** The open conversation was deleted by its owner, or the user left or was removed. */
|
|
48
|
+
conversationRemoved: 'assistant-conversation-removed'
|
|
47
49
|
};
|
|
48
50
|
|
|
49
51
|
/*
|
|
@@ -77,7 +79,9 @@ var en = {
|
|
|
77
79
|
launcher: {
|
|
78
80
|
open: "Open assistant",
|
|
79
81
|
close: "Close assistant",
|
|
80
|
-
"pending-approval": "An action is waiting for your approval"
|
|
82
|
+
"pending-approval": "An action is waiting for your approval",
|
|
83
|
+
unread: "{{count}} unread conversations",
|
|
84
|
+
running: "{{count}} conversations working"
|
|
81
85
|
},
|
|
82
86
|
panel: {
|
|
83
87
|
title: "Assistant",
|
|
@@ -95,7 +99,12 @@ var en = {
|
|
|
95
99
|
"dismiss-error": "Dismiss message",
|
|
96
100
|
usage: "{{inputTokens}} input tokens, {{outputTokens}} output tokens, {{toolCalls}} tool calls",
|
|
97
101
|
"open-preferences": "Your assistant preferences",
|
|
98
|
-
"open-admin": "Assistant administration"
|
|
102
|
+
"open-admin": "Assistant administration",
|
|
103
|
+
share: "Share conversation",
|
|
104
|
+
"add-members": "Add people to the conversation",
|
|
105
|
+
"notifications-on": "Turn notifications on",
|
|
106
|
+
"notifications-off": "Turn notifications off",
|
|
107
|
+
members: "People in this conversation"
|
|
99
108
|
},
|
|
100
109
|
"agent-picker": {
|
|
101
110
|
label: "Agent",
|
|
@@ -115,7 +124,11 @@ var en = {
|
|
|
115
124
|
"waiting-for-approval": "Waiting for approval",
|
|
116
125
|
failed: "Failed",
|
|
117
126
|
archived: "Archived"
|
|
118
|
-
}
|
|
127
|
+
},
|
|
128
|
+
leave: "Leave conversation",
|
|
129
|
+
unread: "Unread",
|
|
130
|
+
shared: "Shared with {{count}} people",
|
|
131
|
+
"shared-with-you": "Shared with you"
|
|
119
132
|
},
|
|
120
133
|
thread: {
|
|
121
134
|
label: "Conversation",
|
|
@@ -124,7 +137,8 @@ var en = {
|
|
|
124
137
|
tool: "Tool",
|
|
125
138
|
working: "The assistant is working",
|
|
126
139
|
loading: "Loading conversation",
|
|
127
|
-
elapsed: "{{seconds}}s"
|
|
140
|
+
elapsed: "{{seconds}}s",
|
|
141
|
+
participant: "Participant"
|
|
128
142
|
},
|
|
129
143
|
composer: {
|
|
130
144
|
label: "Message",
|
|
@@ -136,7 +150,9 @@ var en = {
|
|
|
136
150
|
"too-long": "The message is too long.",
|
|
137
151
|
status: {
|
|
138
152
|
running: "You can keep typing while the assistant works. Sending is temporarily unavailable.",
|
|
139
|
-
"waiting-for-approval": "You can keep typing while an action waits for your approval. Sending is temporarily unavailable."
|
|
153
|
+
"waiting-for-approval": "You can keep typing while an action waits for your approval. Sending is temporarily unavailable.",
|
|
154
|
+
"running-by": "{{name}} is working with the assistant…",
|
|
155
|
+
"waiting-for-approval-by": "Waiting for {{name}} to decide…"
|
|
140
156
|
}
|
|
141
157
|
},
|
|
142
158
|
"tool-call": {
|
|
@@ -184,7 +200,9 @@ var en = {
|
|
|
184
200
|
approved: "Approved",
|
|
185
201
|
rejected: "Rejected",
|
|
186
202
|
expired: "Expired"
|
|
187
|
-
}
|
|
203
|
+
},
|
|
204
|
+
"waiting-for": "Waiting for {{name}} to decide. The action uses their permissions.",
|
|
205
|
+
"waiting-for-participant": "Waiting for the participant who started this task to decide."
|
|
188
206
|
},
|
|
189
207
|
"result-codes": {
|
|
190
208
|
"api-bridge-validation": "The request was not valid.",
|
|
@@ -242,10 +260,21 @@ var en = {
|
|
|
242
260
|
"assistant-tool-call-limit-reached": "The assistant reached the maximum number of steps for one answer. Ask a narrower question.",
|
|
243
261
|
"assistant-tools-disabled": "No further actions were run in this answer because an action was rejected.",
|
|
244
262
|
"assistant-turn-failed": "The assistant could not finish the answer. Try again.",
|
|
245
|
-
"assistant-turn-timeout": "The answer took too long and was stopped. Try again or ask a narrower question."
|
|
263
|
+
"assistant-turn-timeout": "The answer took too long and was stopped. Try again or ask a narrower question.",
|
|
264
|
+
"assistant-owner-required": "Only the owner of this conversation can do this.",
|
|
265
|
+
"assistant-approval-forbidden": "Only the person who started this task can decide about it.",
|
|
266
|
+
"assistant-turn-forbidden": "Only the owner or the person who started this task can stop it.",
|
|
267
|
+
"assistant-share-link-invalid": "This invitation link is no longer valid. Ask the owner for a new one.",
|
|
268
|
+
"assistant-participant-not-found": "This person is not part of the conversation.",
|
|
269
|
+
"assistant-participant-limit-reached": "This conversation has the maximum number of participants.",
|
|
270
|
+
"assistant-directory-unavailable": "Inviting colleagues directly is not available.",
|
|
271
|
+
"assistant-push-unavailable": "Desktop notifications are not available.",
|
|
272
|
+
"assistant-push-subscription-invalid": "This browser could not be registered for notifications.",
|
|
273
|
+
"assistant-conversation-removed": "This conversation is no longer available to you."
|
|
246
274
|
},
|
|
247
275
|
notices: {
|
|
248
|
-
"no-answer": "The assistant finished without an answer. Try again or rephrase your question."
|
|
276
|
+
"no-answer": "The assistant finished without an answer. Try again or rephrase your question.",
|
|
277
|
+
"conversation-removed": "This conversation is no longer available to you."
|
|
249
278
|
},
|
|
250
279
|
preferences: {
|
|
251
280
|
title: "Your preferences",
|
|
@@ -282,7 +311,16 @@ var en = {
|
|
|
282
311
|
back: "Back to the conversation",
|
|
283
312
|
loading: "Loading your preferences",
|
|
284
313
|
"load-failed": "Your preferences could not be loaded.",
|
|
285
|
-
retry: "Try again"
|
|
314
|
+
retry: "Try again",
|
|
315
|
+
"notifications-label": "Notifications",
|
|
316
|
+
notifications: "Desktop notifications",
|
|
317
|
+
"notifications-state": {
|
|
318
|
+
on: "You get a notification when a longer task finishes or needs your approval while you are away.",
|
|
319
|
+
prompt: "Your browser asks for permission the next time you send a message.",
|
|
320
|
+
blocked: "Your browser blocks notifications for this site. Allow them in the browser settings.",
|
|
321
|
+
off: "Notifications are off for all your devices.",
|
|
322
|
+
unavailable: "Notifications are not available."
|
|
323
|
+
}
|
|
286
324
|
},
|
|
287
325
|
admin: {
|
|
288
326
|
title: "Assistant administration",
|
|
@@ -467,6 +505,45 @@ var en = {
|
|
|
467
505
|
excluded: "Not sent with this message: {{page}}",
|
|
468
506
|
exclude: "Do not send {{page}} with this message",
|
|
469
507
|
include: "Send {{page}} with this message again"
|
|
508
|
+
},
|
|
509
|
+
activity: {
|
|
510
|
+
label: "Other conversations",
|
|
511
|
+
running: "Working",
|
|
512
|
+
waiting: "Waiting for approval",
|
|
513
|
+
unread: "Unread"
|
|
514
|
+
},
|
|
515
|
+
share: {
|
|
516
|
+
title: "Share conversation",
|
|
517
|
+
"members-title": "People in this conversation",
|
|
518
|
+
back: "Back to the conversation",
|
|
519
|
+
description: "People you share with read the whole conversation, including tool results, and can continue it with their own permissions.",
|
|
520
|
+
"link-title": "Invitation link",
|
|
521
|
+
"link-label": "Invitation link",
|
|
522
|
+
"link-hint": "Anyone in your organization who may use this assistant can join with the link. A new link replaces the old one; people who joined stay.",
|
|
523
|
+
create: "Create invitation link",
|
|
524
|
+
copy: "Copy link",
|
|
525
|
+
copied: "Link copied.",
|
|
526
|
+
renew: "New link",
|
|
527
|
+
revoke: "Stop link",
|
|
528
|
+
"invite-title": "Invite a colleague",
|
|
529
|
+
"search-label": "Search colleagues",
|
|
530
|
+
"search-placeholder": "Search by name",
|
|
531
|
+
searching: "Searching…",
|
|
532
|
+
"no-results": "No colleagues found.",
|
|
533
|
+
invite: "Invite",
|
|
534
|
+
invited: "{{name}} was invited.",
|
|
535
|
+
members: "People",
|
|
536
|
+
count: "{{count}} of {{max}} invited",
|
|
537
|
+
"not-shared": "Only you take part in this conversation.",
|
|
538
|
+
unnamed: "Unnamed colleague",
|
|
539
|
+
you: "(you)",
|
|
540
|
+
role: {
|
|
541
|
+
owner: "Owner",
|
|
542
|
+
participant: "Participant"
|
|
543
|
+
},
|
|
544
|
+
remove: "Remove {{name}}",
|
|
545
|
+
leave: "Leave conversation",
|
|
546
|
+
"no-conversation": "Start or open a conversation to share it."
|
|
470
547
|
}
|
|
471
548
|
}
|
|
472
549
|
};
|
|
@@ -476,7 +553,9 @@ var nl = {
|
|
|
476
553
|
launcher: {
|
|
477
554
|
open: "Assistent openen",
|
|
478
555
|
close: "Assistent sluiten",
|
|
479
|
-
"pending-approval": "Er wacht een actie op je goedkeuring"
|
|
556
|
+
"pending-approval": "Er wacht een actie op je goedkeuring",
|
|
557
|
+
unread: "{{count}} ongelezen gesprekken",
|
|
558
|
+
running: "{{count}} gesprekken bezig"
|
|
480
559
|
},
|
|
481
560
|
panel: {
|
|
482
561
|
title: "Assistent",
|
|
@@ -494,7 +573,12 @@ var nl = {
|
|
|
494
573
|
"dismiss-error": "Melding sluiten",
|
|
495
574
|
usage: "{{inputTokens}} invoertokens, {{outputTokens}} uitvoertokens, {{toolCalls}} toolaanroepen",
|
|
496
575
|
"open-preferences": "Je assistentvoorkeuren",
|
|
497
|
-
"open-admin": "Beheer van de assistent"
|
|
576
|
+
"open-admin": "Beheer van de assistent",
|
|
577
|
+
share: "Gesprek delen",
|
|
578
|
+
"add-members": "Mensen toevoegen aan het gesprek",
|
|
579
|
+
"notifications-on": "Notificaties aanzetten",
|
|
580
|
+
"notifications-off": "Notificaties uitzetten",
|
|
581
|
+
members: "Mensen in dit gesprek"
|
|
498
582
|
},
|
|
499
583
|
"agent-picker": {
|
|
500
584
|
label: "Agent",
|
|
@@ -514,7 +598,11 @@ var nl = {
|
|
|
514
598
|
"waiting-for-approval": "Wacht op goedkeuring",
|
|
515
599
|
failed: "Mislukt",
|
|
516
600
|
archived: "Gearchiveerd"
|
|
517
|
-
}
|
|
601
|
+
},
|
|
602
|
+
leave: "Gesprek verlaten",
|
|
603
|
+
unread: "Ongelezen",
|
|
604
|
+
shared: "Gedeeld met {{count}} mensen",
|
|
605
|
+
"shared-with-you": "Met jou gedeeld"
|
|
518
606
|
},
|
|
519
607
|
thread: {
|
|
520
608
|
label: "Gesprek",
|
|
@@ -523,7 +611,8 @@ var nl = {
|
|
|
523
611
|
tool: "Tool",
|
|
524
612
|
working: "De assistent is bezig",
|
|
525
613
|
loading: "Gesprek laden",
|
|
526
|
-
elapsed: "{{seconds}} s"
|
|
614
|
+
elapsed: "{{seconds}} s",
|
|
615
|
+
participant: "Deelnemer"
|
|
527
616
|
},
|
|
528
617
|
composer: {
|
|
529
618
|
label: "Bericht",
|
|
@@ -535,7 +624,9 @@ var nl = {
|
|
|
535
624
|
"too-long": "Het bericht is te lang.",
|
|
536
625
|
status: {
|
|
537
626
|
running: "Je kunt verder typen terwijl de assistent bezig is. Versturen is tijdelijk niet mogelijk.",
|
|
538
|
-
"waiting-for-approval": "Je kunt verder typen terwijl een actie op je goedkeuring wacht. Versturen is tijdelijk niet mogelijk."
|
|
627
|
+
"waiting-for-approval": "Je kunt verder typen terwijl een actie op je goedkeuring wacht. Versturen is tijdelijk niet mogelijk.",
|
|
628
|
+
"running-by": "{{name}} werkt met de assistent…",
|
|
629
|
+
"waiting-for-approval-by": "Wacht op de beslissing van {{name}}…"
|
|
539
630
|
}
|
|
540
631
|
},
|
|
541
632
|
"tool-call": {
|
|
@@ -583,7 +674,9 @@ var nl = {
|
|
|
583
674
|
approved: "Goedgekeurd",
|
|
584
675
|
rejected: "Afgewezen",
|
|
585
676
|
expired: "Verlopen"
|
|
586
|
-
}
|
|
677
|
+
},
|
|
678
|
+
"waiting-for": "Wacht op de beslissing van {{name}}. De actie gebruikt diens rechten.",
|
|
679
|
+
"waiting-for-participant": "Wacht op de beslissing van de deelnemer die deze taak startte."
|
|
587
680
|
},
|
|
588
681
|
"result-codes": {
|
|
589
682
|
"api-bridge-validation": "Het verzoek was niet geldig.",
|
|
@@ -641,10 +734,21 @@ var nl = {
|
|
|
641
734
|
"assistant-tool-call-limit-reached": "De assistent heeft het maximale aantal stappen voor één antwoord bereikt. Stel een gerichtere vraag.",
|
|
642
735
|
"assistant-tools-disabled": "In dit antwoord zijn geen verdere acties uitgevoerd omdat een actie is afgewezen.",
|
|
643
736
|
"assistant-turn-failed": "De assistent kon het antwoord niet afmaken. Probeer het opnieuw.",
|
|
644
|
-
"assistant-turn-timeout": "Het antwoord duurde te lang en is gestopt. Probeer het opnieuw of stel een gerichtere vraag."
|
|
737
|
+
"assistant-turn-timeout": "Het antwoord duurde te lang en is gestopt. Probeer het opnieuw of stel een gerichtere vraag.",
|
|
738
|
+
"assistant-owner-required": "Alleen de eigenaar van dit gesprek kan dit doen.",
|
|
739
|
+
"assistant-approval-forbidden": "Alleen degene die deze taak startte, kan erover beslissen.",
|
|
740
|
+
"assistant-turn-forbidden": "Alleen de eigenaar of degene die deze taak startte, kan haar stoppen.",
|
|
741
|
+
"assistant-share-link-invalid": "Deze uitnodigingslink is niet meer geldig. Vraag de eigenaar om een nieuwe.",
|
|
742
|
+
"assistant-participant-not-found": "Deze persoon doet niet mee aan het gesprek.",
|
|
743
|
+
"assistant-participant-limit-reached": "Dit gesprek heeft het maximale aantal deelnemers.",
|
|
744
|
+
"assistant-directory-unavailable": "Collega’s direct uitnodigen is niet beschikbaar.",
|
|
745
|
+
"assistant-push-unavailable": "Bureaubladmeldingen zijn niet beschikbaar.",
|
|
746
|
+
"assistant-push-subscription-invalid": "Deze browser kon niet worden aangemeld voor meldingen.",
|
|
747
|
+
"assistant-conversation-removed": "Dit gesprek is niet meer voor je beschikbaar."
|
|
645
748
|
},
|
|
646
749
|
notices: {
|
|
647
|
-
"no-answer": "De assistent is klaar zonder antwoord. Probeer het opnieuw of stel je vraag anders."
|
|
750
|
+
"no-answer": "De assistent is klaar zonder antwoord. Probeer het opnieuw of stel je vraag anders.",
|
|
751
|
+
"conversation-removed": "Dit gesprek is niet meer voor je beschikbaar."
|
|
648
752
|
},
|
|
649
753
|
preferences: {
|
|
650
754
|
title: "Je voorkeuren",
|
|
@@ -681,7 +785,16 @@ var nl = {
|
|
|
681
785
|
back: "Terug naar het gesprek",
|
|
682
786
|
loading: "Je voorkeuren laden",
|
|
683
787
|
"load-failed": "Je voorkeuren konden niet worden geladen.",
|
|
684
|
-
retry: "Opnieuw proberen"
|
|
788
|
+
retry: "Opnieuw proberen",
|
|
789
|
+
"notifications-label": "Meldingen",
|
|
790
|
+
notifications: "Bureaubladmeldingen",
|
|
791
|
+
"notifications-state": {
|
|
792
|
+
on: "Je krijgt een melding als een langere taak klaar is of je goedkeuring nodig heeft terwijl je weg bent.",
|
|
793
|
+
prompt: "Je browser vraagt om toestemming wanneer je het volgende bericht verstuurt.",
|
|
794
|
+
blocked: "Je browser blokkeert meldingen voor deze site. Sta ze toe in de browserinstellingen.",
|
|
795
|
+
off: "Meldingen staan uit op al je apparaten.",
|
|
796
|
+
unavailable: "Meldingen zijn niet beschikbaar."
|
|
797
|
+
}
|
|
685
798
|
},
|
|
686
799
|
admin: {
|
|
687
800
|
title: "Beheer van de assistent",
|
|
@@ -866,6 +979,45 @@ var nl = {
|
|
|
866
979
|
excluded: "Niet meegestuurd bij dit bericht: {{page}}",
|
|
867
980
|
exclude: "{{page}} niet meesturen met dit bericht",
|
|
868
981
|
include: "{{page}} toch meesturen met dit bericht"
|
|
982
|
+
},
|
|
983
|
+
activity: {
|
|
984
|
+
label: "Andere gesprekken",
|
|
985
|
+
running: "Bezig",
|
|
986
|
+
waiting: "Wacht op goedkeuring",
|
|
987
|
+
unread: "Ongelezen"
|
|
988
|
+
},
|
|
989
|
+
share: {
|
|
990
|
+
title: "Gesprek delen",
|
|
991
|
+
"members-title": "Mensen in dit gesprek",
|
|
992
|
+
back: "Terug naar het gesprek",
|
|
993
|
+
description: "Mensen met wie je deelt lezen het hele gesprek, ook de resultaten van tools, en kunnen het voortzetten met hun eigen rechten.",
|
|
994
|
+
"link-title": "Uitnodigingslink",
|
|
995
|
+
"link-label": "Uitnodigingslink",
|
|
996
|
+
"link-hint": "Iedereen in je organisatie die deze assistent mag gebruiken, kan met de link meedoen. Een nieuwe link vervangt de oude; wie al meedoet, blijft.",
|
|
997
|
+
create: "Uitnodigingslink maken",
|
|
998
|
+
copy: "Link kopiëren",
|
|
999
|
+
copied: "Link gekopieerd.",
|
|
1000
|
+
renew: "Nieuwe link",
|
|
1001
|
+
revoke: "Link stoppen",
|
|
1002
|
+
"invite-title": "Collega uitnodigen",
|
|
1003
|
+
"search-label": "Collega’s zoeken",
|
|
1004
|
+
"search-placeholder": "Zoek op naam",
|
|
1005
|
+
searching: "Zoeken…",
|
|
1006
|
+
"no-results": "Geen collega’s gevonden.",
|
|
1007
|
+
invite: "Uitnodigen",
|
|
1008
|
+
invited: "{{name}} is uitgenodigd.",
|
|
1009
|
+
members: "Mensen",
|
|
1010
|
+
count: "{{count}} van {{max}} uitgenodigd",
|
|
1011
|
+
"not-shared": "Alleen jij doet mee aan dit gesprek.",
|
|
1012
|
+
unnamed: "Collega zonder naam",
|
|
1013
|
+
you: "(jij)",
|
|
1014
|
+
role: {
|
|
1015
|
+
owner: "Eigenaar",
|
|
1016
|
+
participant: "Deelnemer"
|
|
1017
|
+
},
|
|
1018
|
+
remove: "{{name}} verwijderen",
|
|
1019
|
+
leave: "Gesprek verlaten",
|
|
1020
|
+
"no-conversation": "Start of open een gesprek om het te delen."
|
|
869
1021
|
}
|
|
870
1022
|
}
|
|
871
1023
|
};
|
|
@@ -931,10 +1083,10 @@ class NhAssistantTranslationMerger {
|
|
|
931
1083
|
const merged = mergeTrees(bundleRoot, isTree(existing) ? existing : {});
|
|
932
1084
|
this.translate?.setTranslation(language, { [rootKey]: merged }, true);
|
|
933
1085
|
}
|
|
934
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
935
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.
|
|
1086
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantTranslationMerger, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1087
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantTranslationMerger, providedIn: 'root' }); }
|
|
936
1088
|
}
|
|
937
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
1089
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantTranslationMerger, decorators: [{
|
|
938
1090
|
type: Injectable,
|
|
939
1091
|
args: [{ providedIn: 'root' }]
|
|
940
1092
|
}] });
|
|
@@ -1069,10 +1221,10 @@ class NhAssistantTransport {
|
|
|
1069
1221
|
const base = this.config.apiBaseUrl.replace(/\/+$/, '');
|
|
1070
1222
|
return `${base}/${path}`;
|
|
1071
1223
|
}
|
|
1072
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
1073
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.
|
|
1224
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantTransport, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1225
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantTransport }); }
|
|
1074
1226
|
}
|
|
1075
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
1227
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantTransport, decorators: [{
|
|
1076
1228
|
type: Injectable
|
|
1077
1229
|
}] });
|
|
1078
1230
|
function createApiError(status, code) {
|
|
@@ -1154,10 +1306,10 @@ class NhAssistantAdminApiService {
|
|
|
1154
1306
|
request(method, path, body) {
|
|
1155
1307
|
return this.transport.json(method, path, body, adminStatusCodes);
|
|
1156
1308
|
}
|
|
1157
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
1158
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.
|
|
1309
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantAdminApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1310
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantAdminApiService }); }
|
|
1159
1311
|
}
|
|
1160
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
1312
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantAdminApiService, decorators: [{
|
|
1161
1313
|
type: Injectable
|
|
1162
1314
|
}] });
|
|
1163
1315
|
|
|
@@ -1289,6 +1441,52 @@ class NhAssistantApiService {
|
|
|
1289
1441
|
cancel(conversationId) {
|
|
1290
1442
|
return this.transport.json('POST', `conversations/${encodeURIComponent(conversationId)}/cancel`);
|
|
1291
1443
|
}
|
|
1444
|
+
/** Raises the caller's read position to `sequence`, or to the latest message when omitted. */
|
|
1445
|
+
markRead(conversationId, sequence) {
|
|
1446
|
+
return this.transport.json('POST', `conversations/${encodeURIComponent(conversationId)}/read`, sequence === undefined ? {} : { sequence });
|
|
1447
|
+
}
|
|
1448
|
+
/** Owner only: creates a new invitation link; an earlier link stops working. */
|
|
1449
|
+
createShareLink(conversationId) {
|
|
1450
|
+
return this.transport.json('POST', `conversations/${encodeURIComponent(conversationId)}/share-link`);
|
|
1451
|
+
}
|
|
1452
|
+
/** Owner only: the invitation link stops working; people who joined stay. */
|
|
1453
|
+
revokeShareLink(conversationId) {
|
|
1454
|
+
return this.transport.json('DELETE', `conversations/${encodeURIComponent(conversationId)}/share-link`);
|
|
1455
|
+
}
|
|
1456
|
+
/** Joins a shared conversation with the token of its invitation link. */
|
|
1457
|
+
joinConversation(conversationId, token) {
|
|
1458
|
+
return this.transport.json('POST', `conversations/${encodeURIComponent(conversationId)}/join`, { token });
|
|
1459
|
+
}
|
|
1460
|
+
/** Owner only: searches the application's directory for people to invite. */
|
|
1461
|
+
searchParticipantCandidates(conversationId, query) {
|
|
1462
|
+
const path = `conversations/${encodeURIComponent(conversationId)}/participant-candidates?query=${encodeURIComponent(query)}`;
|
|
1463
|
+
return this.transport.json('GET', path);
|
|
1464
|
+
}
|
|
1465
|
+
/** Owner only: invites a person the directory returned. */
|
|
1466
|
+
inviteParticipant(conversationId, actorId) {
|
|
1467
|
+
return this.transport.json('POST', `conversations/${encodeURIComponent(conversationId)}/participants`, { actorId });
|
|
1468
|
+
}
|
|
1469
|
+
/** The owner removes a participant, or a participant passes their own id to leave. */
|
|
1470
|
+
removeParticipant(conversationId, actorId) {
|
|
1471
|
+
const path = `conversations/${encodeURIComponent(conversationId)}/participants/${encodeURIComponent(actorId)}`;
|
|
1472
|
+
return this.transport.json('DELETE', path);
|
|
1473
|
+
}
|
|
1474
|
+
/** The caller's notification choice and the server's Web Push key. */
|
|
1475
|
+
getNotificationSettings() {
|
|
1476
|
+
return this.transport.json('GET', 'notifications');
|
|
1477
|
+
}
|
|
1478
|
+
/** Turns push notifications on or off for every browser of the caller. */
|
|
1479
|
+
updateNotificationSettings(pushEnabled) {
|
|
1480
|
+
return this.transport.json('PUT', 'notifications', { pushEnabled });
|
|
1481
|
+
}
|
|
1482
|
+
/** Registers this browser's push subscription for the caller. */
|
|
1483
|
+
subscribePush(subscription) {
|
|
1484
|
+
return this.transport.json('PUT', 'notifications/push-subscription', subscription);
|
|
1485
|
+
}
|
|
1486
|
+
/** Removes this browser's push subscription. */
|
|
1487
|
+
unsubscribePush(endpoint) {
|
|
1488
|
+
return this.transport.json('DELETE', 'notifications/push-subscription', { endpoint });
|
|
1489
|
+
}
|
|
1292
1490
|
/** The caller's own assistant preferences. */
|
|
1293
1491
|
getPreferences() {
|
|
1294
1492
|
return this.transport.json('GET', 'preferences');
|
|
@@ -1373,13 +1571,386 @@ class NhAssistantApiService {
|
|
|
1373
1571
|
subscriber.next({ type: 'error', data: { code, messageKey: nhAssistantErrorMessageKey(code) } });
|
|
1374
1572
|
subscriber.complete();
|
|
1375
1573
|
}
|
|
1376
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
1377
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.
|
|
1574
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1575
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantApiService }); }
|
|
1378
1576
|
}
|
|
1379
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
1577
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantApiService, decorators: [{
|
|
1380
1578
|
type: Injectable
|
|
1381
1579
|
}] });
|
|
1382
1580
|
|
|
1581
|
+
const reconnectDelaysMs = [0, 2_000, 5_000, 10_000, 30_000];
|
|
1582
|
+
const restartDelayMs = 30_000;
|
|
1583
|
+
/**
|
|
1584
|
+
* Live updates of the assistant hub: conversation changes, read positions, removals and the
|
|
1585
|
+
* turn events of other sessions. Connects only while the store asks for it, reconnects on its
|
|
1586
|
+
* own and emits `resync` after every (re)connect, because updates sent while disconnected are lost.
|
|
1587
|
+
*/
|
|
1588
|
+
class NhAssistantLiveService {
|
|
1589
|
+
constructor() {
|
|
1590
|
+
this.config = inject(NH_ASSISTANT_CONFIG);
|
|
1591
|
+
this.injector = inject(EnvironmentInjector);
|
|
1592
|
+
this.browser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
1593
|
+
this.changedSubject = new Subject();
|
|
1594
|
+
this.readSubject = new Subject();
|
|
1595
|
+
this.removedSubject = new Subject();
|
|
1596
|
+
this.eventSubject = new Subject();
|
|
1597
|
+
this.resyncSubject = new Subject();
|
|
1598
|
+
this.stateSignal = signal('off', ...(ngDevMode ? [{ debugName: "stateSignal" }] : []));
|
|
1599
|
+
this.hubPath = null;
|
|
1600
|
+
this.hubUrl = null;
|
|
1601
|
+
this.revision = 0;
|
|
1602
|
+
this.changed$ = this.changedSubject.asObservable();
|
|
1603
|
+
this.read$ = this.readSubject.asObservable();
|
|
1604
|
+
this.removed$ = this.removedSubject.asObservable();
|
|
1605
|
+
this.events$ = this.eventSubject.asObservable();
|
|
1606
|
+
/** Reload snapshots: emitted after every (re)connect. */
|
|
1607
|
+
this.resync$ = this.resyncSubject.asObservable();
|
|
1608
|
+
this.state = this.stateSignal.asReadonly();
|
|
1609
|
+
inject(DestroyRef).onDestroy(() => {
|
|
1610
|
+
void this.stop();
|
|
1611
|
+
this.changedSubject.complete();
|
|
1612
|
+
this.readSubject.complete();
|
|
1613
|
+
this.removedSubject.complete();
|
|
1614
|
+
this.eventSubject.complete();
|
|
1615
|
+
this.resyncSubject.complete();
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1618
|
+
/**
|
|
1619
|
+
* Connects to the hub at `hubPath`, or disconnects when it is `null`. Calling it again with the
|
|
1620
|
+
* same path keeps the connection; another path or `restart` reconnects, for example after the
|
|
1621
|
+
* signed-in user changed.
|
|
1622
|
+
*/
|
|
1623
|
+
async connect(hubPath, restart = false) {
|
|
1624
|
+
const url = this.browser && this.config.liveUpdates !== false && hubPath ? this.resolveUrl(hubPath) : null;
|
|
1625
|
+
if (!restart && url === this.hubUrl && this.connection) {
|
|
1626
|
+
return;
|
|
1627
|
+
}
|
|
1628
|
+
await this.stop();
|
|
1629
|
+
this.hubPath = url ? hubPath : null;
|
|
1630
|
+
this.hubUrl = url;
|
|
1631
|
+
if (!url) {
|
|
1632
|
+
return;
|
|
1633
|
+
}
|
|
1634
|
+
const revision = ++this.revision;
|
|
1635
|
+
this.stateSignal.set('connecting');
|
|
1636
|
+
try {
|
|
1637
|
+
const connection = await this.build(url);
|
|
1638
|
+
if (revision !== this.revision) {
|
|
1639
|
+
await connection.stop().catch(() => undefined);
|
|
1640
|
+
return;
|
|
1641
|
+
}
|
|
1642
|
+
this.connection = connection;
|
|
1643
|
+
await connection.start();
|
|
1644
|
+
if (revision === this.revision) {
|
|
1645
|
+
this.stateSignal.set('connected');
|
|
1646
|
+
this.resyncSubject.next();
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
catch {
|
|
1650
|
+
// Live updates are optional; snapshots keep the assistant correct. Try again later.
|
|
1651
|
+
if (revision === this.revision) {
|
|
1652
|
+
this.stateSignal.set('disconnected');
|
|
1653
|
+
this.scheduleRestart(revision);
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
async stop() {
|
|
1658
|
+
this.revision++;
|
|
1659
|
+
this.hubUrl = null;
|
|
1660
|
+
clearTimeout(this.restartTimer);
|
|
1661
|
+
const connection = this.connection;
|
|
1662
|
+
this.connection = undefined;
|
|
1663
|
+
this.stateSignal.set('off');
|
|
1664
|
+
if (connection) {
|
|
1665
|
+
await connection.stop().catch(() => undefined);
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
async build(url) {
|
|
1669
|
+
const signalR = await import('@microsoft/signalr');
|
|
1670
|
+
const connection = new signalR.HubConnectionBuilder()
|
|
1671
|
+
.withUrl(url, {
|
|
1672
|
+
accessTokenFactory: async () => (await this.token()) ?? ''
|
|
1673
|
+
})
|
|
1674
|
+
.withAutomaticReconnect(reconnectDelaysMs)
|
|
1675
|
+
.configureLogging(signalR.LogLevel.None)
|
|
1676
|
+
.build();
|
|
1677
|
+
connection.on('ConversationChanged', (message) => this.changedSubject.next(message));
|
|
1678
|
+
connection.on('ConversationRead', (message) => this.readSubject.next(message));
|
|
1679
|
+
connection.on('ConversationRemoved', (message) => this.removedSubject.next(message));
|
|
1680
|
+
connection.on('ConversationEvent', (message) => this.eventSubject.next(message));
|
|
1681
|
+
connection.onreconnecting(() => this.stateSignal.set('reconnecting'));
|
|
1682
|
+
connection.onreconnected(() => {
|
|
1683
|
+
this.stateSignal.set('connected');
|
|
1684
|
+
this.resyncSubject.next();
|
|
1685
|
+
});
|
|
1686
|
+
connection.onclose(() => {
|
|
1687
|
+
if (this.connection === connection) {
|
|
1688
|
+
this.connection = undefined;
|
|
1689
|
+
this.stateSignal.set('disconnected');
|
|
1690
|
+
this.scheduleRestart(this.revision);
|
|
1691
|
+
}
|
|
1692
|
+
});
|
|
1693
|
+
return connection;
|
|
1694
|
+
}
|
|
1695
|
+
/** Starts over after the automatic reconnect gave up or the first start failed. */
|
|
1696
|
+
scheduleRestart(revision) {
|
|
1697
|
+
clearTimeout(this.restartTimer);
|
|
1698
|
+
this.restartTimer = setTimeout(() => {
|
|
1699
|
+
if (revision === this.revision && this.hubPath) {
|
|
1700
|
+
void this.connect(this.hubPath, true);
|
|
1701
|
+
}
|
|
1702
|
+
}, restartDelayMs);
|
|
1703
|
+
}
|
|
1704
|
+
async token() {
|
|
1705
|
+
try {
|
|
1706
|
+
return await runInInjectionContext(this.injector, () => this.config.getAccessToken());
|
|
1707
|
+
}
|
|
1708
|
+
catch {
|
|
1709
|
+
return null;
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
/** `hubBaseUrl` + `hubPath`, or the hub path on the origin of `apiBaseUrl`. */
|
|
1713
|
+
resolveUrl(hubPath) {
|
|
1714
|
+
const page = globalThis.location?.href ?? 'http://localhost';
|
|
1715
|
+
if (this.config.hubBaseUrl) {
|
|
1716
|
+
const base = new URL(this.config.hubBaseUrl, page).toString().replace(/\/+$/, '');
|
|
1717
|
+
return `${base}/${hubPath.replace(/^\/+/, '')}`;
|
|
1718
|
+
}
|
|
1719
|
+
return new URL(hubPath, new URL(this.config.apiBaseUrl, page).origin).toString();
|
|
1720
|
+
}
|
|
1721
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantLiveService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1722
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantLiveService }); }
|
|
1723
|
+
}
|
|
1724
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantLiveService, decorators: [{
|
|
1725
|
+
type: Injectable
|
|
1726
|
+
}], ctorParameters: () => [] });
|
|
1727
|
+
|
|
1728
|
+
const openConversationMessage = 'nh-assistant.open-conversation';
|
|
1729
|
+
/**
|
|
1730
|
+
* Keeps this browser's Web Push subscription in line with the user's choice. Notifications are on
|
|
1731
|
+
* by default; because browsers only ask for permission after a user action, the assistant asks
|
|
1732
|
+
* when the user sends a message or turns notifications on. The bundled service worker shows a
|
|
1733
|
+
* notification only when no window of the application is focused.
|
|
1734
|
+
*/
|
|
1735
|
+
class NhAssistantPushService {
|
|
1736
|
+
constructor() {
|
|
1737
|
+
this.api = inject(NhAssistantApiService);
|
|
1738
|
+
this.config = inject(NH_ASSISTANT_CONFIG);
|
|
1739
|
+
this.translate = inject(TranslateService, { optional: true });
|
|
1740
|
+
this.browser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
1741
|
+
this.settingsState = signal(null, ...(ngDevMode ? [{ debugName: "settingsState" }] : []));
|
|
1742
|
+
this.permissionState = signal('default', ...(ngDevMode ? [{ debugName: "permissionState" }] : []));
|
|
1743
|
+
this.subscribedState = signal(false, ...(ngDevMode ? [{ debugName: "subscribedState" }] : []));
|
|
1744
|
+
this.busyState = signal(false, ...(ngDevMode ? [{ debugName: "busyState" }] : []));
|
|
1745
|
+
this.openSubject = new Subject();
|
|
1746
|
+
this.revision = 0;
|
|
1747
|
+
this.onWorkerMessage = (event) => {
|
|
1748
|
+
const data = event.data;
|
|
1749
|
+
if (data?.type === openConversationMessage && typeof data.conversationId === 'string') {
|
|
1750
|
+
this.openSubject.next(data.conversationId);
|
|
1751
|
+
}
|
|
1752
|
+
};
|
|
1753
|
+
/** The caller's server-side choice, or `null` before it was loaded. */
|
|
1754
|
+
this.settings = this.settingsState.asReadonly();
|
|
1755
|
+
this.busy = this.busyState.asReadonly();
|
|
1756
|
+
/** Conversations a clicked notification asks to open. */
|
|
1757
|
+
this.openConversation$ = this.openSubject.asObservable();
|
|
1758
|
+
this.state = computed(() => {
|
|
1759
|
+
const settings = this.settingsState();
|
|
1760
|
+
if (!settings?.pushAvailable || !this.isSupported()) {
|
|
1761
|
+
return 'unavailable';
|
|
1762
|
+
}
|
|
1763
|
+
if (!settings.pushEnabled) {
|
|
1764
|
+
return 'off';
|
|
1765
|
+
}
|
|
1766
|
+
const permission = this.permissionState();
|
|
1767
|
+
if (permission === 'denied' || permission === 'unsupported') {
|
|
1768
|
+
return 'blocked';
|
|
1769
|
+
}
|
|
1770
|
+
return permission === 'granted' && this.subscribedState() ? 'on' : 'prompt';
|
|
1771
|
+
}, ...(ngDevMode ? [{ debugName: "state" }] : []));
|
|
1772
|
+
inject(DestroyRef).onDestroy(() => {
|
|
1773
|
+
if (this.browser && this.isSupported()) {
|
|
1774
|
+
navigator.serviceWorker.removeEventListener('message', this.onWorkerMessage);
|
|
1775
|
+
}
|
|
1776
|
+
this.openSubject.complete();
|
|
1777
|
+
});
|
|
1778
|
+
if (this.browser && this.isSupported()) {
|
|
1779
|
+
navigator.serviceWorker.addEventListener('message', this.onWorkerMessage);
|
|
1780
|
+
navigator.serviceWorker.startMessages();
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
/**
|
|
1784
|
+
* Loads the user's choice and, when permission was granted before, renews this browser's
|
|
1785
|
+
* subscription, which also moves it to the current user and to a rotated server key.
|
|
1786
|
+
*/
|
|
1787
|
+
async activate(serverSupportsPush) {
|
|
1788
|
+
const revision = ++this.revision;
|
|
1789
|
+
if (!serverSupportsPush || !this.isSupported()) {
|
|
1790
|
+
this.settingsState.set(null);
|
|
1791
|
+
return;
|
|
1792
|
+
}
|
|
1793
|
+
try {
|
|
1794
|
+
const settings = await firstValueFrom(this.api.getNotificationSettings());
|
|
1795
|
+
if (revision !== this.revision) {
|
|
1796
|
+
return;
|
|
1797
|
+
}
|
|
1798
|
+
this.settingsState.set(settings);
|
|
1799
|
+
this.permissionState.set(Notification.permission);
|
|
1800
|
+
if (settings.pushEnabled && Notification.permission === 'granted') {
|
|
1801
|
+
await this.subscribe(settings);
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
catch {
|
|
1805
|
+
// Notifications are optional; the assistant works without them.
|
|
1806
|
+
if (revision === this.revision) {
|
|
1807
|
+
this.settingsState.set(null);
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
/**
|
|
1812
|
+
* Forgets the user. The browser subscription is cancelled, so a previous user on a shared device
|
|
1813
|
+
* stops receiving notifications even when the server cannot be told.
|
|
1814
|
+
*/
|
|
1815
|
+
async deactivate() {
|
|
1816
|
+
this.revision++;
|
|
1817
|
+
this.settingsState.set(null);
|
|
1818
|
+
this.subscribedState.set(false);
|
|
1819
|
+
if (!this.isSupported()) {
|
|
1820
|
+
return;
|
|
1821
|
+
}
|
|
1822
|
+
try {
|
|
1823
|
+
const registration = await navigator.serviceWorker.getRegistration(this.scope());
|
|
1824
|
+
const subscription = await registration?.pushManager.getSubscription();
|
|
1825
|
+
await subscription?.unsubscribe();
|
|
1826
|
+
}
|
|
1827
|
+
catch {
|
|
1828
|
+
// Nothing to cancel.
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
/** Asks for permission once, after a user action such as sending a message. */
|
|
1832
|
+
async onUserGesture() {
|
|
1833
|
+
const settings = this.settingsState();
|
|
1834
|
+
if (!settings?.pushEnabled || !settings.pushAvailable || !this.isSupported() || Notification.permission !== 'default') {
|
|
1835
|
+
return;
|
|
1836
|
+
}
|
|
1837
|
+
const permission = await Notification.requestPermission();
|
|
1838
|
+
this.permissionState.set(permission);
|
|
1839
|
+
if (permission === 'granted') {
|
|
1840
|
+
await this.subscribe(settings);
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
/** Turns notifications on or off for every browser of the user. Call it from a user action. */
|
|
1844
|
+
async setEnabled(enabled) {
|
|
1845
|
+
this.busyState.set(true);
|
|
1846
|
+
try {
|
|
1847
|
+
const settings = await firstValueFrom(this.api.updateNotificationSettings(enabled));
|
|
1848
|
+
this.settingsState.set(settings);
|
|
1849
|
+
if (!this.isSupported()) {
|
|
1850
|
+
return;
|
|
1851
|
+
}
|
|
1852
|
+
if (enabled) {
|
|
1853
|
+
if (Notification.permission === 'default') {
|
|
1854
|
+
this.permissionState.set(await Notification.requestPermission());
|
|
1855
|
+
}
|
|
1856
|
+
if (Notification.permission === 'granted') {
|
|
1857
|
+
await this.subscribe(settings);
|
|
1858
|
+
}
|
|
1859
|
+
return;
|
|
1860
|
+
}
|
|
1861
|
+
await this.unsubscribe();
|
|
1862
|
+
}
|
|
1863
|
+
finally {
|
|
1864
|
+
this.busyState.set(false);
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
async subscribe(settings) {
|
|
1868
|
+
if (!settings.publicKey) {
|
|
1869
|
+
return;
|
|
1870
|
+
}
|
|
1871
|
+
const revision = this.revision;
|
|
1872
|
+
const registration = await this.register();
|
|
1873
|
+
const key = decodeBase64Url(settings.publicKey);
|
|
1874
|
+
let subscription = await registration.pushManager.getSubscription();
|
|
1875
|
+
if (subscription && !sameKey(subscription.options.applicationServerKey, key)) {
|
|
1876
|
+
// The server rotated its key; a subscription for the old key would be refused.
|
|
1877
|
+
await subscription.unsubscribe();
|
|
1878
|
+
subscription = null;
|
|
1879
|
+
}
|
|
1880
|
+
subscription ??= await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: key });
|
|
1881
|
+
const json = subscription.toJSON();
|
|
1882
|
+
if (!json.endpoint || !json.keys?.['p256dh'] || !json.keys['auth'] || revision !== this.revision) {
|
|
1883
|
+
return;
|
|
1884
|
+
}
|
|
1885
|
+
await firstValueFrom(this.api.subscribePush({
|
|
1886
|
+
endpoint: json.endpoint,
|
|
1887
|
+
keys: { p256dh: json.keys['p256dh'], auth: json.keys['auth'] },
|
|
1888
|
+
language: this.translate?.getCurrentLang() ?? navigator.language
|
|
1889
|
+
}), { defaultValue: undefined });
|
|
1890
|
+
if (revision === this.revision) {
|
|
1891
|
+
this.subscribedState.set(true);
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
async unsubscribe() {
|
|
1895
|
+
this.subscribedState.set(false);
|
|
1896
|
+
const registration = await navigator.serviceWorker.getRegistration(this.scope());
|
|
1897
|
+
const subscription = await registration?.pushManager.getSubscription();
|
|
1898
|
+
if (!subscription) {
|
|
1899
|
+
return;
|
|
1900
|
+
}
|
|
1901
|
+
await firstValueFrom(this.api.unsubscribePush(subscription.endpoint), { defaultValue: undefined }).catch(() => undefined);
|
|
1902
|
+
await subscription.unsubscribe();
|
|
1903
|
+
}
|
|
1904
|
+
register() {
|
|
1905
|
+
const push = this.config.push;
|
|
1906
|
+
this.registration ??= (async () => {
|
|
1907
|
+
const url = new URL(push.serviceWorkerUrl, location.href);
|
|
1908
|
+
url.searchParams.set('app', new URL(push.openUrl ?? '/', location.href).href);
|
|
1909
|
+
if (push.icon) {
|
|
1910
|
+
url.searchParams.set('icon', new URL(push.icon, location.href).href);
|
|
1911
|
+
}
|
|
1912
|
+
return navigator.serviceWorker.register(url.href, { scope: this.scope() });
|
|
1913
|
+
})();
|
|
1914
|
+
return this.registration;
|
|
1915
|
+
}
|
|
1916
|
+
scope() {
|
|
1917
|
+
const push = this.config.push;
|
|
1918
|
+
if (!push) {
|
|
1919
|
+
return '/';
|
|
1920
|
+
}
|
|
1921
|
+
return push.scope ?? new URL('./', new URL(push.serviceWorkerUrl, location.href)).pathname;
|
|
1922
|
+
}
|
|
1923
|
+
isSupported() {
|
|
1924
|
+
return this.browser
|
|
1925
|
+
&& !!this.config.push?.serviceWorkerUrl
|
|
1926
|
+
&& globalThis.isSecureContext === true
|
|
1927
|
+
&& 'serviceWorker' in navigator
|
|
1928
|
+
&& 'PushManager' in globalThis
|
|
1929
|
+
&& 'Notification' in globalThis;
|
|
1930
|
+
}
|
|
1931
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantPushService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1932
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantPushService }); }
|
|
1933
|
+
}
|
|
1934
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantPushService, decorators: [{
|
|
1935
|
+
type: Injectable
|
|
1936
|
+
}], ctorParameters: () => [] });
|
|
1937
|
+
function decodeBase64Url(value) {
|
|
1938
|
+
const base64 = value.replace(/-/g, '+').replace(/_/g, '/').padEnd(Math.ceil(value.length / 4) * 4, '=');
|
|
1939
|
+
const binary = atob(base64);
|
|
1940
|
+
const bytes = new Uint8Array(new ArrayBuffer(binary.length));
|
|
1941
|
+
for (let index = 0; index < binary.length; index++) {
|
|
1942
|
+
bytes[index] = binary.charCodeAt(index);
|
|
1943
|
+
}
|
|
1944
|
+
return bytes;
|
|
1945
|
+
}
|
|
1946
|
+
function sameKey(current, expected) {
|
|
1947
|
+
if (!current) {
|
|
1948
|
+
return false;
|
|
1949
|
+
}
|
|
1950
|
+
const bytes = new Uint8Array(current);
|
|
1951
|
+
return bytes.length === expected.length && bytes.every((byte, index) => byte === expected[index]);
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1383
1954
|
/**
|
|
1384
1955
|
* Applies one assistant event to a conversation and returns the new conversation.
|
|
1385
1956
|
* The input is never mutated, so the result can be stored in a signal directly.
|
|
@@ -1491,6 +2062,36 @@ function applyNhAssistantApprovalDecision(conversation, approvalId, decision) {
|
|
|
1491
2062
|
}
|
|
1492
2063
|
return updated;
|
|
1493
2064
|
}
|
|
2065
|
+
/**
|
|
2066
|
+
* Applies a live update of another session: `message.created` adds a participant's message,
|
|
2067
|
+
* the turn events behave exactly like the starting request's stream. Unknown types are ignored.
|
|
2068
|
+
*/
|
|
2069
|
+
function applyNhAssistantLiveEvent(conversation, event) {
|
|
2070
|
+
if (event.type === 'message.created') {
|
|
2071
|
+
const message = event.data;
|
|
2072
|
+
if (!message?.id || conversation.messages.some(existing => existing.id === message.id)) {
|
|
2073
|
+
return conversation;
|
|
2074
|
+
}
|
|
2075
|
+
return { ...conversation, messages: [...conversation.messages, message] };
|
|
2076
|
+
}
|
|
2077
|
+
if (!NH_ASSISTANT_SSE_EVENT_TYPES.includes(event.type)) {
|
|
2078
|
+
return conversation;
|
|
2079
|
+
}
|
|
2080
|
+
return applyNhAssistantEvent(conversation, { type: event.type, data: event.data });
|
|
2081
|
+
}
|
|
2082
|
+
/** Applies the shared state of a conversation that a live update announced. */
|
|
2083
|
+
function applyNhAssistantConversationChange(conversation, change) {
|
|
2084
|
+
return {
|
|
2085
|
+
...conversation,
|
|
2086
|
+
status: change.status,
|
|
2087
|
+
title: change.title,
|
|
2088
|
+
updatedAt: change.updatedAt,
|
|
2089
|
+
activeActorId: change.activeActorId,
|
|
2090
|
+
lastMessageSequence: change.lastMessageSequence,
|
|
2091
|
+
participantCount: change.participantCount,
|
|
2092
|
+
pendingApproval: change.status === 'waiting-for-approval' ? conversation.pendingApproval : null
|
|
2093
|
+
};
|
|
2094
|
+
}
|
|
1494
2095
|
/**
|
|
1495
2096
|
* True when an assistant message after the latest user message contains non-blank text,
|
|
1496
2097
|
* so the latest turn produced a visible answer. Tool calls and approvals alone do not count.
|
|
@@ -1752,10 +2353,10 @@ class NhAssistantUiState {
|
|
|
1752
2353
|
return { ...emptyState };
|
|
1753
2354
|
}
|
|
1754
2355
|
}
|
|
1755
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
1756
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.
|
|
2356
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantUiState, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2357
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantUiState }); }
|
|
1757
2358
|
}
|
|
1758
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
2359
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantUiState, decorators: [{
|
|
1759
2360
|
type: Injectable
|
|
1760
2361
|
}] });
|
|
1761
2362
|
function validId(value) {
|
|
@@ -1764,11 +2365,23 @@ function validId(value) {
|
|
|
1764
2365
|
|
|
1765
2366
|
/** Notice code for a completed turn that produced no answer text and no server code. */
|
|
1766
2367
|
const NH_ASSISTANT_NO_ANSWER_NOTICE_CODE = 'assistant-no-answer';
|
|
2368
|
+
/** True when a conversation has messages the caller has not read. A running turn is not unread yet. */
|
|
2369
|
+
function nhAssistantIsUnread(summary) {
|
|
2370
|
+
if (summary.status === 'running' || summary.lastReadSequence === undefined) {
|
|
2371
|
+
return false;
|
|
2372
|
+
}
|
|
2373
|
+
return (summary.lastMessageSequence ?? 0) > summary.lastReadSequence;
|
|
2374
|
+
}
|
|
1767
2375
|
const conversationPageSize = 50;
|
|
1768
2376
|
const cancelGracePeriodMs = 5_000;
|
|
2377
|
+
const maxSessions = 20;
|
|
2378
|
+
const refreshDelayMs = 300;
|
|
2379
|
+
const newConversationKey = '';
|
|
1769
2380
|
/**
|
|
1770
|
-
* Signal-based state of one assistant scope: availability, agents, the conversation list
|
|
1771
|
-
*
|
|
2381
|
+
* Signal-based state of one assistant scope: availability, agents, the conversation list and
|
|
2382
|
+
* every conversation this tab has opened. Several conversations can run turns at the same time;
|
|
2383
|
+
* the public signals describe the conversation that is open in the panel. With live updates the
|
|
2384
|
+
* store follows turns of other people and of the user's other tabs.
|
|
1772
2385
|
*/
|
|
1773
2386
|
class NhAssistantStore {
|
|
1774
2387
|
constructor() {
|
|
@@ -1777,6 +2390,10 @@ class NhAssistantStore {
|
|
|
1777
2390
|
this.injector = inject(EnvironmentInjector);
|
|
1778
2391
|
this.accessPolicy = inject(NH_ASSISTANT_ACCESS_POLICY);
|
|
1779
2392
|
this.uiState = inject(NhAssistantUiState);
|
|
2393
|
+
this.live = inject(NhAssistantLiveService);
|
|
2394
|
+
this.push = inject(NhAssistantPushService);
|
|
2395
|
+
this.document = inject(DOCUMENT);
|
|
2396
|
+
this.browser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
1780
2397
|
this.accessGrantedState = signal(null, ...(ngDevMode ? [{ debugName: "accessGrantedState" }] : []));
|
|
1781
2398
|
this.statusState = signal(null, ...(ngDevMode ? [{ debugName: "statusState" }] : []));
|
|
1782
2399
|
this.statusLoadingState = signal(false, ...(ngDevMode ? [{ debugName: "statusLoadingState" }] : []));
|
|
@@ -1784,19 +2401,30 @@ class NhAssistantStore {
|
|
|
1784
2401
|
this.conversationsState = signal([], ...(ngDevMode ? [{ debugName: "conversationsState" }] : []));
|
|
1785
2402
|
this.conversationsTotalState = signal(0, ...(ngDevMode ? [{ debugName: "conversationsTotalState" }] : []));
|
|
1786
2403
|
this.conversationsLoadingState = signal(false, ...(ngDevMode ? [{ debugName: "conversationsLoadingState" }] : []));
|
|
1787
|
-
this.
|
|
2404
|
+
this.sessionsState = signal(new Map(), ...(ngDevMode ? [{ debugName: "sessionsState" }] : []));
|
|
2405
|
+
this.activeIdState = signal(null, ...(ngDevMode ? [{ debugName: "activeIdState" }] : []));
|
|
1788
2406
|
this.conversationLoadingState = signal(false, ...(ngDevMode ? [{ debugName: "conversationLoadingState" }] : []));
|
|
1789
|
-
|
|
1790
|
-
this.
|
|
1791
|
-
this.
|
|
1792
|
-
this.noticeState = signal(null, ...(ngDevMode ? [{ debugName: "noticeState" }] : []));
|
|
1793
|
-
this.lastUsageState = signal(null, ...(ngDevMode ? [{ debugName: "lastUsageState" }] : []));
|
|
2407
|
+
/** The conversation a message is being prepared for; `''` while a new conversation is created. */
|
|
2408
|
+
this.sendingState = signal(null, ...(ngDevMode ? [{ debugName: "sendingState" }] : []));
|
|
2409
|
+
this.draftErrorState = signal(null, ...(ngDevMode ? [{ debugName: "draftErrorState" }] : []));
|
|
1794
2410
|
this.restoredDraftState = signal(null, ...(ngDevMode ? [{ debugName: "restoredDraftState" }] : []));
|
|
1795
2411
|
this.pageContextState = signal(null, ...(ngDevMode ? [{ debugName: "pageContextState" }] : []));
|
|
1796
2412
|
this.pageContextExcludedState = signal(false, ...(ngDevMode ? [{ debugName: "pageContextExcludedState" }] : []));
|
|
1797
2413
|
this.restorePanelOpenState = signal(false, ...(ngDevMode ? [{ debugName: "restorePanelOpenState" }] : []));
|
|
2414
|
+
this.viewingState = signal(false, ...(ngDevMode ? [{ debugName: "viewingState" }] : []));
|
|
2415
|
+
this.pageVisibleState = signal(true, ...(ngDevMode ? [{ debugName: "pageVisibleState" }] : []));
|
|
2416
|
+
this.streams = new Map();
|
|
2417
|
+
this.cancelTimers = new Map();
|
|
2418
|
+
this.pendingSendResolves = new Map();
|
|
2419
|
+
this.markingRead = new Set();
|
|
2420
|
+
this.liveSubscription = new Subscription();
|
|
1798
2421
|
this.statusRevision = 0;
|
|
1799
2422
|
this.accountRevision = 0;
|
|
2423
|
+
this.collaborationKey = null;
|
|
2424
|
+
this.activeSession = computed(() => {
|
|
2425
|
+
const id = this.activeIdState();
|
|
2426
|
+
return id === null ? null : this.sessionsState().get(id) ?? null;
|
|
2427
|
+
}, ...(ngDevMode ? [{ debugName: "activeSession" }] : []));
|
|
1800
2428
|
/** True when the access policy allows the user and the server reports the assistant as enabled. */
|
|
1801
2429
|
this.enabled = computed(() => this.accessGrantedState() === true && this.statusState()?.enabled === true, ...(ngDevMode ? [{ debugName: "enabled" }] : []));
|
|
1802
2430
|
this.accessGranted = this.accessGrantedState.asReadonly();
|
|
@@ -1805,20 +2433,28 @@ class NhAssistantStore {
|
|
|
1805
2433
|
this.statusLoading = this.statusLoadingState.asReadonly();
|
|
1806
2434
|
this.limits = computed(() => this.statusState()?.limits ?? null, ...(ngDevMode ? [{ debugName: "limits" }] : []));
|
|
1807
2435
|
this.agents = computed(() => this.statusState()?.agents ?? [], ...(ngDevMode ? [{ debugName: "agents" }] : []));
|
|
2436
|
+
/** Sharing, live update and notification features the server offers. */
|
|
2437
|
+
this.collaboration = computed(() => this.statusState()?.collaboration ?? null, ...(ngDevMode ? [{ debugName: "collaboration" }] : []));
|
|
1808
2438
|
this.selectedAgentId = this.selectedAgentIdState.asReadonly();
|
|
1809
2439
|
this.selectedAgent = computed(() => this.agents().find(agent => agent.id === this.selectedAgentIdState()) ?? null, ...(ngDevMode ? [{ debugName: "selectedAgent" }] : []));
|
|
1810
2440
|
this.conversations = this.conversationsState.asReadonly();
|
|
1811
2441
|
this.conversationsTotal = this.conversationsTotalState.asReadonly();
|
|
1812
2442
|
this.conversationsLoading = this.conversationsLoadingState.asReadonly();
|
|
1813
|
-
|
|
2443
|
+
/** The conversation open in the panel, or `null` for a new one. */
|
|
2444
|
+
this.activeConversation = computed(() => this.activeSession()?.conversation ?? null, ...(ngDevMode ? [{ debugName: "activeConversation" }] : []));
|
|
1814
2445
|
this.conversationLoading = this.conversationLoadingState.asReadonly();
|
|
1815
|
-
this
|
|
1816
|
-
this.
|
|
1817
|
-
|
|
1818
|
-
|
|
2446
|
+
/** True while this tab sends a message or streams a turn of the open conversation. */
|
|
2447
|
+
this.streaming = computed(() => {
|
|
2448
|
+
const sending = this.sendingState();
|
|
2449
|
+
const activeId = this.activeIdState();
|
|
2450
|
+
return (sending !== null && sending === (activeId ?? newConversationKey)) || this.activeSession()?.streaming === true;
|
|
2451
|
+
}, ...(ngDevMode ? [{ debugName: "streaming" }] : []));
|
|
2452
|
+
this.pendingApproval = computed(() => this.activeConversation()?.pendingApproval ?? null, ...(ngDevMode ? [{ debugName: "pendingApproval" }] : []));
|
|
2453
|
+
this.deciding = computed(() => this.activeSession()?.deciding === true, ...(ngDevMode ? [{ debugName: "deciding" }] : []));
|
|
2454
|
+
this.error = computed(() => this.activeIdState() === null ? this.draftErrorState() : this.activeSession()?.error ?? null, ...(ngDevMode ? [{ debugName: "error" }] : []));
|
|
1819
2455
|
/** Non-blocking remark about the latest completed turn; shown only while no error is shown. */
|
|
1820
|
-
this.notice = this.
|
|
1821
|
-
this.lastUsage = this.
|
|
2456
|
+
this.notice = computed(() => this.activeSession()?.notice ?? null, ...(ngDevMode ? [{ debugName: "notice" }] : []));
|
|
2457
|
+
this.lastUsage = computed(() => this.activeSession()?.lastUsage ?? null, ...(ngDevMode ? [{ debugName: "lastUsage" }] : []));
|
|
1822
2458
|
/** Text of a message the server did not accept, so the composer can offer it again. */
|
|
1823
2459
|
this.restoredDraft = this.restoredDraftState.asReadonly();
|
|
1824
2460
|
/** Page context that the next message would send, for the transparency chip. */
|
|
@@ -1829,14 +2465,94 @@ class NhAssistantStore {
|
|
|
1829
2465
|
this.restorePanelOpen = this.restorePanelOpenState.asReadonly();
|
|
1830
2466
|
/** True while the user can send: enabled, an agent is chosen and no turn runs or waits. */
|
|
1831
2467
|
this.canSend = computed(() => {
|
|
1832
|
-
const conversation = this.
|
|
2468
|
+
const conversation = this.activeConversation();
|
|
1833
2469
|
const blocked = conversation !== null && conversation.status !== 'idle';
|
|
1834
|
-
return this.enabled() && this.selectedAgentIdState() !== null && !this.
|
|
2470
|
+
return this.enabled() && this.selectedAgentIdState() !== null && !this.streaming() && !this.conversationLoadingState() && !blocked;
|
|
1835
2471
|
}, ...(ngDevMode ? [{ debugName: "canSend" }] : []));
|
|
1836
|
-
|
|
2472
|
+
/** The owner and participants of the open conversation; empty while it is not shared. */
|
|
2473
|
+
this.members = computed(() => this.activeConversation()?.members ?? [], ...(ngDevMode ? [{ debugName: "members" }] : []));
|
|
2474
|
+
/** True when the current user owns the open conversation (or it is new). */
|
|
2475
|
+
this.isOwner = computed(() => (this.activeConversation()?.role ?? 'owner') === 'owner', ...(ngDevMode ? [{ debugName: "isOwner" }] : []));
|
|
2476
|
+
/** True when the current user may decide the pending approval: it belongs to their own turn. */
|
|
2477
|
+
this.canDecide = computed(() => {
|
|
2478
|
+
const conversation = this.activeConversation();
|
|
2479
|
+
if (!conversation?.pendingApproval) {
|
|
2480
|
+
return false;
|
|
2481
|
+
}
|
|
2482
|
+
return !conversation.activeActorId || !conversation.currentActorId || conversation.activeActorId === conversation.currentActorId;
|
|
2483
|
+
}, ...(ngDevMode ? [{ debugName: "canDecide" }] : []));
|
|
2484
|
+
/** The member whose turn runs or waits in the open conversation, when it is someone else. */
|
|
2485
|
+
this.activeMember = computed(() => {
|
|
2486
|
+
const conversation = this.activeConversation();
|
|
2487
|
+
if (!conversation?.activeActorId || conversation.activeActorId === conversation.currentActorId) {
|
|
2488
|
+
return null;
|
|
2489
|
+
}
|
|
2490
|
+
return conversation.members?.find(member => member.actorId === conversation.activeActorId) ?? null;
|
|
2491
|
+
}, ...(ngDevMode ? [{ debugName: "activeMember" }] : []));
|
|
2492
|
+
/** Conversations that need attention: running, waiting for approval or unread. Most recent first. */
|
|
2493
|
+
this.activity = computed(() => {
|
|
2494
|
+
const sessions = this.sessionsState();
|
|
2495
|
+
const activeId = this.activeIdState();
|
|
2496
|
+
return this.conversationsState()
|
|
2497
|
+
.map(summary => {
|
|
2498
|
+
const session = sessions.get(summary.id);
|
|
2499
|
+
const status = session?.streaming ? session.conversation.status : summary.status;
|
|
2500
|
+
return {
|
|
2501
|
+
id: summary.id,
|
|
2502
|
+
title: summary.title,
|
|
2503
|
+
status,
|
|
2504
|
+
unread: nhAssistantIsUnread({ ...summary, status }),
|
|
2505
|
+
shared: (summary.participantCount ?? 0) > 0,
|
|
2506
|
+
active: summary.id === activeId,
|
|
2507
|
+
updatedAt: summary.updatedAt
|
|
2508
|
+
};
|
|
2509
|
+
})
|
|
2510
|
+
.filter(item => item.status === 'running' || item.status === 'waiting-for-approval' || item.unread);
|
|
2511
|
+
}, ...(ngDevMode ? [{ debugName: "activity" }] : []));
|
|
2512
|
+
this.runningCount = computed(() => this.activity().filter(item => item.status === 'running').length, ...(ngDevMode ? [{ debugName: "runningCount" }] : []));
|
|
2513
|
+
this.waitingCount = computed(() => this.activity().filter(item => item.status === 'waiting-for-approval').length, ...(ngDevMode ? [{ debugName: "waitingCount" }] : []));
|
|
2514
|
+
this.unreadCount = computed(() => this.activity().filter(item => item.unread).length, ...(ngDevMode ? [{ debugName: "unreadCount" }] : []));
|
|
2515
|
+
/** Live update connection state. */
|
|
2516
|
+
this.liveState = this.live.state;
|
|
2517
|
+
const destroyRef = inject(DestroyRef);
|
|
2518
|
+
destroyRef.onDestroy(() => {
|
|
1837
2519
|
this.accessSubscription?.unsubscribe();
|
|
1838
|
-
this.
|
|
1839
|
-
|
|
2520
|
+
this.liveSubscription.unsubscribe();
|
|
2521
|
+
this.stopAllTurns();
|
|
2522
|
+
clearTimeout(this.refreshTimer);
|
|
2523
|
+
});
|
|
2524
|
+
this.liveSubscription.add(this.live.changed$.subscribe(change => this.onLiveChanged(change)));
|
|
2525
|
+
this.liveSubscription.add(this.live.read$.subscribe(read => this.onLiveRead(read)));
|
|
2526
|
+
this.liveSubscription.add(this.live.removed$.subscribe(removed => this.onLiveRemoved(removed)));
|
|
2527
|
+
this.liveSubscription.add(this.live.events$.subscribe(event => this.onLiveEvent(event)));
|
|
2528
|
+
this.liveSubscription.add(this.live.resync$.subscribe(() => this.resync()));
|
|
2529
|
+
if (this.browser) {
|
|
2530
|
+
const update = () => this.pageVisibleState.set(this.document.visibilityState === 'visible' && this.document.hasFocus());
|
|
2531
|
+
const visible = () => {
|
|
2532
|
+
update();
|
|
2533
|
+
if (this.document.visibilityState === 'visible' && this.enabled()) {
|
|
2534
|
+
// Without live updates, or on another instance, coming back reloads the list.
|
|
2535
|
+
void this.refreshConversations();
|
|
2536
|
+
}
|
|
2537
|
+
};
|
|
2538
|
+
this.document.addEventListener('visibilitychange', visible);
|
|
2539
|
+
globalThis.addEventListener?.('focus', update);
|
|
2540
|
+
globalThis.addEventListener?.('blur', update);
|
|
2541
|
+
destroyRef.onDestroy(() => {
|
|
2542
|
+
this.document.removeEventListener('visibilitychange', visible);
|
|
2543
|
+
globalThis.removeEventListener?.('focus', update);
|
|
2544
|
+
globalThis.removeEventListener?.('blur', update);
|
|
2545
|
+
});
|
|
2546
|
+
update();
|
|
2547
|
+
}
|
|
2548
|
+
// Marks the open conversation as read while the user looks at it.
|
|
2549
|
+
effect(() => {
|
|
2550
|
+
this.activeIdState();
|
|
2551
|
+
this.viewingState();
|
|
2552
|
+
this.pageVisibleState();
|
|
2553
|
+
this.conversationsState();
|
|
2554
|
+
this.sessionsState();
|
|
2555
|
+
untracked(() => this.markActiveReadWhenSeen());
|
|
1840
2556
|
});
|
|
1841
2557
|
}
|
|
1842
2558
|
/** Evaluates the access policy and loads the server status once. Safe to call repeatedly. */
|
|
@@ -1848,21 +2564,9 @@ class NhAssistantStore {
|
|
|
1848
2564
|
async reloadStatus() {
|
|
1849
2565
|
const revision = ++this.statusRevision;
|
|
1850
2566
|
if (this.accessGrantedState() !== true) {
|
|
1851
|
-
this.
|
|
1852
|
-
this.stopLocalTurn();
|
|
2567
|
+
this.resetAccount();
|
|
1853
2568
|
this.statusState.set(null);
|
|
1854
|
-
this.activeConversationState.set(null);
|
|
1855
2569
|
this.selectedAgentIdState.set(null);
|
|
1856
|
-
this.conversationsState.set([]);
|
|
1857
|
-
this.conversationsTotalState.set(0);
|
|
1858
|
-
this.conversationLoadingState.set(false);
|
|
1859
|
-
this.conversationsLoadingState.set(false);
|
|
1860
|
-
this.pageContextState.set(null);
|
|
1861
|
-
this.pageContextExcludedState.set(false);
|
|
1862
|
-
this.restoredDraftState.set(null);
|
|
1863
|
-
this.errorState.set(null);
|
|
1864
|
-
this.noticeState.set(null);
|
|
1865
|
-
this.lastUsageState.set(null);
|
|
1866
2570
|
this.restorePanelOpenState.set(false);
|
|
1867
2571
|
this.uiState.deactivate();
|
|
1868
2572
|
return;
|
|
@@ -1874,20 +2578,8 @@ class NhAssistantStore {
|
|
|
1874
2578
|
return;
|
|
1875
2579
|
}
|
|
1876
2580
|
if (restored.changed) {
|
|
1877
|
-
this.
|
|
1878
|
-
this.stopLocalTurn();
|
|
2581
|
+
this.resetAccount();
|
|
1879
2582
|
this.statusState.set(null);
|
|
1880
|
-
this.activeConversationState.set(null);
|
|
1881
|
-
this.conversationsState.set([]);
|
|
1882
|
-
this.conversationsTotalState.set(0);
|
|
1883
|
-
this.conversationLoadingState.set(false);
|
|
1884
|
-
this.conversationsLoadingState.set(false);
|
|
1885
|
-
this.pageContextState.set(null);
|
|
1886
|
-
this.pageContextExcludedState.set(false);
|
|
1887
|
-
this.restoredDraftState.set(null);
|
|
1888
|
-
this.errorState.set(null);
|
|
1889
|
-
this.noticeState.set(null);
|
|
1890
|
-
this.lastUsageState.set(null);
|
|
1891
2583
|
this.selectedAgentIdState.set(restored.state.agentId);
|
|
1892
2584
|
this.restorePanelOpenState.set(restored.state.panelOpen);
|
|
1893
2585
|
}
|
|
@@ -1898,7 +2590,8 @@ class NhAssistantStore {
|
|
|
1898
2590
|
this.statusState.set(status);
|
|
1899
2591
|
if (status.enabled) {
|
|
1900
2592
|
this.selectInitialAgent(status.agents);
|
|
1901
|
-
|
|
2593
|
+
this.startCollaboration(status);
|
|
2594
|
+
if (this.activeIdState() === null && restored.state.conversationId) {
|
|
1902
2595
|
await this.loadConversation(restored.state.conversationId, true);
|
|
1903
2596
|
}
|
|
1904
2597
|
}
|
|
@@ -1924,13 +2617,13 @@ class NhAssistantStore {
|
|
|
1924
2617
|
try {
|
|
1925
2618
|
const page = await firstValueFrom(this.api.listConversations(1, conversationPageSize));
|
|
1926
2619
|
if (revision === this.accountRevision) {
|
|
1927
|
-
this.conversationsState.set(page.items);
|
|
2620
|
+
this.conversationsState.set(page.items.map(item => this.withLocalState(item)));
|
|
1928
2621
|
this.conversationsTotalState.set(page.total);
|
|
1929
2622
|
}
|
|
1930
2623
|
}
|
|
1931
2624
|
catch (error) {
|
|
1932
2625
|
if (revision === this.accountRevision) {
|
|
1933
|
-
this.setError(error);
|
|
2626
|
+
this.setError(this.activeIdState(), error);
|
|
1934
2627
|
}
|
|
1935
2628
|
}
|
|
1936
2629
|
finally {
|
|
@@ -1939,72 +2632,37 @@ class NhAssistantStore {
|
|
|
1939
2632
|
}
|
|
1940
2633
|
}
|
|
1941
2634
|
}
|
|
1942
|
-
/** Chooses the agent for the next new conversation. Switching away from the
|
|
2635
|
+
/** Chooses the agent for the next new conversation. Switching away from the open conversation's agent starts a new one. */
|
|
1943
2636
|
selectAgent(agentId) {
|
|
1944
2637
|
if (!this.agents().some(agent => agent.id === agentId)) {
|
|
1945
2638
|
return;
|
|
1946
2639
|
}
|
|
1947
2640
|
this.selectedAgentIdState.set(agentId);
|
|
1948
2641
|
this.uiState.update({ agentId });
|
|
1949
|
-
const active = this.
|
|
1950
|
-
if (active && active.agentId !== agentId
|
|
2642
|
+
const active = this.activeConversation();
|
|
2643
|
+
if (active && active.agentId !== agentId) {
|
|
1951
2644
|
this.startNewConversation();
|
|
1952
2645
|
}
|
|
1953
2646
|
}
|
|
1954
|
-
/**
|
|
2647
|
+
/**
|
|
2648
|
+
* Leaves the open conversation; the next message creates a new one. Turns of other
|
|
2649
|
+
* conversations keep running in the background.
|
|
2650
|
+
*/
|
|
1955
2651
|
startNewConversation() {
|
|
1956
|
-
|
|
1957
|
-
return;
|
|
1958
|
-
}
|
|
1959
|
-
this.activeConversationState.set(null);
|
|
2652
|
+
this.activeIdState.set(null);
|
|
1960
2653
|
this.uiState.update({ conversationId: null });
|
|
1961
|
-
this.
|
|
1962
|
-
this.noticeState.set(null);
|
|
1963
|
-
this.lastUsageState.set(null);
|
|
2654
|
+
this.draftErrorState.set(null);
|
|
1964
2655
|
}
|
|
2656
|
+
/** Opens a conversation; one that this tab already follows opens immediately. */
|
|
1965
2657
|
async openConversation(conversationId) {
|
|
1966
2658
|
await this.loadConversation(conversationId, false);
|
|
1967
2659
|
}
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
}
|
|
1972
|
-
const revision = this.accountRevision;
|
|
1973
|
-
this.conversationLoadingState.set(true);
|
|
1974
|
-
this.errorState.set(null);
|
|
1975
|
-
this.noticeState.set(null);
|
|
1976
|
-
try {
|
|
1977
|
-
const conversation = await firstValueFrom(this.api.getConversation(conversationId));
|
|
1978
|
-
if (revision !== this.accountRevision) {
|
|
1979
|
-
return;
|
|
1980
|
-
}
|
|
1981
|
-
if (!this.agents().some(agent => agent.id === conversation.agentId)) {
|
|
1982
|
-
this.uiState.update({ conversationId: null });
|
|
1983
|
-
return;
|
|
1984
|
-
}
|
|
1985
|
-
this.activeConversationState.set(conversation);
|
|
1986
|
-
this.selectedAgentIdState.set(conversation.agentId);
|
|
1987
|
-
this.uiState.update({ conversationId, agentId: conversation.agentId });
|
|
1988
|
-
}
|
|
1989
|
-
catch (error) {
|
|
1990
|
-
if (revision !== this.accountRevision) {
|
|
1991
|
-
return;
|
|
1992
|
-
}
|
|
1993
|
-
if (error instanceof NhAssistantApiError && (error.status === 403 || error.status === 404)) {
|
|
1994
|
-
this.uiState.update({ conversationId: null });
|
|
1995
|
-
}
|
|
1996
|
-
if (!restoring) {
|
|
1997
|
-
this.setError(error);
|
|
1998
|
-
}
|
|
1999
|
-
}
|
|
2000
|
-
finally {
|
|
2001
|
-
if (revision === this.accountRevision) {
|
|
2002
|
-
this.conversationLoadingState.set(false);
|
|
2003
|
-
}
|
|
2004
|
-
}
|
|
2660
|
+
/** Tells the store whether the user can see the open thread, so it can mark it as read. */
|
|
2661
|
+
setViewing(viewing) {
|
|
2662
|
+
this.viewingState.set(viewing);
|
|
2005
2663
|
}
|
|
2006
2664
|
async deleteConversation(conversationId) {
|
|
2007
|
-
if (this.
|
|
2665
|
+
if (this.sessionsState().get(conversationId)?.streaming) {
|
|
2008
2666
|
return;
|
|
2009
2667
|
}
|
|
2010
2668
|
const revision = this.accountRevision;
|
|
@@ -2013,26 +2671,22 @@ class NhAssistantStore {
|
|
|
2013
2671
|
if (revision !== this.accountRevision) {
|
|
2014
2672
|
return;
|
|
2015
2673
|
}
|
|
2016
|
-
this.
|
|
2017
|
-
this.conversationsTotalState.update(total => Math.max(0, total - 1));
|
|
2018
|
-
if (this.activeConversationState()?.id === conversationId) {
|
|
2019
|
-
this.activeConversationState.set(null);
|
|
2020
|
-
this.noticeState.set(null);
|
|
2021
|
-
this.uiState.update({ conversationId: null });
|
|
2022
|
-
}
|
|
2674
|
+
this.forget(conversationId);
|
|
2023
2675
|
}
|
|
2024
2676
|
catch (error) {
|
|
2025
2677
|
if (revision === this.accountRevision) {
|
|
2026
|
-
this.setError(error);
|
|
2678
|
+
this.setError(this.activeIdState(), error);
|
|
2027
2679
|
}
|
|
2028
2680
|
}
|
|
2029
2681
|
}
|
|
2030
2682
|
/**
|
|
2031
|
-
* Sends a user message. Creates a conversation with the selected agent when none is
|
|
2032
|
-
*
|
|
2033
|
-
* Resolves `false` when the message was not accepted.
|
|
2683
|
+
* Sends a user message. Creates a conversation with the selected agent when none is open,
|
|
2684
|
+
* shows the message optimistically and applies the streamed events. Other conversations keep
|
|
2685
|
+
* their own turns. Resolves `false` when the message was not accepted.
|
|
2034
2686
|
*/
|
|
2035
2687
|
async send(text) {
|
|
2688
|
+
// Browsers only ask for notification permission during a user action such as this one.
|
|
2689
|
+
void this.push.onUserGesture().catch(() => undefined);
|
|
2036
2690
|
const trimmed = text.trim();
|
|
2037
2691
|
await this.initialize();
|
|
2038
2692
|
await this.reloadStatus();
|
|
@@ -2040,51 +2694,65 @@ class NhAssistantStore {
|
|
|
2040
2694
|
if (trimmed.length === 0 || trimmed.length > maxChars || !this.canSend()) {
|
|
2041
2695
|
return false;
|
|
2042
2696
|
}
|
|
2043
|
-
this.errorState.set(null);
|
|
2044
|
-
this.noticeState.set(null);
|
|
2045
|
-
this.restoredDraftState.set(null);
|
|
2046
|
-
this.streamingState.set(true);
|
|
2047
2697
|
const accountRevision = this.accountRevision;
|
|
2048
|
-
let conversation = this.
|
|
2698
|
+
let conversation = this.activeConversation();
|
|
2699
|
+
this.sendingState.set(conversation?.id ?? newConversationKey);
|
|
2700
|
+
this.restoredDraftState.set(null);
|
|
2701
|
+
this.draftErrorState.set(null);
|
|
2049
2702
|
if (!conversation) {
|
|
2050
2703
|
conversation = await this.createConversation();
|
|
2051
2704
|
if (!conversation) {
|
|
2052
2705
|
if (accountRevision === this.accountRevision) {
|
|
2053
|
-
this.
|
|
2706
|
+
this.sendingState.set(null);
|
|
2054
2707
|
this.restoredDraftState.set(text);
|
|
2055
2708
|
}
|
|
2056
2709
|
return false;
|
|
2057
2710
|
}
|
|
2711
|
+
this.sendingState.set(conversation.id);
|
|
2058
2712
|
}
|
|
2059
2713
|
const clientContext = await this.contextForMessage();
|
|
2060
2714
|
if (accountRevision !== this.accountRevision) {
|
|
2061
2715
|
return false;
|
|
2062
2716
|
}
|
|
2717
|
+
const conversationId = conversation.id;
|
|
2063
2718
|
const clientMessageId = createClientMessageId();
|
|
2064
2719
|
const previousStatus = conversation.status;
|
|
2065
|
-
this.
|
|
2066
|
-
...
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2720
|
+
this.patchSession(conversationId, session => ({
|
|
2721
|
+
...session,
|
|
2722
|
+
error: null,
|
|
2723
|
+
notice: null,
|
|
2724
|
+
conversation: {
|
|
2725
|
+
...session.conversation,
|
|
2726
|
+
status: 'running',
|
|
2727
|
+
messages: [
|
|
2728
|
+
...session.conversation.messages,
|
|
2729
|
+
{
|
|
2730
|
+
id: clientMessageId,
|
|
2731
|
+
role: 'user',
|
|
2732
|
+
createdAt: new Date().toISOString(),
|
|
2733
|
+
parts: [{ type: 'text', text: trimmed }],
|
|
2734
|
+
authorActorId: session.conversation.currentActorId ?? null
|
|
2735
|
+
}
|
|
2736
|
+
]
|
|
2737
|
+
}
|
|
2738
|
+
}));
|
|
2739
|
+
this.patchSummary(conversationId, { status: 'running', updatedAt: new Date().toISOString() });
|
|
2074
2740
|
return new Promise(resolve => {
|
|
2075
|
-
this.
|
|
2741
|
+
this.pendingSendResolves.set(conversationId, resolve);
|
|
2076
2742
|
let started = false;
|
|
2077
2743
|
let failedAfterStart = false;
|
|
2078
|
-
this.runStream(this.api.sendMessage(conversationId, {
|
|
2744
|
+
this.runStream(conversationId, this.api.sendMessage(conversationId, {
|
|
2079
2745
|
text: trimmed,
|
|
2080
2746
|
clientMessageId,
|
|
2081
2747
|
...(clientContext === undefined ? {} : { clientContext })
|
|
2082
2748
|
}), {
|
|
2083
2749
|
next: event => {
|
|
2084
2750
|
if (event.type === 'error' && !started) {
|
|
2085
|
-
this.removeMessage(clientMessageId, previousStatus);
|
|
2086
|
-
this.
|
|
2087
|
-
|
|
2751
|
+
this.removeMessage(conversationId, clientMessageId, previousStatus);
|
|
2752
|
+
if (this.activeIdState() === conversationId) {
|
|
2753
|
+
this.restoredDraftState.set(text);
|
|
2754
|
+
}
|
|
2755
|
+
this.patchSession(conversationId, session => ({ ...session, error: event.data }));
|
|
2088
2756
|
resolve(false);
|
|
2089
2757
|
return;
|
|
2090
2758
|
}
|
|
@@ -2095,114 +2763,290 @@ class NhAssistantStore {
|
|
|
2095
2763
|
if (event.type === 'error') {
|
|
2096
2764
|
failedAfterStart = true;
|
|
2097
2765
|
}
|
|
2098
|
-
this.applyEvent(event, clientMessageId);
|
|
2766
|
+
this.applyEvent(conversationId, event, clientMessageId);
|
|
2099
2767
|
},
|
|
2100
2768
|
complete: () => {
|
|
2101
|
-
this.
|
|
2769
|
+
this.pendingSendResolves.delete(conversationId);
|
|
2102
2770
|
resolve(started);
|
|
2103
2771
|
this.afterTurn(conversationId, failedAfterStart);
|
|
2104
2772
|
}
|
|
2105
2773
|
});
|
|
2774
|
+
this.sendingState.set(null);
|
|
2106
2775
|
});
|
|
2107
2776
|
}
|
|
2108
2777
|
/** Approves or rejects the pending approval. Ignores repeated calls while a decision is in flight. */
|
|
2109
2778
|
decide(decision, reason) {
|
|
2110
|
-
const
|
|
2779
|
+
const session = this.activeSession();
|
|
2780
|
+
const conversation = session?.conversation;
|
|
2111
2781
|
const approval = conversation?.pendingApproval ?? null;
|
|
2112
|
-
if (!conversation || !approval ||
|
|
2782
|
+
if (!session || !conversation || !approval || session.deciding || session.streaming || !this.canDecide()) {
|
|
2113
2783
|
return;
|
|
2114
2784
|
}
|
|
2115
|
-
|
|
2116
|
-
this.
|
|
2117
|
-
this.errorState.set(null);
|
|
2118
|
-
this.noticeState.set(null);
|
|
2785
|
+
const conversationId = conversation.id;
|
|
2786
|
+
this.patchSession(conversationId, current => ({ ...current, deciding: true, error: null, notice: null }));
|
|
2119
2787
|
let accepted = false;
|
|
2120
2788
|
let failed = false;
|
|
2121
2789
|
const request = { decision, expectedProposalHash: approval.proposalHash, ...(reason ? { reason } : {}) };
|
|
2122
|
-
this.runStream(this.api.decideApproval(
|
|
2790
|
+
this.runStream(conversationId, this.api.decideApproval(conversationId, approval.approvalId, request), {
|
|
2123
2791
|
next: event => {
|
|
2124
2792
|
if (!accepted && event.type !== 'error') {
|
|
2125
2793
|
accepted = true;
|
|
2126
|
-
this.
|
|
2794
|
+
this.patchSession(conversationId, current => ({
|
|
2795
|
+
...current,
|
|
2796
|
+
conversation: applyNhAssistantApprovalDecision(current.conversation, approval.approvalId, decision)
|
|
2797
|
+
}));
|
|
2127
2798
|
}
|
|
2128
2799
|
if (event.type === 'error') {
|
|
2129
2800
|
failed = true;
|
|
2130
2801
|
if (!accepted) {
|
|
2131
|
-
this.
|
|
2802
|
+
this.patchSession(conversationId, current => ({ ...current, error: event.data }));
|
|
2132
2803
|
return;
|
|
2133
2804
|
}
|
|
2134
2805
|
}
|
|
2135
|
-
this.applyEvent(event);
|
|
2806
|
+
this.applyEvent(conversationId, event);
|
|
2136
2807
|
},
|
|
2137
2808
|
complete: () => {
|
|
2138
|
-
this.
|
|
2139
|
-
this.afterTurn(
|
|
2809
|
+
this.patchSession(conversationId, current => ({ ...current, deciding: false }));
|
|
2810
|
+
this.afterTurn(conversationId, failed);
|
|
2140
2811
|
}
|
|
2141
2812
|
});
|
|
2142
2813
|
}
|
|
2143
|
-
/** Asks the server to stop the running turn or to abandon a waiting approval. */
|
|
2814
|
+
/** Asks the server to stop the running turn or to abandon a waiting approval of the open conversation. */
|
|
2144
2815
|
async cancel() {
|
|
2145
|
-
const
|
|
2146
|
-
|
|
2816
|
+
const session = this.activeSession();
|
|
2817
|
+
const conversation = session?.conversation;
|
|
2818
|
+
if (!session || !conversation || (conversation.status === 'idle' && !session.streaming)) {
|
|
2147
2819
|
return;
|
|
2148
2820
|
}
|
|
2821
|
+
const conversationId = conversation.id;
|
|
2149
2822
|
const revision = this.accountRevision;
|
|
2150
2823
|
try {
|
|
2151
|
-
await firstValueFrom(this.api.cancel(
|
|
2824
|
+
await firstValueFrom(this.api.cancel(conversationId), { defaultValue: undefined });
|
|
2152
2825
|
}
|
|
2153
2826
|
catch (error) {
|
|
2154
2827
|
if (revision === this.accountRevision) {
|
|
2155
|
-
this.setError(error);
|
|
2828
|
+
this.setError(conversationId, error);
|
|
2156
2829
|
}
|
|
2157
2830
|
return;
|
|
2158
2831
|
}
|
|
2159
2832
|
if (revision !== this.accountRevision) {
|
|
2160
2833
|
return;
|
|
2161
2834
|
}
|
|
2162
|
-
if (!this.
|
|
2163
|
-
await this.
|
|
2835
|
+
if (!this.sessionsState().get(conversationId)?.streaming) {
|
|
2836
|
+
await this.reloadSession(conversationId);
|
|
2164
2837
|
return;
|
|
2165
2838
|
}
|
|
2166
2839
|
// The server ends the stream with turn.completed(cancelled); stop waiting if it does not.
|
|
2167
|
-
clearTimeout(this.
|
|
2168
|
-
this.
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
this.
|
|
2172
|
-
this.
|
|
2173
|
-
|
|
2840
|
+
clearTimeout(this.cancelTimers.get(conversationId));
|
|
2841
|
+
this.cancelTimers.set(conversationId, setTimeout(() => {
|
|
2842
|
+
this.cancelTimers.delete(conversationId);
|
|
2843
|
+
if (this.sessionsState().get(conversationId)?.streaming) {
|
|
2844
|
+
this.streams.get(conversationId)?.unsubscribe();
|
|
2845
|
+
this.streams.delete(conversationId);
|
|
2846
|
+
this.patchSession(conversationId, current => ({ ...current, streaming: false, deciding: false }));
|
|
2847
|
+
void this.reloadSession(conversationId);
|
|
2174
2848
|
}
|
|
2175
|
-
}, cancelGracePeriodMs);
|
|
2176
|
-
}
|
|
2177
|
-
clearError() {
|
|
2178
|
-
this.errorState.set(null);
|
|
2179
|
-
}
|
|
2180
|
-
clearNotice() {
|
|
2181
|
-
this.noticeState.set(null);
|
|
2182
|
-
}
|
|
2183
|
-
/** Persists only drawer visibility, never the page context or a message draft. */
|
|
2184
|
-
setPanelOpen(open) {
|
|
2185
|
-
this.restorePanelOpenState.set(open);
|
|
2186
|
-
this.uiState.update({ panelOpen: open });
|
|
2849
|
+
}, cancelGracePeriodMs));
|
|
2187
2850
|
}
|
|
2188
|
-
/**
|
|
2189
|
-
async
|
|
2851
|
+
/** Joins a shared conversation with an invitation link and opens it. Resolves `false` on failure. */
|
|
2852
|
+
async joinConversation(conversationId, token) {
|
|
2853
|
+
await this.initialize();
|
|
2190
2854
|
const revision = this.accountRevision;
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2855
|
+
try {
|
|
2856
|
+
const conversation = await firstValueFrom(this.api.joinConversation(conversationId, token));
|
|
2857
|
+
if (revision !== this.accountRevision) {
|
|
2858
|
+
return false;
|
|
2859
|
+
}
|
|
2860
|
+
this.upsertSession(conversation);
|
|
2861
|
+
this.setActive(conversation.id, conversation.agentId);
|
|
2862
|
+
void this.refreshConversations();
|
|
2863
|
+
return true;
|
|
2864
|
+
}
|
|
2865
|
+
catch (error) {
|
|
2866
|
+
if (revision === this.accountRevision) {
|
|
2867
|
+
this.startNewConversation();
|
|
2868
|
+
this.setError(null, error);
|
|
2869
|
+
}
|
|
2870
|
+
return false;
|
|
2194
2871
|
}
|
|
2195
2872
|
}
|
|
2196
|
-
/**
|
|
2197
|
-
|
|
2198
|
-
this.
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2873
|
+
/** Owner only: creates a new invitation link for the open conversation and returns its token. */
|
|
2874
|
+
async createShareLink() {
|
|
2875
|
+
const conversationId = this.activeIdState();
|
|
2876
|
+
if (!conversationId) {
|
|
2877
|
+
return null;
|
|
2878
|
+
}
|
|
2879
|
+
try {
|
|
2880
|
+
const link = await firstValueFrom(this.api.createShareLink(conversationId));
|
|
2881
|
+
this.patchSession(conversationId, session => ({
|
|
2882
|
+
...session,
|
|
2883
|
+
conversation: { ...session.conversation, shareToken: link.token }
|
|
2884
|
+
}));
|
|
2885
|
+
return link.token;
|
|
2886
|
+
}
|
|
2887
|
+
catch (error) {
|
|
2888
|
+
this.setError(conversationId, error);
|
|
2889
|
+
return null;
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
/** Owner only: the invitation link of the open conversation stops working. */
|
|
2893
|
+
async revokeShareLink() {
|
|
2894
|
+
const conversationId = this.activeIdState();
|
|
2895
|
+
if (!conversationId) {
|
|
2896
|
+
return false;
|
|
2897
|
+
}
|
|
2898
|
+
try {
|
|
2899
|
+
await firstValueFrom(this.api.revokeShareLink(conversationId), { defaultValue: undefined });
|
|
2900
|
+
this.patchSession(conversationId, session => ({
|
|
2901
|
+
...session,
|
|
2902
|
+
conversation: { ...session.conversation, shareToken: null }
|
|
2903
|
+
}));
|
|
2904
|
+
return true;
|
|
2905
|
+
}
|
|
2906
|
+
catch (error) {
|
|
2907
|
+
this.setError(conversationId, error);
|
|
2908
|
+
return false;
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
/** Owner only: searches the application's directory for people to invite to the open conversation. */
|
|
2912
|
+
async searchParticipantCandidates(query) {
|
|
2913
|
+
const conversationId = this.activeIdState();
|
|
2914
|
+
if (!conversationId || query.trim().length < 2) {
|
|
2915
|
+
return [];
|
|
2916
|
+
}
|
|
2917
|
+
try {
|
|
2918
|
+
return await firstValueFrom(this.api.searchParticipantCandidates(conversationId, query.trim()));
|
|
2919
|
+
}
|
|
2920
|
+
catch (error) {
|
|
2921
|
+
this.setError(conversationId, error);
|
|
2922
|
+
return [];
|
|
2923
|
+
}
|
|
2924
|
+
}
|
|
2925
|
+
/** Owner only: invites a person from the directory to the open conversation. */
|
|
2926
|
+
async inviteParticipant(actorId) {
|
|
2927
|
+
const conversationId = this.activeIdState();
|
|
2928
|
+
if (!conversationId) {
|
|
2929
|
+
return false;
|
|
2930
|
+
}
|
|
2931
|
+
try {
|
|
2932
|
+
await firstValueFrom(this.api.inviteParticipant(conversationId, actorId), { defaultValue: undefined });
|
|
2933
|
+
await this.reloadSession(conversationId);
|
|
2934
|
+
return true;
|
|
2935
|
+
}
|
|
2936
|
+
catch (error) {
|
|
2937
|
+
this.setError(conversationId, error);
|
|
2938
|
+
return false;
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2941
|
+
/**
|
|
2942
|
+
* The owner removes a participant of the open conversation; with the current user's own id a
|
|
2943
|
+
* participant leaves it.
|
|
2944
|
+
*/
|
|
2945
|
+
async removeParticipant(actorId) {
|
|
2946
|
+
const conversation = this.activeConversation();
|
|
2947
|
+
if (!conversation) {
|
|
2948
|
+
return false;
|
|
2949
|
+
}
|
|
2950
|
+
try {
|
|
2951
|
+
await firstValueFrom(this.api.removeParticipant(conversation.id, actorId), { defaultValue: undefined });
|
|
2952
|
+
if (actorId === conversation.currentActorId) {
|
|
2953
|
+
this.forget(conversation.id);
|
|
2954
|
+
}
|
|
2955
|
+
else {
|
|
2956
|
+
await this.reloadSession(conversation.id);
|
|
2957
|
+
}
|
|
2958
|
+
return true;
|
|
2959
|
+
}
|
|
2960
|
+
catch (error) {
|
|
2961
|
+
this.setError(conversation.id, error);
|
|
2962
|
+
return false;
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
clearError() {
|
|
2966
|
+
const activeId = this.activeIdState();
|
|
2967
|
+
if (activeId === null) {
|
|
2968
|
+
this.draftErrorState.set(null);
|
|
2969
|
+
return;
|
|
2970
|
+
}
|
|
2971
|
+
this.patchSession(activeId, session => ({ ...session, error: null }));
|
|
2972
|
+
}
|
|
2973
|
+
clearNotice() {
|
|
2974
|
+
const activeId = this.activeIdState();
|
|
2975
|
+
if (activeId !== null) {
|
|
2976
|
+
this.patchSession(activeId, session => ({ ...session, notice: null }));
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2979
|
+
/** Persists only drawer visibility, never the page context or a message draft. */
|
|
2980
|
+
setPanelOpen(open) {
|
|
2981
|
+
this.restorePanelOpenState.set(open);
|
|
2982
|
+
this.uiState.update({ panelOpen: open });
|
|
2983
|
+
}
|
|
2984
|
+
/** Reads the host's page context again so the chip shows what the next message sends. */
|
|
2985
|
+
async refreshPageContext() {
|
|
2986
|
+
const revision = this.accountRevision;
|
|
2987
|
+
const context = await this.readPageContext();
|
|
2988
|
+
if (revision === this.accountRevision) {
|
|
2989
|
+
this.pageContextState.set(context ?? null);
|
|
2990
|
+
}
|
|
2991
|
+
}
|
|
2992
|
+
/** Leaves the page context out of the next message only, or includes it again. */
|
|
2993
|
+
setPageContextExcluded(excluded) {
|
|
2994
|
+
this.pageContextExcludedState.set(excluded);
|
|
2995
|
+
}
|
|
2996
|
+
/** Returns and clears the draft of a message that was not accepted. */
|
|
2997
|
+
consumeRestoredDraft() {
|
|
2998
|
+
const draft = this.restoredDraftState();
|
|
2203
2999
|
this.restoredDraftState.set(null);
|
|
2204
3000
|
return draft;
|
|
2205
3001
|
}
|
|
3002
|
+
async loadConversation(conversationId, restoring) {
|
|
3003
|
+
if (this.sessionsState().has(conversationId)) {
|
|
3004
|
+
const session = this.sessionsState().get(conversationId);
|
|
3005
|
+
this.setActive(conversationId, session.conversation.agentId);
|
|
3006
|
+
return;
|
|
3007
|
+
}
|
|
3008
|
+
if (this.activeIdState() === conversationId) {
|
|
3009
|
+
return;
|
|
3010
|
+
}
|
|
3011
|
+
const revision = this.accountRevision;
|
|
3012
|
+
this.conversationLoadingState.set(true);
|
|
3013
|
+
this.draftErrorState.set(null);
|
|
3014
|
+
try {
|
|
3015
|
+
const conversation = await firstValueFrom(this.api.getConversation(conversationId));
|
|
3016
|
+
if (revision !== this.accountRevision) {
|
|
3017
|
+
return;
|
|
3018
|
+
}
|
|
3019
|
+
if (!this.agents().some(agent => agent.id === conversation.agentId)) {
|
|
3020
|
+
this.uiState.update({ conversationId: null });
|
|
3021
|
+
return;
|
|
3022
|
+
}
|
|
3023
|
+
this.upsertSession(conversation);
|
|
3024
|
+
this.setActive(conversationId, conversation.agentId);
|
|
3025
|
+
}
|
|
3026
|
+
catch (error) {
|
|
3027
|
+
if (revision !== this.accountRevision) {
|
|
3028
|
+
return;
|
|
3029
|
+
}
|
|
3030
|
+
if (error instanceof NhAssistantApiError && (error.status === 403 || error.status === 404)) {
|
|
3031
|
+
this.uiState.update({ conversationId: null });
|
|
3032
|
+
}
|
|
3033
|
+
if (!restoring) {
|
|
3034
|
+
this.setError(null, error);
|
|
3035
|
+
}
|
|
3036
|
+
}
|
|
3037
|
+
finally {
|
|
3038
|
+
if (revision === this.accountRevision) {
|
|
3039
|
+
this.conversationLoadingState.set(false);
|
|
3040
|
+
}
|
|
3041
|
+
}
|
|
3042
|
+
}
|
|
3043
|
+
setActive(conversationId, agentId) {
|
|
3044
|
+
this.activeIdState.set(conversationId);
|
|
3045
|
+
if (agentId) {
|
|
3046
|
+
this.selectedAgentIdState.set(agentId);
|
|
3047
|
+
}
|
|
3048
|
+
this.uiState.update(agentId ? { conversationId, agentId } : { conversationId });
|
|
3049
|
+
}
|
|
2206
3050
|
/**
|
|
2207
3051
|
* Page context for one message: `undefined` omits the field (no getter, getter failed or
|
|
2208
3052
|
* invalid shape), `null` sends an explicit "none" (no context or left out by the user).
|
|
@@ -2260,6 +3104,37 @@ class NhAssistantStore {
|
|
|
2260
3104
|
});
|
|
2261
3105
|
});
|
|
2262
3106
|
}
|
|
3107
|
+
/** Connects live updates and notifications once per account and server configuration. */
|
|
3108
|
+
startCollaboration(status) {
|
|
3109
|
+
const collaboration = status.collaboration ?? null;
|
|
3110
|
+
const key = `${this.accountRevision}|${collaboration?.hubPath ?? ''}|${collaboration?.push === true}`;
|
|
3111
|
+
if (key === this.collaborationKey) {
|
|
3112
|
+
return;
|
|
3113
|
+
}
|
|
3114
|
+
const accountChanged = this.collaborationKey?.split('|')[0] !== String(this.accountRevision);
|
|
3115
|
+
this.collaborationKey = key;
|
|
3116
|
+
void this.live.connect(collaboration?.hubPath ?? null, accountChanged);
|
|
3117
|
+
void this.push.activate(collaboration?.push === true);
|
|
3118
|
+
}
|
|
3119
|
+
/** Forgets everything about the previous account: turns, conversations, live updates and push. */
|
|
3120
|
+
resetAccount() {
|
|
3121
|
+
this.accountRevision++;
|
|
3122
|
+
this.collaborationKey = null;
|
|
3123
|
+
this.stopAllTurns();
|
|
3124
|
+
this.sessionsState.set(new Map());
|
|
3125
|
+
this.activeIdState.set(null);
|
|
3126
|
+
this.conversationsState.set([]);
|
|
3127
|
+
this.conversationsTotalState.set(0);
|
|
3128
|
+
this.conversationLoadingState.set(false);
|
|
3129
|
+
this.conversationsLoadingState.set(false);
|
|
3130
|
+
this.pageContextState.set(null);
|
|
3131
|
+
this.pageContextExcludedState.set(false);
|
|
3132
|
+
this.restoredDraftState.set(null);
|
|
3133
|
+
this.draftErrorState.set(null);
|
|
3134
|
+
this.sendingState.set(null);
|
|
3135
|
+
void this.live.stop();
|
|
3136
|
+
void this.push.deactivate().catch(() => undefined);
|
|
3137
|
+
}
|
|
2263
3138
|
async createConversation() {
|
|
2264
3139
|
const agentId = this.selectedAgentIdState();
|
|
2265
3140
|
if (!agentId) {
|
|
@@ -2271,108 +3146,305 @@ class NhAssistantStore {
|
|
|
2271
3146
|
if (revision !== this.accountRevision) {
|
|
2272
3147
|
return null;
|
|
2273
3148
|
}
|
|
2274
|
-
this.
|
|
2275
|
-
this.
|
|
3149
|
+
this.upsertSession(conversation);
|
|
3150
|
+
this.setActive(conversation.id);
|
|
2276
3151
|
this.conversationsState.update(items => [toSummary(conversation), ...items.filter(item => item.id !== conversation.id)]);
|
|
2277
3152
|
this.conversationsTotalState.update(total => total + 1);
|
|
2278
3153
|
return conversation;
|
|
2279
3154
|
}
|
|
2280
3155
|
catch (error) {
|
|
2281
3156
|
if (revision === this.accountRevision) {
|
|
2282
|
-
this.setError(error);
|
|
3157
|
+
this.setError(null, error);
|
|
2283
3158
|
}
|
|
2284
3159
|
return null;
|
|
2285
3160
|
}
|
|
2286
3161
|
}
|
|
2287
|
-
runStream(stream, handlers) {
|
|
2288
|
-
this.
|
|
2289
|
-
this.
|
|
3162
|
+
runStream(conversationId, stream, handlers) {
|
|
3163
|
+
this.streams.get(conversationId)?.unsubscribe();
|
|
3164
|
+
this.patchSession(conversationId, session => ({ ...session, streaming: true }));
|
|
2290
3165
|
const finish = () => {
|
|
2291
|
-
clearTimeout(this.
|
|
2292
|
-
this.
|
|
3166
|
+
clearTimeout(this.cancelTimers.get(conversationId));
|
|
3167
|
+
this.cancelTimers.delete(conversationId);
|
|
3168
|
+
this.streams.delete(conversationId);
|
|
3169
|
+
this.patchSession(conversationId, session => ({ ...session, streaming: false }));
|
|
2293
3170
|
handlers.complete();
|
|
2294
3171
|
};
|
|
2295
|
-
|
|
3172
|
+
const subscription = stream.subscribe({
|
|
2296
3173
|
next: event => handlers.next(event),
|
|
2297
3174
|
error: () => {
|
|
2298
|
-
this.
|
|
3175
|
+
this.patchSession(conversationId, session => ({ ...session, error: clientError(NhAssistantClientErrorCodes.network) }));
|
|
2299
3176
|
finish();
|
|
2300
3177
|
},
|
|
2301
3178
|
complete: finish
|
|
2302
3179
|
});
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
this.streamSubscription?.unsubscribe();
|
|
2306
|
-
this.pendingSendResolve?.(false);
|
|
2307
|
-
this.pendingSendResolve = undefined;
|
|
2308
|
-
clearTimeout(this.cancelTimer);
|
|
2309
|
-
this.streamingState.set(false);
|
|
2310
|
-
this.decidingState.set(false);
|
|
2311
|
-
}
|
|
2312
|
-
applyEvent(event, clientMessageId) {
|
|
2313
|
-
this.updateActive(conversation => applyNhAssistantEvent(conversation, event, clientMessageId));
|
|
2314
|
-
if (event.type === 'error') {
|
|
2315
|
-
this.errorState.set(event.data);
|
|
2316
|
-
}
|
|
2317
|
-
if (event.type === 'turn.completed') {
|
|
2318
|
-
this.lastUsageState.set(event.data.usage);
|
|
2319
|
-
if (event.data.status === 'failed') {
|
|
2320
|
-
const code = event.data.errorCode ?? NhAssistantClientErrorCodes.server;
|
|
2321
|
-
this.errorState.set(clientError(code));
|
|
2322
|
-
}
|
|
2323
|
-
else if (event.data.status === 'completed') {
|
|
2324
|
-
this.noticeState.set(this.completionNotice(event.data.errorCode));
|
|
2325
|
-
}
|
|
3180
|
+
if (!subscription.closed) {
|
|
3181
|
+
this.streams.set(conversationId, subscription);
|
|
2326
3182
|
}
|
|
2327
3183
|
}
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
return clientError(errorCode);
|
|
3184
|
+
stopAllTurns() {
|
|
3185
|
+
for (const subscription of this.streams.values()) {
|
|
3186
|
+
subscription.unsubscribe();
|
|
2332
3187
|
}
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
3188
|
+
this.streams.clear();
|
|
3189
|
+
for (const resolve of this.pendingSendResolves.values()) {
|
|
3190
|
+
resolve(false);
|
|
2336
3191
|
}
|
|
2337
|
-
|
|
3192
|
+
this.pendingSendResolves.clear();
|
|
3193
|
+
for (const timer of this.cancelTimers.values()) {
|
|
3194
|
+
clearTimeout(timer);
|
|
3195
|
+
}
|
|
3196
|
+
this.cancelTimers.clear();
|
|
3197
|
+
}
|
|
3198
|
+
applyEvent(conversationId, event, clientMessageId) {
|
|
3199
|
+
this.patchSession(conversationId, session => {
|
|
3200
|
+
const conversation = applyNhAssistantEvent(session.conversation, event, clientMessageId);
|
|
3201
|
+
let { error, notice, lastUsage } = session;
|
|
3202
|
+
if (event.type === 'error') {
|
|
3203
|
+
error = event.data;
|
|
3204
|
+
}
|
|
3205
|
+
if (event.type === 'turn.completed') {
|
|
3206
|
+
lastUsage = event.data.usage;
|
|
3207
|
+
if (event.data.status === 'failed') {
|
|
3208
|
+
error = clientError(event.data.errorCode ?? NhAssistantClientErrorCodes.server);
|
|
3209
|
+
}
|
|
3210
|
+
else if (event.data.status === 'completed') {
|
|
3211
|
+
notice = completionNotice(conversation, event.data.errorCode);
|
|
3212
|
+
}
|
|
3213
|
+
}
|
|
3214
|
+
return { ...session, conversation, error, notice, lastUsage };
|
|
3215
|
+
});
|
|
2338
3216
|
}
|
|
2339
3217
|
afterTurn(conversationId, reload) {
|
|
2340
|
-
const
|
|
2341
|
-
if (
|
|
2342
|
-
|
|
3218
|
+
const session = this.sessionsState().get(conversationId);
|
|
3219
|
+
if (session) {
|
|
3220
|
+
const status = session.conversation.status;
|
|
3221
|
+
this.patchSummary(conversationId, summary => ({
|
|
3222
|
+
...summary,
|
|
3223
|
+
status,
|
|
3224
|
+
updatedAt: new Date().toISOString(),
|
|
3225
|
+
activeActorId: status === 'waiting-for-approval' ? summary.activeActorId ?? session.conversation.currentActorId ?? null : null
|
|
3226
|
+
}));
|
|
2343
3227
|
}
|
|
2344
3228
|
if (reload) {
|
|
2345
|
-
void this.
|
|
2346
|
-
}
|
|
2347
|
-
if (!active?.title) {
|
|
2348
|
-
void this.refreshConversations();
|
|
3229
|
+
void this.reloadSession(conversationId);
|
|
2349
3230
|
}
|
|
3231
|
+
// The list carries the exact title, latest message and read position of the finished turn.
|
|
3232
|
+
void this.refreshConversations();
|
|
2350
3233
|
}
|
|
2351
|
-
async
|
|
3234
|
+
async reloadSession(conversationId) {
|
|
2352
3235
|
const revision = this.accountRevision;
|
|
2353
3236
|
try {
|
|
2354
3237
|
const conversation = await firstValueFrom(this.api.getConversation(conversationId));
|
|
2355
|
-
|
|
2356
|
-
|
|
3238
|
+
const session = this.sessionsState().get(conversationId);
|
|
3239
|
+
if (revision === this.accountRevision && session && !session.streaming) {
|
|
3240
|
+
this.upsertSession(conversation);
|
|
2357
3241
|
}
|
|
2358
3242
|
}
|
|
3243
|
+
catch (error) {
|
|
3244
|
+
if (error instanceof NhAssistantApiError && (error.status === 403 || error.status === 404)) {
|
|
3245
|
+
this.forget(conversationId);
|
|
3246
|
+
}
|
|
3247
|
+
// Otherwise keep the local state; the error of the turn is already shown.
|
|
3248
|
+
}
|
|
3249
|
+
}
|
|
3250
|
+
markActiveReadWhenSeen() {
|
|
3251
|
+
const conversationId = this.activeIdState();
|
|
3252
|
+
if (!conversationId || !this.viewingState() || !this.pageVisibleState() || this.markingRead.has(conversationId)) {
|
|
3253
|
+
return;
|
|
3254
|
+
}
|
|
3255
|
+
const session = this.sessionsState().get(conversationId);
|
|
3256
|
+
const summary = this.conversationsState().find(item => item.id === conversationId);
|
|
3257
|
+
if (!session || session.streaming || !summary || !nhAssistantIsUnread(summary)) {
|
|
3258
|
+
return;
|
|
3259
|
+
}
|
|
3260
|
+
this.markingRead.add(conversationId);
|
|
3261
|
+
const revision = this.accountRevision;
|
|
3262
|
+
let request;
|
|
3263
|
+
try {
|
|
3264
|
+
request = this.api.markRead(conversationId);
|
|
3265
|
+
}
|
|
2359
3266
|
catch {
|
|
2360
|
-
|
|
3267
|
+
this.markingRead.delete(conversationId);
|
|
3268
|
+
return;
|
|
2361
3269
|
}
|
|
3270
|
+
firstValueFrom(request, { defaultValue: undefined })
|
|
3271
|
+
.then(() => {
|
|
3272
|
+
if (revision === this.accountRevision) {
|
|
3273
|
+
this.patchSummary(conversationId, item => ({ ...item, lastReadSequence: item.lastMessageSequence ?? item.lastReadSequence }));
|
|
3274
|
+
}
|
|
3275
|
+
})
|
|
3276
|
+
.catch(() => undefined)
|
|
3277
|
+
.finally(() => this.markingRead.delete(conversationId));
|
|
2362
3278
|
}
|
|
2363
|
-
|
|
2364
|
-
const
|
|
2365
|
-
if (
|
|
2366
|
-
this.
|
|
3279
|
+
onLiveChanged(change) {
|
|
3280
|
+
const known = this.conversationsState().some(item => item.id === change.conversationId);
|
|
3281
|
+
if (!known) {
|
|
3282
|
+
this.scheduleRefresh();
|
|
3283
|
+
}
|
|
3284
|
+
else {
|
|
3285
|
+
this.patchSummary(change.conversationId, summary => ({
|
|
3286
|
+
...summary,
|
|
3287
|
+
status: change.status,
|
|
3288
|
+
title: change.title,
|
|
3289
|
+
updatedAt: change.updatedAt,
|
|
3290
|
+
activeActorId: change.activeActorId,
|
|
3291
|
+
lastMessageSequence: Math.max(summary.lastMessageSequence ?? 0, change.lastMessageSequence),
|
|
3292
|
+
participantCount: change.participantCount
|
|
3293
|
+
}));
|
|
3294
|
+
}
|
|
3295
|
+
const session = this.sessionsState().get(change.conversationId);
|
|
3296
|
+
if (!session || session.streaming) {
|
|
3297
|
+
return;
|
|
3298
|
+
}
|
|
3299
|
+
const wasRunning = session.conversation.status === 'running';
|
|
3300
|
+
this.patchSession(change.conversationId, current => ({
|
|
3301
|
+
...current,
|
|
3302
|
+
conversation: applyNhAssistantConversationChange(current.conversation, change)
|
|
3303
|
+
}));
|
|
3304
|
+
if (wasRunning && change.status !== 'running') {
|
|
3305
|
+
// Another session ended its turn; the snapshot holds the exact answer and approvals.
|
|
3306
|
+
void this.reloadSession(change.conversationId);
|
|
3307
|
+
}
|
|
3308
|
+
else if (session.conversation.participantCount !== change.participantCount) {
|
|
3309
|
+
void this.reloadSession(change.conversationId);
|
|
3310
|
+
}
|
|
3311
|
+
}
|
|
3312
|
+
onLiveRead(read) {
|
|
3313
|
+
this.patchSummary(read.conversationId, summary => ({
|
|
3314
|
+
...summary,
|
|
3315
|
+
lastReadSequence: Math.max(summary.lastReadSequence ?? 0, read.lastReadSequence)
|
|
3316
|
+
}));
|
|
3317
|
+
}
|
|
3318
|
+
onLiveRemoved(removed) {
|
|
3319
|
+
const wasActive = this.activeIdState() === removed.conversationId;
|
|
3320
|
+
this.forget(removed.conversationId);
|
|
3321
|
+
if (wasActive) {
|
|
3322
|
+
this.draftErrorState.set(clientError(NhAssistantClientErrorCodes.conversationRemoved));
|
|
3323
|
+
}
|
|
3324
|
+
}
|
|
3325
|
+
onLiveEvent(event) {
|
|
3326
|
+
const session = this.sessionsState().get(event.conversationId);
|
|
3327
|
+
if (event.type === 'turn.started') {
|
|
3328
|
+
this.patchSummary(event.conversationId, summary => ({ ...summary, status: 'running', activeActorId: event.actorId }));
|
|
3329
|
+
}
|
|
3330
|
+
if (!session || session.streaming) {
|
|
3331
|
+
// This tab streams the turn itself, or does not show the conversation.
|
|
3332
|
+
return;
|
|
3333
|
+
}
|
|
3334
|
+
this.patchSession(event.conversationId, current => ({
|
|
3335
|
+
...current,
|
|
3336
|
+
conversation: applyNhAssistantLiveEvent(current.conversation, event)
|
|
3337
|
+
}));
|
|
3338
|
+
}
|
|
3339
|
+
/** After a reconnect: updates sent while disconnected are lost, so reload what is shown. */
|
|
3340
|
+
resync() {
|
|
3341
|
+
if (!this.enabled()) {
|
|
3342
|
+
return;
|
|
3343
|
+
}
|
|
3344
|
+
void this.refreshConversations();
|
|
3345
|
+
for (const [conversationId, session] of this.sessionsState()) {
|
|
3346
|
+
if (!session.streaming) {
|
|
3347
|
+
void this.reloadSession(conversationId);
|
|
3348
|
+
}
|
|
2367
3349
|
}
|
|
2368
3350
|
}
|
|
2369
|
-
|
|
2370
|
-
this.
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
3351
|
+
scheduleRefresh() {
|
|
3352
|
+
clearTimeout(this.refreshTimer);
|
|
3353
|
+
this.refreshTimer = setTimeout(() => void this.refreshConversations(), refreshDelayMs);
|
|
3354
|
+
}
|
|
3355
|
+
/** Stores a snapshot, keeping at most `maxSessions` idle conversations in memory. */
|
|
3356
|
+
upsertSession(conversation) {
|
|
3357
|
+
this.sessionsState.update(sessions => {
|
|
3358
|
+
const next = new Map(sessions);
|
|
3359
|
+
const existing = next.get(conversation.id);
|
|
3360
|
+
next.set(conversation.id, existing
|
|
3361
|
+
? { ...existing, conversation }
|
|
3362
|
+
: { conversation, streaming: false, deciding: false, error: null, notice: null, lastUsage: null });
|
|
3363
|
+
if (next.size > maxSessions) {
|
|
3364
|
+
const activeId = this.activeIdState();
|
|
3365
|
+
for (const [id, session] of next) {
|
|
3366
|
+
if (next.size <= maxSessions) {
|
|
3367
|
+
break;
|
|
3368
|
+
}
|
|
3369
|
+
if (id !== activeId && id !== conversation.id && !session.streaming) {
|
|
3370
|
+
next.delete(id);
|
|
3371
|
+
}
|
|
3372
|
+
}
|
|
3373
|
+
}
|
|
3374
|
+
return next;
|
|
3375
|
+
});
|
|
3376
|
+
this.patchSummary(conversation.id, summary => ({
|
|
3377
|
+
...summary,
|
|
3378
|
+
title: conversation.title,
|
|
3379
|
+
status: conversation.status,
|
|
3380
|
+
updatedAt: conversation.updatedAt,
|
|
3381
|
+
...(conversation.lastMessageSequence === undefined ? {} : { lastMessageSequence: conversation.lastMessageSequence }),
|
|
3382
|
+
...(conversation.lastReadSequence === undefined ? {} : { lastReadSequence: conversation.lastReadSequence }),
|
|
3383
|
+
...(conversation.activeActorId === undefined ? {} : { activeActorId: conversation.activeActorId }),
|
|
3384
|
+
...(conversation.members === undefined ? {} : { participantCount: Math.max(0, conversation.members.length - 1) })
|
|
2374
3385
|
}));
|
|
2375
3386
|
}
|
|
3387
|
+
patchSession(conversationId, update) {
|
|
3388
|
+
const session = this.sessionsState().get(conversationId);
|
|
3389
|
+
if (!session) {
|
|
3390
|
+
return;
|
|
3391
|
+
}
|
|
3392
|
+
this.sessionsState.update(sessions => new Map(sessions).set(conversationId, update(session)));
|
|
3393
|
+
}
|
|
3394
|
+
patchSummary(conversationId, update) {
|
|
3395
|
+
this.conversationsState.update(items => items.map(item => {
|
|
3396
|
+
if (item.id !== conversationId) {
|
|
3397
|
+
return item;
|
|
3398
|
+
}
|
|
3399
|
+
return typeof update === 'function' ? update(item) : { ...item, ...update };
|
|
3400
|
+
}));
|
|
3401
|
+
}
|
|
3402
|
+
/**
|
|
3403
|
+
* Keeps local knowledge when the list is reloaded: the status of turns this tab streams, and a
|
|
3404
|
+
* read position the server already confirmed, which a list response sent earlier may not show.
|
|
3405
|
+
*/
|
|
3406
|
+
withLocalState(summary) {
|
|
3407
|
+
const session = this.sessionsState().get(summary.id);
|
|
3408
|
+
const known = this.conversationsState().find(item => item.id === summary.id)?.lastReadSequence;
|
|
3409
|
+
const lastReadSequence = known !== undefined && summary.lastReadSequence !== undefined
|
|
3410
|
+
? Math.max(known, summary.lastReadSequence)
|
|
3411
|
+
: summary.lastReadSequence;
|
|
3412
|
+
return {
|
|
3413
|
+
...summary,
|
|
3414
|
+
lastReadSequence,
|
|
3415
|
+
status: session?.streaming ? session.conversation.status : summary.status
|
|
3416
|
+
};
|
|
3417
|
+
}
|
|
3418
|
+
/** Removes a deleted, left or inaccessible conversation everywhere. */
|
|
3419
|
+
forget(conversationId) {
|
|
3420
|
+
this.streams.get(conversationId)?.unsubscribe();
|
|
3421
|
+
this.streams.delete(conversationId);
|
|
3422
|
+
this.sessionsState.update(sessions => {
|
|
3423
|
+
const next = new Map(sessions);
|
|
3424
|
+
next.delete(conversationId);
|
|
3425
|
+
return next;
|
|
3426
|
+
});
|
|
3427
|
+
const before = this.conversationsState().length;
|
|
3428
|
+
this.conversationsState.update(items => items.filter(item => item.id !== conversationId));
|
|
3429
|
+
if (this.conversationsState().length < before) {
|
|
3430
|
+
this.conversationsTotalState.update(total => Math.max(0, total - 1));
|
|
3431
|
+
}
|
|
3432
|
+
if (this.activeIdState() === conversationId) {
|
|
3433
|
+
this.activeIdState.set(null);
|
|
3434
|
+
this.uiState.update({ conversationId: null });
|
|
3435
|
+
}
|
|
3436
|
+
}
|
|
3437
|
+
removeMessage(conversationId, messageId, status) {
|
|
3438
|
+
this.patchSession(conversationId, session => ({
|
|
3439
|
+
...session,
|
|
3440
|
+
conversation: {
|
|
3441
|
+
...session.conversation,
|
|
3442
|
+
status,
|
|
3443
|
+
messages: session.conversation.messages.filter(message => message.id !== messageId)
|
|
3444
|
+
}
|
|
3445
|
+
}));
|
|
3446
|
+
this.patchSummary(conversationId, { status });
|
|
3447
|
+
}
|
|
2376
3448
|
selectInitialAgent(agents) {
|
|
2377
3449
|
const current = this.selectedAgentIdState();
|
|
2378
3450
|
if (current && agents.some(agent => agent.id === current)) {
|
|
@@ -2382,22 +3454,36 @@ class NhAssistantStore {
|
|
|
2382
3454
|
this.selectedAgentIdState.set(preferred?.id ?? null);
|
|
2383
3455
|
this.uiState.update({ agentId: preferred?.id ?? null });
|
|
2384
3456
|
}
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
3457
|
+
/** Shows an error on a conversation, or on the new-conversation view for `null`. */
|
|
3458
|
+
setError(conversationId, error) {
|
|
3459
|
+
const value = error instanceof NhAssistantApiError
|
|
3460
|
+
? { code: error.code, messageKey: error.messageKey }
|
|
3461
|
+
: clientError(NhAssistantClientErrorCodes.server);
|
|
3462
|
+
if (conversationId === null || !this.sessionsState().has(conversationId)) {
|
|
3463
|
+
this.draftErrorState.set(value);
|
|
2388
3464
|
return;
|
|
2389
3465
|
}
|
|
2390
|
-
this.
|
|
3466
|
+
this.patchSession(conversationId, session => ({ ...session, error: value }));
|
|
2391
3467
|
}
|
|
2392
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
2393
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.
|
|
3468
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3469
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantStore }); }
|
|
2394
3470
|
}
|
|
2395
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
3471
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantStore, decorators: [{
|
|
2396
3472
|
type: Injectable
|
|
2397
3473
|
}], ctorParameters: () => [] });
|
|
2398
3474
|
function clientError(code) {
|
|
2399
3475
|
return { code, messageKey: nhAssistantErrorMessageKey(code) };
|
|
2400
3476
|
}
|
|
3477
|
+
/** A completed turn with a server code, or without any answer text, explains itself with a notice. */
|
|
3478
|
+
function completionNotice(conversation, errorCode) {
|
|
3479
|
+
if (errorCode) {
|
|
3480
|
+
return clientError(errorCode);
|
|
3481
|
+
}
|
|
3482
|
+
if (!nhAssistantLatestTurnHasText(conversation)) {
|
|
3483
|
+
return { code: NH_ASSISTANT_NO_ANSWER_NOTICE_CODE, messageKey: 'nh-assistant.notices.no-answer' };
|
|
3484
|
+
}
|
|
3485
|
+
return null;
|
|
3486
|
+
}
|
|
2401
3487
|
function toSummary(conversation) {
|
|
2402
3488
|
return {
|
|
2403
3489
|
id: conversation.id,
|
|
@@ -2405,7 +3491,12 @@ function toSummary(conversation) {
|
|
|
2405
3491
|
title: conversation.title,
|
|
2406
3492
|
status: conversation.status,
|
|
2407
3493
|
createdAt: conversation.createdAt,
|
|
2408
|
-
updatedAt: conversation.updatedAt
|
|
3494
|
+
updatedAt: conversation.updatedAt,
|
|
3495
|
+
role: conversation.role,
|
|
3496
|
+
participantCount: conversation.members ? Math.max(0, conversation.members.length - 1) : conversation.participantCount,
|
|
3497
|
+
lastMessageSequence: conversation.lastMessageSequence,
|
|
3498
|
+
lastReadSequence: conversation.lastReadSequence,
|
|
3499
|
+
activeActorId: conversation.activeActorId
|
|
2409
3500
|
};
|
|
2410
3501
|
}
|
|
2411
3502
|
function createClientMessageId() {
|
|
@@ -2569,10 +3660,10 @@ class NhAssistantPanelService {
|
|
|
2569
3660
|
this.overlayRef?.dispose();
|
|
2570
3661
|
this.overlayRef = undefined;
|
|
2571
3662
|
}
|
|
2572
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
2573
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.
|
|
3663
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantPanelService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
3664
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantPanelService }); }
|
|
2574
3665
|
}
|
|
2575
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
3666
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantPanelService, decorators: [{
|
|
2576
3667
|
type: Injectable
|
|
2577
3668
|
}], ctorParameters: () => [] });
|
|
2578
3669
|
|
|
@@ -2588,6 +3679,8 @@ function provideNhAssistant(config) {
|
|
|
2588
3679
|
NhAssistantTransport,
|
|
2589
3680
|
NhAssistantApiService,
|
|
2590
3681
|
NhAssistantAdminApiService,
|
|
3682
|
+
NhAssistantLiveService,
|
|
3683
|
+
NhAssistantPushService,
|
|
2591
3684
|
NhAssistantStore,
|
|
2592
3685
|
NhAssistantUiState,
|
|
2593
3686
|
NhAssistantPanelService,
|
|
@@ -2597,6 +3690,17 @@ function provideNhAssistant(config) {
|
|
|
2597
3690
|
]);
|
|
2598
3691
|
}
|
|
2599
3692
|
|
|
3693
|
+
/** Fragment the panel reads to join a shared conversation: `#nh-assistant-join=<id>.<token>`. */
|
|
3694
|
+
const NH_ASSISTANT_JOIN_FRAGMENT = 'nh-assistant-join';
|
|
3695
|
+
/** Fragment a clicked notification opens: `#nh-assistant-conversation=<id>`. */
|
|
3696
|
+
const NH_ASSISTANT_CONVERSATION_FRAGMENT = 'nh-assistant-conversation';
|
|
3697
|
+
/** Builds the default invitation link: the current page with the join fragment. */
|
|
3698
|
+
function nhAssistantShareLink(conversationId, token) {
|
|
3699
|
+
const location = globalThis.location;
|
|
3700
|
+
const page = location ? `${location.origin}${location.pathname}${location.search}` : '';
|
|
3701
|
+
return `${page}#${NH_ASSISTANT_JOIN_FRAGMENT}=${encodeURIComponent(conversationId)}.${encodeURIComponent(token)}`;
|
|
3702
|
+
}
|
|
3703
|
+
|
|
2600
3704
|
/**
|
|
2601
3705
|
* Optional icon classes of the host's icon library, for example `{ close: 'ph ph-x' }`.
|
|
2602
3706
|
* Icons without a class use the library's small built-in outline glyphs.
|
|
@@ -2621,7 +3725,11 @@ const builtInPaths = {
|
|
|
2621
3725
|
edit: 'M4 20h4L19 9l-4-4L4 16zM13.5 6.5l4 4',
|
|
2622
3726
|
refresh: 'M20 11a8 8 0 0 0-14.3-4.9L4 8M4 4v4h4M4 13a8 8 0 0 0 14.3 4.9L20 16M20 20v-4h-4',
|
|
2623
3727
|
link: 'M10 14a4 4 0 0 0 5.7 0l3-3a4 4 0 0 0-5.7-5.7l-1 1M14 10a4 4 0 0 0-5.7 0l-3 3a4 4 0 0 0 5.7 5.7l1-1',
|
|
2624
|
-
page: 'M6 3h8l4 4v14H6zM14 3v4h4M9 12h6M9 16h6'
|
|
3728
|
+
page: 'M6 3h8l4 4v14H6zM14 3v4h4M9 12h6M9 16h6',
|
|
3729
|
+
users: 'M9 11a3.5 3.5 0 1 0 0-7 3.5 3.5 0 1 0 0 7zM3 20c0-3.3 2.7-6 6-6s6 2.7 6 6M16 4.5a3.5 3.5 0 0 1 0 6.5M18 14.5c1.8.9 3 2.8 3 5',
|
|
3730
|
+
copy: 'M9 9h11v11H9zM5 15H4V4h11v1',
|
|
3731
|
+
bell: 'M6 16V11a6 6 0 0 1 12 0v5l2 2H4zM10 20a2 2 0 0 0 4 0',
|
|
3732
|
+
leave: 'M10 4H5v16h5M14 8l4 4-4 4M18 12H9'
|
|
2625
3733
|
};
|
|
2626
3734
|
/** Decorative icon. The surrounding control provides the accessible name. */
|
|
2627
3735
|
class NhAssistantIconComponent {
|
|
@@ -2631,8 +3739,8 @@ class NhAssistantIconComponent {
|
|
|
2631
3739
|
this.iconClass = computed(() => this.icons?.[this.name()] ?? null, ...(ngDevMode ? [{ debugName: "iconClass" }] : []));
|
|
2632
3740
|
this.path = computed(() => builtInPaths[this.name()], ...(ngDevMode ? [{ debugName: "path" }] : []));
|
|
2633
3741
|
}
|
|
2634
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
2635
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.
|
|
3742
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantIconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3743
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.33", type: NhAssistantIconComponent, isStandalone: true, selector: "nh-assistant-icon", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
|
|
2636
3744
|
@if (iconClass(); as iconClass) {
|
|
2637
3745
|
<i [class]="iconClass" aria-hidden="true"></i>
|
|
2638
3746
|
} @else {
|
|
@@ -2642,7 +3750,7 @@ class NhAssistantIconComponent {
|
|
|
2642
3750
|
}
|
|
2643
3751
|
`, isInline: true, styles: [":host{display:inline-flex;align-items:center;justify-content:center;line-height:1;font-size:1.15em}svg{display:block}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2644
3752
|
}
|
|
2645
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
3753
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantIconComponent, decorators: [{
|
|
2646
3754
|
type: Component,
|
|
2647
3755
|
args: [{ selector: 'nh-assistant-icon', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
2648
3756
|
@if (iconClass(); as iconClass) {
|
|
@@ -2657,8 +3765,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImpo
|
|
|
2657
3765
|
|
|
2658
3766
|
/**
|
|
2659
3767
|
* Header button that opens the assistant panel. It renders nothing while the assistant is
|
|
2660
|
-
* disabled on the server or the access policy denies the user.
|
|
2661
|
-
* that waits
|
|
3768
|
+
* disabled on the server or the access policy denies the user. One small badge shows what needs
|
|
3769
|
+
* attention, in this order: an approval that waits, the number of unread conversations, or a
|
|
3770
|
+
* turn that runs. Projected content replaces the default icon.
|
|
2662
3771
|
*/
|
|
2663
3772
|
class NhAssistantLauncherComponent {
|
|
2664
3773
|
constructor() {
|
|
@@ -2666,6 +3775,16 @@ class NhAssistantLauncherComponent {
|
|
|
2666
3775
|
this.panel = inject(NhAssistantPanelService);
|
|
2667
3776
|
this.waitingForApproval = computed(() => this.store.pendingApproval() !== null ||
|
|
2668
3777
|
this.store.conversations().some(conversation => conversation.status === 'waiting-for-approval'), ...(ngDevMode ? [{ debugName: "waitingForApproval" }] : []));
|
|
3778
|
+
this.badge = computed(() => {
|
|
3779
|
+
if (this.waitingForApproval()) {
|
|
3780
|
+
return 'waiting';
|
|
3781
|
+
}
|
|
3782
|
+
if (this.store.unreadCount() > 0) {
|
|
3783
|
+
return 'unread';
|
|
3784
|
+
}
|
|
3785
|
+
return this.store.runningCount() > 0 || this.store.streaming() ? 'running' : null;
|
|
3786
|
+
}, ...(ngDevMode ? [{ debugName: "badge" }] : []));
|
|
3787
|
+
this.unreadLabel = computed(() => this.store.unreadCount() > 9 ? '9+' : String(this.store.unreadCount()), ...(ngDevMode ? [{ debugName: "unreadLabel" }] : []));
|
|
2669
3788
|
this.label = computed(() => this.panel.isOpen() ? 'nh-assistant.launcher.close' : 'nh-assistant.launcher.open', ...(ngDevMode ? [{ debugName: "label" }] : []));
|
|
2670
3789
|
// Browser only: the status request and access policy need the signed-in user.
|
|
2671
3790
|
afterNextRender(() => {
|
|
@@ -2677,8 +3796,8 @@ class NhAssistantLauncherComponent {
|
|
|
2677
3796
|
}
|
|
2678
3797
|
});
|
|
2679
3798
|
}
|
|
2680
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
2681
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.
|
|
3799
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantLauncherComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3800
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.33", type: NhAssistantLauncherComponent, isStandalone: true, selector: "nh-assistant-launcher", ngImport: i0, template: `
|
|
2682
3801
|
@if (store.enabled()) {
|
|
2683
3802
|
<button
|
|
2684
3803
|
class="launcher"
|
|
@@ -2689,15 +3808,31 @@ class NhAssistantLauncherComponent {
|
|
|
2689
3808
|
[attr.title]="label() | translate"
|
|
2690
3809
|
(click)="panel.toggle()">
|
|
2691
3810
|
<ng-content><nh-assistant-icon name="assistant" /></ng-content>
|
|
3811
|
+
@switch (badge()) {
|
|
3812
|
+
@case ('waiting') {
|
|
3813
|
+
<span class="badge" data-state="waiting" aria-hidden="true"></span>
|
|
3814
|
+
}
|
|
3815
|
+
@case ('unread') {
|
|
3816
|
+
<span class="badge count" data-state="unread" aria-hidden="true">{{ unreadLabel() }}</span>
|
|
3817
|
+
}
|
|
3818
|
+
@case ('running') {
|
|
3819
|
+
<span class="badge" data-state="running" aria-hidden="true"></span>
|
|
3820
|
+
}
|
|
3821
|
+
}
|
|
2692
3822
|
@if (waitingForApproval()) {
|
|
2693
|
-
<span class="badge" aria-hidden="true"></span>
|
|
2694
3823
|
<span class="visually-hidden">{{ 'nh-assistant.launcher.pending-approval' | translate }}</span>
|
|
2695
3824
|
}
|
|
3825
|
+
@if (store.unreadCount() > 0) {
|
|
3826
|
+
<span class="visually-hidden">{{ 'nh-assistant.launcher.unread' | translate: { count: store.unreadCount() } }}</span>
|
|
3827
|
+
}
|
|
3828
|
+
@if (store.runningCount() > 0) {
|
|
3829
|
+
<span class="visually-hidden">{{ 'nh-assistant.launcher.running' | translate: { count: store.runningCount() } }}</span>
|
|
3830
|
+
}
|
|
2696
3831
|
</button>
|
|
2697
3832
|
}
|
|
2698
|
-
`, isInline: true, 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))}}:host{display:inline-flex}.launcher{position:relative;display:inline-flex;align-items:center;justify-content:center;width:var(--nh-assistant-launcher-size, 40px);height:var(--nh-assistant-launcher-size, 40px);border:1px solid var(--nh-assistant-launcher-border, var(--nha-border));border-radius:var(--nha-radius-control);padding:0;color:var(--nh-assistant-launcher-color, var(--nha-text));background:var(--nh-assistant-launcher-background, transparent);font-size:18px;cursor:pointer}.launcher:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.launcher:hover,.launcher.open{border-color:var(--nha-highlight);color:var(--nha-highlight)}.badge{position:absolute;top:6px;right:6px;width:9px;height:9px;border:2px solid var(--nh-assistant-launcher-badge-ring, var(--nha-background));border-radius:50%;background:var(--nha-highlight)}.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}\n"], dependencies: [{ kind: "component", type: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3833
|
+
`, isInline: true, 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))}}:host{display:inline-flex}.launcher{position:relative;display:inline-flex;align-items:center;justify-content:center;width:var(--nh-assistant-launcher-size, 40px);height:var(--nh-assistant-launcher-size, 40px);border:1px solid var(--nh-assistant-launcher-border, var(--nha-border));border-radius:var(--nha-radius-control);padding:0;color:var(--nh-assistant-launcher-color, var(--nha-text));background:var(--nh-assistant-launcher-background, transparent);font-size:18px;cursor:pointer}.launcher:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.launcher:hover,.launcher.open{border-color:var(--nha-highlight);color:var(--nha-highlight)}.badge{position:absolute;top:6px;right:6px;width:9px;height:9px;border:2px solid var(--nh-assistant-launcher-badge-ring, var(--nha-background));border-radius:50%;background:var(--nha-highlight)}.badge[data-state=waiting]{background:var(--nha-warning)}.badge.count{top:2px;right:2px;display:inline-flex;align-items:center;justify-content:center;width:auto;min-width:16px;height:16px;border-radius:999px;padding:0 3px;color:var(--nha-accent-contrast);font-size:10px;font-weight:700;line-height:1}.badge[data-state=running]{border-color:color-mix(in srgb,var(--nha-highlight) 30%,transparent);border-top-color:var(--nha-highlight);background:var(--nh-assistant-launcher-badge-ring, var(--nha-background));animation:nh-assistant-spin .9s linear infinite}@media(prefers-reduced-motion:reduce){.badge[data-state=running]{border-color:var(--nha-highlight);animation:none}}@keyframes nh-assistant-spin{to{transform:rotate(360deg)}}.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}\n"], dependencies: [{ kind: "component", type: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2699
3834
|
}
|
|
2700
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
3835
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantLauncherComponent, decorators: [{
|
|
2701
3836
|
type: Component,
|
|
2702
3837
|
args: [{ selector: 'nh-assistant-launcher', standalone: true, imports: [TranslatePipe, NhAssistantIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
2703
3838
|
@if (store.enabled()) {
|
|
@@ -2710,13 +3845,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImpo
|
|
|
2710
3845
|
[attr.title]="label() | translate"
|
|
2711
3846
|
(click)="panel.toggle()">
|
|
2712
3847
|
<ng-content><nh-assistant-icon name="assistant" /></ng-content>
|
|
3848
|
+
@switch (badge()) {
|
|
3849
|
+
@case ('waiting') {
|
|
3850
|
+
<span class="badge" data-state="waiting" aria-hidden="true"></span>
|
|
3851
|
+
}
|
|
3852
|
+
@case ('unread') {
|
|
3853
|
+
<span class="badge count" data-state="unread" aria-hidden="true">{{ unreadLabel() }}</span>
|
|
3854
|
+
}
|
|
3855
|
+
@case ('running') {
|
|
3856
|
+
<span class="badge" data-state="running" aria-hidden="true"></span>
|
|
3857
|
+
}
|
|
3858
|
+
}
|
|
2713
3859
|
@if (waitingForApproval()) {
|
|
2714
|
-
<span class="badge" aria-hidden="true"></span>
|
|
2715
3860
|
<span class="visually-hidden">{{ 'nh-assistant.launcher.pending-approval' | translate }}</span>
|
|
2716
3861
|
}
|
|
3862
|
+
@if (store.unreadCount() > 0) {
|
|
3863
|
+
<span class="visually-hidden">{{ 'nh-assistant.launcher.unread' | translate: { count: store.unreadCount() } }}</span>
|
|
3864
|
+
}
|
|
3865
|
+
@if (store.runningCount() > 0) {
|
|
3866
|
+
<span class="visually-hidden">{{ 'nh-assistant.launcher.running' | translate: { count: store.runningCount() } }}</span>
|
|
3867
|
+
}
|
|
2717
3868
|
</button>
|
|
2718
3869
|
}
|
|
2719
|
-
`, 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))}}:host{display:inline-flex}.launcher{position:relative;display:inline-flex;align-items:center;justify-content:center;width:var(--nh-assistant-launcher-size, 40px);height:var(--nh-assistant-launcher-size, 40px);border:1px solid var(--nh-assistant-launcher-border, var(--nha-border));border-radius:var(--nha-radius-control);padding:0;color:var(--nh-assistant-launcher-color, var(--nha-text));background:var(--nh-assistant-launcher-background, transparent);font-size:18px;cursor:pointer}.launcher:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.launcher:hover,.launcher.open{border-color:var(--nha-highlight);color:var(--nha-highlight)}.badge{position:absolute;top:6px;right:6px;width:9px;height:9px;border:2px solid var(--nh-assistant-launcher-badge-ring, var(--nha-background));border-radius:50%;background:var(--nha-highlight)}.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}\n"] }]
|
|
3870
|
+
`, 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))}}:host{display:inline-flex}.launcher{position:relative;display:inline-flex;align-items:center;justify-content:center;width:var(--nh-assistant-launcher-size, 40px);height:var(--nh-assistant-launcher-size, 40px);border:1px solid var(--nh-assistant-launcher-border, var(--nha-border));border-radius:var(--nha-radius-control);padding:0;color:var(--nh-assistant-launcher-color, var(--nha-text));background:var(--nh-assistant-launcher-background, transparent);font-size:18px;cursor:pointer}.launcher:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.launcher:hover,.launcher.open{border-color:var(--nha-highlight);color:var(--nha-highlight)}.badge{position:absolute;top:6px;right:6px;width:9px;height:9px;border:2px solid var(--nh-assistant-launcher-badge-ring, var(--nha-background));border-radius:50%;background:var(--nha-highlight)}.badge[data-state=waiting]{background:var(--nha-warning)}.badge.count{top:2px;right:2px;display:inline-flex;align-items:center;justify-content:center;width:auto;min-width:16px;height:16px;border-radius:999px;padding:0 3px;color:var(--nha-accent-contrast);font-size:10px;font-weight:700;line-height:1}.badge[data-state=running]{border-color:color-mix(in srgb,var(--nha-highlight) 30%,transparent);border-top-color:var(--nha-highlight);background:var(--nh-assistant-launcher-badge-ring, var(--nha-background));animation:nh-assistant-spin .9s linear infinite}@media(prefers-reduced-motion:reduce){.badge[data-state=running]{border-color:var(--nha-highlight);animation:none}}@keyframes nh-assistant-spin{to{transform:rotate(360deg)}}.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}\n"] }]
|
|
2720
3871
|
}], ctorParameters: () => [] });
|
|
2721
3872
|
|
|
2722
3873
|
/**
|
|
@@ -2760,15 +3911,81 @@ class NhAssistantTranslatePipe {
|
|
|
2760
3911
|
}
|
|
2761
3912
|
return fallback;
|
|
2762
3913
|
}
|
|
2763
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
2764
|
-
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.
|
|
3914
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantTranslatePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
3915
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantTranslatePipe, isStandalone: true, name: "nhAssistantTranslate", pure: false }); }
|
|
2765
3916
|
}
|
|
2766
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
3917
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantTranslatePipe, decorators: [{
|
|
2767
3918
|
type: Pipe,
|
|
2768
3919
|
args: [{ name: 'nhAssistantTranslate', standalone: true, pure: false }]
|
|
2769
3920
|
}], ctorParameters: () => [] });
|
|
2770
3921
|
|
|
2771
|
-
|
|
3922
|
+
/**
|
|
3923
|
+
* One slim row of the other conversations that need attention: a running turn, an approval that
|
|
3924
|
+
* waits, or an unread answer. It renders nothing when there are none, so it costs no space in the
|
|
3925
|
+
* usual case. Selecting a chip opens that conversation; its turn keeps running meanwhile.
|
|
3926
|
+
*/
|
|
3927
|
+
class NhAssistantActivityComponent {
|
|
3928
|
+
constructor() {
|
|
3929
|
+
this.items = input.required(...(ngDevMode ? [{ debugName: "items" }] : []));
|
|
3930
|
+
this.conversationSelect = output();
|
|
3931
|
+
/** The open conversation already shows its own state. */
|
|
3932
|
+
this.others = computed(() => this.items().filter(item => !item.active), ...(ngDevMode ? [{ debugName: "others" }] : []));
|
|
3933
|
+
}
|
|
3934
|
+
state(item) {
|
|
3935
|
+
if (item.status === 'running') {
|
|
3936
|
+
return 'running';
|
|
3937
|
+
}
|
|
3938
|
+
return item.status === 'waiting-for-approval' ? 'waiting' : 'unread';
|
|
3939
|
+
}
|
|
3940
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantActivityComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3941
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.33", type: NhAssistantActivityComponent, isStandalone: true, selector: "nh-assistant-activity", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { conversationSelect: "conversationSelect" }, ngImport: i0, template: `
|
|
3942
|
+
@if (others().length > 0) {
|
|
3943
|
+
<nav class="strip" [attr.aria-label]="'nh-assistant.activity.label' | translate">
|
|
3944
|
+
@for (item of others(); track item.id) {
|
|
3945
|
+
<button
|
|
3946
|
+
class="chip"
|
|
3947
|
+
type="button"
|
|
3948
|
+
[attr.data-state]="state(item)"
|
|
3949
|
+
[attr.title]="(item.title || ('nh-assistant.conversation-list.untitled' | translate)) + ' — ' + ('nh-assistant.activity.' + state(item) | translate)"
|
|
3950
|
+
(click)="conversationSelect.emit(item.id)">
|
|
3951
|
+
<span class="indicator" aria-hidden="true"></span>
|
|
3952
|
+
<span class="title">{{ item.title || ('nh-assistant.conversation-list.untitled' | translate) }}</span>
|
|
3953
|
+
@if (item.shared) {
|
|
3954
|
+
<nh-assistant-icon class="shared" name="users" />
|
|
3955
|
+
}
|
|
3956
|
+
<span class="visually-hidden">{{ 'nh-assistant.activity.' + state(item) | translate }}</span>
|
|
3957
|
+
</button>
|
|
3958
|
+
}
|
|
3959
|
+
</nav>
|
|
3960
|
+
}
|
|
3961
|
+
`, isInline: true, 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))}}:host{display:block;min-width:0}.strip{display:flex;gap:6px;overflow-x:auto;padding:0 12px 8px 16px;scrollbar-width:none}.strip::-webkit-scrollbar{display:none}.chip{display:inline-flex;flex:none;align-items:center;gap:6px;max-width:180px;height:26px;border:1px solid var(--nha-border);border-radius:999px;padding:0 10px 0 8px;color:var(--nha-text);background:var(--nha-surface);font:inherit;font-size:12px;cursor:pointer}.chip:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.chip:hover{border-color:var(--nha-highlight)}.title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.shared{flex:none;color:var(--nha-text-muted);font-size:12px}.indicator{flex:none;width:8px;height:8px;border-radius:50%;background:var(--nha-accent)}.chip[data-state=unread] .indicator{background:var(--nha-highlight)}.chip[data-state=waiting] .indicator{background:var(--nha-warning)}.chip[data-state=running] .indicator{width:10px;height:10px;border:2px solid color-mix(in srgb,var(--nha-highlight) 30%,transparent);border-top-color:var(--nha-highlight);background:transparent;animation:nh-assistant-spin .9s linear infinite}@media(prefers-reduced-motion:reduce){.chip[data-state=running] .indicator{border-color:var(--nha-highlight);animation:none}}@keyframes nh-assistant-spin{to{transform:rotate(360deg)}}.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}\n"], dependencies: [{ kind: "component", type: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3962
|
+
}
|
|
3963
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantActivityComponent, decorators: [{
|
|
3964
|
+
type: Component,
|
|
3965
|
+
args: [{ selector: 'nh-assistant-activity', standalone: true, imports: [TranslatePipe, NhAssistantIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
3966
|
+
@if (others().length > 0) {
|
|
3967
|
+
<nav class="strip" [attr.aria-label]="'nh-assistant.activity.label' | translate">
|
|
3968
|
+
@for (item of others(); track item.id) {
|
|
3969
|
+
<button
|
|
3970
|
+
class="chip"
|
|
3971
|
+
type="button"
|
|
3972
|
+
[attr.data-state]="state(item)"
|
|
3973
|
+
[attr.title]="(item.title || ('nh-assistant.conversation-list.untitled' | translate)) + ' — ' + ('nh-assistant.activity.' + state(item) | translate)"
|
|
3974
|
+
(click)="conversationSelect.emit(item.id)">
|
|
3975
|
+
<span class="indicator" aria-hidden="true"></span>
|
|
3976
|
+
<span class="title">{{ item.title || ('nh-assistant.conversation-list.untitled' | translate) }}</span>
|
|
3977
|
+
@if (item.shared) {
|
|
3978
|
+
<nh-assistant-icon class="shared" name="users" />
|
|
3979
|
+
}
|
|
3980
|
+
<span class="visually-hidden">{{ 'nh-assistant.activity.' + state(item) | translate }}</span>
|
|
3981
|
+
</button>
|
|
3982
|
+
}
|
|
3983
|
+
</nav>
|
|
3984
|
+
}
|
|
3985
|
+
`, 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))}}:host{display:block;min-width:0}.strip{display:flex;gap:6px;overflow-x:auto;padding:0 12px 8px 16px;scrollbar-width:none}.strip::-webkit-scrollbar{display:none}.chip{display:inline-flex;flex:none;align-items:center;gap:6px;max-width:180px;height:26px;border:1px solid var(--nha-border);border-radius:999px;padding:0 10px 0 8px;color:var(--nha-text);background:var(--nha-surface);font:inherit;font-size:12px;cursor:pointer}.chip:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.chip:hover{border-color:var(--nha-highlight)}.title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.shared{flex:none;color:var(--nha-text-muted);font-size:12px}.indicator{flex:none;width:8px;height:8px;border-radius:50%;background:var(--nha-accent)}.chip[data-state=unread] .indicator{background:var(--nha-highlight)}.chip[data-state=waiting] .indicator{background:var(--nha-warning)}.chip[data-state=running] .indicator{width:10px;height:10px;border:2px solid color-mix(in srgb,var(--nha-highlight) 30%,transparent);border-top-color:var(--nha-highlight);background:transparent;animation:nh-assistant-spin .9s linear infinite}@media(prefers-reduced-motion:reduce){.chip[data-state=running] .indicator{border-color:var(--nha-highlight);animation:none}}@keyframes nh-assistant-spin{to{transform:rotate(360deg)}}.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}\n"] }]
|
|
3986
|
+
}], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }], conversationSelect: [{ type: i0.Output, args: ["conversationSelect"] }] } });
|
|
3987
|
+
|
|
3988
|
+
let nextId$6 = 0;
|
|
2772
3989
|
/**
|
|
2773
3990
|
* Chooses the agent for new conversations. Agent names come from the host's translation
|
|
2774
3991
|
* keys; agents created by an administrator carry literal text, which is shown as is.
|
|
@@ -2778,20 +3995,23 @@ class NhAssistantAgentPickerComponent {
|
|
|
2778
3995
|
this.agents = input.required(...(ngDevMode ? [{ debugName: "agents" }] : []));
|
|
2779
3996
|
this.selectedAgentId = input(null, ...(ngDevMode ? [{ debugName: "selectedAgentId" }] : []));
|
|
2780
3997
|
this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
3998
|
+
/** Compact prompt-bar control; capability information remains in the tooltip and accessible description. */
|
|
3999
|
+
this.compact = input(false, ...(ngDevMode ? [{ debugName: "compact" }] : []));
|
|
2781
4000
|
this.agentChange = output();
|
|
2782
|
-
this.selectId = `nh-assistant-agent-picker-${nextId$
|
|
4001
|
+
this.selectId = `nh-assistant-agent-picker-${nextId$6++}`;
|
|
2783
4002
|
this.selected = computed(() => this.agents().find(agent => agent.id === this.selectedAgentId()) ?? null, ...(ngDevMode ? [{ debugName: "selected" }] : []));
|
|
2784
4003
|
}
|
|
2785
4004
|
onChange(event) {
|
|
2786
4005
|
this.agentChange.emit(event.target.value);
|
|
2787
4006
|
}
|
|
2788
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
2789
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.
|
|
4007
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantAgentPickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4008
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.33", type: NhAssistantAgentPickerComponent, isStandalone: true, selector: "nh-assistant-agent-picker", inputs: { agents: { classPropertyName: "agents", publicName: "agents", isSignal: true, isRequired: true, transformFunction: null }, selectedAgentId: { classPropertyName: "selectedAgentId", publicName: "selectedAgentId", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, compact: { classPropertyName: "compact", publicName: "compact", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { agentChange: "agentChange" }, host: { properties: { "class.compact": "compact()" } }, ngImport: i0, template: `
|
|
2790
4009
|
<label class="visually-hidden" [for]="selectId">{{ 'nh-assistant.agent-picker.label' | translate }}</label>
|
|
2791
4010
|
<select
|
|
2792
4011
|
[id]="selectId"
|
|
2793
4012
|
[disabled]="disabled() || agents().length < 2"
|
|
2794
|
-
[attr.
|
|
4013
|
+
[attr.aria-describedby]="selected() ? selectId + '-capability' : null"
|
|
4014
|
+
[attr.title]="selected() ? (([selected()!.descriptionKey] | nhAssistantTranslate: selected()!.descriptionKey) + (compact() ? ' · ' + ((selected()!.canMutate ? 'nh-assistant.agent-picker.can-mutate' : 'nh-assistant.agent-picker.read-only') | translate) : '')) : null"
|
|
2795
4015
|
(change)="onChange($event)">
|
|
2796
4016
|
@for (agent of agents(); track agent.id) {
|
|
2797
4017
|
<option [value]="agent.id" [selected]="agent.id === selectedAgentId()">
|
|
@@ -2800,20 +4020,21 @@ class NhAssistantAgentPickerComponent {
|
|
|
2800
4020
|
}
|
|
2801
4021
|
</select>
|
|
2802
4022
|
@if (selected(); as agent) {
|
|
2803
|
-
<span class="capability" [attr.data-can-mutate]="agent.canMutate">
|
|
4023
|
+
<span class="capability" [id]="selectId + '-capability'" [class.visually-hidden]="compact()" [attr.data-can-mutate]="agent.canMutate">
|
|
2804
4024
|
{{ (agent.canMutate ? 'nh-assistant.agent-picker.can-mutate' : 'nh-assistant.agent-picker.read-only') | translate }}
|
|
2805
4025
|
</span>
|
|
2806
4026
|
}
|
|
2807
|
-
`, isInline: true, 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))}}:host{display:flex;flex-direction:column;gap:2px;min-width:0}select{align-self:flex-start;max-width:100%;min-height:32px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:4px 28px 4px 10px;color:var(--nha-text);background:var(--nha-background);font:inherit;font-size:13px;font-weight:650}select:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}select:disabled{opacity:1;cursor:default}.capability{padding-left:2px;color:var(--nha-text-muted);font-size:11px}.capability[data-can-mutate=true]{color:var(--nha-warning)}.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}\n"], dependencies: [{ kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: NhAssistantTranslatePipe, name: "nhAssistantTranslate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
4027
|
+
`, isInline: true, 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))}}:host{display:flex;flex-direction:column;gap:2px;min-width:0}:host(.compact){flex-direction:row;align-items:center}:host(.compact) select{min-width:0;max-width:100%;border-color:transparent;padding-left:8px;background:transparent;cursor:pointer}:host(.compact) select:hover:not(:disabled){border-color:var(--nha-border);background:var(--nha-surface-strong)}:host(.compact) select:disabled{cursor:default}select{align-self:flex-start;max-width:100%;min-height:32px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:4px 28px 4px 10px;color:var(--nha-text);background:var(--nha-background);font:inherit;font-size:13px;font-weight:650}select:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}select:disabled{opacity:1;cursor:default}.capability{padding-left:2px;color:var(--nha-text-muted);font-size:11px}.capability[data-can-mutate=true]{color:var(--nha-warning)}.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}\n"], dependencies: [{ kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: NhAssistantTranslatePipe, name: "nhAssistantTranslate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2808
4028
|
}
|
|
2809
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
4029
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantAgentPickerComponent, decorators: [{
|
|
2810
4030
|
type: Component,
|
|
2811
|
-
args: [{ selector: 'nh-assistant-agent-picker', standalone: true, imports: [TranslatePipe, NhAssistantTranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
4031
|
+
args: [{ selector: 'nh-assistant-agent-picker', standalone: true, imports: [TranslatePipe, NhAssistantTranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, host: { '[class.compact]': 'compact()' }, template: `
|
|
2812
4032
|
<label class="visually-hidden" [for]="selectId">{{ 'nh-assistant.agent-picker.label' | translate }}</label>
|
|
2813
4033
|
<select
|
|
2814
4034
|
[id]="selectId"
|
|
2815
4035
|
[disabled]="disabled() || agents().length < 2"
|
|
2816
|
-
[attr.
|
|
4036
|
+
[attr.aria-describedby]="selected() ? selectId + '-capability' : null"
|
|
4037
|
+
[attr.title]="selected() ? (([selected()!.descriptionKey] | nhAssistantTranslate: selected()!.descriptionKey) + (compact() ? ' · ' + ((selected()!.canMutate ? 'nh-assistant.agent-picker.can-mutate' : 'nh-assistant.agent-picker.read-only') | translate) : '')) : null"
|
|
2817
4038
|
(change)="onChange($event)">
|
|
2818
4039
|
@for (agent of agents(); track agent.id) {
|
|
2819
4040
|
<option [value]="agent.id" [selected]="agent.id === selectedAgentId()">
|
|
@@ -2822,14 +4043,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImpo
|
|
|
2822
4043
|
}
|
|
2823
4044
|
</select>
|
|
2824
4045
|
@if (selected(); as agent) {
|
|
2825
|
-
<span class="capability" [attr.data-can-mutate]="agent.canMutate">
|
|
4046
|
+
<span class="capability" [id]="selectId + '-capability'" [class.visually-hidden]="compact()" [attr.data-can-mutate]="agent.canMutate">
|
|
2826
4047
|
{{ (agent.canMutate ? 'nh-assistant.agent-picker.can-mutate' : 'nh-assistant.agent-picker.read-only') | translate }}
|
|
2827
4048
|
</span>
|
|
2828
4049
|
}
|
|
2829
|
-
`, 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))}}:host{display:flex;flex-direction:column;gap:2px;min-width:0}select{align-self:flex-start;max-width:100%;min-height:32px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:4px 28px 4px 10px;color:var(--nha-text);background:var(--nha-background);font:inherit;font-size:13px;font-weight:650}select:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}select:disabled{opacity:1;cursor:default}.capability{padding-left:2px;color:var(--nha-text-muted);font-size:11px}.capability[data-can-mutate=true]{color:var(--nha-warning)}.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}\n"] }]
|
|
2830
|
-
}], propDecorators: { agents: [{ type: i0.Input, args: [{ isSignal: true, alias: "agents", required: true }] }], selectedAgentId: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedAgentId", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], agentChange: [{ type: i0.Output, args: ["agentChange"] }] } });
|
|
4050
|
+
`, 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))}}:host{display:flex;flex-direction:column;gap:2px;min-width:0}:host(.compact){flex-direction:row;align-items:center}:host(.compact) select{min-width:0;max-width:100%;border-color:transparent;padding-left:8px;background:transparent;cursor:pointer}:host(.compact) select:hover:not(:disabled){border-color:var(--nha-border);background:var(--nha-surface-strong)}:host(.compact) select:disabled{cursor:default}select{align-self:flex-start;max-width:100%;min-height:32px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:4px 28px 4px 10px;color:var(--nha-text);background:var(--nha-background);font:inherit;font-size:13px;font-weight:650}select:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}select:disabled{opacity:1;cursor:default}.capability{padding-left:2px;color:var(--nha-text-muted);font-size:11px}.capability[data-can-mutate=true]{color:var(--nha-warning)}.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}\n"] }]
|
|
4051
|
+
}], propDecorators: { agents: [{ type: i0.Input, args: [{ isSignal: true, alias: "agents", required: true }] }], selectedAgentId: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedAgentId", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], compact: [{ type: i0.Input, args: [{ isSignal: true, alias: "compact", required: false }] }], agentChange: [{ type: i0.Output, args: ["agentChange"] }] } });
|
|
2831
4052
|
|
|
2832
|
-
let nextId$
|
|
4053
|
+
let nextId$5 = 0;
|
|
2833
4054
|
/**
|
|
2834
4055
|
* Message input. Enter sends when sending is available; Shift+Enter inserts a new line.
|
|
2835
4056
|
* Drafting remains available while the assistant runs or waits for approval.
|
|
@@ -2844,6 +4065,10 @@ class NhAssistantComposerComponent {
|
|
|
2844
4065
|
this.busy = input(false, ...(ngDevMode ? [{ debugName: "busy" }] : []));
|
|
2845
4066
|
/** Announces why sending is temporarily unavailable. */
|
|
2846
4067
|
this.status = input(null, ...(ngDevMode ? [{ debugName: "status" }] : []));
|
|
4068
|
+
/** Name of the participant whose turn runs or waits, when it is someone else. */
|
|
4069
|
+
this.statusActor = input(null, ...(ngDevMode ? [{ debugName: "statusActor" }] : []));
|
|
4070
|
+
/** False when the user may not stop the running turn, such as another participant's turn. */
|
|
4071
|
+
this.canStop = input(true, ...(ngDevMode ? [{ debugName: "canStop" }] : []));
|
|
2847
4072
|
this.maxLength = input(null, ...(ngDevMode ? [{ debugName: "maxLength" }] : []));
|
|
2848
4073
|
/** Text to put back into the input, for example a message the server did not accept. */
|
|
2849
4074
|
this.restore = input(null, ...(ngDevMode ? [{ debugName: "restore" }] : []));
|
|
@@ -2854,7 +4079,7 @@ class NhAssistantComposerComponent {
|
|
|
2854
4079
|
this.textarea = viewChild.required('textarea');
|
|
2855
4080
|
this.ownedFocus = false;
|
|
2856
4081
|
this.previousBusy = false;
|
|
2857
|
-
this.inputId = `nh-assistant-composer-${nextId$
|
|
4082
|
+
this.inputId = `nh-assistant-composer-${nextId$5++}`;
|
|
2858
4083
|
this.hintId = `${this.inputId}-hint`;
|
|
2859
4084
|
this.text = signal('', ...(ngDevMode ? [{ debugName: "text" }] : []));
|
|
2860
4085
|
this.length = computed(() => this.text().trim().length, ...(ngDevMode ? [{ debugName: "length" }] : []));
|
|
@@ -2925,13 +4150,13 @@ class NhAssistantComposerComponent {
|
|
|
2925
4150
|
this.textarea().nativeElement.focus({ preventScroll: true });
|
|
2926
4151
|
}
|
|
2927
4152
|
}
|
|
2928
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
2929
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.
|
|
4153
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantComposerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4154
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.33", type: NhAssistantComposerComponent, isStandalone: true, selector: "nh-assistant-composer", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, sendDisabled: { classPropertyName: "sendDisabled", publicName: "sendDisabled", isSignal: true, isRequired: false, transformFunction: null }, busy: { classPropertyName: "busy", publicName: "busy", isSignal: true, isRequired: false, transformFunction: null }, status: { classPropertyName: "status", publicName: "status", isSignal: true, isRequired: false, transformFunction: null }, statusActor: { classPropertyName: "statusActor", publicName: "statusActor", isSignal: true, isRequired: false, transformFunction: null }, canStop: { classPropertyName: "canStop", publicName: "canStop", isSignal: true, isRequired: false, transformFunction: null }, maxLength: { classPropertyName: "maxLength", publicName: "maxLength", isSignal: true, isRequired: false, transformFunction: null }, restore: { classPropertyName: "restore", publicName: "restore", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { send: "send", cancel: "cancel", restored: "restored" }, host: { listeners: { "focusin": "onFocusIn()", "focusout": "onFocusOut($event)" } }, viewQueries: [{ propertyName: "textarea", first: true, predicate: ["textarea"], descendants: true, isSignal: true }], ngImport: i0, template: "<form class=\"composer\" (submit)=\"onSubmit($event)\">\n <label class=\"visually-hidden\" [for]=\"inputId\">{{ 'nh-assistant.composer.label' | translate }}</label>\n <textarea\n #textarea\n data-nh-assistant-autofocus\n rows=\"2\"\n [id]=\"inputId\"\n [value]=\"text()\"\n [disabled]=\"disabled()\"\n [attr.placeholder]=\"'nh-assistant.composer.placeholder' | translate\"\n [attr.aria-describedby]=\"hintId\"\n [attr.aria-invalid]=\"tooLong() ? true : null\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"></textarea>\n\n <div class=\"prompt-bar\">\n <div class=\"prompt-controls\">\n <ng-content select=\"[nhAssistantComposerControls]\" />\n </div>\n <div class=\"actions\">\n @if (busy() && canStop()) {\n <button\n class=\"stop\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.composer.stop' | translate\"\n [attr.title]=\"'nh-assistant.composer.stop' | translate\"\n (click)=\"cancel.emit()\">\n <nh-assistant-icon name=\"stop\" />\n </button>\n } @else {\n <button\n class=\"send\"\n type=\"submit\"\n [disabled]=\"!canSend()\"\n [attr.aria-label]=\"'nh-assistant.composer.send' | translate\"\n [attr.title]=\"'nh-assistant.composer.send' | translate\">\n <nh-assistant-icon name=\"send\" />\n </button>\n }\n </div>\n </div>\n</form>\n\n@if (status(); as status) {\n <p class=\"composer-status\" role=\"status\" aria-live=\"polite\">\n @if (statusActor(); as name) {\n {{ 'nh-assistant.composer.status.' + status + '-by' | translate: { name } }}\n } @else {\n {{ 'nh-assistant.composer.status.' + status | translate }}\n }\n </p>\n}\n\n<p class=\"hint\" [id]=\"hintId\">\n @if (tooLong()) {\n <span class=\"error\">{{ 'nh-assistant.composer.too-long' | translate }}</span>\n } @else {\n <span>{{ 'nh-assistant.composer.hint' | translate }}</span>\n }\n @if (showCounter()) {\n <span class=\"counter\" [class.error]=\"tooLong()\">\n {{ 'nh-assistant.composer.characters' | translate: { count: length(), max: maxLength() } }}\n </span>\n }\n</p>\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))}}:host{display:block}.composer{display:flex;flex-direction:column;gap:8px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:8px 8px 8px 14px;background:var(--nha-surface);box-shadow:0 2px 6px color-mix(in srgb,var(--nha-text) 4%,transparent)}.composer:focus-within{border-color:var(--nha-focus);box-shadow:0 0 0 2px color-mix(in srgb,var(--nha-focus) 16%,transparent)}textarea{width:100%;min-width:0;min-height:44px;max-height:180px;border:0;padding:6px 0;color:var(--nha-text);background:transparent;font:inherit;font-size:14px;line-height:1.45;resize:vertical;outline:none}textarea::placeholder{color:var(--nha-text-muted);opacity:1}textarea:disabled{cursor:not-allowed}.prompt-bar{display:flex;align-items:center;gap:8px;min-width:0}.prompt-controls{flex:1;min-width:0}.actions{display:flex;flex-shrink:0}.send,.stop{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;width:40px;height:40px}.send:focus-visible,.stop:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.send:hover:not(:disabled),.stop:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}.send:disabled,.stop:disabled{cursor:not-allowed;opacity:.5}.send:not(:disabled){color:var(--nha-accent-contrast);background:var(--nha-accent)}.send:not(:disabled):hover{color:var(--nha-accent-contrast);background:var(--nha-accent);filter:brightness(1.12)}.stop{color:var(--nha-danger);border-color:var(--nha-danger)}.hint,.composer-status{display:flex;justify-content:space-between;gap:8px;margin:6px 2px 0;color:var(--nha-text-muted);font-size:11px}.composer-status{margin-bottom:0}.counter{flex:none;font-variant-numeric:tabular-nums}.error{color:var(--nha-danger)}.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}\n"], dependencies: [{ kind: "component", type: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2930
4155
|
}
|
|
2931
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
4156
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantComposerComponent, decorators: [{
|
|
2932
4157
|
type: Component,
|
|
2933
|
-
args: [{ selector: 'nh-assistant-composer', standalone: true, imports: [TranslatePipe, NhAssistantIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<form class=\"composer\" (submit)=\"onSubmit($event)\">\n <label class=\"visually-hidden\" [for]=\"inputId\">{{ 'nh-assistant.composer.label' | translate }}</label>\n <textarea\n #textarea\n data-nh-assistant-autofocus\n rows=\"2\"\n [id]=\"inputId\"\n [value]=\"text()\"\n [disabled]=\"disabled()\"\n [attr.placeholder]=\"'nh-assistant.composer.placeholder' | translate\"\n [attr.aria-describedby]=\"hintId\"\n [attr.aria-invalid]=\"tooLong() ? true : null\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"></textarea>\n\n <div class=\"actions\">\n
|
|
2934
|
-
}], ctorParameters: () => [], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], sendDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "sendDisabled", required: false }] }], busy: [{ type: i0.Input, args: [{ isSignal: true, alias: "busy", required: false }] }], status: [{ type: i0.Input, args: [{ isSignal: true, alias: "status", required: false }] }], maxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLength", required: false }] }], restore: [{ type: i0.Input, args: [{ isSignal: true, alias: "restore", required: false }] }], send: [{ type: i0.Output, args: ["send"] }], cancel: [{ type: i0.Output, args: ["cancel"] }], restored: [{ type: i0.Output, args: ["restored"] }], textarea: [{ type: i0.ViewChild, args: ['textarea', { isSignal: true }] }], onFocusIn: [{
|
|
4158
|
+
args: [{ selector: 'nh-assistant-composer', standalone: true, imports: [TranslatePipe, NhAssistantIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<form class=\"composer\" (submit)=\"onSubmit($event)\">\n <label class=\"visually-hidden\" [for]=\"inputId\">{{ 'nh-assistant.composer.label' | translate }}</label>\n <textarea\n #textarea\n data-nh-assistant-autofocus\n rows=\"2\"\n [id]=\"inputId\"\n [value]=\"text()\"\n [disabled]=\"disabled()\"\n [attr.placeholder]=\"'nh-assistant.composer.placeholder' | translate\"\n [attr.aria-describedby]=\"hintId\"\n [attr.aria-invalid]=\"tooLong() ? true : null\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"></textarea>\n\n <div class=\"prompt-bar\">\n <div class=\"prompt-controls\">\n <ng-content select=\"[nhAssistantComposerControls]\" />\n </div>\n <div class=\"actions\">\n @if (busy() && canStop()) {\n <button\n class=\"stop\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.composer.stop' | translate\"\n [attr.title]=\"'nh-assistant.composer.stop' | translate\"\n (click)=\"cancel.emit()\">\n <nh-assistant-icon name=\"stop\" />\n </button>\n } @else {\n <button\n class=\"send\"\n type=\"submit\"\n [disabled]=\"!canSend()\"\n [attr.aria-label]=\"'nh-assistant.composer.send' | translate\"\n [attr.title]=\"'nh-assistant.composer.send' | translate\">\n <nh-assistant-icon name=\"send\" />\n </button>\n }\n </div>\n </div>\n</form>\n\n@if (status(); as status) {\n <p class=\"composer-status\" role=\"status\" aria-live=\"polite\">\n @if (statusActor(); as name) {\n {{ 'nh-assistant.composer.status.' + status + '-by' | translate: { name } }}\n } @else {\n {{ 'nh-assistant.composer.status.' + status | translate }}\n }\n </p>\n}\n\n<p class=\"hint\" [id]=\"hintId\">\n @if (tooLong()) {\n <span class=\"error\">{{ 'nh-assistant.composer.too-long' | translate }}</span>\n } @else {\n <span>{{ 'nh-assistant.composer.hint' | translate }}</span>\n }\n @if (showCounter()) {\n <span class=\"counter\" [class.error]=\"tooLong()\">\n {{ 'nh-assistant.composer.characters' | translate: { count: length(), max: maxLength() } }}\n </span>\n }\n</p>\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))}}:host{display:block}.composer{display:flex;flex-direction:column;gap:8px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:8px 8px 8px 14px;background:var(--nha-surface);box-shadow:0 2px 6px color-mix(in srgb,var(--nha-text) 4%,transparent)}.composer:focus-within{border-color:var(--nha-focus);box-shadow:0 0 0 2px color-mix(in srgb,var(--nha-focus) 16%,transparent)}textarea{width:100%;min-width:0;min-height:44px;max-height:180px;border:0;padding:6px 0;color:var(--nha-text);background:transparent;font:inherit;font-size:14px;line-height:1.45;resize:vertical;outline:none}textarea::placeholder{color:var(--nha-text-muted);opacity:1}textarea:disabled{cursor:not-allowed}.prompt-bar{display:flex;align-items:center;gap:8px;min-width:0}.prompt-controls{flex:1;min-width:0}.actions{display:flex;flex-shrink:0}.send,.stop{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;width:40px;height:40px}.send:focus-visible,.stop:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.send:hover:not(:disabled),.stop:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}.send:disabled,.stop:disabled{cursor:not-allowed;opacity:.5}.send:not(:disabled){color:var(--nha-accent-contrast);background:var(--nha-accent)}.send:not(:disabled):hover{color:var(--nha-accent-contrast);background:var(--nha-accent);filter:brightness(1.12)}.stop{color:var(--nha-danger);border-color:var(--nha-danger)}.hint,.composer-status{display:flex;justify-content:space-between;gap:8px;margin:6px 2px 0;color:var(--nha-text-muted);font-size:11px}.composer-status{margin-bottom:0}.counter{flex:none;font-variant-numeric:tabular-nums}.error{color:var(--nha-danger)}.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}\n"] }]
|
|
4159
|
+
}], ctorParameters: () => [], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], sendDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "sendDisabled", required: false }] }], busy: [{ type: i0.Input, args: [{ isSignal: true, alias: "busy", required: false }] }], status: [{ type: i0.Input, args: [{ isSignal: true, alias: "status", required: false }] }], statusActor: [{ type: i0.Input, args: [{ isSignal: true, alias: "statusActor", required: false }] }], canStop: [{ type: i0.Input, args: [{ isSignal: true, alias: "canStop", required: false }] }], maxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLength", required: false }] }], restore: [{ type: i0.Input, args: [{ isSignal: true, alias: "restore", required: false }] }], send: [{ type: i0.Output, args: ["send"] }], cancel: [{ type: i0.Output, args: ["cancel"] }], restored: [{ type: i0.Output, args: ["restored"] }], textarea: [{ type: i0.ViewChild, args: ['textarea', { isSignal: true }] }], onFocusIn: [{
|
|
2935
4160
|
type: HostListener,
|
|
2936
4161
|
args: ['focusin']
|
|
2937
4162
|
}], onFocusOut: [{
|
|
@@ -2939,7 +4164,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImpo
|
|
|
2939
4164
|
args: ['focusout', ['$event']]
|
|
2940
4165
|
}] } });
|
|
2941
4166
|
|
|
2942
|
-
/**
|
|
4167
|
+
/**
|
|
4168
|
+
* The user's own and shared conversations with loading and empty states; selecting one opens it.
|
|
4169
|
+
* Each row marks a running turn, a waiting approval and unread messages. A participant leaves a
|
|
4170
|
+
* shared conversation instead of deleting it.
|
|
4171
|
+
*/
|
|
2943
4172
|
class NhAssistantConversationListComponent {
|
|
2944
4173
|
constructor() {
|
|
2945
4174
|
this.conversations = input.required(...(ngDevMode ? [{ debugName: "conversations" }] : []));
|
|
@@ -2948,33 +4177,48 @@ class NhAssistantConversationListComponent {
|
|
|
2948
4177
|
this.loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
|
|
2949
4178
|
this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
2950
4179
|
this.conversationSelect = output();
|
|
4180
|
+
/** The owner deletes the conversation; a participant leaves it. */
|
|
2951
4181
|
this.conversationDelete = output();
|
|
2952
4182
|
this.conversationCreate = output();
|
|
2953
4183
|
}
|
|
2954
|
-
|
|
2955
|
-
|
|
4184
|
+
unread(conversation) {
|
|
4185
|
+
return nhAssistantIsUnread(conversation);
|
|
4186
|
+
}
|
|
4187
|
+
indicator(conversation) {
|
|
4188
|
+
if (conversation.status === 'running') {
|
|
4189
|
+
return 'running';
|
|
4190
|
+
}
|
|
4191
|
+
if (conversation.status === 'waiting-for-approval') {
|
|
4192
|
+
return 'waiting';
|
|
4193
|
+
}
|
|
4194
|
+
return this.unread(conversation) ? 'unread' : null;
|
|
4195
|
+
}
|
|
4196
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantConversationListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4197
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.33", type: NhAssistantConversationListComponent, isStandalone: true, selector: "nh-assistant-conversation-list", inputs: { conversations: { classPropertyName: "conversations", publicName: "conversations", isSignal: true, isRequired: true, transformFunction: null }, total: { classPropertyName: "total", publicName: "total", isSignal: true, isRequired: false, transformFunction: null }, activeConversationId: { classPropertyName: "activeConversationId", publicName: "activeConversationId", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { conversationSelect: "conversationSelect", conversationDelete: "conversationDelete", conversationCreate: "conversationCreate" }, ngImport: i0, template: "<nav class=\"list\" [attr.aria-label]=\"'nh-assistant.conversation-list.title' | translate\" [attr.aria-busy]=\"loading()\">\n <header class=\"header\">\n <h3>{{ 'nh-assistant.conversation-list.title' | translate }}</h3>\n @if (conversations().length > 0 && total() > conversations().length) {\n <span class=\"count\">{{ 'nh-assistant.conversation-list.count' | translate: { count: conversations().length, total: total() } }}</span>\n }\n <button\n class=\"icon-button\"\n type=\"button\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'nh-assistant.panel.new-conversation' | translate\"\n [attr.title]=\"'nh-assistant.panel.new-conversation' | translate\"\n (click)=\"conversationCreate.emit()\">\n <nh-assistant-icon name=\"plus\" />\n </button>\n </header>\n\n @if (loading() && conversations().length === 0) {\n <p class=\"state\" role=\"status\">{{ 'nh-assistant.conversation-list.loading' | translate }}</p>\n } @else if (conversations().length === 0) {\n <p class=\"state\">{{ 'nh-assistant.conversation-list.empty' | translate }}</p>\n } @else {\n <ul>\n @for (conversation of conversations(); track conversation.id) {\n <li [class.active]=\"conversation.id === activeConversationId()\" [class.unread]=\"unread(conversation)\">\n <button\n class=\"item\"\n type=\"button\"\n [disabled]=\"disabled()\"\n [attr.aria-current]=\"conversation.id === activeConversationId() ? 'true' : null\"\n (click)=\"conversationSelect.emit(conversation.id)\">\n <span class=\"title-row\">\n <span class=\"indicator\" [attr.data-state]=\"indicator(conversation)\" aria-hidden=\"true\"></span>\n <span class=\"title\">{{ conversation.title || ('nh-assistant.conversation-list.untitled' | translate) }}</span>\n @if ((conversation.participantCount ?? 0) > 0) {\n <nh-assistant-icon\n class=\"shared\"\n name=\"users\"\n [attr.title]=\"'nh-assistant.conversation-list.shared' | translate: { count: conversation.participantCount }\" />\n }\n </span>\n <span class=\"meta\">\n <span class=\"status\" [attr.data-status]=\"conversation.status\">{{ 'nh-assistant.conversation-list.status.' + conversation.status | translate }}</span>\n @if (unread(conversation)) {\n <span class=\"unread-label\">{{ 'nh-assistant.conversation-list.unread' | translate }}</span>\n }\n @if (conversation.role === 'participant') {\n <span>{{ 'nh-assistant.conversation-list.shared-with-you' | translate }}</span>\n }\n <time [attr.datetime]=\"conversation.updatedAt\">{{ conversation.updatedAt | date: 'short' }}</time>\n </span>\n </button>\n <button\n class=\"icon-button delete\"\n type=\"button\"\n [disabled]=\"disabled() || conversation.status === 'running'\"\n [attr.aria-label]=\"((conversation.role === 'participant' ? 'nh-assistant.conversation-list.leave' : 'nh-assistant.conversation-list.delete') | translate) + ': ' + (conversation.title || ('nh-assistant.conversation-list.untitled' | translate))\"\n [attr.title]=\"(conversation.role === 'participant' ? 'nh-assistant.conversation-list.leave' : 'nh-assistant.conversation-list.delete') | translate\"\n (click)=\"conversationDelete.emit(conversation.id)\">\n <nh-assistant-icon [name]=\"conversation.role === 'participant' ? 'leave' : 'trash'\" />\n </button>\n </li>\n }\n </ul>\n }\n</nav>\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))}}:host{display:flex;flex-direction:column;min-height:0;color:var(--nha-text)}.list{display:flex;flex:1;flex-direction:column;min-height:0}.header{display:flex;align-items:center;gap:8px;padding:12px 12px 8px 16px}h3{flex:1;margin:0;font-size:13px;font-weight:700}.count{color:var(--nha-text-muted);font-size:11px}.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}.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}.state{margin:0;padding:12px 16px;color:var(--nha-text-muted);font-size:13px}ul{flex:1;min-height:0;margin:0;overflow-y:auto;padding:0 8px 12px;list-style:none}li{display:flex;align-items:center;gap:4px;border-radius:var(--nha-radius-control)}li.active{background:var(--nha-surface-strong)}.item{display:flex;flex:1;flex-direction:column;gap:2px;min-width:0;border:0;border-radius:var(--nha-radius-control);padding:8px;color:inherit;background:transparent;font:inherit;text-align:left;cursor:pointer}.item:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.item:hover:not(:disabled){background:var(--nha-surface-strong)}.item:disabled{cursor:not-allowed;opacity:.6}.title-row{display:flex;align-items:center;gap:6px;min-width:0}.title{overflow:hidden;font-size:13px;font-weight:650;text-overflow:ellipsis;white-space:nowrap}li.unread .title{font-weight:800}.shared{flex:none;color:var(--nha-text-muted);font-size:12px}.indicator{flex:none;width:8px;height:8px;border-radius:50%}.indicator:not([data-state]){display:none}.indicator[data-state=unread]{background:var(--nha-highlight)}.indicator[data-state=waiting]{background:var(--nha-warning)}.indicator[data-state=running]{width:10px;height:10px;border:2px solid color-mix(in srgb,var(--nha-highlight) 30%,transparent);border-top-color:var(--nha-highlight);animation:nh-assistant-spin .9s linear infinite}@media(prefers-reduced-motion:reduce){.indicator[data-state=running]{border-color:var(--nha-highlight);animation:none}}@keyframes nh-assistant-spin{to{transform:rotate(360deg)}}.unread-label{color:var(--nha-highlight);font-weight:700}.meta{display:flex;gap:8px;color:var(--nha-text-muted);font-size:11px}.status[data-status=waiting-for-approval]{color:var(--nha-warning);font-weight:700}.status[data-status=failed]{color:var(--nha-danger)}.delete{flex:none}\n"], dependencies: [{ kind: "component", type: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }, { kind: "pipe", type: DatePipe, name: "date" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2956
4198
|
}
|
|
2957
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
4199
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantConversationListComponent, decorators: [{
|
|
2958
4200
|
type: Component,
|
|
2959
|
-
args: [{ selector: 'nh-assistant-conversation-list', standalone: true, imports: [DatePipe, TranslatePipe, NhAssistantIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav class=\"list\" [attr.aria-label]=\"'nh-assistant.conversation-list.title' | translate\" [attr.aria-busy]=\"loading()\">\n <header class=\"header\">\n <h3>{{ 'nh-assistant.conversation-list.title' | translate }}</h3>\n @if (conversations().length > 0 && total() > conversations().length) {\n <span class=\"count\">{{ 'nh-assistant.conversation-list.count' | translate: { count: conversations().length, total: total() } }}</span>\n }\n <button\n class=\"icon-button\"\n type=\"button\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'nh-assistant.panel.new-conversation' | translate\"\n [attr.title]=\"'nh-assistant.panel.new-conversation' | translate\"\n (click)=\"conversationCreate.emit()\">\n <nh-assistant-icon name=\"plus\" />\n </button>\n </header>\n\n @if (loading() && conversations().length === 0) {\n <p class=\"state\" role=\"status\">{{ 'nh-assistant.conversation-list.loading' | translate }}</p>\n } @else if (conversations().length === 0) {\n <p class=\"state\">{{ 'nh-assistant.conversation-list.empty' | translate }}</p>\n } @else {\n <ul>\n @for (conversation of conversations(); track conversation.id) {\n <li [class.active]=\"conversation.id === activeConversationId()\">\n <button\n class=\"item\"\n type=\"button\"\n [disabled]=\"disabled()\"\n [attr.aria-current]=\"conversation.id === activeConversationId() ? 'true' : null\"\n (click)=\"conversationSelect.emit(conversation.id)\">\n <span class=\"title\">{{ conversation.title || ('nh-assistant.conversation-list.untitled' | translate) }}</span>\n <span class=\"meta\">\n <span class=\"status\" [attr.data-status]=\"conversation.status\">{{ 'nh-assistant.conversation-list.status.' + conversation.status | translate }}</span>\n <time [attr.datetime]=\"conversation.updatedAt\">{{ conversation.updatedAt | date: 'short' }}</time>\n </span>\n </button>\n <button\n class=\"icon-button delete\"\n type=\"button\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"('nh-assistant.conversation-list.delete' | translate) + ': ' + (conversation.title || ('nh-assistant.conversation-list.untitled' | translate))\"\n [attr.title]=\"'nh-assistant.conversation-list.delete' | translate\"\n (click)=\"conversationDelete.emit(conversation.id)\">\n <nh-assistant-icon name=\"trash\" />\n </button>\n </li>\n }\n </ul>\n }\n</nav>\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))}}:host{display:flex;flex-direction:column;min-height:0;color:var(--nha-text)}.list{display:flex;flex:1;flex-direction:column;min-height:0}.header{display:flex;align-items:center;gap:8px;padding:12px 12px 8px 16px}h3{flex:1;margin:0;font-size:13px;font-weight:700}.count{color:var(--nha-text-muted);font-size:11px}.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}.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}.state{margin:0;padding:12px 16px;color:var(--nha-text-muted);font-size:13px}ul{flex:1;min-height:0;margin:0;overflow-y:auto;padding:0 8px 12px;list-style:none}li{display:flex;align-items:center;gap:4px;border-radius:var(--nha-radius-control)}li.active{background:var(--nha-surface-strong)}.item{display:flex;flex:1;flex-direction:column;gap:2px;min-width:0;border:0;border-radius:var(--nha-radius-control);padding:8px;color:inherit;background:transparent;font:inherit;text-align:left;cursor:pointer}.item:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.item:hover:not(:disabled){background:var(--nha-surface-strong)}.item:disabled{cursor:not-allowed;opacity:.6}.title{overflow:hidden;font-size:13px;font-weight:650;text-overflow:ellipsis;white-space:nowrap}.meta{display:flex;gap:8px;color:var(--nha-text-muted);font-size:11px}.status[data-status=waiting-for-approval]{color:var(--nha-warning);font-weight:700}.status[data-status=failed]{color:var(--nha-danger)}.delete{flex:none}\n"] }]
|
|
4201
|
+
args: [{ selector: 'nh-assistant-conversation-list', standalone: true, imports: [DatePipe, TranslatePipe, NhAssistantIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav class=\"list\" [attr.aria-label]=\"'nh-assistant.conversation-list.title' | translate\" [attr.aria-busy]=\"loading()\">\n <header class=\"header\">\n <h3>{{ 'nh-assistant.conversation-list.title' | translate }}</h3>\n @if (conversations().length > 0 && total() > conversations().length) {\n <span class=\"count\">{{ 'nh-assistant.conversation-list.count' | translate: { count: conversations().length, total: total() } }}</span>\n }\n <button\n class=\"icon-button\"\n type=\"button\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'nh-assistant.panel.new-conversation' | translate\"\n [attr.title]=\"'nh-assistant.panel.new-conversation' | translate\"\n (click)=\"conversationCreate.emit()\">\n <nh-assistant-icon name=\"plus\" />\n </button>\n </header>\n\n @if (loading() && conversations().length === 0) {\n <p class=\"state\" role=\"status\">{{ 'nh-assistant.conversation-list.loading' | translate }}</p>\n } @else if (conversations().length === 0) {\n <p class=\"state\">{{ 'nh-assistant.conversation-list.empty' | translate }}</p>\n } @else {\n <ul>\n @for (conversation of conversations(); track conversation.id) {\n <li [class.active]=\"conversation.id === activeConversationId()\" [class.unread]=\"unread(conversation)\">\n <button\n class=\"item\"\n type=\"button\"\n [disabled]=\"disabled()\"\n [attr.aria-current]=\"conversation.id === activeConversationId() ? 'true' : null\"\n (click)=\"conversationSelect.emit(conversation.id)\">\n <span class=\"title-row\">\n <span class=\"indicator\" [attr.data-state]=\"indicator(conversation)\" aria-hidden=\"true\"></span>\n <span class=\"title\">{{ conversation.title || ('nh-assistant.conversation-list.untitled' | translate) }}</span>\n @if ((conversation.participantCount ?? 0) > 0) {\n <nh-assistant-icon\n class=\"shared\"\n name=\"users\"\n [attr.title]=\"'nh-assistant.conversation-list.shared' | translate: { count: conversation.participantCount }\" />\n }\n </span>\n <span class=\"meta\">\n <span class=\"status\" [attr.data-status]=\"conversation.status\">{{ 'nh-assistant.conversation-list.status.' + conversation.status | translate }}</span>\n @if (unread(conversation)) {\n <span class=\"unread-label\">{{ 'nh-assistant.conversation-list.unread' | translate }}</span>\n }\n @if (conversation.role === 'participant') {\n <span>{{ 'nh-assistant.conversation-list.shared-with-you' | translate }}</span>\n }\n <time [attr.datetime]=\"conversation.updatedAt\">{{ conversation.updatedAt | date: 'short' }}</time>\n </span>\n </button>\n <button\n class=\"icon-button delete\"\n type=\"button\"\n [disabled]=\"disabled() || conversation.status === 'running'\"\n [attr.aria-label]=\"((conversation.role === 'participant' ? 'nh-assistant.conversation-list.leave' : 'nh-assistant.conversation-list.delete') | translate) + ': ' + (conversation.title || ('nh-assistant.conversation-list.untitled' | translate))\"\n [attr.title]=\"(conversation.role === 'participant' ? 'nh-assistant.conversation-list.leave' : 'nh-assistant.conversation-list.delete') | translate\"\n (click)=\"conversationDelete.emit(conversation.id)\">\n <nh-assistant-icon [name]=\"conversation.role === 'participant' ? 'leave' : 'trash'\" />\n </button>\n </li>\n }\n </ul>\n }\n</nav>\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))}}:host{display:flex;flex-direction:column;min-height:0;color:var(--nha-text)}.list{display:flex;flex:1;flex-direction:column;min-height:0}.header{display:flex;align-items:center;gap:8px;padding:12px 12px 8px 16px}h3{flex:1;margin:0;font-size:13px;font-weight:700}.count{color:var(--nha-text-muted);font-size:11px}.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}.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}.state{margin:0;padding:12px 16px;color:var(--nha-text-muted);font-size:13px}ul{flex:1;min-height:0;margin:0;overflow-y:auto;padding:0 8px 12px;list-style:none}li{display:flex;align-items:center;gap:4px;border-radius:var(--nha-radius-control)}li.active{background:var(--nha-surface-strong)}.item{display:flex;flex:1;flex-direction:column;gap:2px;min-width:0;border:0;border-radius:var(--nha-radius-control);padding:8px;color:inherit;background:transparent;font:inherit;text-align:left;cursor:pointer}.item:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.item:hover:not(:disabled){background:var(--nha-surface-strong)}.item:disabled{cursor:not-allowed;opacity:.6}.title-row{display:flex;align-items:center;gap:6px;min-width:0}.title{overflow:hidden;font-size:13px;font-weight:650;text-overflow:ellipsis;white-space:nowrap}li.unread .title{font-weight:800}.shared{flex:none;color:var(--nha-text-muted);font-size:12px}.indicator{flex:none;width:8px;height:8px;border-radius:50%}.indicator:not([data-state]){display:none}.indicator[data-state=unread]{background:var(--nha-highlight)}.indicator[data-state=waiting]{background:var(--nha-warning)}.indicator[data-state=running]{width:10px;height:10px;border:2px solid color-mix(in srgb,var(--nha-highlight) 30%,transparent);border-top-color:var(--nha-highlight);animation:nh-assistant-spin .9s linear infinite}@media(prefers-reduced-motion:reduce){.indicator[data-state=running]{border-color:var(--nha-highlight);animation:none}}@keyframes nh-assistant-spin{to{transform:rotate(360deg)}}.unread-label{color:var(--nha-highlight);font-weight:700}.meta{display:flex;gap:8px;color:var(--nha-text-muted);font-size:11px}.status[data-status=waiting-for-approval]{color:var(--nha-warning);font-weight:700}.status[data-status=failed]{color:var(--nha-danger)}.delete{flex:none}\n"] }]
|
|
2960
4202
|
}], propDecorators: { conversations: [{ type: i0.Input, args: [{ isSignal: true, alias: "conversations", required: true }] }], total: [{ type: i0.Input, args: [{ isSignal: true, alias: "total", required: false }] }], activeConversationId: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeConversationId", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], conversationSelect: [{ type: i0.Output, args: ["conversationSelect"] }], conversationDelete: [{ type: i0.Output, args: ["conversationDelete"] }], conversationCreate: [{ type: i0.Output, args: ["conversationCreate"] }] } });
|
|
2961
4203
|
|
|
2962
4204
|
/** Maximum length of the user's own instructions. */
|
|
2963
4205
|
const NH_ASSISTANT_MAX_CUSTOM_INSTRUCTIONS = 1_000;
|
|
2964
|
-
let nextId$
|
|
4206
|
+
let nextId$4 = 0;
|
|
2965
4207
|
/**
|
|
2966
|
-
* The user's own assistant preferences: style, form of address,
|
|
2967
|
-
* optional instructions.
|
|
2968
|
-
* application context and
|
|
4208
|
+
* The user's own assistant preferences: desktop notifications, style, form of address,
|
|
4209
|
+
* answer length and optional instructions. Notifications apply immediately; the other
|
|
4210
|
+
* preferences steer style only and are saved together, below the application context and
|
|
4211
|
+
* agent instructions.
|
|
2969
4212
|
*/
|
|
2970
4213
|
class NhAssistantPreferencesComponent {
|
|
2971
4214
|
constructor() {
|
|
2972
4215
|
this.api = inject(NhAssistantApiService);
|
|
4216
|
+
this.push = inject(NhAssistantPushService);
|
|
2973
4217
|
/** Emitted when the user leaves the preferences. */
|
|
2974
4218
|
this.closed = output();
|
|
2975
4219
|
/** Emitted with the stored preferences after a successful save. */
|
|
2976
4220
|
this.saved = output();
|
|
2977
|
-
this.id = `nh-assistant-preferences-${nextId$
|
|
4221
|
+
this.id = `nh-assistant-preferences-${nextId$4++}`;
|
|
2978
4222
|
this.styles = ['default', 'direct', 'personal', 'detailed'];
|
|
2979
4223
|
this.addressForms = ['informal', 'formal'];
|
|
2980
4224
|
this.responseLengths = ['short', 'normal', 'long'];
|
|
@@ -3019,6 +4263,19 @@ class NhAssistantPreferencesComponent {
|
|
|
3019
4263
|
}
|
|
3020
4264
|
});
|
|
3021
4265
|
}
|
|
4266
|
+
/** Turns desktop notifications on or off; turning them on asks the browser for permission. */
|
|
4267
|
+
async toggleNotifications(event) {
|
|
4268
|
+
const enabled = event.target.checked;
|
|
4269
|
+
this.error.set(null);
|
|
4270
|
+
try {
|
|
4271
|
+
await this.push.setEnabled(enabled);
|
|
4272
|
+
}
|
|
4273
|
+
catch (error) {
|
|
4274
|
+
this.error.set(error instanceof NhAssistantApiError
|
|
4275
|
+
? { code: error.code, messageKey: error.messageKey }
|
|
4276
|
+
: { code: 'assistant-server', messageKey: nhAssistantErrorMessageKey('assistant-server') });
|
|
4277
|
+
}
|
|
4278
|
+
}
|
|
3022
4279
|
onInstructionsInput(event) {
|
|
3023
4280
|
this.customInstructions.set(event.target.value);
|
|
3024
4281
|
this.savedMessage.set(false);
|
|
@@ -3064,14 +4321,139 @@ class NhAssistantPreferencesComponent {
|
|
|
3064
4321
|
this.responseLength.set(preferences.responseLength);
|
|
3065
4322
|
this.customInstructions.set(preferences.customInstructions ?? '');
|
|
3066
4323
|
}
|
|
3067
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
3068
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: NhAssistantPreferencesComponent, isStandalone: true, selector: "nh-assistant-preferences", outputs: { closed: "closed", saved: "saved" }, ngImport: i0, template: "<section class=\"preferences\" [attr.aria-labelledby]=\"id + '-title'\" [attr.aria-busy]=\"loading() || saving()\">\n <header class=\"header\">\n <h3 [id]=\"id + '-title'\">{{ 'nh-assistant.preferences.title' | translate }}</h3>\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.preferences.back' | translate\"\n [attr.title]=\"'nh-assistant.preferences.back' | translate\"\n (click)=\"closed.emit()\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </header>\n\n @if (loading()) {\n <p class=\"state\" role=\"status\">{{ 'nh-assistant.preferences.loading' | translate }}</p>\n } @else if (loadFailed()) {\n <div class=\"state\" role=\"alert\">\n <p>{{ 'nh-assistant.preferences.load-failed' | translate }}</p>\n <button class=\"secondary\" type=\"button\" (click)=\"load()\">{{ 'nh-assistant.preferences.retry' | translate }}</button>\n </div>\n } @else {\n <form class=\"form\" (submit)=\"$event.preventDefault(); save()\">\n <p class=\"description\">{{ 'nh-assistant.preferences.description' | translate }}</p>\n\n <fieldset>\n <legend>{{ 'nh-assistant.preferences.style-label' | translate }}</legend>\n <div class=\"options styles\">\n @for (option of styles; track option) {\n <label class=\"option\" [class.selected]=\"style() === option\">\n <input type=\"radio\" [name]=\"id + '-style'\" [value]=\"option\" [checked]=\"style() === option\" (change)=\"choose(style, option)\" />\n <span>\n <strong>{{ 'nh-assistant.preferences.style.' + option | translate }}</strong>\n <small>{{ 'nh-assistant.preferences.style.' + option + '-description' | translate }}</small>\n </span>\n </label>\n }\n </div>\n </fieldset>\n\n <fieldset>\n <legend>{{ 'nh-assistant.preferences.address-form-label' | translate }}</legend>\n <div class=\"options segmented\">\n @for (option of addressForms; track option) {\n <label class=\"option\" [class.selected]=\"addressForm() === option\">\n <input type=\"radio\" [name]=\"id + '-address'\" [value]=\"option\" [checked]=\"addressForm() === option\" (change)=\"choose(addressForm, option)\" />\n <span><strong>{{ 'nh-assistant.preferences.address-form.' + option | translate }}</strong></span>\n </label>\n }\n </div>\n </fieldset>\n\n <fieldset>\n <legend>{{ 'nh-assistant.preferences.response-length-label' | translate }}</legend>\n <div class=\"options segmented\">\n @for (option of responseLengths; track option) {\n <label class=\"option\" [class.selected]=\"responseLength() === option\">\n <input type=\"radio\" [name]=\"id + '-length'\" [value]=\"option\" [checked]=\"responseLength() === option\" (change)=\"choose(responseLength, option)\" />\n <span><strong>{{ 'nh-assistant.preferences.response-length.' + option | translate }}</strong></span>\n </label>\n }\n </div>\n </fieldset>\n\n <div class=\"field\">\n <label [for]=\"id + '-instructions'\">{{ 'nh-assistant.preferences.custom-instructions-label' | translate }}</label>\n <textarea\n rows=\"4\"\n [id]=\"id + '-instructions'\"\n [value]=\"customInstructions()\"\n [attr.aria-describedby]=\"id + '-instructions-hint'\"\n [attr.aria-invalid]=\"tooLong() ? true : null\"\n (input)=\"onInstructionsInput($event)\"></textarea>\n <p class=\"hint\" [id]=\"id + '-instructions-hint'\">\n @if (tooLong()) {\n <span class=\"error-text\">{{ 'nh-assistant.preferences.too-long' | translate }}</span>\n } @else {\n <span>{{ 'nh-assistant.preferences.custom-instructions-hint' | translate }}</span>\n }\n <span class=\"counter\" [class.error-text]=\"tooLong()\">\n {{ 'nh-assistant.preferences.characters' | translate: { count: instructionsLength(), max: maxInstructions } }}\n </span>\n </p>\n </div>\n\n @if (error(); as error) {\n <p class=\"message error\" role=\"alert\">\n <nh-assistant-icon name=\"warning\" />\n {{ errorKeys() | nhAssistantTranslate: error.code }}\n </p>\n }\n @if (savedMessage()) {\n <p class=\"message success\" role=\"status\">\n <nh-assistant-icon name=\"check\" />\n {{ 'nh-assistant.preferences.saved' | translate }}\n </p>\n }\n\n <div class=\"actions\">\n <button class=\"secondary\" type=\"button\" (click)=\"closed.emit()\">{{ 'nh-assistant.preferences.back' | translate }}</button>\n <button class=\"primary\" type=\"submit\" [disabled]=\"!canSave()\">\n {{ (saving() ? 'nh-assistant.preferences.saving' : 'nh-assistant.preferences.save') | translate }}\n </button>\n </div>\n </form>\n }\n</section>\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))}}:host{display:flex;flex-direction:column;min-height:0;color:var(--nha-text)}:host fieldset{min-width:0;margin:0;border:0;padding:0}:host legend,:host .label,:host .field>label{display:block;margin-bottom:6px;padding:0;color:var(--nha-text);font-size:12px;font-weight:700}:host input[type=text],:host input[type=url],:host input[type=password],:host input[type=search],:host select,:host textarea{width:100%;min-width:0;min-height:38px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:8px 10px;color:var(--nha-text);background:var(--nha-background);font:inherit;font-size:13px}:host input[type=text]:focus-visible,:host input[type=url]:focus-visible,:host input[type=password]:focus-visible,:host input[type=search]:focus-visible,:host select:focus-visible,:host textarea:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host input[type=text]:disabled,:host input[type=url]:disabled,:host input[type=password]:disabled,:host input[type=search]:disabled,:host select:disabled,:host textarea:disabled{cursor:not-allowed;opacity:.6}:host input[type=text][aria-invalid=true],:host input[type=url][aria-invalid=true],:host input[type=password][aria-invalid=true],:host input[type=search][aria-invalid=true],:host select[aria-invalid=true],:host textarea[aria-invalid=true]{border-color:var(--nha-danger)}:host textarea{line-height:1.45;resize:vertical}:host .mono{font-family:var(--nha-mono);font-size:12px}:host .field{display:flex;flex-direction:column;min-width:0}:host .hint{display:flex;justify-content:space-between;gap:8px;margin:4px 2px 0;color:var(--nha-text-muted);font-size:11px}:host .counter{flex:none;font-variant-numeric:tabular-nums}:host .error-text{color:var(--nha-danger)}:host .message{display:flex;align-items:center;gap:8px;margin:0;border:1px solid transparent;border-radius:var(--nha-radius-control);padding:8px 12px;font-size:13px}:host .message.error{border-color:var(--nha-danger);color:var(--nha-text);background:var(--nha-danger-soft)}:host .message.warning{border-color:var(--nha-warning);color:var(--nha-text);background:var(--nha-warning-soft)}:host .message.success{border-color:var(--nha-success);color:var(--nha-text);background:var(--nha-success-soft)}:host .message nh-assistant-icon{flex:none}:host .primary,:host .secondary,:host .danger{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:36px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:6px 14px;font:inherit;font-size:13px;font-weight:700;white-space:nowrap;cursor:pointer}:host .primary:focus-visible,:host .secondary:focus-visible,:host .danger:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host .primary:disabled,:host .secondary:disabled,:host .danger:disabled{cursor:not-allowed;opacity:.55}:host .primary{border-color:var(--nha-accent);color:var(--nha-accent-contrast);background:var(--nha-accent)}:host .primary:hover:not(:disabled){filter:brightness(1.12)}:host .secondary{color:var(--nha-text);background:transparent}:host .secondary:hover:not(:disabled){background:var(--nha-surface-strong)}:host .danger{border-color:var(--nha-danger);color:var(--nha-danger);background:transparent}:host .danger:hover:not(:disabled){background:var(--nha-danger-soft)}:host .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}:host .icon-button:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host .icon-button:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}:host .icon-button:disabled{cursor:not-allowed;opacity:.5}:host .actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px}:host .badge{display:inline-flex;align-items:center;gap:4px;border-radius:999px;padding:2px 8px;color:var(--nha-text-muted);background:var(--nha-surface-strong);font-size:11px;font-weight:700;white-space:nowrap}:host .badge.success{color:var(--nha-success);background:var(--nha-success-soft)}:host .badge.warning{color:var(--nha-warning);background:var(--nha-warning-soft)}:host .badge.danger{color:var(--nha-danger);background:var(--nha-danger-soft)}:host .visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}.preferences{display:flex;flex:1;flex-direction:column;min-height:0}.header{display:flex;align-items:center;gap:8px;padding:12px 12px 8px 16px}h3{flex:1;margin:0;font-size:15px;font-weight:700}.state{display:flex;flex-direction:column;align-items:flex-start;gap:10px;margin:0;padding:12px 16px;color:var(--nha-text-muted);font-size:13px}.state p{margin:0}.form{display:flex;flex:1;flex-direction:column;gap:18px;min-height:0;overflow-y:auto;padding:0 16px 16px}.description{margin:0;color:var(--nha-text-muted);font-size:13px}.options{display:grid;gap:8px}.styles{grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}.segmented{grid-template-columns:repeat(auto-fit,minmax(90px,1fr))}.option{position:relative;display:flex;align-items:flex-start;gap:8px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:10px;background:var(--nha-background);cursor:pointer}.option:hover{background:var(--nha-surface)}.option.selected{border-color:var(--nha-highlight);background:var(--nha-surface);box-shadow:inset 0 0 0 1px var(--nha-highlight)}.option:focus-within{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.option input{flex:none;margin:2px 0 0;accent-color:var(--nha-highlight)}.option span{display:flex;flex-direction:column;gap:2px;min-width:0}.option strong{font-size:13px}.option small{color:var(--nha-text-muted);font-size:12px}\n"], dependencies: [{ kind: "component", type: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: NhAssistantTranslatePipe, name: "nhAssistantTranslate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
4324
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantPreferencesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4325
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.33", type: NhAssistantPreferencesComponent, isStandalone: true, selector: "nh-assistant-preferences", outputs: { closed: "closed", saved: "saved" }, ngImport: i0, template: "<section class=\"preferences\" [attr.aria-labelledby]=\"id + '-title'\" [attr.aria-busy]=\"loading() || saving()\">\n <header class=\"header\">\n <h3 [id]=\"id + '-title'\">{{ 'nh-assistant.preferences.title' | translate }}</h3>\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.preferences.back' | translate\"\n [attr.title]=\"'nh-assistant.preferences.back' | translate\"\n (click)=\"closed.emit()\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </header>\n\n @if (loading()) {\n <p class=\"state\" role=\"status\">{{ 'nh-assistant.preferences.loading' | translate }}</p>\n } @else if (loadFailed()) {\n <div class=\"state\" role=\"alert\">\n <p>{{ 'nh-assistant.preferences.load-failed' | translate }}</p>\n <button class=\"secondary\" type=\"button\" (click)=\"load()\">{{ 'nh-assistant.preferences.retry' | translate }}</button>\n </div>\n } @else {\n <form class=\"form\" (submit)=\"$event.preventDefault(); save()\">\n @if (push.state() !== 'unavailable') {\n <fieldset class=\"notifications\">\n <legend>{{ 'nh-assistant.preferences.notifications-label' | translate }}</legend>\n <label class=\"switch\">\n <input\n type=\"checkbox\"\n [checked]=\"push.settings()?.pushEnabled === true\"\n [disabled]=\"push.busy()\"\n [attr.aria-describedby]=\"id + '-notifications-hint'\"\n (change)=\"toggleNotifications($event)\" />\n <span>{{ 'nh-assistant.preferences.notifications' | translate }}</span>\n </label>\n <p class=\"hint\" [id]=\"id + '-notifications-hint'\">\n <span>{{ 'nh-assistant.preferences.notifications-state.' + push.state() | translate }}</span>\n </p>\n </fieldset>\n }\n\n <p class=\"description\">{{ 'nh-assistant.preferences.description' | translate }}</p>\n\n <fieldset>\n <legend>{{ 'nh-assistant.preferences.style-label' | translate }}</legend>\n <div class=\"options styles\">\n @for (option of styles; track option) {\n <label class=\"option\" [class.selected]=\"style() === option\">\n <input type=\"radio\" [name]=\"id + '-style'\" [value]=\"option\" [checked]=\"style() === option\" (change)=\"choose(style, option)\" />\n <span>\n <strong>{{ 'nh-assistant.preferences.style.' + option | translate }}</strong>\n <small>{{ 'nh-assistant.preferences.style.' + option + '-description' | translate }}</small>\n </span>\n </label>\n }\n </div>\n </fieldset>\n\n <fieldset>\n <legend>{{ 'nh-assistant.preferences.address-form-label' | translate }}</legend>\n <div class=\"options segmented\">\n @for (option of addressForms; track option) {\n <label class=\"option\" [class.selected]=\"addressForm() === option\">\n <input type=\"radio\" [name]=\"id + '-address'\" [value]=\"option\" [checked]=\"addressForm() === option\" (change)=\"choose(addressForm, option)\" />\n <span><strong>{{ 'nh-assistant.preferences.address-form.' + option | translate }}</strong></span>\n </label>\n }\n </div>\n </fieldset>\n\n <fieldset>\n <legend>{{ 'nh-assistant.preferences.response-length-label' | translate }}</legend>\n <div class=\"options segmented\">\n @for (option of responseLengths; track option) {\n <label class=\"option\" [class.selected]=\"responseLength() === option\">\n <input type=\"radio\" [name]=\"id + '-length'\" [value]=\"option\" [checked]=\"responseLength() === option\" (change)=\"choose(responseLength, option)\" />\n <span><strong>{{ 'nh-assistant.preferences.response-length.' + option | translate }}</strong></span>\n </label>\n }\n </div>\n </fieldset>\n\n <div class=\"field\">\n <label [for]=\"id + '-instructions'\">{{ 'nh-assistant.preferences.custom-instructions-label' | translate }}</label>\n <textarea\n rows=\"4\"\n [id]=\"id + '-instructions'\"\n [value]=\"customInstructions()\"\n [attr.aria-describedby]=\"id + '-instructions-hint'\"\n [attr.aria-invalid]=\"tooLong() ? true : null\"\n (input)=\"onInstructionsInput($event)\"></textarea>\n <p class=\"hint\" [id]=\"id + '-instructions-hint'\">\n @if (tooLong()) {\n <span class=\"error-text\">{{ 'nh-assistant.preferences.too-long' | translate }}</span>\n } @else {\n <span>{{ 'nh-assistant.preferences.custom-instructions-hint' | translate }}</span>\n }\n <span class=\"counter\" [class.error-text]=\"tooLong()\">\n {{ 'nh-assistant.preferences.characters' | translate: { count: instructionsLength(), max: maxInstructions } }}\n </span>\n </p>\n </div>\n\n @if (error(); as error) {\n <p class=\"message error\" role=\"alert\">\n <nh-assistant-icon name=\"warning\" />\n {{ errorKeys() | nhAssistantTranslate: error.code }}\n </p>\n }\n @if (savedMessage()) {\n <p class=\"message success\" role=\"status\">\n <nh-assistant-icon name=\"check\" />\n {{ 'nh-assistant.preferences.saved' | translate }}\n </p>\n }\n\n <div class=\"actions\">\n <button class=\"secondary\" type=\"button\" (click)=\"closed.emit()\">{{ 'nh-assistant.preferences.back' | translate }}</button>\n <button class=\"primary\" type=\"submit\" [disabled]=\"!canSave()\">\n {{ (saving() ? 'nh-assistant.preferences.saving' : 'nh-assistant.preferences.save') | translate }}\n </button>\n </div>\n </form>\n }\n</section>\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))}}:host{display:flex;flex-direction:column;min-height:0;color:var(--nha-text)}:host fieldset{min-width:0;margin:0;border:0;padding:0}:host legend,:host .label,:host .field>label{display:block;margin-bottom:6px;padding:0;color:var(--nha-text);font-size:12px;font-weight:700}:host input[type=text],:host input[type=url],:host input[type=password],:host input[type=search],:host select,:host textarea{width:100%;min-width:0;min-height:38px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:8px 10px;color:var(--nha-text);background:var(--nha-background);font:inherit;font-size:13px}:host input[type=text]:focus-visible,:host input[type=url]:focus-visible,:host input[type=password]:focus-visible,:host input[type=search]:focus-visible,:host select:focus-visible,:host textarea:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host input[type=text]:disabled,:host input[type=url]:disabled,:host input[type=password]:disabled,:host input[type=search]:disabled,:host select:disabled,:host textarea:disabled{cursor:not-allowed;opacity:.6}:host input[type=text][aria-invalid=true],:host input[type=url][aria-invalid=true],:host input[type=password][aria-invalid=true],:host input[type=search][aria-invalid=true],:host select[aria-invalid=true],:host textarea[aria-invalid=true]{border-color:var(--nha-danger)}:host textarea{line-height:1.45;resize:vertical}:host .mono{font-family:var(--nha-mono);font-size:12px}:host .field{display:flex;flex-direction:column;min-width:0}:host .hint{display:flex;justify-content:space-between;gap:8px;margin:4px 2px 0;color:var(--nha-text-muted);font-size:11px}:host .counter{flex:none;font-variant-numeric:tabular-nums}:host .error-text{color:var(--nha-danger)}:host .message{display:flex;align-items:center;gap:8px;margin:0;border:1px solid transparent;border-radius:var(--nha-radius-control);padding:8px 12px;font-size:13px}:host .message.error{border-color:var(--nha-danger);color:var(--nha-text);background:var(--nha-danger-soft)}:host .message.warning{border-color:var(--nha-warning);color:var(--nha-text);background:var(--nha-warning-soft)}:host .message.success{border-color:var(--nha-success);color:var(--nha-text);background:var(--nha-success-soft)}:host .message nh-assistant-icon{flex:none}:host .primary,:host .secondary,:host .danger{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:36px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:6px 14px;font:inherit;font-size:13px;font-weight:700;white-space:nowrap;cursor:pointer}:host .primary:focus-visible,:host .secondary:focus-visible,:host .danger:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host .primary:disabled,:host .secondary:disabled,:host .danger:disabled{cursor:not-allowed;opacity:.55}:host .primary{border-color:var(--nha-accent);color:var(--nha-accent-contrast);background:var(--nha-accent)}:host .primary:hover:not(:disabled){filter:brightness(1.12)}:host .secondary{color:var(--nha-text);background:transparent}:host .secondary:hover:not(:disabled){background:var(--nha-surface-strong)}:host .danger{border-color:var(--nha-danger);color:var(--nha-danger);background:transparent}:host .danger:hover:not(:disabled){background:var(--nha-danger-soft)}:host .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}:host .icon-button:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host .icon-button:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}:host .icon-button:disabled{cursor:not-allowed;opacity:.5}:host .actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px}:host .badge{display:inline-flex;align-items:center;gap:4px;border-radius:999px;padding:2px 8px;color:var(--nha-text-muted);background:var(--nha-surface-strong);font-size:11px;font-weight:700;white-space:nowrap}:host .badge.success{color:var(--nha-success);background:var(--nha-success-soft)}:host .badge.warning{color:var(--nha-warning);background:var(--nha-warning-soft)}:host .badge.danger{color:var(--nha-danger);background:var(--nha-danger-soft)}:host .visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}.preferences{display:flex;flex:1;flex-direction:column;min-height:0}.header{display:flex;align-items:center;gap:8px;padding:12px 12px 8px 16px}h3{flex:1;margin:0;font-size:15px;font-weight:700}.state{display:flex;flex-direction:column;align-items:flex-start;gap:10px;margin:0;padding:12px 16px;color:var(--nha-text-muted);font-size:13px}.state p{margin:0}.form{display:flex;flex:1;flex-direction:column;gap:18px;min-height:0;overflow-y:auto;padding:0 16px 16px}.description{margin:0;color:var(--nha-text-muted);font-size:13px}.options{display:grid;gap:8px}.styles{grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}.segmented{grid-template-columns:repeat(auto-fit,minmax(90px,1fr))}.option{position:relative;display:flex;align-items:flex-start;gap:8px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:10px;background:var(--nha-background);cursor:pointer}.option:hover{background:var(--nha-surface)}.option.selected{border-color:var(--nha-highlight);background:var(--nha-surface);box-shadow:inset 0 0 0 1px var(--nha-highlight)}.option:focus-within{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.option input{flex:none;margin:2px 0 0;accent-color:var(--nha-highlight)}.option span{display:flex;flex-direction:column;gap:2px;min-width:0}.option strong{font-size:13px}.option small{color:var(--nha-text-muted);font-size:12px}.switch{display:flex;align-items:center;gap:8px;font-size:13px;font-weight:650;cursor:pointer}.switch input{width:16px;height:16px;margin:0;accent-color:var(--nha-highlight)}\n"], dependencies: [{ kind: "component", type: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: NhAssistantTranslatePipe, name: "nhAssistantTranslate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3069
4326
|
}
|
|
3070
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
4327
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantPreferencesComponent, decorators: [{
|
|
3071
4328
|
type: Component,
|
|
3072
|
-
args: [{ selector: 'nh-assistant-preferences', standalone: true, imports: [TranslatePipe, NhAssistantTranslatePipe, NhAssistantIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<section class=\"preferences\" [attr.aria-labelledby]=\"id + '-title'\" [attr.aria-busy]=\"loading() || saving()\">\n <header class=\"header\">\n <h3 [id]=\"id + '-title'\">{{ 'nh-assistant.preferences.title' | translate }}</h3>\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.preferences.back' | translate\"\n [attr.title]=\"'nh-assistant.preferences.back' | translate\"\n (click)=\"closed.emit()\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </header>\n\n @if (loading()) {\n <p class=\"state\" role=\"status\">{{ 'nh-assistant.preferences.loading' | translate }}</p>\n } @else if (loadFailed()) {\n <div class=\"state\" role=\"alert\">\n <p>{{ 'nh-assistant.preferences.load-failed' | translate }}</p>\n <button class=\"secondary\" type=\"button\" (click)=\"load()\">{{ 'nh-assistant.preferences.retry' | translate }}</button>\n </div>\n } @else {\n <form class=\"form\" (submit)=\"$event.preventDefault(); save()\">\n <p class=\"description\">{{ 'nh-assistant.preferences.description' | translate }}</p>\n\n <fieldset>\n <legend>{{ 'nh-assistant.preferences.style-label' | translate }}</legend>\n <div class=\"options styles\">\n @for (option of styles; track option) {\n <label class=\"option\" [class.selected]=\"style() === option\">\n <input type=\"radio\" [name]=\"id + '-style'\" [value]=\"option\" [checked]=\"style() === option\" (change)=\"choose(style, option)\" />\n <span>\n <strong>{{ 'nh-assistant.preferences.style.' + option | translate }}</strong>\n <small>{{ 'nh-assistant.preferences.style.' + option + '-description' | translate }}</small>\n </span>\n </label>\n }\n </div>\n </fieldset>\n\n <fieldset>\n <legend>{{ 'nh-assistant.preferences.address-form-label' | translate }}</legend>\n <div class=\"options segmented\">\n @for (option of addressForms; track option) {\n <label class=\"option\" [class.selected]=\"addressForm() === option\">\n <input type=\"radio\" [name]=\"id + '-address'\" [value]=\"option\" [checked]=\"addressForm() === option\" (change)=\"choose(addressForm, option)\" />\n <span><strong>{{ 'nh-assistant.preferences.address-form.' + option | translate }}</strong></span>\n </label>\n }\n </div>\n </fieldset>\n\n <fieldset>\n <legend>{{ 'nh-assistant.preferences.response-length-label' | translate }}</legend>\n <div class=\"options segmented\">\n @for (option of responseLengths; track option) {\n <label class=\"option\" [class.selected]=\"responseLength() === option\">\n <input type=\"radio\" [name]=\"id + '-length'\" [value]=\"option\" [checked]=\"responseLength() === option\" (change)=\"choose(responseLength, option)\" />\n <span><strong>{{ 'nh-assistant.preferences.response-length.' + option | translate }}</strong></span>\n </label>\n }\n </div>\n </fieldset>\n\n <div class=\"field\">\n <label [for]=\"id + '-instructions'\">{{ 'nh-assistant.preferences.custom-instructions-label' | translate }}</label>\n <textarea\n rows=\"4\"\n [id]=\"id + '-instructions'\"\n [value]=\"customInstructions()\"\n [attr.aria-describedby]=\"id + '-instructions-hint'\"\n [attr.aria-invalid]=\"tooLong() ? true : null\"\n (input)=\"onInstructionsInput($event)\"></textarea>\n <p class=\"hint\" [id]=\"id + '-instructions-hint'\">\n @if (tooLong()) {\n <span class=\"error-text\">{{ 'nh-assistant.preferences.too-long' | translate }}</span>\n } @else {\n <span>{{ 'nh-assistant.preferences.custom-instructions-hint' | translate }}</span>\n }\n <span class=\"counter\" [class.error-text]=\"tooLong()\">\n {{ 'nh-assistant.preferences.characters' | translate: { count: instructionsLength(), max: maxInstructions } }}\n </span>\n </p>\n </div>\n\n @if (error(); as error) {\n <p class=\"message error\" role=\"alert\">\n <nh-assistant-icon name=\"warning\" />\n {{ errorKeys() | nhAssistantTranslate: error.code }}\n </p>\n }\n @if (savedMessage()) {\n <p class=\"message success\" role=\"status\">\n <nh-assistant-icon name=\"check\" />\n {{ 'nh-assistant.preferences.saved' | translate }}\n </p>\n }\n\n <div class=\"actions\">\n <button class=\"secondary\" type=\"button\" (click)=\"closed.emit()\">{{ 'nh-assistant.preferences.back' | translate }}</button>\n <button class=\"primary\" type=\"submit\" [disabled]=\"!canSave()\">\n {{ (saving() ? 'nh-assistant.preferences.saving' : 'nh-assistant.preferences.save') | translate }}\n </button>\n </div>\n </form>\n }\n</section>\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))}}:host{display:flex;flex-direction:column;min-height:0;color:var(--nha-text)}:host fieldset{min-width:0;margin:0;border:0;padding:0}:host legend,:host .label,:host .field>label{display:block;margin-bottom:6px;padding:0;color:var(--nha-text);font-size:12px;font-weight:700}:host input[type=text],:host input[type=url],:host input[type=password],:host input[type=search],:host select,:host textarea{width:100%;min-width:0;min-height:38px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:8px 10px;color:var(--nha-text);background:var(--nha-background);font:inherit;font-size:13px}:host input[type=text]:focus-visible,:host input[type=url]:focus-visible,:host input[type=password]:focus-visible,:host input[type=search]:focus-visible,:host select:focus-visible,:host textarea:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host input[type=text]:disabled,:host input[type=url]:disabled,:host input[type=password]:disabled,:host input[type=search]:disabled,:host select:disabled,:host textarea:disabled{cursor:not-allowed;opacity:.6}:host input[type=text][aria-invalid=true],:host input[type=url][aria-invalid=true],:host input[type=password][aria-invalid=true],:host input[type=search][aria-invalid=true],:host select[aria-invalid=true],:host textarea[aria-invalid=true]{border-color:var(--nha-danger)}:host textarea{line-height:1.45;resize:vertical}:host .mono{font-family:var(--nha-mono);font-size:12px}:host .field{display:flex;flex-direction:column;min-width:0}:host .hint{display:flex;justify-content:space-between;gap:8px;margin:4px 2px 0;color:var(--nha-text-muted);font-size:11px}:host .counter{flex:none;font-variant-numeric:tabular-nums}:host .error-text{color:var(--nha-danger)}:host .message{display:flex;align-items:center;gap:8px;margin:0;border:1px solid transparent;border-radius:var(--nha-radius-control);padding:8px 12px;font-size:13px}:host .message.error{border-color:var(--nha-danger);color:var(--nha-text);background:var(--nha-danger-soft)}:host .message.warning{border-color:var(--nha-warning);color:var(--nha-text);background:var(--nha-warning-soft)}:host .message.success{border-color:var(--nha-success);color:var(--nha-text);background:var(--nha-success-soft)}:host .message nh-assistant-icon{flex:none}:host .primary,:host .secondary,:host .danger{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:36px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:6px 14px;font:inherit;font-size:13px;font-weight:700;white-space:nowrap;cursor:pointer}:host .primary:focus-visible,:host .secondary:focus-visible,:host .danger:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host .primary:disabled,:host .secondary:disabled,:host .danger:disabled{cursor:not-allowed;opacity:.55}:host .primary{border-color:var(--nha-accent);color:var(--nha-accent-contrast);background:var(--nha-accent)}:host .primary:hover:not(:disabled){filter:brightness(1.12)}:host .secondary{color:var(--nha-text);background:transparent}:host .secondary:hover:not(:disabled){background:var(--nha-surface-strong)}:host .danger{border-color:var(--nha-danger);color:var(--nha-danger);background:transparent}:host .danger:hover:not(:disabled){background:var(--nha-danger-soft)}:host .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}:host .icon-button:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host .icon-button:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}:host .icon-button:disabled{cursor:not-allowed;opacity:.5}:host .actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px}:host .badge{display:inline-flex;align-items:center;gap:4px;border-radius:999px;padding:2px 8px;color:var(--nha-text-muted);background:var(--nha-surface-strong);font-size:11px;font-weight:700;white-space:nowrap}:host .badge.success{color:var(--nha-success);background:var(--nha-success-soft)}:host .badge.warning{color:var(--nha-warning);background:var(--nha-warning-soft)}:host .badge.danger{color:var(--nha-danger);background:var(--nha-danger-soft)}:host .visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}.preferences{display:flex;flex:1;flex-direction:column;min-height:0}.header{display:flex;align-items:center;gap:8px;padding:12px 12px 8px 16px}h3{flex:1;margin:0;font-size:15px;font-weight:700}.state{display:flex;flex-direction:column;align-items:flex-start;gap:10px;margin:0;padding:12px 16px;color:var(--nha-text-muted);font-size:13px}.state p{margin:0}.form{display:flex;flex:1;flex-direction:column;gap:18px;min-height:0;overflow-y:auto;padding:0 16px 16px}.description{margin:0;color:var(--nha-text-muted);font-size:13px}.options{display:grid;gap:8px}.styles{grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}.segmented{grid-template-columns:repeat(auto-fit,minmax(90px,1fr))}.option{position:relative;display:flex;align-items:flex-start;gap:8px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:10px;background:var(--nha-background);cursor:pointer}.option:hover{background:var(--nha-surface)}.option.selected{border-color:var(--nha-highlight);background:var(--nha-surface);box-shadow:inset 0 0 0 1px var(--nha-highlight)}.option:focus-within{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.option input{flex:none;margin:2px 0 0;accent-color:var(--nha-highlight)}.option span{display:flex;flex-direction:column;gap:2px;min-width:0}.option strong{font-size:13px}.option small{color:var(--nha-text-muted);font-size:12px}\n"] }]
|
|
4329
|
+
args: [{ selector: 'nh-assistant-preferences', standalone: true, imports: [TranslatePipe, NhAssistantTranslatePipe, NhAssistantIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<section class=\"preferences\" [attr.aria-labelledby]=\"id + '-title'\" [attr.aria-busy]=\"loading() || saving()\">\n <header class=\"header\">\n <h3 [id]=\"id + '-title'\">{{ 'nh-assistant.preferences.title' | translate }}</h3>\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.preferences.back' | translate\"\n [attr.title]=\"'nh-assistant.preferences.back' | translate\"\n (click)=\"closed.emit()\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </header>\n\n @if (loading()) {\n <p class=\"state\" role=\"status\">{{ 'nh-assistant.preferences.loading' | translate }}</p>\n } @else if (loadFailed()) {\n <div class=\"state\" role=\"alert\">\n <p>{{ 'nh-assistant.preferences.load-failed' | translate }}</p>\n <button class=\"secondary\" type=\"button\" (click)=\"load()\">{{ 'nh-assistant.preferences.retry' | translate }}</button>\n </div>\n } @else {\n <form class=\"form\" (submit)=\"$event.preventDefault(); save()\">\n @if (push.state() !== 'unavailable') {\n <fieldset class=\"notifications\">\n <legend>{{ 'nh-assistant.preferences.notifications-label' | translate }}</legend>\n <label class=\"switch\">\n <input\n type=\"checkbox\"\n [checked]=\"push.settings()?.pushEnabled === true\"\n [disabled]=\"push.busy()\"\n [attr.aria-describedby]=\"id + '-notifications-hint'\"\n (change)=\"toggleNotifications($event)\" />\n <span>{{ 'nh-assistant.preferences.notifications' | translate }}</span>\n </label>\n <p class=\"hint\" [id]=\"id + '-notifications-hint'\">\n <span>{{ 'nh-assistant.preferences.notifications-state.' + push.state() | translate }}</span>\n </p>\n </fieldset>\n }\n\n <p class=\"description\">{{ 'nh-assistant.preferences.description' | translate }}</p>\n\n <fieldset>\n <legend>{{ 'nh-assistant.preferences.style-label' | translate }}</legend>\n <div class=\"options styles\">\n @for (option of styles; track option) {\n <label class=\"option\" [class.selected]=\"style() === option\">\n <input type=\"radio\" [name]=\"id + '-style'\" [value]=\"option\" [checked]=\"style() === option\" (change)=\"choose(style, option)\" />\n <span>\n <strong>{{ 'nh-assistant.preferences.style.' + option | translate }}</strong>\n <small>{{ 'nh-assistant.preferences.style.' + option + '-description' | translate }}</small>\n </span>\n </label>\n }\n </div>\n </fieldset>\n\n <fieldset>\n <legend>{{ 'nh-assistant.preferences.address-form-label' | translate }}</legend>\n <div class=\"options segmented\">\n @for (option of addressForms; track option) {\n <label class=\"option\" [class.selected]=\"addressForm() === option\">\n <input type=\"radio\" [name]=\"id + '-address'\" [value]=\"option\" [checked]=\"addressForm() === option\" (change)=\"choose(addressForm, option)\" />\n <span><strong>{{ 'nh-assistant.preferences.address-form.' + option | translate }}</strong></span>\n </label>\n }\n </div>\n </fieldset>\n\n <fieldset>\n <legend>{{ 'nh-assistant.preferences.response-length-label' | translate }}</legend>\n <div class=\"options segmented\">\n @for (option of responseLengths; track option) {\n <label class=\"option\" [class.selected]=\"responseLength() === option\">\n <input type=\"radio\" [name]=\"id + '-length'\" [value]=\"option\" [checked]=\"responseLength() === option\" (change)=\"choose(responseLength, option)\" />\n <span><strong>{{ 'nh-assistant.preferences.response-length.' + option | translate }}</strong></span>\n </label>\n }\n </div>\n </fieldset>\n\n <div class=\"field\">\n <label [for]=\"id + '-instructions'\">{{ 'nh-assistant.preferences.custom-instructions-label' | translate }}</label>\n <textarea\n rows=\"4\"\n [id]=\"id + '-instructions'\"\n [value]=\"customInstructions()\"\n [attr.aria-describedby]=\"id + '-instructions-hint'\"\n [attr.aria-invalid]=\"tooLong() ? true : null\"\n (input)=\"onInstructionsInput($event)\"></textarea>\n <p class=\"hint\" [id]=\"id + '-instructions-hint'\">\n @if (tooLong()) {\n <span class=\"error-text\">{{ 'nh-assistant.preferences.too-long' | translate }}</span>\n } @else {\n <span>{{ 'nh-assistant.preferences.custom-instructions-hint' | translate }}</span>\n }\n <span class=\"counter\" [class.error-text]=\"tooLong()\">\n {{ 'nh-assistant.preferences.characters' | translate: { count: instructionsLength(), max: maxInstructions } }}\n </span>\n </p>\n </div>\n\n @if (error(); as error) {\n <p class=\"message error\" role=\"alert\">\n <nh-assistant-icon name=\"warning\" />\n {{ errorKeys() | nhAssistantTranslate: error.code }}\n </p>\n }\n @if (savedMessage()) {\n <p class=\"message success\" role=\"status\">\n <nh-assistant-icon name=\"check\" />\n {{ 'nh-assistant.preferences.saved' | translate }}\n </p>\n }\n\n <div class=\"actions\">\n <button class=\"secondary\" type=\"button\" (click)=\"closed.emit()\">{{ 'nh-assistant.preferences.back' | translate }}</button>\n <button class=\"primary\" type=\"submit\" [disabled]=\"!canSave()\">\n {{ (saving() ? 'nh-assistant.preferences.saving' : 'nh-assistant.preferences.save') | translate }}\n </button>\n </div>\n </form>\n }\n</section>\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))}}:host{display:flex;flex-direction:column;min-height:0;color:var(--nha-text)}:host fieldset{min-width:0;margin:0;border:0;padding:0}:host legend,:host .label,:host .field>label{display:block;margin-bottom:6px;padding:0;color:var(--nha-text);font-size:12px;font-weight:700}:host input[type=text],:host input[type=url],:host input[type=password],:host input[type=search],:host select,:host textarea{width:100%;min-width:0;min-height:38px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:8px 10px;color:var(--nha-text);background:var(--nha-background);font:inherit;font-size:13px}:host input[type=text]:focus-visible,:host input[type=url]:focus-visible,:host input[type=password]:focus-visible,:host input[type=search]:focus-visible,:host select:focus-visible,:host textarea:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host input[type=text]:disabled,:host input[type=url]:disabled,:host input[type=password]:disabled,:host input[type=search]:disabled,:host select:disabled,:host textarea:disabled{cursor:not-allowed;opacity:.6}:host input[type=text][aria-invalid=true],:host input[type=url][aria-invalid=true],:host input[type=password][aria-invalid=true],:host input[type=search][aria-invalid=true],:host select[aria-invalid=true],:host textarea[aria-invalid=true]{border-color:var(--nha-danger)}:host textarea{line-height:1.45;resize:vertical}:host .mono{font-family:var(--nha-mono);font-size:12px}:host .field{display:flex;flex-direction:column;min-width:0}:host .hint{display:flex;justify-content:space-between;gap:8px;margin:4px 2px 0;color:var(--nha-text-muted);font-size:11px}:host .counter{flex:none;font-variant-numeric:tabular-nums}:host .error-text{color:var(--nha-danger)}:host .message{display:flex;align-items:center;gap:8px;margin:0;border:1px solid transparent;border-radius:var(--nha-radius-control);padding:8px 12px;font-size:13px}:host .message.error{border-color:var(--nha-danger);color:var(--nha-text);background:var(--nha-danger-soft)}:host .message.warning{border-color:var(--nha-warning);color:var(--nha-text);background:var(--nha-warning-soft)}:host .message.success{border-color:var(--nha-success);color:var(--nha-text);background:var(--nha-success-soft)}:host .message nh-assistant-icon{flex:none}:host .primary,:host .secondary,:host .danger{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:36px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:6px 14px;font:inherit;font-size:13px;font-weight:700;white-space:nowrap;cursor:pointer}:host .primary:focus-visible,:host .secondary:focus-visible,:host .danger:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host .primary:disabled,:host .secondary:disabled,:host .danger:disabled{cursor:not-allowed;opacity:.55}:host .primary{border-color:var(--nha-accent);color:var(--nha-accent-contrast);background:var(--nha-accent)}:host .primary:hover:not(:disabled){filter:brightness(1.12)}:host .secondary{color:var(--nha-text);background:transparent}:host .secondary:hover:not(:disabled){background:var(--nha-surface-strong)}:host .danger{border-color:var(--nha-danger);color:var(--nha-danger);background:transparent}:host .danger:hover:not(:disabled){background:var(--nha-danger-soft)}:host .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}:host .icon-button:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host .icon-button:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}:host .icon-button:disabled{cursor:not-allowed;opacity:.5}:host .actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px}:host .badge{display:inline-flex;align-items:center;gap:4px;border-radius:999px;padding:2px 8px;color:var(--nha-text-muted);background:var(--nha-surface-strong);font-size:11px;font-weight:700;white-space:nowrap}:host .badge.success{color:var(--nha-success);background:var(--nha-success-soft)}:host .badge.warning{color:var(--nha-warning);background:var(--nha-warning-soft)}:host .badge.danger{color:var(--nha-danger);background:var(--nha-danger-soft)}:host .visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}.preferences{display:flex;flex:1;flex-direction:column;min-height:0}.header{display:flex;align-items:center;gap:8px;padding:12px 12px 8px 16px}h3{flex:1;margin:0;font-size:15px;font-weight:700}.state{display:flex;flex-direction:column;align-items:flex-start;gap:10px;margin:0;padding:12px 16px;color:var(--nha-text-muted);font-size:13px}.state p{margin:0}.form{display:flex;flex:1;flex-direction:column;gap:18px;min-height:0;overflow-y:auto;padding:0 16px 16px}.description{margin:0;color:var(--nha-text-muted);font-size:13px}.options{display:grid;gap:8px}.styles{grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}.segmented{grid-template-columns:repeat(auto-fit,minmax(90px,1fr))}.option{position:relative;display:flex;align-items:flex-start;gap:8px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:10px;background:var(--nha-background);cursor:pointer}.option:hover{background:var(--nha-surface)}.option.selected{border-color:var(--nha-highlight);background:var(--nha-surface);box-shadow:inset 0 0 0 1px var(--nha-highlight)}.option:focus-within{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.option input{flex:none;margin:2px 0 0;accent-color:var(--nha-highlight)}.option span{display:flex;flex-direction:column;gap:2px;min-width:0}.option strong{font-size:13px}.option small{color:var(--nha-text-muted);font-size:12px}.switch{display:flex;align-items:center;gap:8px;font-size:13px;font-weight:650;cursor:pointer}.switch input{width:16px;height:16px;margin:0;accent-color:var(--nha-highlight)}\n"] }]
|
|
3073
4330
|
}], ctorParameters: () => [], propDecorators: { closed: [{ type: i0.Output, args: ["closed"] }], saved: [{ type: i0.Output, args: ["saved"] }] } });
|
|
3074
4331
|
|
|
4332
|
+
let nextId$3 = 0;
|
|
4333
|
+
const searchDelayMs = 250;
|
|
4334
|
+
/**
|
|
4335
|
+
* Sharing of the open conversation. The owner creates, copies, renews or stops an invitation
|
|
4336
|
+
* link, invites colleagues through the application's directory when it offers one, and removes
|
|
4337
|
+
* participants. A participant sees who takes part and can leave.
|
|
4338
|
+
*/
|
|
4339
|
+
class NhAssistantShareComponent {
|
|
4340
|
+
constructor() {
|
|
4341
|
+
this.store = inject(NhAssistantStore);
|
|
4342
|
+
this.config = inject(NH_ASSISTANT_CONFIG);
|
|
4343
|
+
this.searchRevision = 0;
|
|
4344
|
+
/** Emitted when the user leaves the view. */
|
|
4345
|
+
this.closed = output();
|
|
4346
|
+
/** Show the invitation link, participants, or both (the default for custom hosts). */
|
|
4347
|
+
this.section = input('all', ...(ngDevMode ? [{ debugName: "section" }] : []));
|
|
4348
|
+
this.id = `nh-assistant-share-${nextId$3++}`;
|
|
4349
|
+
this.conversation = this.store.activeConversation;
|
|
4350
|
+
this.isOwner = this.store.isOwner;
|
|
4351
|
+
this.busy = signal(false, ...(ngDevMode ? [{ debugName: "busy" }] : []));
|
|
4352
|
+
this.copied = signal(false, ...(ngDevMode ? [{ debugName: "copied" }] : []));
|
|
4353
|
+
this.query = signal('', ...(ngDevMode ? [{ debugName: "query" }] : []));
|
|
4354
|
+
this.searching = signal(false, ...(ngDevMode ? [{ debugName: "searching" }] : []));
|
|
4355
|
+
this.candidates = signal([], ...(ngDevMode ? [{ debugName: "candidates" }] : []));
|
|
4356
|
+
this.invited = signal(null, ...(ngDevMode ? [{ debugName: "invited" }] : []));
|
|
4357
|
+
this.directory = computed(() => this.store.collaboration()?.directory === true && this.isOwner(), ...(ngDevMode ? [{ debugName: "directory" }] : []));
|
|
4358
|
+
this.maxParticipants = computed(() => this.store.collaboration()?.maxParticipants ?? null, ...(ngDevMode ? [{ debugName: "maxParticipants" }] : []));
|
|
4359
|
+
this.participants = computed(() => this.store.members().filter(member => member.role === 'participant'), ...(ngDevMode ? [{ debugName: "participants" }] : []));
|
|
4360
|
+
this.link = computed(() => {
|
|
4361
|
+
const conversation = this.conversation();
|
|
4362
|
+
if (!conversation?.shareToken) {
|
|
4363
|
+
return null;
|
|
4364
|
+
}
|
|
4365
|
+
return (this.config.buildShareLink ?? nhAssistantShareLink)(conversation.id, conversation.shareToken);
|
|
4366
|
+
}, ...(ngDevMode ? [{ debugName: "link" }] : []));
|
|
4367
|
+
this.full = computed(() => {
|
|
4368
|
+
const max = this.maxParticipants();
|
|
4369
|
+
return max !== null && this.participants().length >= max;
|
|
4370
|
+
}, ...(ngDevMode ? [{ debugName: "full" }] : []));
|
|
4371
|
+
inject(DestroyRef).onDestroy(() => clearTimeout(this.searchTimer));
|
|
4372
|
+
}
|
|
4373
|
+
async createLink() {
|
|
4374
|
+
await this.run(() => this.store.createShareLink());
|
|
4375
|
+
this.copied.set(false);
|
|
4376
|
+
}
|
|
4377
|
+
async revokeLink() {
|
|
4378
|
+
await this.run(() => this.store.revokeShareLink());
|
|
4379
|
+
this.copied.set(false);
|
|
4380
|
+
}
|
|
4381
|
+
async copy(input) {
|
|
4382
|
+
const link = this.link();
|
|
4383
|
+
if (!link) {
|
|
4384
|
+
return;
|
|
4385
|
+
}
|
|
4386
|
+
try {
|
|
4387
|
+
await navigator.clipboard.writeText(link);
|
|
4388
|
+
}
|
|
4389
|
+
catch {
|
|
4390
|
+
// Without clipboard access the selected text can be copied by hand.
|
|
4391
|
+
input.select();
|
|
4392
|
+
}
|
|
4393
|
+
this.copied.set(true);
|
|
4394
|
+
}
|
|
4395
|
+
onSearch(event) {
|
|
4396
|
+
const value = event.target.value;
|
|
4397
|
+
this.query.set(value);
|
|
4398
|
+
this.invited.set(null);
|
|
4399
|
+
clearTimeout(this.searchTimer);
|
|
4400
|
+
const revision = ++this.searchRevision;
|
|
4401
|
+
if (value.trim().length < 2) {
|
|
4402
|
+
this.candidates.set([]);
|
|
4403
|
+
this.searching.set(false);
|
|
4404
|
+
return;
|
|
4405
|
+
}
|
|
4406
|
+
this.searching.set(true);
|
|
4407
|
+
this.searchTimer = setTimeout(async () => {
|
|
4408
|
+
const results = await this.store.searchParticipantCandidates(value);
|
|
4409
|
+
if (revision === this.searchRevision) {
|
|
4410
|
+
this.candidates.set(results);
|
|
4411
|
+
this.searching.set(false);
|
|
4412
|
+
}
|
|
4413
|
+
}, searchDelayMs);
|
|
4414
|
+
}
|
|
4415
|
+
async invite(entry) {
|
|
4416
|
+
const invited = await this.run(() => this.store.inviteParticipant(entry.actorId));
|
|
4417
|
+
if (invited) {
|
|
4418
|
+
this.invited.set(entry.displayName);
|
|
4419
|
+
this.candidates.update(items => items.filter(item => item.actorId !== entry.actorId));
|
|
4420
|
+
}
|
|
4421
|
+
}
|
|
4422
|
+
async remove(member) {
|
|
4423
|
+
await this.run(() => this.store.removeParticipant(member.actorId));
|
|
4424
|
+
}
|
|
4425
|
+
async leave() {
|
|
4426
|
+
const actorId = this.conversation()?.currentActorId;
|
|
4427
|
+
if (!actorId) {
|
|
4428
|
+
return;
|
|
4429
|
+
}
|
|
4430
|
+
if (await this.run(() => this.store.removeParticipant(actorId))) {
|
|
4431
|
+
this.closed.emit();
|
|
4432
|
+
}
|
|
4433
|
+
}
|
|
4434
|
+
isCurrentUser(member) {
|
|
4435
|
+
return member.actorId === this.conversation()?.currentActorId;
|
|
4436
|
+
}
|
|
4437
|
+
async run(action) {
|
|
4438
|
+
if (this.busy()) {
|
|
4439
|
+
return undefined;
|
|
4440
|
+
}
|
|
4441
|
+
this.busy.set(true);
|
|
4442
|
+
try {
|
|
4443
|
+
return await action();
|
|
4444
|
+
}
|
|
4445
|
+
finally {
|
|
4446
|
+
this.busy.set(false);
|
|
4447
|
+
}
|
|
4448
|
+
}
|
|
4449
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantShareComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4450
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.33", type: NhAssistantShareComponent, isStandalone: true, selector: "nh-assistant-share", inputs: { section: { classPropertyName: "section", publicName: "section", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed" }, ngImport: i0, template: "<section class=\"share\" [attr.aria-labelledby]=\"id + '-title'\" [attr.aria-busy]=\"busy()\">\n <header class=\"header\">\n <h3 [id]=\"id + '-title'\">\n {{ (isOwner() && section() !== 'members' ? 'nh-assistant.share.title' : 'nh-assistant.share.members-title') | translate }}\n </h3>\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.share.back' | translate\"\n [attr.title]=\"'nh-assistant.share.back' | translate\"\n (click)=\"closed.emit()\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </header>\n\n @if (conversation(); as conversation) {\n <div class=\"content\">\n @if (isOwner()) {\n <p class=\"description\">{{ 'nh-assistant.share.description' | translate }}</p>\n\n @if (section() !== 'members' || !directory()) {\n <section class=\"block\" [attr.aria-labelledby]=\"id + '-link'\">\n <h4 [id]=\"id + '-link'\">{{ 'nh-assistant.share.link-title' | translate }}</h4>\n @if (link(); as link) {\n <div class=\"link-row\">\n <label class=\"visually-hidden\" [for]=\"id + '-link-input'\">{{ 'nh-assistant.share.link-label' | translate }}</label>\n <input #linkInput type=\"text\" readonly [id]=\"id + '-link-input'\" [value]=\"link\" (focus)=\"linkInput.select()\" />\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.share.copy' | translate\"\n [attr.title]=\"'nh-assistant.share.copy' | translate\"\n (click)=\"copy(linkInput)\">\n <nh-assistant-icon name=\"copy\" />\n </button>\n </div>\n @if (copied()) {\n <p class=\"message success\" role=\"status\">\n <nh-assistant-icon name=\"check\" />\n {{ 'nh-assistant.share.copied' | translate }}\n </p>\n }\n <div class=\"actions\">\n <button class=\"secondary\" type=\"button\" [disabled]=\"busy()\" (click)=\"revokeLink()\">\n {{ 'nh-assistant.share.revoke' | translate }}\n </button>\n <button class=\"secondary\" type=\"button\" [disabled]=\"busy()\" (click)=\"createLink()\">\n {{ 'nh-assistant.share.renew' | translate }}\n </button>\n </div>\n <p class=\"hint\">{{ 'nh-assistant.share.link-hint' | translate }}</p>\n } @else {\n <button class=\"primary\" type=\"button\" [disabled]=\"busy() || full()\" (click)=\"createLink()\">\n <nh-assistant-icon name=\"link\" />\n {{ 'nh-assistant.share.create' | translate }}\n </button>\n }\n </section>\n }\n\n @if (directory() && section() !== 'link') {\n <section class=\"block\" [attr.aria-labelledby]=\"id + '-invite'\">\n <h4 [id]=\"id + '-invite'\">{{ 'nh-assistant.share.invite-title' | translate }}</h4>\n <label class=\"visually-hidden\" [for]=\"id + '-search'\">{{ 'nh-assistant.share.search-label' | translate }}</label>\n <input\n type=\"search\"\n autocomplete=\"off\"\n [id]=\"id + '-search'\"\n [value]=\"query()\"\n [disabled]=\"full()\"\n [attr.placeholder]=\"'nh-assistant.share.search-placeholder' | translate\"\n (input)=\"onSearch($event)\" />\n @if (searching()) {\n <p class=\"hint\" role=\"status\">{{ 'nh-assistant.share.searching' | translate }}</p>\n } @else if (query().trim().length >= 2 && candidates().length === 0 && !invited()) {\n <p class=\"hint\">{{ 'nh-assistant.share.no-results' | translate }}</p>\n }\n @if (candidates().length > 0) {\n <ul class=\"people\">\n @for (entry of candidates(); track entry.actorId) {\n <li>\n <span class=\"person\">\n <strong>{{ entry.displayName }}</strong>\n @if (entry.detail) {\n <small>{{ entry.detail }}</small>\n }\n </span>\n <button class=\"secondary small\" type=\"button\" [disabled]=\"busy() || full()\" (click)=\"invite(entry)\">\n {{ 'nh-assistant.share.invite' | translate }}\n </button>\n </li>\n }\n </ul>\n }\n @if (invited(); as name) {\n <p class=\"message success\" role=\"status\">\n <nh-assistant-icon name=\"check\" />\n {{ 'nh-assistant.share.invited' | translate: { name } }}\n </p>\n }\n </section>\n }\n }\n\n @if (section() !== 'link') {\n <section class=\"block\" [attr.aria-labelledby]=\"id + '-members'\">\n <h4 [id]=\"id + '-members'\">\n {{ 'nh-assistant.share.members' | translate }}\n @if (maxParticipants() !== null && isOwner()) {\n <span class=\"count\">{{ 'nh-assistant.share.count' | translate: { count: participants().length, max: maxParticipants() } }}</span>\n }\n </h4>\n @if (store.members().length === 0) {\n <p class=\"hint\">{{ 'nh-assistant.share.not-shared' | translate }}</p>\n } @else {\n <ul class=\"people\">\n @for (member of store.members(); track member.actorId) {\n <li>\n <span class=\"person\">\n <strong>\n {{ member.displayName || ('nh-assistant.share.unnamed' | translate) }}\n @if (isCurrentUser(member)) {\n <small class=\"you\">{{ 'nh-assistant.share.you' | translate }}</small>\n }\n </strong>\n <small>{{ 'nh-assistant.share.role.' + member.role | translate }}</small>\n </span>\n @if (isOwner() && member.role === 'participant') {\n <button\n class=\"icon-button\"\n type=\"button\"\n [disabled]=\"busy()\"\n [attr.aria-label]=\"'nh-assistant.share.remove' | translate: { name: member.displayName || ('nh-assistant.share.unnamed' | translate) }\"\n [attr.title]=\"'nh-assistant.share.remove' | translate: { name: member.displayName || ('nh-assistant.share.unnamed' | translate) }\"\n (click)=\"remove(member)\">\n <nh-assistant-icon name=\"trash\" />\n </button>\n }\n </li>\n }\n </ul>\n }\n </section>\n }\n\n @if (!isOwner()) {\n <div class=\"actions\">\n <button class=\"secondary danger\" type=\"button\" [disabled]=\"busy() || conversation.status === 'running'\" (click)=\"leave()\">\n <nh-assistant-icon name=\"leave\" />\n {{ 'nh-assistant.share.leave' | translate }}\n </button>\n </div>\n }\n </div>\n } @else {\n <p class=\"state\">{{ 'nh-assistant.share.no-conversation' | translate }}</p>\n }\n</section>\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))}}:host{display:flex;flex-direction:column;min-height:0;color:var(--nha-text)}:host fieldset{min-width:0;margin:0;border:0;padding:0}:host legend,:host .label,:host .field>label{display:block;margin-bottom:6px;padding:0;color:var(--nha-text);font-size:12px;font-weight:700}:host input[type=text],:host input[type=url],:host input[type=password],:host input[type=search],:host select,:host textarea{width:100%;min-width:0;min-height:38px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:8px 10px;color:var(--nha-text);background:var(--nha-background);font:inherit;font-size:13px}:host input[type=text]:focus-visible,:host input[type=url]:focus-visible,:host input[type=password]:focus-visible,:host input[type=search]:focus-visible,:host select:focus-visible,:host textarea:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host input[type=text]:disabled,:host input[type=url]:disabled,:host input[type=password]:disabled,:host input[type=search]:disabled,:host select:disabled,:host textarea:disabled{cursor:not-allowed;opacity:.6}:host input[type=text][aria-invalid=true],:host input[type=url][aria-invalid=true],:host input[type=password][aria-invalid=true],:host input[type=search][aria-invalid=true],:host select[aria-invalid=true],:host textarea[aria-invalid=true]{border-color:var(--nha-danger)}:host textarea{line-height:1.45;resize:vertical}:host .mono{font-family:var(--nha-mono);font-size:12px}:host .field{display:flex;flex-direction:column;min-width:0}:host .hint{display:flex;justify-content:space-between;gap:8px;margin:4px 2px 0;color:var(--nha-text-muted);font-size:11px}:host .counter{flex:none;font-variant-numeric:tabular-nums}:host .error-text{color:var(--nha-danger)}:host .message{display:flex;align-items:center;gap:8px;margin:0;border:1px solid transparent;border-radius:var(--nha-radius-control);padding:8px 12px;font-size:13px}:host .message.error{border-color:var(--nha-danger);color:var(--nha-text);background:var(--nha-danger-soft)}:host .message.warning{border-color:var(--nha-warning);color:var(--nha-text);background:var(--nha-warning-soft)}:host .message.success{border-color:var(--nha-success);color:var(--nha-text);background:var(--nha-success-soft)}:host .message nh-assistant-icon{flex:none}:host .primary,:host .secondary,:host .danger{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:36px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:6px 14px;font:inherit;font-size:13px;font-weight:700;white-space:nowrap;cursor:pointer}:host .primary:focus-visible,:host .secondary:focus-visible,:host .danger:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host .primary:disabled,:host .secondary:disabled,:host .danger:disabled{cursor:not-allowed;opacity:.55}:host .primary{border-color:var(--nha-accent);color:var(--nha-accent-contrast);background:var(--nha-accent)}:host .primary:hover:not(:disabled){filter:brightness(1.12)}:host .secondary{color:var(--nha-text);background:transparent}:host .secondary:hover:not(:disabled){background:var(--nha-surface-strong)}:host .danger{border-color:var(--nha-danger);color:var(--nha-danger);background:transparent}:host .danger:hover:not(:disabled){background:var(--nha-danger-soft)}:host .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}:host .icon-button:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host .icon-button:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}:host .icon-button:disabled{cursor:not-allowed;opacity:.5}:host .actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px}:host .badge{display:inline-flex;align-items:center;gap:4px;border-radius:999px;padding:2px 8px;color:var(--nha-text-muted);background:var(--nha-surface-strong);font-size:11px;font-weight:700;white-space:nowrap}:host .badge.success{color:var(--nha-success);background:var(--nha-success-soft)}:host .badge.warning{color:var(--nha-warning);background:var(--nha-warning-soft)}:host .badge.danger{color:var(--nha-danger);background:var(--nha-danger-soft)}:host .visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}.share{display:flex;flex:1;flex-direction:column;min-height:0}.header{display:flex;align-items:center;gap:8px;padding:12px 12px 8px 16px}h3{flex:1;margin:0;font-size:15px;font-weight:700}.content{display:flex;flex:1;flex-direction:column;gap:18px;min-height:0;overflow-y:auto;padding:0 16px 16px}.description,.state{margin:0;color:var(--nha-text-muted);font-size:13px}.state{padding:12px 16px}.block{display:flex;flex-direction:column;gap:8px}h4{display:flex;align-items:baseline;gap:8px;margin:0;font-size:12px;font-weight:700}.count{color:var(--nha-text-muted);font-size:11px;font-weight:400}.link-row{display:flex;align-items:center;gap:4px}.link-row input{font-family:var(--nha-mono);font-size:12px}.actions{justify-content:flex-start}.people{display:flex;flex-direction:column;gap:4px;margin:0;padding:0;list-style:none}.people li{display:flex;align-items:center;gap:8px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:6px 6px 6px 10px}.person{display:flex;flex:1;flex-direction:column;min-width:0}.person strong{overflow:hidden;font-size:13px;text-overflow:ellipsis;white-space:nowrap}.person small{color:var(--nha-text-muted);font-size:11px}.you{margin-left:4px;font-weight:400}.small{min-height:30px;padding:4px 10px;font-size:12px}\n"], dependencies: [{ kind: "component", type: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
4451
|
+
}
|
|
4452
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantShareComponent, decorators: [{
|
|
4453
|
+
type: Component,
|
|
4454
|
+
args: [{ selector: 'nh-assistant-share', standalone: true, imports: [TranslatePipe, NhAssistantIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<section class=\"share\" [attr.aria-labelledby]=\"id + '-title'\" [attr.aria-busy]=\"busy()\">\n <header class=\"header\">\n <h3 [id]=\"id + '-title'\">\n {{ (isOwner() && section() !== 'members' ? 'nh-assistant.share.title' : 'nh-assistant.share.members-title') | translate }}\n </h3>\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.share.back' | translate\"\n [attr.title]=\"'nh-assistant.share.back' | translate\"\n (click)=\"closed.emit()\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </header>\n\n @if (conversation(); as conversation) {\n <div class=\"content\">\n @if (isOwner()) {\n <p class=\"description\">{{ 'nh-assistant.share.description' | translate }}</p>\n\n @if (section() !== 'members' || !directory()) {\n <section class=\"block\" [attr.aria-labelledby]=\"id + '-link'\">\n <h4 [id]=\"id + '-link'\">{{ 'nh-assistant.share.link-title' | translate }}</h4>\n @if (link(); as link) {\n <div class=\"link-row\">\n <label class=\"visually-hidden\" [for]=\"id + '-link-input'\">{{ 'nh-assistant.share.link-label' | translate }}</label>\n <input #linkInput type=\"text\" readonly [id]=\"id + '-link-input'\" [value]=\"link\" (focus)=\"linkInput.select()\" />\n <button\n class=\"icon-button\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.share.copy' | translate\"\n [attr.title]=\"'nh-assistant.share.copy' | translate\"\n (click)=\"copy(linkInput)\">\n <nh-assistant-icon name=\"copy\" />\n </button>\n </div>\n @if (copied()) {\n <p class=\"message success\" role=\"status\">\n <nh-assistant-icon name=\"check\" />\n {{ 'nh-assistant.share.copied' | translate }}\n </p>\n }\n <div class=\"actions\">\n <button class=\"secondary\" type=\"button\" [disabled]=\"busy()\" (click)=\"revokeLink()\">\n {{ 'nh-assistant.share.revoke' | translate }}\n </button>\n <button class=\"secondary\" type=\"button\" [disabled]=\"busy()\" (click)=\"createLink()\">\n {{ 'nh-assistant.share.renew' | translate }}\n </button>\n </div>\n <p class=\"hint\">{{ 'nh-assistant.share.link-hint' | translate }}</p>\n } @else {\n <button class=\"primary\" type=\"button\" [disabled]=\"busy() || full()\" (click)=\"createLink()\">\n <nh-assistant-icon name=\"link\" />\n {{ 'nh-assistant.share.create' | translate }}\n </button>\n }\n </section>\n }\n\n @if (directory() && section() !== 'link') {\n <section class=\"block\" [attr.aria-labelledby]=\"id + '-invite'\">\n <h4 [id]=\"id + '-invite'\">{{ 'nh-assistant.share.invite-title' | translate }}</h4>\n <label class=\"visually-hidden\" [for]=\"id + '-search'\">{{ 'nh-assistant.share.search-label' | translate }}</label>\n <input\n type=\"search\"\n autocomplete=\"off\"\n [id]=\"id + '-search'\"\n [value]=\"query()\"\n [disabled]=\"full()\"\n [attr.placeholder]=\"'nh-assistant.share.search-placeholder' | translate\"\n (input)=\"onSearch($event)\" />\n @if (searching()) {\n <p class=\"hint\" role=\"status\">{{ 'nh-assistant.share.searching' | translate }}</p>\n } @else if (query().trim().length >= 2 && candidates().length === 0 && !invited()) {\n <p class=\"hint\">{{ 'nh-assistant.share.no-results' | translate }}</p>\n }\n @if (candidates().length > 0) {\n <ul class=\"people\">\n @for (entry of candidates(); track entry.actorId) {\n <li>\n <span class=\"person\">\n <strong>{{ entry.displayName }}</strong>\n @if (entry.detail) {\n <small>{{ entry.detail }}</small>\n }\n </span>\n <button class=\"secondary small\" type=\"button\" [disabled]=\"busy() || full()\" (click)=\"invite(entry)\">\n {{ 'nh-assistant.share.invite' | translate }}\n </button>\n </li>\n }\n </ul>\n }\n @if (invited(); as name) {\n <p class=\"message success\" role=\"status\">\n <nh-assistant-icon name=\"check\" />\n {{ 'nh-assistant.share.invited' | translate: { name } }}\n </p>\n }\n </section>\n }\n }\n\n @if (section() !== 'link') {\n <section class=\"block\" [attr.aria-labelledby]=\"id + '-members'\">\n <h4 [id]=\"id + '-members'\">\n {{ 'nh-assistant.share.members' | translate }}\n @if (maxParticipants() !== null && isOwner()) {\n <span class=\"count\">{{ 'nh-assistant.share.count' | translate: { count: participants().length, max: maxParticipants() } }}</span>\n }\n </h4>\n @if (store.members().length === 0) {\n <p class=\"hint\">{{ 'nh-assistant.share.not-shared' | translate }}</p>\n } @else {\n <ul class=\"people\">\n @for (member of store.members(); track member.actorId) {\n <li>\n <span class=\"person\">\n <strong>\n {{ member.displayName || ('nh-assistant.share.unnamed' | translate) }}\n @if (isCurrentUser(member)) {\n <small class=\"you\">{{ 'nh-assistant.share.you' | translate }}</small>\n }\n </strong>\n <small>{{ 'nh-assistant.share.role.' + member.role | translate }}</small>\n </span>\n @if (isOwner() && member.role === 'participant') {\n <button\n class=\"icon-button\"\n type=\"button\"\n [disabled]=\"busy()\"\n [attr.aria-label]=\"'nh-assistant.share.remove' | translate: { name: member.displayName || ('nh-assistant.share.unnamed' | translate) }\"\n [attr.title]=\"'nh-assistant.share.remove' | translate: { name: member.displayName || ('nh-assistant.share.unnamed' | translate) }\"\n (click)=\"remove(member)\">\n <nh-assistant-icon name=\"trash\" />\n </button>\n }\n </li>\n }\n </ul>\n }\n </section>\n }\n\n @if (!isOwner()) {\n <div class=\"actions\">\n <button class=\"secondary danger\" type=\"button\" [disabled]=\"busy() || conversation.status === 'running'\" (click)=\"leave()\">\n <nh-assistant-icon name=\"leave\" />\n {{ 'nh-assistant.share.leave' | translate }}\n </button>\n </div>\n }\n </div>\n } @else {\n <p class=\"state\">{{ 'nh-assistant.share.no-conversation' | translate }}</p>\n }\n</section>\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))}}:host{display:flex;flex-direction:column;min-height:0;color:var(--nha-text)}:host fieldset{min-width:0;margin:0;border:0;padding:0}:host legend,:host .label,:host .field>label{display:block;margin-bottom:6px;padding:0;color:var(--nha-text);font-size:12px;font-weight:700}:host input[type=text],:host input[type=url],:host input[type=password],:host input[type=search],:host select,:host textarea{width:100%;min-width:0;min-height:38px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:8px 10px;color:var(--nha-text);background:var(--nha-background);font:inherit;font-size:13px}:host input[type=text]:focus-visible,:host input[type=url]:focus-visible,:host input[type=password]:focus-visible,:host input[type=search]:focus-visible,:host select:focus-visible,:host textarea:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host input[type=text]:disabled,:host input[type=url]:disabled,:host input[type=password]:disabled,:host input[type=search]:disabled,:host select:disabled,:host textarea:disabled{cursor:not-allowed;opacity:.6}:host input[type=text][aria-invalid=true],:host input[type=url][aria-invalid=true],:host input[type=password][aria-invalid=true],:host input[type=search][aria-invalid=true],:host select[aria-invalid=true],:host textarea[aria-invalid=true]{border-color:var(--nha-danger)}:host textarea{line-height:1.45;resize:vertical}:host .mono{font-family:var(--nha-mono);font-size:12px}:host .field{display:flex;flex-direction:column;min-width:0}:host .hint{display:flex;justify-content:space-between;gap:8px;margin:4px 2px 0;color:var(--nha-text-muted);font-size:11px}:host .counter{flex:none;font-variant-numeric:tabular-nums}:host .error-text{color:var(--nha-danger)}:host .message{display:flex;align-items:center;gap:8px;margin:0;border:1px solid transparent;border-radius:var(--nha-radius-control);padding:8px 12px;font-size:13px}:host .message.error{border-color:var(--nha-danger);color:var(--nha-text);background:var(--nha-danger-soft)}:host .message.warning{border-color:var(--nha-warning);color:var(--nha-text);background:var(--nha-warning-soft)}:host .message.success{border-color:var(--nha-success);color:var(--nha-text);background:var(--nha-success-soft)}:host .message nh-assistant-icon{flex:none}:host .primary,:host .secondary,:host .danger{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:36px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:6px 14px;font:inherit;font-size:13px;font-weight:700;white-space:nowrap;cursor:pointer}:host .primary:focus-visible,:host .secondary:focus-visible,:host .danger:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host .primary:disabled,:host .secondary:disabled,:host .danger:disabled{cursor:not-allowed;opacity:.55}:host .primary{border-color:var(--nha-accent);color:var(--nha-accent-contrast);background:var(--nha-accent)}:host .primary:hover:not(:disabled){filter:brightness(1.12)}:host .secondary{color:var(--nha-text);background:transparent}:host .secondary:hover:not(:disabled){background:var(--nha-surface-strong)}:host .danger{border-color:var(--nha-danger);color:var(--nha-danger);background:transparent}:host .danger:hover:not(:disabled){background:var(--nha-danger-soft)}:host .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}:host .icon-button:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host .icon-button:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}:host .icon-button:disabled{cursor:not-allowed;opacity:.5}:host .actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px}:host .badge{display:inline-flex;align-items:center;gap:4px;border-radius:999px;padding:2px 8px;color:var(--nha-text-muted);background:var(--nha-surface-strong);font-size:11px;font-weight:700;white-space:nowrap}:host .badge.success{color:var(--nha-success);background:var(--nha-success-soft)}:host .badge.warning{color:var(--nha-warning);background:var(--nha-warning-soft)}:host .badge.danger{color:var(--nha-danger);background:var(--nha-danger-soft)}:host .visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}.share{display:flex;flex:1;flex-direction:column;min-height:0}.header{display:flex;align-items:center;gap:8px;padding:12px 12px 8px 16px}h3{flex:1;margin:0;font-size:15px;font-weight:700}.content{display:flex;flex:1;flex-direction:column;gap:18px;min-height:0;overflow-y:auto;padding:0 16px 16px}.description,.state{margin:0;color:var(--nha-text-muted);font-size:13px}.state{padding:12px 16px}.block{display:flex;flex-direction:column;gap:8px}h4{display:flex;align-items:baseline;gap:8px;margin:0;font-size:12px;font-weight:700}.count{color:var(--nha-text-muted);font-size:11px;font-weight:400}.link-row{display:flex;align-items:center;gap:4px}.link-row input{font-family:var(--nha-mono);font-size:12px}.actions{justify-content:flex-start}.people{display:flex;flex-direction:column;gap:4px;margin:0;padding:0;list-style:none}.people li{display:flex;align-items:center;gap:8px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:6px 6px 6px 10px}.person{display:flex;flex:1;flex-direction:column;min-width:0}.person strong{overflow:hidden;font-size:13px;text-overflow:ellipsis;white-space:nowrap}.person small{color:var(--nha-text-muted);font-size:11px}.you{margin-left:4px;font-weight:400}.small{min-height:30px;padding:4px 10px;font-size:12px}\n"] }]
|
|
4455
|
+
}], ctorParameters: () => [], propDecorators: { closed: [{ type: i0.Output, args: ["closed"] }], section: [{ type: i0.Input, args: [{ isSignal: true, alias: "section", required: false }] }] } });
|
|
4456
|
+
|
|
3075
4457
|
const allowedTags = [
|
|
3076
4458
|
'p', 'br', 'strong', 'em', 'del', 'code', 'pre', 'blockquote', 'ul', 'ol', 'li', 'a',
|
|
3077
4459
|
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'table', 'thead', 'tbody', 'tr', 'th', 'td'
|
|
@@ -3144,10 +4526,10 @@ class NhAssistantMarkdownRenderer {
|
|
|
3144
4526
|
this.purifier = purifier;
|
|
3145
4527
|
return purifier;
|
|
3146
4528
|
}
|
|
3147
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
3148
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.
|
|
4529
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantMarkdownRenderer, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
4530
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantMarkdownRenderer, providedIn: 'root' }); }
|
|
3149
4531
|
}
|
|
3150
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
4532
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantMarkdownRenderer, decorators: [{
|
|
3151
4533
|
type: Injectable,
|
|
3152
4534
|
args: [{ providedIn: 'root' }]
|
|
3153
4535
|
}] });
|
|
@@ -3160,10 +4542,10 @@ class NhAssistantMarkdownPipe {
|
|
|
3160
4542
|
transform(text) {
|
|
3161
4543
|
return this.renderer.render(text);
|
|
3162
4544
|
}
|
|
3163
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
3164
|
-
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.
|
|
4545
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantMarkdownPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
4546
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantMarkdownPipe, isStandalone: true, name: "nhAssistantMarkdown" }); }
|
|
3165
4547
|
}
|
|
3166
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
4548
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantMarkdownPipe, decorators: [{
|
|
3167
4549
|
type: Pipe,
|
|
3168
4550
|
args: [{ name: 'nhAssistantMarkdown', standalone: true }]
|
|
3169
4551
|
}] });
|
|
@@ -3200,10 +4582,10 @@ class NhAssistantPartGroupsPipe {
|
|
|
3200
4582
|
transform(parts) {
|
|
3201
4583
|
return groupNhAssistantParts(parts);
|
|
3202
4584
|
}
|
|
3203
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
3204
|
-
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.
|
|
4585
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantPartGroupsPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
4586
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantPartGroupsPipe, isStandalone: true, name: "nhAssistantPartGroups" }); }
|
|
3205
4587
|
}
|
|
3206
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
4588
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantPartGroupsPipe, decorators: [{
|
|
3207
4589
|
type: Pipe,
|
|
3208
4590
|
args: [{ name: 'nhAssistantPartGroups', standalone: true }]
|
|
3209
4591
|
}] });
|
|
@@ -3229,6 +4611,13 @@ class NhAssistantApprovalCardComponent {
|
|
|
3229
4611
|
this.approval = input.required(...(ngDevMode ? [{ debugName: "approval" }] : []));
|
|
3230
4612
|
/** True while the host submits a decision; disables both buttons. */
|
|
3231
4613
|
this.deciding = input(false, ...(ngDevMode ? [{ debugName: "deciding" }] : []));
|
|
4614
|
+
/**
|
|
4615
|
+
* False when the approval belongs to another participant's turn: only the person whose
|
|
4616
|
+
* permissions the action would use may decide it.
|
|
4617
|
+
*/
|
|
4618
|
+
this.canDecide = input(true, ...(ngDevMode ? [{ debugName: "canDecide" }] : []));
|
|
4619
|
+
/** Name of the participant who decides, shown when the current user cannot. */
|
|
4620
|
+
this.decisionOwner = input(null, ...(ngDevMode ? [{ debugName: "decisionOwner" }] : []));
|
|
3232
4621
|
this.decide = output();
|
|
3233
4622
|
this.now = signal(Date.now(), ...(ngDevMode ? [{ debugName: "now" }] : []));
|
|
3234
4623
|
this.submitted = signal(false, ...(ngDevMode ? [{ debugName: "submitted" }] : []));
|
|
@@ -3237,7 +4626,7 @@ class NhAssistantApprovalCardComponent {
|
|
|
3237
4626
|
(this.approval().status === 'pending' && this.remainingMs() <= 0), ...(ngDevMode ? [{ debugName: "expired" }] : []));
|
|
3238
4627
|
this.effectiveStatus = computed(() => this.expired() ? 'expired' : this.approval().status, ...(ngDevMode ? [{ debugName: "effectiveStatus" }] : []));
|
|
3239
4628
|
this.actionable = computed(() => this.effectiveStatus() === 'pending', ...(ngDevMode ? [{ debugName: "actionable" }] : []));
|
|
3240
|
-
this.disabled = computed(() => !this.actionable() || this.deciding() || this.submitted(), ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
4629
|
+
this.disabled = computed(() => !this.actionable() || !this.canDecide() || this.deciding() || this.submitted(), ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
3241
4630
|
this.countdown = computed(() => formatNhAssistantCountdown(this.remainingMs()), ...(ngDevMode ? [{ debugName: "countdown" }] : []));
|
|
3242
4631
|
this.presentedSummary = computed(() => this.approval().presentation?.summary ?? null, ...(ngDevMode ? [{ debugName: "presentedSummary" }] : []));
|
|
3243
4632
|
// Tick only while the approval is pending and not yet expired.
|
|
@@ -3268,15 +4657,15 @@ class NhAssistantApprovalCardComponent {
|
|
|
3268
4657
|
this.submitted.set(true);
|
|
3269
4658
|
this.decide.emit(decision);
|
|
3270
4659
|
}
|
|
3271
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
3272
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.
|
|
4660
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantApprovalCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4661
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.33", type: NhAssistantApprovalCardComponent, isStandalone: true, selector: "nh-assistant-approval-card", inputs: { approval: { classPropertyName: "approval", publicName: "approval", isSignal: true, isRequired: true, transformFunction: null }, deciding: { classPropertyName: "deciding", publicName: "deciding", isSignal: true, isRequired: false, transformFunction: null }, canDecide: { classPropertyName: "canDecide", publicName: "canDecide", isSignal: true, isRequired: false, transformFunction: null }, decisionOwner: { classPropertyName: "decisionOwner", publicName: "decisionOwner", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { decide: "decide" }, host: { properties: { "attr.data-status": "effectiveStatus()" } }, ngImport: i0, template: "<section class=\"card\" [attr.aria-label]=\"'nh-assistant.approval.title' | translate\">\n <header class=\"header\">\n <nh-assistant-icon [name]=\"actionable() ? 'warning' : effectiveStatus() === 'approved' ? 'check' : 'x'\" />\n <h3>{{ 'nh-assistant.approval.title' | translate }}</h3>\n <span class=\"status\">{{ 'nh-assistant.approval.status.' + effectiveStatus() | translate }}</span>\n </header>\n\n @if (approval().presentation; as presentation) {\n <p class=\"tool-name\">{{ presentation.toolDisplayName }}</p>\n <p class=\"summary\">{{ presentation.summary }}</p>\n @if (presentation.fields.length > 0) {\n <dl class=\"presentation-fields\">\n @for (field of presentation.fields; track $index) {\n <div>\n <dt>{{ field.label }}</dt>\n <dd>{{ field.value }}</dd>\n </div>\n }\n </dl>\n }\n @if (presentation.notice) {\n <p class=\"notice\">{{ presentation.notice }}</p>\n }\n } @else {\n <p class=\"summary\">{{ 'nh-assistant.approval.fallback-summary' | translate }}</p>\n }\n\n <details class=\"technical-details\">\n <summary>{{ 'nh-assistant.approval.technical-details' | translate }}</summary>\n <p class=\"label\">{{ 'nh-assistant.approval.tool-id' | translate }}</p>\n <code>{{ approval().toolId }}</code>\n @if (approval().targets.length > 0) {\n <p class=\"label\">{{ 'nh-assistant.approval.targets' | translate }}</p>\n <ul class=\"targets\">\n @for (target of approval().targets; track $index) {\n <li>{{ target }}</li>\n }\n </ul>\n }\n @if (approval().argumentsPreview) {\n <p class=\"label\">{{ 'nh-assistant.approval.arguments' | translate }}</p>\n <pre>{{ approval().argumentsPreview }}</pre>\n }\n </details>\n\n @if (actionable() && !canDecide()) {\n <p class=\"waiting-for-other\" role=\"status\">\n <nh-assistant-icon name=\"users\" />\n {{ decisionOwner()\n ? ('nh-assistant.approval.waiting-for' | translate: { name: decisionOwner() })\n : ('nh-assistant.approval.waiting-for-participant' | translate) }}\n </p>\n } @else if (actionable()) {\n <p class=\"expiry\" aria-live=\"off\">\n <nh-assistant-icon name=\"clock\" />\n {{ 'nh-assistant.approval.expires-in' | translate: { time: countdown() } }}\n </p>\n <div class=\"actions\">\n <button\n class=\"reject\"\n type=\"button\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'nh-assistant.approval.reject-label' | translate: { summary: presentedSummary() ?? ('nh-assistant.approval.fallback-summary' | translate) }\"\n (click)=\"choose('reject')\">\n {{ 'nh-assistant.approval.reject' | translate }}\n </button>\n <button\n class=\"approve\"\n type=\"button\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'nh-assistant.approval.approve-label' | translate: { summary: presentedSummary() ?? ('nh-assistant.approval.fallback-summary' | translate) }\"\n (click)=\"choose('approve')\">\n {{ 'nh-assistant.approval.approve' | translate }}\n </button>\n </div>\n @if (deciding()) {\n <p class=\"deciding\" role=\"status\">{{ 'nh-assistant.approval.deciding' | translate }}</p>\n }\n } @else if (expired()) {\n <p class=\"expiry\">{{ 'nh-assistant.approval.expired' | translate }}</p>\n }\n</section>\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))}}:host{display:block;--nha-status-color: var(--nha-warning);--nha-status-background: var(--nha-warning-soft)}:host([data-status=approved]){--nha-status-color: var(--nha-success);--nha-status-background: var(--nha-success-soft)}:host([data-status=rejected]),:host([data-status=expired]){--nha-status-color: var(--nha-text-muted);--nha-status-background: var(--nha-surface-strong)}.card{border:1px solid var(--nha-status-color);border-radius:var(--nha-radius-control);padding:12px;color:var(--nha-text);background:var(--nha-background)}.header{display:flex;align-items:center;gap:8px;color:var(--nha-status-color)}h3{flex:1;margin:0;color:var(--nha-text);font-size:14px;font-weight:700}.status{border-radius:999px;padding:2px 8px;color:var(--nha-status-color);background:var(--nha-status-background);font-size:11px;font-weight:700}.summary{margin:8px 0 0;font-size:14px;overflow-wrap:anywhere}.tool-name{margin:10px 0 0;font-size:12px;font-weight:700}.presentation-fields{display:grid;gap:6px;margin:10px 0 0}.presentation-fields div{display:grid;grid-template-columns:minmax(90px,.4fr) minmax(0,1fr);gap:8px}.presentation-fields dt{color:var(--nha-text-muted);font-size:12px;font-weight:700}.presentation-fields dd{margin:0;overflow-wrap:anywhere;font-size:13px}.notice{margin:10px 0 0;color:var(--nha-text-muted);font-size:12px}.technical-details{margin-top:10px;color:var(--nha-text-muted);font-size:12px}.technical-details summary{cursor:pointer;font-weight:700}.technical-details code{overflow-wrap:anywhere}.label{margin:10px 0 4px;color:var(--nha-text-muted);font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.04em}.targets{margin:0;padding-left:18px;font-size:13px;overflow-wrap:anywhere}pre{max-height:160px;margin:0;overflow:auto;border-radius:8px;padding:8px;background:var(--nha-surface);font-family:var(--nha-mono);font-size:12px;white-space:pre-wrap;word-break:break-word}.expiry,.deciding,.waiting-for-other{display:flex;align-items:center;gap:6px;margin:10px 0 0;color:var(--nha-text-muted);font-size:12px;font-variant-numeric:tabular-nums}.actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px;margin-top:12px}button{min-height:36px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:6px 14px;font:inherit;font-size:13px;font-weight:700;cursor:pointer}button:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}button:disabled{cursor:not-allowed;opacity:.55}.reject{color:var(--nha-text);background:transparent}.reject:hover:not(:disabled){background:var(--nha-surface-strong)}.approve{border-color:var(--nha-accent);color:var(--nha-accent-contrast);background:var(--nha-accent)}.approve:hover:not(:disabled){filter:brightness(1.12)}\n"], dependencies: [{ kind: "component", type: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3273
4662
|
}
|
|
3274
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
4663
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantApprovalCardComponent, decorators: [{
|
|
3275
4664
|
type: Component,
|
|
3276
4665
|
args: [{ selector: 'nh-assistant-approval-card', standalone: true, imports: [TranslatePipe, NhAssistantIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
3277
4666
|
'[attr.data-status]': 'effectiveStatus()'
|
|
3278
|
-
}, template: "<section class=\"card\" [attr.aria-label]=\"'nh-assistant.approval.title' | translate\">\n <header class=\"header\">\n <nh-assistant-icon [name]=\"actionable() ? 'warning' : effectiveStatus() === 'approved' ? 'check' : 'x'\" />\n <h3>{{ 'nh-assistant.approval.title' | translate }}</h3>\n <span class=\"status\">{{ 'nh-assistant.approval.status.' + effectiveStatus() | translate }}</span>\n </header>\n\n @if (approval().presentation; as presentation) {\n <p class=\"tool-name\">{{ presentation.toolDisplayName }}</p>\n <p class=\"summary\">{{ presentation.summary }}</p>\n @if (presentation.fields.length > 0) {\n <dl class=\"presentation-fields\">\n @for (field of presentation.fields; track $index) {\n <div>\n <dt>{{ field.label }}</dt>\n <dd>{{ field.value }}</dd>\n </div>\n }\n </dl>\n }\n @if (presentation.notice) {\n <p class=\"notice\">{{ presentation.notice }}</p>\n }\n } @else {\n <p class=\"summary\">{{ 'nh-assistant.approval.fallback-summary' | translate }}</p>\n }\n\n <details class=\"technical-details\">\n <summary>{{ 'nh-assistant.approval.technical-details' | translate }}</summary>\n <p class=\"label\">{{ 'nh-assistant.approval.tool-id' | translate }}</p>\n <code>{{ approval().toolId }}</code>\n @if (approval().targets.length > 0) {\n <p class=\"label\">{{ 'nh-assistant.approval.targets' | translate }}</p>\n <ul class=\"targets\">\n @for (target of approval().targets; track $index) {\n <li>{{ target }}</li>\n }\n </ul>\n }\n @if (approval().argumentsPreview) {\n <p class=\"label\">{{ 'nh-assistant.approval.arguments' | translate }}</p>\n <pre>{{ approval().argumentsPreview }}</pre>\n }\n </details>\n\n @if (actionable()) {\n <p class=\"expiry\" aria-live=\"off\">\n <nh-assistant-icon name=\"clock\" />\n {{ 'nh-assistant.approval.expires-in' | translate: { time: countdown() } }}\n </p>\n <div class=\"actions\">\n <button\n class=\"reject\"\n type=\"button\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'nh-assistant.approval.reject-label' | translate: { summary: presentedSummary() ?? ('nh-assistant.approval.fallback-summary' | translate) }\"\n (click)=\"choose('reject')\">\n {{ 'nh-assistant.approval.reject' | translate }}\n </button>\n <button\n class=\"approve\"\n type=\"button\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'nh-assistant.approval.approve-label' | translate: { summary: presentedSummary() ?? ('nh-assistant.approval.fallback-summary' | translate) }\"\n (click)=\"choose('approve')\">\n {{ 'nh-assistant.approval.approve' | translate }}\n </button>\n </div>\n @if (deciding()) {\n <p class=\"deciding\" role=\"status\">{{ 'nh-assistant.approval.deciding' | translate }}</p>\n }\n } @else if (expired()) {\n <p class=\"expiry\">{{ 'nh-assistant.approval.expired' | translate }}</p>\n }\n</section>\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))}}:host{display:block;--nha-status-color: var(--nha-warning);--nha-status-background: var(--nha-warning-soft)}:host([data-status=approved]){--nha-status-color: var(--nha-success);--nha-status-background: var(--nha-success-soft)}:host([data-status=rejected]),:host([data-status=expired]){--nha-status-color: var(--nha-text-muted);--nha-status-background: var(--nha-surface-strong)}.card{border:1px solid var(--nha-status-color);border-radius:var(--nha-radius-control);padding:12px;color:var(--nha-text);background:var(--nha-background)}.header{display:flex;align-items:center;gap:8px;color:var(--nha-status-color)}h3{flex:1;margin:0;color:var(--nha-text);font-size:14px;font-weight:700}.status{border-radius:999px;padding:2px 8px;color:var(--nha-status-color);background:var(--nha-status-background);font-size:11px;font-weight:700}.summary{margin:8px 0 0;font-size:14px;overflow-wrap:anywhere}.tool-name{margin:10px 0 0;font-size:12px;font-weight:700}.presentation-fields{display:grid;gap:6px;margin:10px 0 0}.presentation-fields div{display:grid;grid-template-columns:minmax(90px,.4fr) minmax(0,1fr);gap:8px}.presentation-fields dt{color:var(--nha-text-muted);font-size:12px;font-weight:700}.presentation-fields dd{margin:0;overflow-wrap:anywhere;font-size:13px}.notice{margin:10px 0 0;color:var(--nha-text-muted);font-size:12px}.technical-details{margin-top:10px;color:var(--nha-text-muted);font-size:12px}.technical-details summary{cursor:pointer;font-weight:700}.technical-details code{overflow-wrap:anywhere}.label{margin:10px 0 4px;color:var(--nha-text-muted);font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.04em}.targets{margin:0;padding-left:18px;font-size:13px;overflow-wrap:anywhere}pre{max-height:160px;margin:0;overflow:auto;border-radius:8px;padding:8px;background:var(--nha-surface);font-family:var(--nha-mono);font-size:12px;white-space:pre-wrap;word-break:break-word}.expiry,.deciding{display:flex;align-items:center;gap:6px;margin:10px 0 0;color:var(--nha-text-muted);font-size:12px;font-variant-numeric:tabular-nums}.actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px;margin-top:12px}button{min-height:36px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:6px 14px;font:inherit;font-size:13px;font-weight:700;cursor:pointer}button:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}button:disabled{cursor:not-allowed;opacity:.55}.reject{color:var(--nha-text);background:transparent}.reject:hover:not(:disabled){background:var(--nha-surface-strong)}.approve{border-color:var(--nha-accent);color:var(--nha-accent-contrast);background:var(--nha-accent)}.approve:hover:not(:disabled){filter:brightness(1.12)}\n"] }]
|
|
3279
|
-
}], ctorParameters: () => [], propDecorators: { approval: [{ type: i0.Input, args: [{ isSignal: true, alias: "approval", required: true }] }], deciding: [{ type: i0.Input, args: [{ isSignal: true, alias: "deciding", required: false }] }], decide: [{ type: i0.Output, args: ["decide"] }] } });
|
|
4667
|
+
}, template: "<section class=\"card\" [attr.aria-label]=\"'nh-assistant.approval.title' | translate\">\n <header class=\"header\">\n <nh-assistant-icon [name]=\"actionable() ? 'warning' : effectiveStatus() === 'approved' ? 'check' : 'x'\" />\n <h3>{{ 'nh-assistant.approval.title' | translate }}</h3>\n <span class=\"status\">{{ 'nh-assistant.approval.status.' + effectiveStatus() | translate }}</span>\n </header>\n\n @if (approval().presentation; as presentation) {\n <p class=\"tool-name\">{{ presentation.toolDisplayName }}</p>\n <p class=\"summary\">{{ presentation.summary }}</p>\n @if (presentation.fields.length > 0) {\n <dl class=\"presentation-fields\">\n @for (field of presentation.fields; track $index) {\n <div>\n <dt>{{ field.label }}</dt>\n <dd>{{ field.value }}</dd>\n </div>\n }\n </dl>\n }\n @if (presentation.notice) {\n <p class=\"notice\">{{ presentation.notice }}</p>\n }\n } @else {\n <p class=\"summary\">{{ 'nh-assistant.approval.fallback-summary' | translate }}</p>\n }\n\n <details class=\"technical-details\">\n <summary>{{ 'nh-assistant.approval.technical-details' | translate }}</summary>\n <p class=\"label\">{{ 'nh-assistant.approval.tool-id' | translate }}</p>\n <code>{{ approval().toolId }}</code>\n @if (approval().targets.length > 0) {\n <p class=\"label\">{{ 'nh-assistant.approval.targets' | translate }}</p>\n <ul class=\"targets\">\n @for (target of approval().targets; track $index) {\n <li>{{ target }}</li>\n }\n </ul>\n }\n @if (approval().argumentsPreview) {\n <p class=\"label\">{{ 'nh-assistant.approval.arguments' | translate }}</p>\n <pre>{{ approval().argumentsPreview }}</pre>\n }\n </details>\n\n @if (actionable() && !canDecide()) {\n <p class=\"waiting-for-other\" role=\"status\">\n <nh-assistant-icon name=\"users\" />\n {{ decisionOwner()\n ? ('nh-assistant.approval.waiting-for' | translate: { name: decisionOwner() })\n : ('nh-assistant.approval.waiting-for-participant' | translate) }}\n </p>\n } @else if (actionable()) {\n <p class=\"expiry\" aria-live=\"off\">\n <nh-assistant-icon name=\"clock\" />\n {{ 'nh-assistant.approval.expires-in' | translate: { time: countdown() } }}\n </p>\n <div class=\"actions\">\n <button\n class=\"reject\"\n type=\"button\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'nh-assistant.approval.reject-label' | translate: { summary: presentedSummary() ?? ('nh-assistant.approval.fallback-summary' | translate) }\"\n (click)=\"choose('reject')\">\n {{ 'nh-assistant.approval.reject' | translate }}\n </button>\n <button\n class=\"approve\"\n type=\"button\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'nh-assistant.approval.approve-label' | translate: { summary: presentedSummary() ?? ('nh-assistant.approval.fallback-summary' | translate) }\"\n (click)=\"choose('approve')\">\n {{ 'nh-assistant.approval.approve' | translate }}\n </button>\n </div>\n @if (deciding()) {\n <p class=\"deciding\" role=\"status\">{{ 'nh-assistant.approval.deciding' | translate }}</p>\n }\n } @else if (expired()) {\n <p class=\"expiry\">{{ 'nh-assistant.approval.expired' | translate }}</p>\n }\n</section>\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))}}:host{display:block;--nha-status-color: var(--nha-warning);--nha-status-background: var(--nha-warning-soft)}:host([data-status=approved]){--nha-status-color: var(--nha-success);--nha-status-background: var(--nha-success-soft)}:host([data-status=rejected]),:host([data-status=expired]){--nha-status-color: var(--nha-text-muted);--nha-status-background: var(--nha-surface-strong)}.card{border:1px solid var(--nha-status-color);border-radius:var(--nha-radius-control);padding:12px;color:var(--nha-text);background:var(--nha-background)}.header{display:flex;align-items:center;gap:8px;color:var(--nha-status-color)}h3{flex:1;margin:0;color:var(--nha-text);font-size:14px;font-weight:700}.status{border-radius:999px;padding:2px 8px;color:var(--nha-status-color);background:var(--nha-status-background);font-size:11px;font-weight:700}.summary{margin:8px 0 0;font-size:14px;overflow-wrap:anywhere}.tool-name{margin:10px 0 0;font-size:12px;font-weight:700}.presentation-fields{display:grid;gap:6px;margin:10px 0 0}.presentation-fields div{display:grid;grid-template-columns:minmax(90px,.4fr) minmax(0,1fr);gap:8px}.presentation-fields dt{color:var(--nha-text-muted);font-size:12px;font-weight:700}.presentation-fields dd{margin:0;overflow-wrap:anywhere;font-size:13px}.notice{margin:10px 0 0;color:var(--nha-text-muted);font-size:12px}.technical-details{margin-top:10px;color:var(--nha-text-muted);font-size:12px}.technical-details summary{cursor:pointer;font-weight:700}.technical-details code{overflow-wrap:anywhere}.label{margin:10px 0 4px;color:var(--nha-text-muted);font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.04em}.targets{margin:0;padding-left:18px;font-size:13px;overflow-wrap:anywhere}pre{max-height:160px;margin:0;overflow:auto;border-radius:8px;padding:8px;background:var(--nha-surface);font-family:var(--nha-mono);font-size:12px;white-space:pre-wrap;word-break:break-word}.expiry,.deciding,.waiting-for-other{display:flex;align-items:center;gap:6px;margin:10px 0 0;color:var(--nha-text-muted);font-size:12px;font-variant-numeric:tabular-nums}.actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px;margin-top:12px}button{min-height:36px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:6px 14px;font:inherit;font-size:13px;font-weight:700;cursor:pointer}button:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}button:disabled{cursor:not-allowed;opacity:.55}.reject{color:var(--nha-text);background:transparent}.reject:hover:not(:disabled){background:var(--nha-surface-strong)}.approve{border-color:var(--nha-accent);color:var(--nha-accent-contrast);background:var(--nha-accent)}.approve:hover:not(:disabled){filter:brightness(1.12)}\n"] }]
|
|
4668
|
+
}], ctorParameters: () => [], propDecorators: { approval: [{ type: i0.Input, args: [{ isSignal: true, alias: "approval", required: true }] }], deciding: [{ type: i0.Input, args: [{ isSignal: true, alias: "deciding", required: false }] }], canDecide: [{ type: i0.Input, args: [{ isSignal: true, alias: "canDecide", required: false }] }], decisionOwner: [{ type: i0.Input, args: [{ isSignal: true, alias: "decisionOwner", required: false }] }], decide: [{ type: i0.Output, args: ["decide"] }] } });
|
|
3280
4669
|
|
|
3281
4670
|
let nextId$2 = 0;
|
|
3282
4671
|
const statusIcons = {
|
|
@@ -3298,10 +4687,10 @@ class NhAssistantToolCallCardComponent {
|
|
|
3298
4687
|
toggle() {
|
|
3299
4688
|
this.expanded.update(expanded => !expanded);
|
|
3300
4689
|
}
|
|
3301
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
3302
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.
|
|
4690
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantToolCallCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4691
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.33", type: NhAssistantToolCallCardComponent, isStandalone: true, selector: "nh-assistant-tool-call-card", inputs: { part: { classPropertyName: "part", publicName: "part", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.data-status": "part().status" } }, ngImport: i0, template: "<article class=\"card\" [attr.aria-label]=\"'nh-assistant.tool-call.label' | translate: { name: part().displayName }\">\n <header class=\"header\">\n <span class=\"status-icon\" [class.spinning]=\"part().status === 'running'\">\n <nh-assistant-icon [name]=\"statusIcon()\" />\n </span>\n <span class=\"title\">{{ part().displayName }}</span>\n <span class=\"status\">{{ 'nh-assistant.tool-call.status.' + part().status | translate }}</span>\n @if (hasDetails()) {\n <button\n class=\"toggle\"\n type=\"button\"\n [attr.aria-expanded]=\"expanded()\"\n [attr.aria-controls]=\"detailsId\"\n [attr.aria-label]=\"(expanded() ? 'nh-assistant.tool-call.hide-details' : 'nh-assistant.tool-call.show-details') | translate\"\n [attr.title]=\"(expanded() ? 'nh-assistant.tool-call.hide-details' : 'nh-assistant.tool-call.show-details') | translate\"\n (click)=\"toggle()\">\n <nh-assistant-icon name=\"chevron-down\" [class.rotated]=\"expanded()\" />\n </button>\n }\n </header>\n\n @if (part().resultCode; as resultCode) {\n <p class=\"result-code\">\n {{ ['nh-assistant.result-codes.' + resultCode, 'nh-assistant.errors.' + resultCode] | nhAssistantTranslate: resultCode }}\n </p>\n }\n\n @if (hasDetails()) {\n <div class=\"details\" [id]=\"detailsId\" [hidden]=\"!expanded()\">\n <p class=\"label\">{{ 'nh-assistant.tool-call.tool-id' | translate }}</p>\n <code>{{ part().toolId }}</code>\n @if (part().argumentsPreview; as argumentsPreview) {\n <p class=\"label\">{{ 'nh-assistant.tool-call.arguments' | translate }}</p>\n <pre>{{ argumentsPreview }}</pre>\n }\n @if (part().resultPreview; as resultPreview) {\n <p class=\"label\">{{ 'nh-assistant.tool-call.result' | translate }}</p>\n <pre>{{ resultPreview }}</pre>\n }\n </div>\n }\n</article>\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))}}:host{display:block;font:inherit;--nha-status-color: var(--nha-text-muted);--nha-status-background: var(--nha-surface-strong)}:host([data-status=succeeded]){--nha-status-color: var(--nha-success);--nha-status-background: var(--nha-success-soft)}:host([data-status=failed]){--nha-status-color: var(--nha-danger);--nha-status-background: var(--nha-danger-soft)}:host([data-status=awaiting-approval]){--nha-status-color: var(--nha-warning);--nha-status-background: var(--nha-warning-soft)}:host([data-status=rejected]){--nha-status-color: var(--nha-text-muted)}.card{border:1px solid var(--nha-border);border-left:3px solid var(--nha-status-color);border-radius:var(--nha-radius-control);padding:8px 10px;color:var(--nha-text);background:var(--nha-background)}.header{display:flex;align-items:center;gap:8px;min-width:0}.status-icon{display:inline-flex;color:var(--nha-status-color)}.title{flex:1;min-width:0;overflow:hidden;font-size:13px;font-weight:650;text-overflow:ellipsis;white-space:nowrap}.status{flex:none;border-radius:999px;padding:2px 8px;color:var(--nha-status-color);background:var(--nha-status-background);font-size:11px;font-weight:700}.toggle{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;width:28px;height:28px}.toggle:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.toggle:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}.toggle:disabled{cursor:not-allowed;opacity:.5}.rotated{transform:rotate(180deg)}.result-code{margin:6px 0 0;color:var(--nha-text-muted);font-size:12px}.details{margin-top:8px}.label{margin:8px 0 4px;color:var(--nha-text-muted);font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.04em}pre{max-height:180px;margin:0;overflow:auto;border-radius:8px;padding:8px;background:var(--nha-surface);font-family:var(--nha-mono);font-size:12px;white-space:pre-wrap;word-break:break-word}@media(prefers-reduced-motion:no-preference){.spinning{animation:nha-pulse 1.2s ease-in-out infinite}nh-assistant-icon{transition:transform .16s ease}}@keyframes nha-pulse{50%{opacity:.35}}\n"], dependencies: [{ kind: "component", type: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: NhAssistantTranslatePipe, name: "nhAssistantTranslate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3303
4692
|
}
|
|
3304
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
4693
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantToolCallCardComponent, decorators: [{
|
|
3305
4694
|
type: Component,
|
|
3306
4695
|
args: [{ selector: 'nh-assistant-tool-call-card', standalone: true, imports: [TranslatePipe, NhAssistantTranslatePipe, NhAssistantIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
3307
4696
|
'[attr.data-status]': 'part().status'
|
|
@@ -3347,10 +4736,10 @@ class NhAssistantToolCallGroupComponent {
|
|
|
3347
4736
|
toggle() {
|
|
3348
4737
|
this.expanded.update(expanded => !expanded);
|
|
3349
4738
|
}
|
|
3350
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
3351
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.
|
|
4739
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantToolCallGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4740
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.33", type: NhAssistantToolCallGroupComponent, isStandalone: true, selector: "nh-assistant-tool-call-group", inputs: { calls: { classPropertyName: "calls", publicName: "calls", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.data-status": "state()" } }, ngImport: i0, template: "<section class=\"group\">\n <button\n class=\"summary\"\n type=\"button\"\n [attr.aria-expanded]=\"expanded()\"\n [attr.aria-controls]=\"callsId\"\n [attr.title]=\"(expanded() ? 'nh-assistant.tool-group.hide-calls' : 'nh-assistant.tool-group.show-calls') | translate\"\n (click)=\"toggle()\">\n <span class=\"status-icon\" [class.spinning]=\"state() === 'running'\">\n <nh-assistant-icon [name]=\"stateIcon()\" />\n </span>\n <span class=\"title\">\n @switch (state()) {\n @case ('running') {\n {{ 'nh-assistant.tool-group.running' | translate: { name: current()?.displayName } }}\n }\n @case ('awaiting-approval') {\n {{ 'nh-assistant.tool-group.awaiting-approval' | translate: { name: current()?.displayName } }}\n }\n @default {\n {{ (total() === 1 ? 'nh-assistant.tool-group.used-one' : 'nh-assistant.tool-group.used-many') | translate: { count: total() } }}\n }\n }\n </span>\n @if (state() === 'running' || state() === 'awaiting-approval') {\n <span class=\"count\">{{ 'nh-assistant.tool-group.progress' | translate: { finished: finished(), total: total() } }}</span>\n } @else if (failed() > 0) {\n <span class=\"count failed\">{{ 'nh-assistant.tool-group.failed' | translate: { count: failed() } }}</span>\n }\n <nh-assistant-icon class=\"chevron\" name=\"chevron-down\" [class.rotated]=\"expanded()\" />\n </button>\n\n <div class=\"calls\" [id]=\"callsId\" [hidden]=\"!expanded()\">\n @if (expanded()) {\n @for (call of calls(); track call.invocationId) {\n <nh-assistant-tool-call-card [part]=\"call\" />\n }\n }\n </div>\n</section>\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))}}:host{display:block;font:inherit;--nha-status-color: var(--nha-text-muted)}:host([data-status=succeeded]){--nha-status-color: var(--nha-success)}:host([data-status=failed]){--nha-status-color: var(--nha-danger)}:host([data-status=awaiting-approval]){--nha-status-color: var(--nha-warning)}.group{border:1px solid var(--nha-border);border-left:3px solid var(--nha-status-color);border-radius:var(--nha-radius-control);color:var(--nha-text);background:var(--nha-background)}.summary{display:flex;align-items:center;gap:8px;width:100%;min-width:0;min-height:40px;border:0;border-radius:var(--nha-radius-control);padding:8px 10px;color:inherit;background:transparent;font:inherit;text-align:left;cursor:pointer}.summary:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.summary:hover{background:var(--nha-surface)}.status-icon{display:inline-flex;flex:none;color:var(--nha-status-color)}.title{flex:1;min-width:0;overflow:hidden;font-size:13px;font-weight:650;text-overflow:ellipsis;white-space:nowrap}.count{flex:none;color:var(--nha-text-muted);font-size:12px;font-variant-numeric:tabular-nums}.count.failed{border-radius:999px;padding:2px 8px;color:var(--nha-danger);background:var(--nha-danger-soft);font-size:11px;font-weight:700}.chevron{flex:none;color:var(--nha-text-muted)}.rotated{transform:rotate(180deg)}.calls{display:flex;flex-direction:column;gap:6px;border-top:1px solid var(--nha-border);padding:8px}@media(prefers-reduced-motion:no-preference){.spinning{animation:nha-pulse 1.2s ease-in-out infinite}.chevron{transition:transform .16s ease}}@keyframes nha-pulse{50%{opacity:.35}}\n"], dependencies: [{ kind: "component", type: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }, { kind: "component", type: NhAssistantToolCallCardComponent, selector: "nh-assistant-tool-call-card", inputs: ["part"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3352
4741
|
}
|
|
3353
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
4742
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantToolCallGroupComponent, decorators: [{
|
|
3354
4743
|
type: Component,
|
|
3355
4744
|
args: [{ selector: 'nh-assistant-tool-call-group', standalone: true, imports: [TranslatePipe, NhAssistantIconComponent, NhAssistantToolCallCardComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
3356
4745
|
'[attr.data-status]': 'state()'
|
|
@@ -3385,7 +4774,16 @@ class NhAssistantThreadComponent {
|
|
|
3385
4774
|
this.streaming = input(false, ...(ngDevMode ? [{ debugName: "streaming" }] : []));
|
|
3386
4775
|
this.deciding = input(false, ...(ngDevMode ? [{ debugName: "deciding" }] : []));
|
|
3387
4776
|
this.markdown = input(true, ...(ngDevMode ? [{ debugName: "markdown" }] : []));
|
|
4777
|
+
/** The owner and participants of a shared conversation; empty while it is not shared. */
|
|
4778
|
+
this.members = input([], ...(ngDevMode ? [{ debugName: "members" }] : []));
|
|
4779
|
+
/** The current user, to tell their own messages from those of others. */
|
|
4780
|
+
this.currentActorId = input(null, ...(ngDevMode ? [{ debugName: "currentActorId" }] : []));
|
|
4781
|
+
/** False when the pending approval belongs to another participant's turn. */
|
|
4782
|
+
this.canDecide = input(true, ...(ngDevMode ? [{ debugName: "canDecide" }] : []));
|
|
4783
|
+
/** Name of the participant who decides the pending approval, when it is someone else. */
|
|
4784
|
+
this.decisionOwner = input(null, ...(ngDevMode ? [{ debugName: "decisionOwner" }] : []));
|
|
3388
4785
|
this.approvalDecision = output();
|
|
4786
|
+
this.names = computed(() => new Map(this.members().map(member => [member.actorId, member.displayName])), ...(ngDevMode ? [{ debugName: "names" }] : []));
|
|
3389
4787
|
this.virtualThreshold = NH_ASSISTANT_VIRTUAL_SCROLL_THRESHOLD;
|
|
3390
4788
|
this.virtual = computed(() => this.messages().length > NH_ASSISTANT_VIRTUAL_SCROLL_THRESHOLD, ...(ngDevMode ? [{ debugName: "virtual" }] : []));
|
|
3391
4789
|
/**
|
|
@@ -3443,6 +4841,17 @@ class NhAssistantThreadComponent {
|
|
|
3443
4841
|
trackMessage(_index, message) {
|
|
3444
4842
|
return message.id;
|
|
3445
4843
|
}
|
|
4844
|
+
/** True for a user message written by another person in a shared conversation. */
|
|
4845
|
+
isOthers(message) {
|
|
4846
|
+
return message.role === 'user'
|
|
4847
|
+
&& !!message.authorActorId
|
|
4848
|
+
&& !!this.currentActorId()
|
|
4849
|
+
&& message.authorActorId !== this.currentActorId();
|
|
4850
|
+
}
|
|
4851
|
+
/** Display name of another person's message, or `null` when the application provides none. */
|
|
4852
|
+
authorName(message) {
|
|
4853
|
+
return message.authorActorId ? this.names().get(message.authorActorId) ?? null : null;
|
|
4854
|
+
}
|
|
3446
4855
|
scrollElement() {
|
|
3447
4856
|
return this.viewport()?.elementRef.nativeElement ?? this.scroller()?.nativeElement ?? null;
|
|
3448
4857
|
}
|
|
@@ -3458,10 +4867,10 @@ class NhAssistantThreadComponent {
|
|
|
3458
4867
|
element.scrollTop = element.scrollHeight;
|
|
3459
4868
|
}
|
|
3460
4869
|
}
|
|
3461
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
3462
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: NhAssistantThreadComponent, isStandalone: true, selector: "nh-assistant-thread", inputs: { messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: true, transformFunction: null }, streaming: { classPropertyName: "streaming", publicName: "streaming", isSignal: true, isRequired: false, transformFunction: null }, deciding: { classPropertyName: "deciding", publicName: "deciding", isSignal: true, isRequired: false, transformFunction: null }, markdown: { classPropertyName: "markdown", publicName: "markdown", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { approvalDecision: "approvalDecision" }, viewQueries: [{ propertyName: "scroller", first: true, predicate: ["scroller"], descendants: true, isSignal: true }, { propertyName: "viewport", first: true, predicate: CdkVirtualScrollViewport, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"log\" role=\"log\" aria-live=\"polite\" [attr.aria-label]=\"'nh-assistant.thread.label' | translate\" [attr.aria-busy]=\"streaming()\">\n @if (virtual()) {\n <cdk-virtual-scroll-viewport class=\"scroller\" itemSize=\"120\" minBufferPx=\"1200\" maxBufferPx=\"2400\">\n <div class=\"row\" *cdkVirtualFor=\"let message of messages(); trackBy: trackMessage\">\n <ng-container [ngTemplateOutlet]=\"messageTemplate\" [ngTemplateOutletContext]=\"{ $implicit: message }\" />\n </div>\n @if (working()) {\n <ng-container [ngTemplateOutlet]=\"workingTemplate\" />\n }\n </cdk-virtual-scroll-viewport>\n } @else {\n <div class=\"scroller\" #scroller tabindex=\"0\">\n @for (message of messages(); track message.id) {\n <div class=\"row\">\n <ng-container [ngTemplateOutlet]=\"messageTemplate\" [ngTemplateOutletContext]=\"{ $implicit: message }\" />\n </div>\n }\n @if (working()) {\n <ng-container [ngTemplateOutlet]=\"workingTemplate\" />\n }\n </div>\n }\n</div>\n\n<ng-template #messageTemplate let-message>\n <article class=\"message\" [attr.data-role]=\"message.role\">\n <p class=\"author\">{{ 'nh-assistant.thread.' + (message.role === 'user' ? 'you' : message.role) | translate }}</p>\n @for (group of message.parts | nhAssistantPartGroups; track group.key) {\n @switch (group.kind) {\n @case ('text') {\n @if (message.role === 'assistant' && markdown()) {\n <div class=\"bubble markdown\" [innerHTML]=\"group.part.text | nhAssistantMarkdown\"></div>\n } @else {\n <div class=\"bubble plain\">{{ group.part.text }}</div>\n }\n }\n @case ('tools') {\n <nh-assistant-tool-call-group class=\"part\" [calls]=\"group.calls\" />\n }\n @case ('approval') {\n <nh-assistant-approval-card class=\"part\" [approval]=\"group.part\" [deciding]=\"deciding()\" (decide)=\"approvalDecision.emit($event)\" />\n }\n }\n }\n </article>\n</ng-template>\n\n<ng-template #workingTemplate>\n <p class=\"working\" role=\"status\">\n <span class=\"dots\" aria-hidden=\"true\"><span></span><span></span><span></span></span>\n {{ 'nh-assistant.thread.working' | translate }}\n @if (elapsedSeconds() > 0) {\n <span class=\"elapsed\">{{ 'nh-assistant.thread.elapsed' | translate: { seconds: elapsedSeconds() } }}</span>\n }\n </p>\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))}}:host{display:flex;flex-direction:column;min-height:0;color:var(--nha-text)}.log{display:flex;flex:1;flex-direction:column;min-height:0}.scroller{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:16px;outline:none}.scroller:focus-visible{box-shadow:inset 0 0 0 2px var(--nha-focus)}cdk-virtual-scroll-viewport.scroller{height:100%}.row+.row{margin-top:14px}.message{display:flex;flex-direction:column;align-items:flex-start;gap:6px;min-width:0}.message[data-role=user]{align-items:flex-end}.author{margin:0;color:var(--nha-text-muted);font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase}.bubble{max-width:100%;border-radius:var(--nha-radius-control);padding:8px 12px;font-size:14px;line-height:1.5;overflow-wrap:anywhere}.plain{white-space:pre-wrap}.message[data-role=user] .bubble{max-width:88%;color:var(--nha-user-text);background:var(--nha-user-bubble)}.message[data-role=assistant] .bubble,.message[data-role=tool] .bubble{border:1px solid var(--nha-border);background:var(--nha-surface)}.part{width:100%}:host ::ng-deep .markdown :first-child{margin-top:0}:host ::ng-deep .markdown :last-child{margin-bottom:0}:host ::ng-deep .markdown p,:host ::ng-deep .markdown ul,:host ::ng-deep .markdown ol,:host ::ng-deep .markdown pre,:host ::ng-deep .markdown blockquote,:host ::ng-deep .markdown table{margin:0 0 8px}:host ::ng-deep .markdown ul,:host ::ng-deep .markdown ol{padding-left:20px}:host ::ng-deep .markdown h1,:host ::ng-deep .markdown h2,:host ::ng-deep .markdown h3,:host ::ng-deep .markdown h4,:host ::ng-deep .markdown h5,:host ::ng-deep .markdown h6{margin:10px 0 6px;font-size:15px;line-height:1.3}:host ::ng-deep .markdown a{color:var(--nha-highlight);text-decoration:underline}:host ::ng-deep .markdown a:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host ::ng-deep .markdown code{border-radius:6px;padding:1px 4px;background:var(--nha-surface-strong);font-family:var(--nha-mono);font-size:12.5px}:host ::ng-deep .markdown pre{overflow-x:auto;border-radius:8px;padding:8px;background:var(--nha-surface-strong)}:host ::ng-deep .markdown pre code{padding:0;background:none}:host ::ng-deep .markdown blockquote{border-left:3px solid var(--nha-border);padding-left:10px;color:var(--nha-text-muted)}:host ::ng-deep .markdown table{display:block;max-width:100%;overflow-x:auto;border-collapse:collapse;font-size:13px}:host ::ng-deep .markdown th,:host ::ng-deep .markdown td{border:1px solid var(--nha-border);padding:4px 8px;text-align:left}.working{display:flex;align-items:center;gap:8px;margin:14px 0 0;color:var(--nha-text-muted);font-size:13px}.elapsed{font-variant-numeric:tabular-nums}.dots{display:inline-flex;gap:3px}.dots span{width:6px;height:6px;border-radius:50%;background:currentColor;opacity:.6}@media(prefers-reduced-motion:no-preference){.dots span{animation:nha-bounce 1s ease-in-out infinite}.dots span:nth-child(2){animation-delay:.15s}.dots span:nth-child(3){animation-delay:.3s}}@keyframes nha-bounce{0%,80%,to{transform:translateY(0);opacity:.35}40%{transform:translateY(-3px);opacity:1}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i1.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i1.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i1.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "component", type: NhAssistantToolCallGroupComponent, selector: "nh-assistant-tool-call-group", inputs: ["calls"] }, { kind: "component", type: NhAssistantApprovalCardComponent, selector: "nh-assistant-approval-card", inputs: ["approval", "deciding"], outputs: ["decide"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: NhAssistantMarkdownPipe, name: "nhAssistantMarkdown" }, { kind: "pipe", type: NhAssistantPartGroupsPipe, name: "nhAssistantPartGroups" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
4870
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantThreadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4871
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.33", type: NhAssistantThreadComponent, isStandalone: true, selector: "nh-assistant-thread", inputs: { messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: true, transformFunction: null }, streaming: { classPropertyName: "streaming", publicName: "streaming", isSignal: true, isRequired: false, transformFunction: null }, deciding: { classPropertyName: "deciding", publicName: "deciding", isSignal: true, isRequired: false, transformFunction: null }, markdown: { classPropertyName: "markdown", publicName: "markdown", isSignal: true, isRequired: false, transformFunction: null }, members: { classPropertyName: "members", publicName: "members", isSignal: true, isRequired: false, transformFunction: null }, currentActorId: { classPropertyName: "currentActorId", publicName: "currentActorId", isSignal: true, isRequired: false, transformFunction: null }, canDecide: { classPropertyName: "canDecide", publicName: "canDecide", isSignal: true, isRequired: false, transformFunction: null }, decisionOwner: { classPropertyName: "decisionOwner", publicName: "decisionOwner", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { approvalDecision: "approvalDecision" }, viewQueries: [{ propertyName: "scroller", first: true, predicate: ["scroller"], descendants: true, isSignal: true }, { propertyName: "viewport", first: true, predicate: CdkVirtualScrollViewport, descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"log\" role=\"log\" aria-live=\"polite\" [attr.aria-label]=\"'nh-assistant.thread.label' | translate\" [attr.aria-busy]=\"streaming()\">\n @if (virtual()) {\n <cdk-virtual-scroll-viewport class=\"scroller\" itemSize=\"120\" minBufferPx=\"1200\" maxBufferPx=\"2400\">\n <div class=\"row\" *cdkVirtualFor=\"let message of messages(); trackBy: trackMessage\">\n <ng-container [ngTemplateOutlet]=\"messageTemplate\" [ngTemplateOutletContext]=\"{ $implicit: message }\" />\n </div>\n @if (working()) {\n <ng-container [ngTemplateOutlet]=\"workingTemplate\" />\n }\n </cdk-virtual-scroll-viewport>\n } @else {\n <div class=\"scroller\" #scroller tabindex=\"0\">\n @for (message of messages(); track message.id) {\n <div class=\"row\">\n <ng-container [ngTemplateOutlet]=\"messageTemplate\" [ngTemplateOutletContext]=\"{ $implicit: message }\" />\n </div>\n }\n @if (working()) {\n <ng-container [ngTemplateOutlet]=\"workingTemplate\" />\n }\n </div>\n }\n</div>\n\n<ng-template #messageTemplate let-message>\n <article class=\"message\" [attr.data-role]=\"message.role\" [attr.data-others]=\"isOthers(message) ? 'true' : null\">\n @if (isOthers(message)) {\n <p class=\"author\">{{ authorName(message) ?? ('nh-assistant.thread.participant' | translate) }}</p>\n } @else {\n <p class=\"author\">{{ 'nh-assistant.thread.' + (message.role === 'user' ? 'you' : message.role) | translate }}</p>\n }\n @for (group of message.parts | nhAssistantPartGroups; track group.key) {\n @switch (group.kind) {\n @case ('text') {\n @if (message.role === 'assistant' && markdown()) {\n <div class=\"bubble markdown\" [innerHTML]=\"group.part.text | nhAssistantMarkdown\"></div>\n } @else {\n <div class=\"bubble plain\">{{ group.part.text }}</div>\n }\n }\n @case ('tools') {\n <nh-assistant-tool-call-group class=\"part\" [calls]=\"group.calls\" />\n }\n @case ('approval') {\n <nh-assistant-approval-card\n class=\"part\"\n [approval]=\"group.part\"\n [deciding]=\"deciding()\"\n [canDecide]=\"canDecide()\"\n [decisionOwner]=\"decisionOwner()\"\n (decide)=\"approvalDecision.emit($event)\" />\n }\n }\n }\n </article>\n</ng-template>\n\n<ng-template #workingTemplate>\n <p class=\"working\" role=\"status\">\n <span class=\"dots\" aria-hidden=\"true\"><span></span><span></span><span></span></span>\n {{ 'nh-assistant.thread.working' | translate }}\n @if (elapsedSeconds() > 0) {\n <span class=\"elapsed\">{{ 'nh-assistant.thread.elapsed' | translate: { seconds: elapsedSeconds() } }}</span>\n }\n </p>\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))}}:host{display:flex;flex-direction:column;min-height:0;color:var(--nha-text)}.log{display:flex;flex:1;flex-direction:column;min-height:0}.scroller{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:16px;outline:none}.scroller:focus-visible{box-shadow:inset 0 0 0 2px var(--nha-focus)}cdk-virtual-scroll-viewport.scroller{height:100%}.row+.row{margin-top:20px}.message{display:flex;flex-direction:column;align-items:flex-start;gap:6px;min-width:0}.message[data-role=user]{align-items:flex-end}.author{margin:0;color:var(--nha-text-muted);font-size:11px;font-weight:600}.bubble{max-width:100%;border-radius:var(--nha-radius-control);padding:8px 12px;font-size:14px;line-height:1.5;overflow-wrap:anywhere}.plain{white-space:pre-wrap}.message[data-role=user] .bubble{max-width:88%;color:var(--nha-user-text);background:var(--nha-user-bubble)}.message[data-others=true]{align-items:flex-start}.message[data-others=true] .bubble{color:var(--nha-text);background:var(--nha-surface-strong)}.message[data-role=assistant] .bubble,.message[data-role=tool] .bubble{padding-inline:0;background:transparent}.part{width:100%}:host ::ng-deep .markdown :first-child{margin-top:0}:host ::ng-deep .markdown :last-child{margin-bottom:0}:host ::ng-deep .markdown p,:host ::ng-deep .markdown ul,:host ::ng-deep .markdown ol,:host ::ng-deep .markdown pre,:host ::ng-deep .markdown blockquote,:host ::ng-deep .markdown table{margin:0 0 8px}:host ::ng-deep .markdown ul,:host ::ng-deep .markdown ol{padding-left:20px}:host ::ng-deep .markdown h1,:host ::ng-deep .markdown h2,:host ::ng-deep .markdown h3,:host ::ng-deep .markdown h4,:host ::ng-deep .markdown h5,:host ::ng-deep .markdown h6{margin:10px 0 6px;font-size:15px;line-height:1.3}:host ::ng-deep .markdown a{color:var(--nha-highlight);text-decoration:underline}:host ::ng-deep .markdown a:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host ::ng-deep .markdown code{border-radius:6px;padding:1px 4px;background:var(--nha-surface-strong);font-family:var(--nha-mono);font-size:12.5px}:host ::ng-deep .markdown pre{overflow-x:auto;border-radius:8px;padding:8px;background:var(--nha-surface-strong)}:host ::ng-deep .markdown pre code{padding:0;background:none}:host ::ng-deep .markdown blockquote{border-left:3px solid var(--nha-border);padding-left:10px;color:var(--nha-text-muted)}:host ::ng-deep .markdown table{display:block;max-width:100%;overflow-x:auto;border-collapse:collapse;font-size:13px}:host ::ng-deep .markdown th,:host ::ng-deep .markdown td{border:1px solid var(--nha-border);padding:4px 8px;text-align:left}.working{display:flex;align-items:center;gap:8px;margin:14px 0 0;color:var(--nha-text-muted);font-size:13px}.elapsed{font-variant-numeric:tabular-nums}.dots{display:inline-flex;gap:3px}.dots span{width:6px;height:6px;border-radius:50%;background:currentColor;opacity:.6}@media(prefers-reduced-motion:no-preference){.dots span{animation:nha-bounce 1s ease-in-out infinite}.dots span:nth-child(2){animation-delay:.15s}.dots span:nth-child(3){animation-delay:.3s}}@keyframes nha-bounce{0%,80%,to{transform:translateY(0);opacity:.35}40%{transform:translateY(-3px);opacity:1}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i1.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i1.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i1.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "component", type: NhAssistantToolCallGroupComponent, selector: "nh-assistant-tool-call-group", inputs: ["calls"] }, { kind: "component", type: NhAssistantApprovalCardComponent, selector: "nh-assistant-approval-card", inputs: ["approval", "deciding", "canDecide", "decisionOwner"], outputs: ["decide"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: NhAssistantMarkdownPipe, name: "nhAssistantMarkdown" }, { kind: "pipe", type: NhAssistantPartGroupsPipe, name: "nhAssistantPartGroups" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3463
4872
|
}
|
|
3464
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
4873
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantThreadComponent, decorators: [{
|
|
3465
4874
|
type: Component,
|
|
3466
4875
|
args: [{ selector: 'nh-assistant-thread', standalone: true, imports: [
|
|
3467
4876
|
NgTemplateOutlet,
|
|
@@ -3471,18 +4880,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.28", ngImpo
|
|
|
3471
4880
|
NhAssistantPartGroupsPipe,
|
|
3472
4881
|
NhAssistantToolCallGroupComponent,
|
|
3473
4882
|
NhAssistantApprovalCardComponent
|
|
3474
|
-
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"log\" role=\"log\" aria-live=\"polite\" [attr.aria-label]=\"'nh-assistant.thread.label' | translate\" [attr.aria-busy]=\"streaming()\">\n @if (virtual()) {\n <cdk-virtual-scroll-viewport class=\"scroller\" itemSize=\"120\" minBufferPx=\"1200\" maxBufferPx=\"2400\">\n <div class=\"row\" *cdkVirtualFor=\"let message of messages(); trackBy: trackMessage\">\n <ng-container [ngTemplateOutlet]=\"messageTemplate\" [ngTemplateOutletContext]=\"{ $implicit: message }\" />\n </div>\n @if (working()) {\n <ng-container [ngTemplateOutlet]=\"workingTemplate\" />\n }\n </cdk-virtual-scroll-viewport>\n } @else {\n <div class=\"scroller\" #scroller tabindex=\"0\">\n @for (message of messages(); track message.id) {\n <div class=\"row\">\n <ng-container [ngTemplateOutlet]=\"messageTemplate\" [ngTemplateOutletContext]=\"{ $implicit: message }\" />\n </div>\n }\n @if (working()) {\n <ng-container [ngTemplateOutlet]=\"workingTemplate\" />\n }\n </div>\n }\n</div>\n\n<ng-template #messageTemplate let-message>\n <article class=\"message\" [attr.data-role]=\"message.role\">\n <p class=\"author\">{{ 'nh-assistant.thread.' + (message.role === 'user' ? 'you' : message.role) | translate }}</p>\n @for (group of message.parts | nhAssistantPartGroups; track group.key) {\n @switch (group.kind) {\n @case ('text') {\n @if (message.role === 'assistant' && markdown()) {\n <div class=\"bubble markdown\" [innerHTML]=\"group.part.text | nhAssistantMarkdown\"></div>\n } @else {\n <div class=\"bubble plain\">{{ group.part.text }}</div>\n }\n }\n @case ('tools') {\n <nh-assistant-tool-call-group class=\"part\" [calls]=\"group.calls\" />\n }\n @case ('approval') {\n <nh-assistant-approval-card
|
|
3475
|
-
}], ctorParameters: () => [], propDecorators: { messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: true }] }], streaming: [{ type: i0.Input, args: [{ isSignal: true, alias: "streaming", required: false }] }], deciding: [{ type: i0.Input, args: [{ isSignal: true, alias: "deciding", required: false }] }], markdown: [{ type: i0.Input, args: [{ isSignal: true, alias: "markdown", required: false }] }], approvalDecision: [{ type: i0.Output, args: ["approvalDecision"] }], scroller: [{ type: i0.ViewChild, args: ['scroller', { isSignal: true }] }], viewport: [{ type: i0.ViewChild, args: [i0.forwardRef(() => CdkVirtualScrollViewport), { isSignal: true }] }] } });
|
|
4883
|
+
], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"log\" role=\"log\" aria-live=\"polite\" [attr.aria-label]=\"'nh-assistant.thread.label' | translate\" [attr.aria-busy]=\"streaming()\">\n @if (virtual()) {\n <cdk-virtual-scroll-viewport class=\"scroller\" itemSize=\"120\" minBufferPx=\"1200\" maxBufferPx=\"2400\">\n <div class=\"row\" *cdkVirtualFor=\"let message of messages(); trackBy: trackMessage\">\n <ng-container [ngTemplateOutlet]=\"messageTemplate\" [ngTemplateOutletContext]=\"{ $implicit: message }\" />\n </div>\n @if (working()) {\n <ng-container [ngTemplateOutlet]=\"workingTemplate\" />\n }\n </cdk-virtual-scroll-viewport>\n } @else {\n <div class=\"scroller\" #scroller tabindex=\"0\">\n @for (message of messages(); track message.id) {\n <div class=\"row\">\n <ng-container [ngTemplateOutlet]=\"messageTemplate\" [ngTemplateOutletContext]=\"{ $implicit: message }\" />\n </div>\n }\n @if (working()) {\n <ng-container [ngTemplateOutlet]=\"workingTemplate\" />\n }\n </div>\n }\n</div>\n\n<ng-template #messageTemplate let-message>\n <article class=\"message\" [attr.data-role]=\"message.role\" [attr.data-others]=\"isOthers(message) ? 'true' : null\">\n @if (isOthers(message)) {\n <p class=\"author\">{{ authorName(message) ?? ('nh-assistant.thread.participant' | translate) }}</p>\n } @else {\n <p class=\"author\">{{ 'nh-assistant.thread.' + (message.role === 'user' ? 'you' : message.role) | translate }}</p>\n }\n @for (group of message.parts | nhAssistantPartGroups; track group.key) {\n @switch (group.kind) {\n @case ('text') {\n @if (message.role === 'assistant' && markdown()) {\n <div class=\"bubble markdown\" [innerHTML]=\"group.part.text | nhAssistantMarkdown\"></div>\n } @else {\n <div class=\"bubble plain\">{{ group.part.text }}</div>\n }\n }\n @case ('tools') {\n <nh-assistant-tool-call-group class=\"part\" [calls]=\"group.calls\" />\n }\n @case ('approval') {\n <nh-assistant-approval-card\n class=\"part\"\n [approval]=\"group.part\"\n [deciding]=\"deciding()\"\n [canDecide]=\"canDecide()\"\n [decisionOwner]=\"decisionOwner()\"\n (decide)=\"approvalDecision.emit($event)\" />\n }\n }\n }\n </article>\n</ng-template>\n\n<ng-template #workingTemplate>\n <p class=\"working\" role=\"status\">\n <span class=\"dots\" aria-hidden=\"true\"><span></span><span></span><span></span></span>\n {{ 'nh-assistant.thread.working' | translate }}\n @if (elapsedSeconds() > 0) {\n <span class=\"elapsed\">{{ 'nh-assistant.thread.elapsed' | translate: { seconds: elapsedSeconds() } }}</span>\n }\n </p>\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))}}:host{display:flex;flex-direction:column;min-height:0;color:var(--nha-text)}.log{display:flex;flex:1;flex-direction:column;min-height:0}.scroller{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:16px;outline:none}.scroller:focus-visible{box-shadow:inset 0 0 0 2px var(--nha-focus)}cdk-virtual-scroll-viewport.scroller{height:100%}.row+.row{margin-top:20px}.message{display:flex;flex-direction:column;align-items:flex-start;gap:6px;min-width:0}.message[data-role=user]{align-items:flex-end}.author{margin:0;color:var(--nha-text-muted);font-size:11px;font-weight:600}.bubble{max-width:100%;border-radius:var(--nha-radius-control);padding:8px 12px;font-size:14px;line-height:1.5;overflow-wrap:anywhere}.plain{white-space:pre-wrap}.message[data-role=user] .bubble{max-width:88%;color:var(--nha-user-text);background:var(--nha-user-bubble)}.message[data-others=true]{align-items:flex-start}.message[data-others=true] .bubble{color:var(--nha-text);background:var(--nha-surface-strong)}.message[data-role=assistant] .bubble,.message[data-role=tool] .bubble{padding-inline:0;background:transparent}.part{width:100%}:host ::ng-deep .markdown :first-child{margin-top:0}:host ::ng-deep .markdown :last-child{margin-bottom:0}:host ::ng-deep .markdown p,:host ::ng-deep .markdown ul,:host ::ng-deep .markdown ol,:host ::ng-deep .markdown pre,:host ::ng-deep .markdown blockquote,:host ::ng-deep .markdown table{margin:0 0 8px}:host ::ng-deep .markdown ul,:host ::ng-deep .markdown ol{padding-left:20px}:host ::ng-deep .markdown h1,:host ::ng-deep .markdown h2,:host ::ng-deep .markdown h3,:host ::ng-deep .markdown h4,:host ::ng-deep .markdown h5,:host ::ng-deep .markdown h6{margin:10px 0 6px;font-size:15px;line-height:1.3}:host ::ng-deep .markdown a{color:var(--nha-highlight);text-decoration:underline}:host ::ng-deep .markdown a:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host ::ng-deep .markdown code{border-radius:6px;padding:1px 4px;background:var(--nha-surface-strong);font-family:var(--nha-mono);font-size:12.5px}:host ::ng-deep .markdown pre{overflow-x:auto;border-radius:8px;padding:8px;background:var(--nha-surface-strong)}:host ::ng-deep .markdown pre code{padding:0;background:none}:host ::ng-deep .markdown blockquote{border-left:3px solid var(--nha-border);padding-left:10px;color:var(--nha-text-muted)}:host ::ng-deep .markdown table{display:block;max-width:100%;overflow-x:auto;border-collapse:collapse;font-size:13px}:host ::ng-deep .markdown th,:host ::ng-deep .markdown td{border:1px solid var(--nha-border);padding:4px 8px;text-align:left}.working{display:flex;align-items:center;gap:8px;margin:14px 0 0;color:var(--nha-text-muted);font-size:13px}.elapsed{font-variant-numeric:tabular-nums}.dots{display:inline-flex;gap:3px}.dots span{width:6px;height:6px;border-radius:50%;background:currentColor;opacity:.6}@media(prefers-reduced-motion:no-preference){.dots span{animation:nha-bounce 1s ease-in-out infinite}.dots span:nth-child(2){animation-delay:.15s}.dots span:nth-child(3){animation-delay:.3s}}@keyframes nha-bounce{0%,80%,to{transform:translateY(0);opacity:.35}40%{transform:translateY(-3px);opacity:1}}\n"] }]
|
|
4884
|
+
}], ctorParameters: () => [], propDecorators: { messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: true }] }], streaming: [{ type: i0.Input, args: [{ isSignal: true, alias: "streaming", required: false }] }], deciding: [{ type: i0.Input, args: [{ isSignal: true, alias: "deciding", required: false }] }], markdown: [{ type: i0.Input, args: [{ isSignal: true, alias: "markdown", required: false }] }], members: [{ type: i0.Input, args: [{ isSignal: true, alias: "members", required: false }] }], currentActorId: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentActorId", required: false }] }], canDecide: [{ type: i0.Input, args: [{ isSignal: true, alias: "canDecide", required: false }] }], decisionOwner: [{ type: i0.Input, args: [{ isSignal: true, alias: "decisionOwner", required: false }] }], approvalDecision: [{ type: i0.Output, args: ["approvalDecision"] }], scroller: [{ type: i0.ViewChild, args: ['scroller', { isSignal: true }] }], viewport: [{ type: i0.ViewChild, args: [i0.forwardRef(() => CdkVirtualScrollViewport), { isSignal: true }] }] } });
|
|
3476
4885
|
|
|
3477
4886
|
let nextId = 0;
|
|
3478
4887
|
/**
|
|
3479
4888
|
* The assistant drawer. Place it once in the application layout; it renders nothing in
|
|
3480
|
-
* place and appears as an overlay when `NhAssistantPanelService.open()` is called.
|
|
4889
|
+
* place and appears as an overlay when `NhAssistantPanelService.open()` is called. It opens
|
|
4890
|
+
* by itself for an invitation link (`#nh-assistant-join=...`) and for a clicked notification.
|
|
3481
4891
|
*/
|
|
3482
4892
|
class NhAssistantPanelComponent {
|
|
3483
4893
|
constructor() {
|
|
3484
4894
|
this.store = inject(NhAssistantStore);
|
|
3485
4895
|
this.panel = inject(NhAssistantPanelService);
|
|
4896
|
+
this.push = inject(NhAssistantPushService);
|
|
3486
4897
|
this.config = inject(NH_ASSISTANT_CONFIG);
|
|
3487
4898
|
this.viewContainerRef = inject(ViewContainerRef);
|
|
3488
4899
|
this.router = inject(Router, { optional: true });
|
|
@@ -3491,6 +4902,11 @@ class NhAssistantPanelComponent {
|
|
|
3491
4902
|
this.markdown = this.config.markdown?.enabled ?? true;
|
|
3492
4903
|
this.showConversations = signal(false, ...(ngDevMode ? [{ debugName: "showConversations" }] : []));
|
|
3493
4904
|
this.showPreferences = signal(false, ...(ngDevMode ? [{ debugName: "showPreferences" }] : []));
|
|
4905
|
+
this.showShare = signal(false, ...(ngDevMode ? [{ debugName: "showShare" }] : []));
|
|
4906
|
+
this.shareSection = signal('all', ...(ngDevMode ? [{ debugName: "shareSection" }] : []));
|
|
4907
|
+
this.notificationError = signal(null, ...(ngDevMode ? [{ debugName: "notificationError" }] : []));
|
|
4908
|
+
this.notificationsLabel = computed(() => this.push.settings()?.pushEnabled
|
|
4909
|
+
? 'nh-assistant.panel.notifications-off' : 'nh-assistant.panel.notifications-on', ...(ngDevMode ? [{ debugName: "notificationsLabel" }] : []));
|
|
3494
4910
|
this.adminRoute = this.config.adminRoute ?? null;
|
|
3495
4911
|
this.conversation = this.store.activeConversation;
|
|
3496
4912
|
this.conversationId = computed(() => this.conversation()?.id ?? null, ...(ngDevMode ? [{ debugName: "conversationId" }] : []));
|
|
@@ -3507,8 +4923,17 @@ class NhAssistantPanelComponent {
|
|
|
3507
4923
|
}
|
|
3508
4924
|
return this.conversation()?.status === 'waiting-for-approval' ? 'waiting-for-approval' : null;
|
|
3509
4925
|
}, ...(ngDevMode ? [{ debugName: "composerStatus" }] : []));
|
|
4926
|
+
/** Name of another participant whose turn runs or waits, or `null`. */
|
|
4927
|
+
this.activeMemberName = computed(() => {
|
|
4928
|
+
const member = this.store.activeMember();
|
|
4929
|
+
return member ? member.displayName ?? '' : null;
|
|
4930
|
+
}, ...(ngDevMode ? [{ debugName: "activeMemberName" }] : []));
|
|
4931
|
+
/** The owner and the person who started the turn may stop it. */
|
|
4932
|
+
this.canStop = computed(() => this.store.isOwner() || this.store.activeMember() === null, ...(ngDevMode ? [{ debugName: "canStop" }] : []));
|
|
4933
|
+
this.shared = computed(() => this.store.members().some(member => member.role === 'participant'), ...(ngDevMode ? [{ debugName: "shared" }] : []));
|
|
4934
|
+
this.canShare = computed(() => this.conversation() !== null && !!this.store.collaboration(), ...(ngDevMode ? [{ debugName: "canShare" }] : []));
|
|
3510
4935
|
this.errorKeys = computed(() => {
|
|
3511
|
-
const error = this.store.error();
|
|
4936
|
+
const error = this.notificationError() ?? this.store.error();
|
|
3512
4937
|
return error ? [error.messageKey, `nh-assistant.errors.${error.code}`, 'nh-assistant.errors.generic'] : [];
|
|
3513
4938
|
}, ...(ngDevMode ? [{ debugName: "errorKeys" }] : []));
|
|
3514
4939
|
this.noticeKeys = computed(() => {
|
|
@@ -3521,9 +4946,18 @@ class NhAssistantPanelComponent {
|
|
|
3521
4946
|
untracked(() => {
|
|
3522
4947
|
this.showConversations.set(false);
|
|
3523
4948
|
this.showPreferences.set(false);
|
|
4949
|
+
this.showShare.set(false);
|
|
3524
4950
|
});
|
|
3525
4951
|
}
|
|
3526
4952
|
});
|
|
4953
|
+
// The open thread counts as seen only while the drawer shows it.
|
|
4954
|
+
effect(() => {
|
|
4955
|
+
const viewing = this.panel.isOpen() && !this.showConversations() && !this.showPreferences() && !this.showShare();
|
|
4956
|
+
untracked(() => this.store.setViewing(viewing));
|
|
4957
|
+
});
|
|
4958
|
+
// A clicked notification opens its conversation in this window.
|
|
4959
|
+
const notifications = this.push.openConversation$.subscribe(conversationId => this.panel.open(conversationId));
|
|
4960
|
+
inject(DestroyRef).onDestroy(() => notifications.unsubscribe());
|
|
3527
4961
|
effect(() => {
|
|
3528
4962
|
if (this.panel.isOpen()) {
|
|
3529
4963
|
untracked(() => {
|
|
@@ -3532,7 +4966,7 @@ class NhAssistantPanelComponent {
|
|
|
3532
4966
|
});
|
|
3533
4967
|
}
|
|
3534
4968
|
});
|
|
3535
|
-
// The drawer stays open while the user navigates; keep the page-context
|
|
4969
|
+
// The drawer stays open while the user navigates; keep the page-context control current.
|
|
3536
4970
|
const navigation = this.router?.events.subscribe(event => {
|
|
3537
4971
|
if (event instanceof NavigationEnd && this.panel.isOpen()) {
|
|
3538
4972
|
this.refreshPageContext();
|
|
@@ -3543,6 +4977,7 @@ class NhAssistantPanelComponent {
|
|
|
3543
4977
|
ngAfterViewInit() {
|
|
3544
4978
|
this.portal = new TemplatePortal(this.drawer(), this.viewContainerRef);
|
|
3545
4979
|
this.panel.registerPanel(this.portal);
|
|
4980
|
+
this.openFromLocation();
|
|
3546
4981
|
}
|
|
3547
4982
|
ngOnDestroy() {
|
|
3548
4983
|
if (this.portal) {
|
|
@@ -3557,12 +4992,36 @@ class NhAssistantPanelComponent {
|
|
|
3557
4992
|
}
|
|
3558
4993
|
toggleConversations() {
|
|
3559
4994
|
this.showPreferences.set(false);
|
|
4995
|
+
this.showShare.set(false);
|
|
3560
4996
|
this.showConversations.update(show => !show);
|
|
3561
4997
|
}
|
|
3562
4998
|
togglePreferences() {
|
|
3563
4999
|
this.showConversations.set(false);
|
|
5000
|
+
this.showShare.set(false);
|
|
3564
5001
|
this.showPreferences.update(show => !show);
|
|
3565
5002
|
}
|
|
5003
|
+
toggleShare(section = 'all') {
|
|
5004
|
+
this.showConversations.set(false);
|
|
5005
|
+
this.showPreferences.set(false);
|
|
5006
|
+
const close = this.showShare() && this.shareSection() === section;
|
|
5007
|
+
this.shareSection.set(section);
|
|
5008
|
+
this.showShare.set(!close);
|
|
5009
|
+
}
|
|
5010
|
+
/** Applies the notification choice immediately, including browser permission handling. */
|
|
5011
|
+
async toggleNotifications() {
|
|
5012
|
+
if (this.push.busy() || this.push.state() === 'unavailable') {
|
|
5013
|
+
return;
|
|
5014
|
+
}
|
|
5015
|
+
this.notificationError.set(null);
|
|
5016
|
+
try {
|
|
5017
|
+
await this.push.setEnabled(!this.push.settings()?.pushEnabled);
|
|
5018
|
+
}
|
|
5019
|
+
catch (error) {
|
|
5020
|
+
this.notificationError.set(error instanceof NhAssistantApiError
|
|
5021
|
+
? { code: error.code, messageKey: error.messageKey }
|
|
5022
|
+
: { code: 'assistant-server', messageKey: nhAssistantErrorMessageKey('assistant-server') });
|
|
5023
|
+
}
|
|
5024
|
+
}
|
|
3566
5025
|
newConversation() {
|
|
3567
5026
|
this.store.startNewConversation();
|
|
3568
5027
|
this.showConversations.set(false);
|
|
@@ -3577,30 +5036,60 @@ class NhAssistantPanelComponent {
|
|
|
3577
5036
|
send(text) {
|
|
3578
5037
|
this.showConversations.set(false);
|
|
3579
5038
|
this.showPreferences.set(false);
|
|
5039
|
+
this.showShare.set(false);
|
|
3580
5040
|
void this.store.send(text);
|
|
3581
5041
|
}
|
|
5042
|
+
/**
|
|
5043
|
+
* Opens the panel for an invitation link or a clicked notification in the URL fragment and
|
|
5044
|
+
* removes the fragment, so a reload or a shared screenshot does not reuse the token.
|
|
5045
|
+
*/
|
|
5046
|
+
openFromLocation() {
|
|
5047
|
+
const location = globalThis.location;
|
|
5048
|
+
const hash = location?.hash?.replace(/^#/, '') ?? '';
|
|
5049
|
+
const separatorIndex = hash.indexOf('=');
|
|
5050
|
+
const name = separatorIndex < 0 ? hash : hash.slice(0, separatorIndex);
|
|
5051
|
+
const value = separatorIndex < 0 ? '' : hash.slice(separatorIndex + 1);
|
|
5052
|
+
if (!value || (name !== NH_ASSISTANT_JOIN_FRAGMENT && name !== NH_ASSISTANT_CONVERSATION_FRAGMENT)) {
|
|
5053
|
+
return;
|
|
5054
|
+
}
|
|
5055
|
+
globalThis.history?.replaceState(globalThis.history.state, '', location.pathname + location.search);
|
|
5056
|
+
if (name === NH_ASSISTANT_CONVERSATION_FRAGMENT) {
|
|
5057
|
+
this.panel.open(decodeURIComponent(value));
|
|
5058
|
+
return;
|
|
5059
|
+
}
|
|
5060
|
+
const tokenIndex = value.indexOf('.');
|
|
5061
|
+
if (tokenIndex <= 0) {
|
|
5062
|
+
return;
|
|
5063
|
+
}
|
|
5064
|
+
const conversationId = decodeURIComponent(value.slice(0, tokenIndex));
|
|
5065
|
+
const token = decodeURIComponent(value.slice(tokenIndex + 1));
|
|
5066
|
+
this.panel.open();
|
|
5067
|
+
void this.store.initialize().then(() => this.store.joinConversation(conversationId, token));
|
|
5068
|
+
}
|
|
3582
5069
|
decide(decision) {
|
|
3583
5070
|
this.store.decide(decision);
|
|
3584
5071
|
}
|
|
3585
5072
|
cancel() {
|
|
3586
5073
|
void this.store.cancel();
|
|
3587
5074
|
}
|
|
3588
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
3589
|
-
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 }); }
|
|
5075
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
5076
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.33", 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 </div>\n <div class=\"header-actions\">\n @if (store.enabled()) {\n @if (push.state() !== 'unavailable') {\n <button\n class=\"icon-button notification-button\"\n type=\"button\"\n [class.active]=\"push.state() === 'on'\"\n [class.off]=\"!push.settings()?.pushEnabled\"\n [class.blocked]=\"push.state() === 'blocked'\"\n [disabled]=\"push.busy()\"\n [attr.aria-pressed]=\"push.settings()?.pushEnabled === true\"\n [attr.aria-label]=\"notificationsLabel() | translate\"\n [attr.title]=\"('nh-assistant.preferences.notifications-state.' + push.state() | translate) + ' \u00B7 ' + (notificationsLabel() | translate)\"\n (click)=\"toggleNotifications()\">\n <nh-assistant-icon name=\"bell\" />\n </button>\n }\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 [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 @if (store.enabled()) {\n <nh-assistant-activity [items]=\"store.activity()\" (conversationSelect)=\"openConversation($event)\" />\n }\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 (showShare()) {\n <nh-assistant-share class=\"overlay-view\" [section]=\"shareSection()\" (closed)=\"showShare.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 (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 [attr.inert]=\"showPreferences() || showShare() || showConversations() ? '' : null\"\n [messages]=\"messages()\"\n [streaming]=\"busy()\"\n [deciding]=\"store.deciding()\"\n [markdown]=\"markdown\"\n [members]=\"store.members()\"\n [currentActorId]=\"conversation()?.currentActorId ?? null\"\n [canDecide]=\"store.canDecide()\"\n [decisionOwner]=\"activeMemberName()\"\n (approvalDecision)=\"decide($event)\" />\n }\n }\n </div>\n\n @if (notificationError() ?? 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(); notificationError.set(null)\">\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() && !showShare()) {\n <footer class=\"footer\" (focusin)=\"refreshPageContext()\">\n @if (closed()) {\n <p class=\"notice\">{{ 'nh-assistant.panel.conversation-closed' | translate }}</p>\n }\n @if (store.collaboration() || store.pageContext()) {\n <div class=\"conversation-actions\">\n @if (store.collaboration()) {\n @if (!conversation() || store.isOwner()) {\n <button class=\"icon-button\" type=\"button\" [disabled]=\"!canShare()\"\n [attr.aria-label]=\"'nh-assistant.panel.share' | translate\"\n [attr.title]=\"'nh-assistant.panel.share' | translate\"\n [attr.aria-expanded]=\"showShare() && shareSection() === 'link'\"\n (click)=\"toggleShare('link')\">\n <nh-assistant-icon name=\"link\" />\n </button>\n }\n <button class=\"icon-button\" type=\"button\" [disabled]=\"!canShare()\"\n [class.active]=\"shared()\"\n [attr.aria-label]=\"(store.isOwner() || !conversation() ? 'nh-assistant.panel.add-members' : 'nh-assistant.panel.members') | translate\"\n [attr.title]=\"(store.isOwner() || !conversation() ? 'nh-assistant.panel.add-members' : 'nh-assistant.panel.members') | translate\"\n [attr.aria-expanded]=\"showShare() && shareSection() === 'members'\"\n (click)=\"toggleShare('members')\">\n <nh-assistant-icon name=\"users\" />\n </button>\n }\n @if (store.pageContext(); as context) {\n <button\n class=\"icon-button page-context-button\"\n [class.active]=\"!store.pageContextExcluded()\"\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=\"page\" />\n </button>\n }\n </div>\n }\n <nh-assistant-composer\n [disabled]=\"closed()\"\n [sendDisabled]=\"composerDisabled()\"\n [busy]=\"busy()\"\n [status]=\"composerStatus()\"\n [statusActor]=\"activeMemberName()\"\n [canStop]=\"canStop()\"\n [maxLength]=\"store.limits()?.maxMessageChars ?? null\"\n [restore]=\"store.restoredDraft()\"\n (restored)=\"store.consumeRestoredDraft()\"\n (send)=\"send($event)\"\n (cancel)=\"cancel()\">\n <nh-assistant-agent-picker\n nhAssistantComposerControls\n [compact]=\"true\"\n [agents]=\"store.agents()\"\n [selectedAgentId]=\"store.selectedAgentId()\"\n (agentChange)=\"store.selectAgent($event)\" />\n </nh-assistant-composer>\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}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:2px;min-width:0}.header-actions .icon-button{width:32px;height:32px}.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{padding:12px 16px 14px}.conversation-actions{display:flex;align-items:center;gap:4px;margin:0 -4px 6px}.page-context-button{margin-left:auto}.notification-button{position:relative}.notification-button.off:after{content:\"\";position:absolute;width:18px;height:2px;border-radius:1px;background:currentColor;transform:rotate(-45deg)}.notification-button.blocked{color:var(--nha-warning)}@media(max-width:359px){.header{gap:6px;padding-inline:12px}.header-actions .icon-button{width:28px;height:32px}}.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: NhAssistantActivityComponent, selector: "nh-assistant-activity", inputs: ["items"], outputs: ["conversationSelect"] }, { kind: "component", type: NhAssistantAgentPickerComponent, selector: "nh-assistant-agent-picker", inputs: ["agents", "selectedAgentId", "disabled", "compact"], outputs: ["agentChange"] }, { kind: "component", type: NhAssistantComposerComponent, selector: "nh-assistant-composer", inputs: ["disabled", "sendDisabled", "busy", "status", "statusActor", "canStop", "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: NhAssistantShareComponent, selector: "nh-assistant-share", inputs: ["section"], outputs: ["closed"] }, { kind: "component", type: NhAssistantThreadComponent, selector: "nh-assistant-thread", inputs: ["messages", "streaming", "deciding", "markdown", "members", "currentActorId", "canDecide", "decisionOwner"], outputs: ["approvalDecision"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: NhAssistantTranslatePipe, name: "nhAssistantTranslate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3590
5077
|
}
|
|
3591
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
5078
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantPanelComponent, decorators: [{
|
|
3592
5079
|
type: Component,
|
|
3593
5080
|
args: [{ selector: 'nh-assistant-panel', standalone: true, imports: [
|
|
3594
5081
|
RouterLink,
|
|
3595
5082
|
TranslatePipe,
|
|
3596
5083
|
NhAssistantTranslatePipe,
|
|
3597
5084
|
NhAssistantIconComponent,
|
|
5085
|
+
NhAssistantActivityComponent,
|
|
3598
5086
|
NhAssistantAgentPickerComponent,
|
|
3599
5087
|
NhAssistantComposerComponent,
|
|
3600
5088
|
NhAssistantConversationListComponent,
|
|
3601
5089
|
NhAssistantPreferencesComponent,
|
|
5090
|
+
NhAssistantShareComponent,
|
|
3602
5091
|
NhAssistantThreadComponent
|
|
3603
|
-
], 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"] }]
|
|
5092
|
+
], 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 </div>\n <div class=\"header-actions\">\n @if (store.enabled()) {\n @if (push.state() !== 'unavailable') {\n <button\n class=\"icon-button notification-button\"\n type=\"button\"\n [class.active]=\"push.state() === 'on'\"\n [class.off]=\"!push.settings()?.pushEnabled\"\n [class.blocked]=\"push.state() === 'blocked'\"\n [disabled]=\"push.busy()\"\n [attr.aria-pressed]=\"push.settings()?.pushEnabled === true\"\n [attr.aria-label]=\"notificationsLabel() | translate\"\n [attr.title]=\"('nh-assistant.preferences.notifications-state.' + push.state() | translate) + ' \u00B7 ' + (notificationsLabel() | translate)\"\n (click)=\"toggleNotifications()\">\n <nh-assistant-icon name=\"bell\" />\n </button>\n }\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 [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 @if (store.enabled()) {\n <nh-assistant-activity [items]=\"store.activity()\" (conversationSelect)=\"openConversation($event)\" />\n }\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 (showShare()) {\n <nh-assistant-share class=\"overlay-view\" [section]=\"shareSection()\" (closed)=\"showShare.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 (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 [attr.inert]=\"showPreferences() || showShare() || showConversations() ? '' : null\"\n [messages]=\"messages()\"\n [streaming]=\"busy()\"\n [deciding]=\"store.deciding()\"\n [markdown]=\"markdown\"\n [members]=\"store.members()\"\n [currentActorId]=\"conversation()?.currentActorId ?? null\"\n [canDecide]=\"store.canDecide()\"\n [decisionOwner]=\"activeMemberName()\"\n (approvalDecision)=\"decide($event)\" />\n }\n }\n </div>\n\n @if (notificationError() ?? 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(); notificationError.set(null)\">\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() && !showShare()) {\n <footer class=\"footer\" (focusin)=\"refreshPageContext()\">\n @if (closed()) {\n <p class=\"notice\">{{ 'nh-assistant.panel.conversation-closed' | translate }}</p>\n }\n @if (store.collaboration() || store.pageContext()) {\n <div class=\"conversation-actions\">\n @if (store.collaboration()) {\n @if (!conversation() || store.isOwner()) {\n <button class=\"icon-button\" type=\"button\" [disabled]=\"!canShare()\"\n [attr.aria-label]=\"'nh-assistant.panel.share' | translate\"\n [attr.title]=\"'nh-assistant.panel.share' | translate\"\n [attr.aria-expanded]=\"showShare() && shareSection() === 'link'\"\n (click)=\"toggleShare('link')\">\n <nh-assistant-icon name=\"link\" />\n </button>\n }\n <button class=\"icon-button\" type=\"button\" [disabled]=\"!canShare()\"\n [class.active]=\"shared()\"\n [attr.aria-label]=\"(store.isOwner() || !conversation() ? 'nh-assistant.panel.add-members' : 'nh-assistant.panel.members') | translate\"\n [attr.title]=\"(store.isOwner() || !conversation() ? 'nh-assistant.panel.add-members' : 'nh-assistant.panel.members') | translate\"\n [attr.aria-expanded]=\"showShare() && shareSection() === 'members'\"\n (click)=\"toggleShare('members')\">\n <nh-assistant-icon name=\"users\" />\n </button>\n }\n @if (store.pageContext(); as context) {\n <button\n class=\"icon-button page-context-button\"\n [class.active]=\"!store.pageContextExcluded()\"\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=\"page\" />\n </button>\n }\n </div>\n }\n <nh-assistant-composer\n [disabled]=\"closed()\"\n [sendDisabled]=\"composerDisabled()\"\n [busy]=\"busy()\"\n [status]=\"composerStatus()\"\n [statusActor]=\"activeMemberName()\"\n [canStop]=\"canStop()\"\n [maxLength]=\"store.limits()?.maxMessageChars ?? null\"\n [restore]=\"store.restoredDraft()\"\n (restored)=\"store.consumeRestoredDraft()\"\n (send)=\"send($event)\"\n (cancel)=\"cancel()\">\n <nh-assistant-agent-picker\n nhAssistantComposerControls\n [compact]=\"true\"\n [agents]=\"store.agents()\"\n [selectedAgentId]=\"store.selectedAgentId()\"\n (agentChange)=\"store.selectAgent($event)\" />\n </nh-assistant-composer>\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}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:2px;min-width:0}.header-actions .icon-button{width:32px;height:32px}.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{padding:12px 16px 14px}.conversation-actions{display:flex;align-items:center;gap:4px;margin:0 -4px 6px}.page-context-button{margin-left:auto}.notification-button{position:relative}.notification-button.off:after{content:\"\";position:absolute;width:18px;height:2px;border-radius:1px;background:currentColor;transform:rotate(-45deg)}.notification-button.blocked{color:var(--nha-warning)}@media(max-width:359px){.header{gap:6px;padding-inline:12px}.header-actions .icon-button{width:28px;height:32px}}.notice{margin:0 0 8px;color:var(--nha-text-muted);font-size:12px}\n"] }]
|
|
3604
5093
|
}], ctorParameters: () => [], propDecorators: { drawer: [{ type: i0.ViewChild, args: ['drawer', { isSignal: true }] }] } });
|
|
3605
5094
|
|
|
3606
5095
|
/** Converts any failure into a user-facing code and translation key, never raw text. */
|
|
@@ -3625,5 +5114,5 @@ const NH_ASSISTANT_DASH_CASE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
|
3625
5114
|
* Generated bundle index. Do not edit.
|
|
3626
5115
|
*/
|
|
3627
5116
|
|
|
3628
|
-
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, NhAssistantToolCallGroupComponent, 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 };
|
|
5117
|
+
export { NH_ASSISTANT_ACCESS_POLICY, NH_ASSISTANT_CONFIG, NH_ASSISTANT_CONVERSATION_FRAGMENT, NH_ASSISTANT_FETCH, NH_ASSISTANT_ICONS, NH_ASSISTANT_JOIN_FRAGMENT, 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, NhAssistantActivityComponent, NhAssistantAdminApiService, NhAssistantAgentPickerComponent, NhAssistantAllowAllAccessPolicy, NhAssistantApiError, NhAssistantApiService, NhAssistantApprovalCardComponent, NhAssistantClientErrorCodes, NhAssistantComposerComponent, NhAssistantConversationListComponent, NhAssistantLauncherComponent, NhAssistantLiveService, NhAssistantPanelComponent, NhAssistantPanelService, NhAssistantPreferencesComponent, NhAssistantPushService, NhAssistantShareComponent, NhAssistantSseParser, NhAssistantStore, NhAssistantThreadComponent, NhAssistantToolCallCardComponent, NhAssistantToolCallGroupComponent, NhAssistantTranslationMerger, applyNhAssistantApprovalDecision, applyNhAssistantConversationChange, applyNhAssistantEvent, applyNhAssistantLiveEvent, describeNhAssistantClientContext, formatNhAssistantCountdown, nhAssistantCodeForStatus, nhAssistantErrorMessageKey, nhAssistantIsUnread, nhAssistantLatestTurnHasText, nhAssistantShareLink, normalizeNhAssistantClientContext, provideNhAssistant, NH_ASSISTANT_DASH_CASE as ɵNH_ASSISTANT_DASH_CASE, NhAssistantIconComponent as ɵNhAssistantIconComponent, NhAssistantTranslatePipe as ɵNhAssistantTranslatePipe, nhAssistantErrorKeys as ɵnhAssistantErrorKeys, toNhAssistantError as ɵtoNhAssistantError };
|
|
3629
5118
|
//# sourceMappingURL=newheap-platform-ai-chat.mjs.map
|