@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.
@@ -1,10 +1,10 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, inject, DestroyRef, Injectable, EnvironmentInjector, runInInjectionContext, signal, computed, Injector, effect, untracked, afterNextRender, makeEnvironmentProviders, provideEnvironmentInitializer, input, ChangeDetectionStrategy, Component, ChangeDetectorRef, Pipe, output, ElementRef, viewChild, HostListener, ViewContainerRef } from '@angular/core';
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, firstValueFrom, isObservable, Subscription } from 'rxjs';
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.28", ngImport: i0, type: NhAssistantTranslationMerger, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
935
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhAssistantTranslationMerger, providedIn: 'root' }); }
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.28", ngImport: i0, type: NhAssistantTranslationMerger, decorators: [{
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.28", ngImport: i0, type: NhAssistantTransport, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1073
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhAssistantTransport }); }
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.28", ngImport: i0, type: NhAssistantTransport, decorators: [{
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.28", ngImport: i0, type: NhAssistantAdminApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1158
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhAssistantAdminApiService }); }
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.28", ngImport: i0, type: NhAssistantAdminApiService, decorators: [{
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.28", ngImport: i0, type: NhAssistantApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1377
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhAssistantApiService }); }
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.28", ngImport: i0, type: NhAssistantApiService, decorators: [{
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.28", ngImport: i0, type: NhAssistantUiState, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1756
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhAssistantUiState }); }
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.28", ngImport: i0, type: NhAssistantUiState, decorators: [{
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
- * and the active conversation with its streamed turn.
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.activeConversationState = signal(null, ...(ngDevMode ? [{ debugName: "activeConversationState" }] : []));
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
- this.streamingState = signal(false, ...(ngDevMode ? [{ debugName: "streamingState" }] : []));
1790
- this.decidingState = signal(false, ...(ngDevMode ? [{ debugName: "decidingState" }] : []));
1791
- this.errorState = signal(null, ...(ngDevMode ? [{ debugName: "errorState" }] : []));
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
- this.activeConversation = this.activeConversationState.asReadonly();
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.streaming = this.streamingState.asReadonly();
1816
- this.pendingApproval = computed(() => this.activeConversationState()?.pendingApproval ?? null, ...(ngDevMode ? [{ debugName: "pendingApproval" }] : []));
1817
- this.deciding = this.decidingState.asReadonly();
1818
- this.error = this.errorState.asReadonly();
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.noticeState.asReadonly();
1821
- this.lastUsage = this.lastUsageState.asReadonly();
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.activeConversationState();
2468
+ const conversation = this.activeConversation();
1833
2469
  const blocked = conversation !== null && conversation.status !== 'idle';
1834
- return this.enabled() && this.selectedAgentIdState() !== null && !this.streamingState() && !this.conversationLoadingState() && !blocked;
2470
+ return this.enabled() && this.selectedAgentIdState() !== null && !this.streaming() && !this.conversationLoadingState() && !blocked;
1835
2471
  }, ...(ngDevMode ? [{ debugName: "canSend" }] : []));
1836
- inject(DestroyRef).onDestroy(() => {
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.streamSubscription?.unsubscribe();
1839
- clearTimeout(this.cancelTimer);
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.accountRevision++;
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.accountRevision++;
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
- if (!this.activeConversationState() && restored.state.conversationId) {
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 active conversation's agent starts a new one. */
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.activeConversationState();
1950
- if (active && active.agentId !== agentId && !this.streamingState()) {
2642
+ const active = this.activeConversation();
2643
+ if (active && active.agentId !== agentId) {
1951
2644
  this.startNewConversation();
1952
2645
  }
1953
2646
  }
1954
- /** Leaves the active conversation; the next message creates a new one. */
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
- if (this.streamingState()) {
1957
- return;
1958
- }
1959
- this.activeConversationState.set(null);
2652
+ this.activeIdState.set(null);
1960
2653
  this.uiState.update({ conversationId: null });
1961
- this.errorState.set(null);
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
- async loadConversation(conversationId, restoring) {
1969
- if (this.streamingState() || this.activeConversationState()?.id === conversationId) {
1970
- return;
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.streamingState() && this.activeConversationState()?.id === conversationId) {
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.conversationsState.update(items => items.filter(item => item.id !== conversationId));
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
- * active, shows the message optimistically and applies the streamed events.
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.activeConversationState();
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.streamingState.set(false);
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.activeConversationState.set({
2066
- ...conversation,
2067
- status: 'running',
2068
- messages: [
2069
- ...conversation.messages,
2070
- { id: clientMessageId, role: 'user', createdAt: new Date().toISOString(), parts: [{ type: 'text', text: trimmed }] }
2071
- ]
2072
- });
2073
- const conversationId = conversation.id;
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.pendingSendResolve = resolve;
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.restoredDraftState.set(text);
2087
- this.errorState.set(event.data);
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.pendingSendResolve = undefined;
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 conversation = this.activeConversationState();
2779
+ const session = this.activeSession();
2780
+ const conversation = session?.conversation;
2111
2781
  const approval = conversation?.pendingApproval ?? null;
2112
- if (!conversation || !approval || this.decidingState() || this.streamingState()) {
2782
+ if (!session || !conversation || !approval || session.deciding || session.streaming || !this.canDecide()) {
2113
2783
  return;
2114
2784
  }
2115
- this.decidingState.set(true);
2116
- this.streamingState.set(true);
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(conversation.id, approval.approvalId, request), {
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.updateActive(current => applyNhAssistantApprovalDecision(current, approval.approvalId, decision));
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.errorState.set(event.data);
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.decidingState.set(false);
2139
- this.afterTurn(conversation.id, failed);
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 conversation = this.activeConversationState();
2146
- if (!conversation || (conversation.status === 'idle' && !this.streamingState())) {
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(conversation.id), { defaultValue: undefined });
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.streamingState()) {
2163
- await this.reloadActiveConversation(conversation.id);
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.cancelTimer);
2168
- this.cancelTimer = setTimeout(() => {
2169
- if (this.streamingState()) {
2170
- this.streamSubscription?.unsubscribe();
2171
- this.streamingState.set(false);
2172
- this.decidingState.set(false);
2173
- void this.reloadActiveConversation(conversation.id);
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
- /** Reads the host's page context again so the chip shows what the next message sends. */
2189
- async refreshPageContext() {
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
- const context = await this.readPageContext();
2192
- if (revision === this.accountRevision) {
2193
- this.pageContextState.set(context ?? null);
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
- /** Leaves the page context out of the next message only, or includes it again. */
2197
- setPageContextExcluded(excluded) {
2198
- this.pageContextExcludedState.set(excluded);
2199
- }
2200
- /** Returns and clears the draft of a message that was not accepted. */
2201
- consumeRestoredDraft() {
2202
- const draft = this.restoredDraftState();
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.activeConversationState.set(conversation);
2275
- this.uiState.update({ conversationId: conversation.id });
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.streamSubscription?.unsubscribe();
2289
- this.streamingState.set(true);
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.cancelTimer);
2292
- this.streamingState.set(false);
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
- this.streamSubscription = stream.subscribe({
3172
+ const subscription = stream.subscribe({
2296
3173
  next: event => handlers.next(event),
2297
3174
  error: () => {
2298
- this.errorState.set(clientError(NhAssistantClientErrorCodes.network));
3175
+ this.patchSession(conversationId, session => ({ ...session, error: clientError(NhAssistantClientErrorCodes.network) }));
2299
3176
  finish();
2300
3177
  },
2301
3178
  complete: finish
2302
3179
  });
2303
- }
2304
- stopLocalTurn() {
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
- /** A completed turn with a server code, or without any answer text, explains itself with a notice. */
2329
- completionNotice(errorCode) {
2330
- if (errorCode) {
2331
- return clientError(errorCode);
3184
+ stopAllTurns() {
3185
+ for (const subscription of this.streams.values()) {
3186
+ subscription.unsubscribe();
2332
3187
  }
2333
- const conversation = this.activeConversationState();
2334
- if (conversation && !nhAssistantLatestTurnHasText(conversation)) {
2335
- return { code: NH_ASSISTANT_NO_ANSWER_NOTICE_CODE, messageKey: 'nh-assistant.notices.no-answer' };
3188
+ this.streams.clear();
3189
+ for (const resolve of this.pendingSendResolves.values()) {
3190
+ resolve(false);
2336
3191
  }
2337
- return null;
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 active = this.activeConversationState();
2341
- if (active?.id === conversationId) {
2342
- this.conversationsState.update(items => items.map(item => item.id === conversationId ? { ...item, status: active.status, updatedAt: new Date().toISOString() } : item));
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.reloadActiveConversation(conversationId);
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 reloadActiveConversation(conversationId) {
3234
+ async reloadSession(conversationId) {
2352
3235
  const revision = this.accountRevision;
2353
3236
  try {
2354
3237
  const conversation = await firstValueFrom(this.api.getConversation(conversationId));
2355
- if (revision === this.accountRevision && this.activeConversationState()?.id === conversationId && !this.streamingState()) {
2356
- this.activeConversationState.set(conversation);
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
- // Keep the local state; the error of the turn is already shown.
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
- updateActive(update) {
2364
- const conversation = this.activeConversationState();
2365
- if (conversation) {
2366
- this.activeConversationState.set(update(conversation));
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
- removeMessage(messageId, status) {
2370
- this.updateActive(conversation => ({
2371
- ...conversation,
2372
- status,
2373
- messages: conversation.messages.filter(message => message.id !== messageId)
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
- setError(error) {
2386
- if (error instanceof NhAssistantApiError) {
2387
- this.errorState.set({ code: error.code, messageKey: error.messageKey });
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.errorState.set(clientError(NhAssistantClientErrorCodes.server));
3466
+ this.patchSession(conversationId, session => ({ ...session, error: value }));
2391
3467
  }
2392
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhAssistantStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2393
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhAssistantStore }); }
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.28", ngImport: i0, type: NhAssistantStore, decorators: [{
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.28", ngImport: i0, type: NhAssistantPanelService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2573
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhAssistantPanelService }); }
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.28", ngImport: i0, type: NhAssistantPanelService, decorators: [{
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.28", ngImport: i0, type: NhAssistantIconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2635
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: NhAssistantIconComponent, isStandalone: true, selector: "nh-assistant-icon", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
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.28", ngImport: i0, type: NhAssistantIconComponent, decorators: [{
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. A badge marks a conversation
2661
- * that waits for approval. Projected content replaces the default icon.
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.28", ngImport: i0, type: NhAssistantLauncherComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2681
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", type: NhAssistantLauncherComponent, isStandalone: true, selector: "nh-assistant-launcher", ngImport: i0, template: `
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.28", ngImport: i0, type: NhAssistantLauncherComponent, decorators: [{
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.28", ngImport: i0, type: NhAssistantTranslatePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
2764
- static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.28", ngImport: i0, type: NhAssistantTranslatePipe, isStandalone: true, name: "nhAssistantTranslate", pure: false }); }
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.28", ngImport: i0, type: NhAssistantTranslatePipe, decorators: [{
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
- let nextId$5 = 0;
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$5++}`;
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.28", ngImport: i0, type: NhAssistantAgentPickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2789
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", 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 } }, outputs: { agentChange: "agentChange" }, ngImport: i0, template: `
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.title]="selected() ? ([selected()!.descriptionKey] | nhAssistantTranslate: selected()!.descriptionKey) : null"
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.28", ngImport: i0, type: NhAssistantAgentPickerComponent, decorators: [{
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.title]="selected() ? ([selected()!.descriptionKey] | nhAssistantTranslate: selected()!.descriptionKey) : null"
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$4 = 0;
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$4++}`;
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.28", ngImport: i0, type: NhAssistantComposerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2929
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", 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 }, 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=\"actions\">\n @if (busy()) {\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</form>\n\n@if (status(); as status) {\n <p class=\"composer-status\" role=\"status\" aria-live=\"polite\">\n {{ 'nh-assistant.composer.status.' + status | translate }}\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;align-items:flex-end;gap:8px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:6px 6px 6px 12px;background:var(--nha-background)}.composer:focus-within{border-color:var(--nha-focus);box-shadow:0 0 0 2px color-mix(in srgb,var(--nha-focus) 30%,transparent)}textarea{flex:1;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}.actions{display:flex}.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 }); }
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.28", ngImport: i0, type: NhAssistantComposerComponent, decorators: [{
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 @if (busy()) {\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</form>\n\n@if (status(); as status) {\n <p class=\"composer-status\" role=\"status\" aria-live=\"polite\">\n {{ 'nh-assistant.composer.status.' + status | translate }}\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;align-items:flex-end;gap:8px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:6px 6px 6px 12px;background:var(--nha-background)}.composer:focus-within{border-color:var(--nha-focus);box-shadow:0 0 0 2px color-mix(in srgb,var(--nha-focus) 30%,transparent)}textarea{flex:1;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}.actions{display:flex}.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"] }]
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
- /** The user's conversations with loading and empty states; selecting one opens it. */
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
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhAssistantConversationListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2955
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", 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()\">\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"], 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 }); }
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.28", ngImport: i0, type: NhAssistantConversationListComponent, decorators: [{
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$3 = 0;
4206
+ let nextId$4 = 0;
2965
4207
  /**
2966
- * The user's own assistant preferences: style, form of address, answer length and
2967
- * optional instructions. Preferences steer style only; the server keeps them below the
2968
- * application context and agent instructions.
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$3++}`;
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.28", ngImport: i0, type: NhAssistantPreferencesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
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.28", ngImport: i0, type: NhAssistantPreferencesComponent, decorators: [{
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.28", ngImport: i0, type: NhAssistantMarkdownRenderer, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3148
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.28", ngImport: i0, type: NhAssistantMarkdownRenderer, providedIn: 'root' }); }
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.28", ngImport: i0, type: NhAssistantMarkdownRenderer, decorators: [{
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.28", ngImport: i0, type: NhAssistantMarkdownPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
3164
- static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.28", ngImport: i0, type: NhAssistantMarkdownPipe, isStandalone: true, name: "nhAssistantMarkdown" }); }
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.28", ngImport: i0, type: NhAssistantMarkdownPipe, decorators: [{
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.28", ngImport: i0, type: NhAssistantPartGroupsPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
3204
- static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "20.3.28", ngImport: i0, type: NhAssistantPartGroupsPipe, isStandalone: true, name: "nhAssistantPartGroups" }); }
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.28", ngImport: i0, type: NhAssistantPartGroupsPipe, decorators: [{
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.28", ngImport: i0, type: NhAssistantApprovalCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3272
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", 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 } }, 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()) {\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"], dependencies: [{ kind: "component", type: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
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.28", ngImport: i0, type: NhAssistantApprovalCardComponent, decorators: [{
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.28", ngImport: i0, type: NhAssistantToolCallCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3302
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", 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 }); }
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.28", ngImport: i0, type: NhAssistantToolCallCardComponent, decorators: [{
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.28", ngImport: i0, type: NhAssistantToolCallGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3351
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.28", 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 }); }
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.28", ngImport: i0, type: NhAssistantToolCallGroupComponent, decorators: [{
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.28", ngImport: i0, type: NhAssistantThreadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
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.28", ngImport: i0, type: NhAssistantThreadComponent, decorators: [{
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 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"] }]
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 chip current.
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.28", ngImport: i0, type: NhAssistantPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
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.28", ngImport: i0, type: NhAssistantPanelComponent, decorators: [{
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