@newheap/platform-ai-chat 0.8.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +185 -3
- package/admin/index.d.ts +79 -4
- package/agent-runs/index.d.ts +342 -0
- package/fesm2022/newheap-platform-ai-chat-admin.mjs +231 -12
- package/fesm2022/newheap-platform-ai-chat-admin.mjs.map +1 -1
- package/fesm2022/newheap-platform-ai-chat-agent-runs.mjs +905 -0
- package/fesm2022/newheap-platform-ai-chat-agent-runs.mjs.map +1 -0
- package/fesm2022/newheap-platform-ai-chat-testing.mjs +30 -1
- package/fesm2022/newheap-platform-ai-chat-testing.mjs.map +1 -1
- package/fesm2022/newheap-platform-ai-chat.mjs +1746 -80
- package/fesm2022/newheap-platform-ai-chat.mjs.map +1 -1
- package/i18n/en.json +304 -8
- package/i18n/nl.json +304 -8
- package/index.d.ts +899 -67
- package/package.json +5 -1
- package/testing/index.d.ts +4 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 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';
|
|
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, linkedSignal, ViewContainerRef } from '@angular/core';
|
|
3
3
|
import { TranslateService, TranslatePipe } from '@ngx-translate/core';
|
|
4
|
-
import { merge, Observable, Subject, firstValueFrom, Subscription, isObservable } from 'rxjs';
|
|
4
|
+
import { merge, Observable, Subject, firstValueFrom, Subscription, isObservable, filter, debounceTime, BehaviorSubject, map, forkJoin } from 'rxjs';
|
|
5
5
|
import { isPlatformBrowser, DOCUMENT, DatePipe, NgTemplateOutlet } from '@angular/common';
|
|
6
6
|
import { BreakpointObserver } from '@angular/cdk/layout';
|
|
7
7
|
import { Overlay } from '@angular/cdk/overlay';
|
|
@@ -48,12 +48,32 @@ const NhAssistantClientErrorCodes = {
|
|
|
48
48
|
conversationRemoved: 'assistant-conversation-removed'
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
/** Maximum number of subjects one `POST subjects/{type}/summary` request accepts. */
|
|
52
|
+
const NH_ASSISTANT_MAX_SUBJECT_SUMMARY = 200;
|
|
53
|
+
/** The feedback categories in the order people choose them. */
|
|
54
|
+
const NH_ASSISTANT_FEEDBACK_CATEGORIES = [
|
|
55
|
+
'incorrect',
|
|
56
|
+
'wrong-value',
|
|
57
|
+
'not-needed',
|
|
58
|
+
'not-now',
|
|
59
|
+
'already-done',
|
|
60
|
+
'not-allowed',
|
|
61
|
+
'other'
|
|
62
|
+
];
|
|
63
|
+
/** The group of each feedback category, as the server reports it. */
|
|
64
|
+
const NH_ASSISTANT_FEEDBACK_GROUPS = {
|
|
65
|
+
incorrect: 'wrong',
|
|
66
|
+
'wrong-value': 'wrong',
|
|
67
|
+
'not-needed': 'wrong',
|
|
68
|
+
'not-now': 'right-but-not-now',
|
|
69
|
+
'already-done': 'right-but-not-now',
|
|
70
|
+
'not-allowed': 'not-allowed',
|
|
71
|
+
other: 'other'
|
|
72
|
+
};
|
|
73
|
+
/** Maximum length of the text of a feedback. */
|
|
74
|
+
const NH_ASSISTANT_MAX_FEEDBACK_TEXT = 1_000;
|
|
75
|
+
/** Maximum number of proposals one `POST approvals/feedback` request accepts. */
|
|
76
|
+
const NH_ASSISTANT_MAX_BULK_FEEDBACK = 50;
|
|
57
77
|
|
|
58
78
|
const NH_ASSISTANT_CONFIG = new InjectionToken('NH_ASSISTANT_CONFIG');
|
|
59
79
|
const NH_ASSISTANT_ACCESS_POLICY = new InjectionToken('NH_ASSISTANT_ACCESS_POLICY');
|
|
@@ -83,6 +103,11 @@ var en = {
|
|
|
83
103
|
unread: "{{count}} unread conversations",
|
|
84
104
|
running: "{{count}} conversations working"
|
|
85
105
|
},
|
|
106
|
+
"subject-badge": {
|
|
107
|
+
"to-decide": "Proposals to decide: {{toDecide}} of {{open}} open.",
|
|
108
|
+
open: "Open proposals: {{open}}.",
|
|
109
|
+
working: "An agent is working on this."
|
|
110
|
+
},
|
|
86
111
|
panel: {
|
|
87
112
|
title: "Assistant",
|
|
88
113
|
"region-label": "Assistant panel",
|
|
@@ -138,7 +163,8 @@ var en = {
|
|
|
138
163
|
working: "The assistant is working",
|
|
139
164
|
loading: "Loading conversation",
|
|
140
165
|
elapsed: "{{seconds}}s",
|
|
141
|
-
participant: "Participant"
|
|
166
|
+
participant: "Participant",
|
|
167
|
+
trigger: "Event"
|
|
142
168
|
},
|
|
143
169
|
composer: {
|
|
144
170
|
label: "Message",
|
|
@@ -185,8 +211,16 @@ var en = {
|
|
|
185
211
|
succeeded: "Completed",
|
|
186
212
|
failed: "Failed",
|
|
187
213
|
"awaiting-approval": "Awaiting approval",
|
|
188
|
-
rejected: "Rejected"
|
|
189
|
-
|
|
214
|
+
rejected: "Rejected",
|
|
215
|
+
"not-executed": "Not run"
|
|
216
|
+
},
|
|
217
|
+
"grant-source": {
|
|
218
|
+
delegated: "On behalf of the initiator",
|
|
219
|
+
agent: "Agent rights",
|
|
220
|
+
"delegated-hint": "The agent used the rights of the person who started it.",
|
|
221
|
+
"agent-hint": "The agent used a right it was granted itself, beyond the rights of the people involved."
|
|
222
|
+
},
|
|
223
|
+
restricted: "Shielded: the agent obtained this with its own rights, which you do not have."
|
|
190
224
|
},
|
|
191
225
|
"tool-group": {
|
|
192
226
|
running: "Calling {{name}}…",
|
|
@@ -195,6 +229,7 @@ var en = {
|
|
|
195
229
|
"used-many": "Used {{count}} tools",
|
|
196
230
|
progress: "{{finished}} of {{total}} done",
|
|
197
231
|
failed: "{{count}} failed",
|
|
232
|
+
"not-executed": "{{count}} not run",
|
|
198
233
|
"show-calls": "Show tool calls",
|
|
199
234
|
"hide-calls": "Hide tool calls"
|
|
200
235
|
},
|
|
@@ -216,10 +251,58 @@ var en = {
|
|
|
216
251
|
pending: "Pending",
|
|
217
252
|
approved: "Approved",
|
|
218
253
|
rejected: "Rejected",
|
|
219
|
-
expired: "Expired"
|
|
254
|
+
expired: "Expired",
|
|
255
|
+
withdrawn: "Withdrawn",
|
|
256
|
+
stale: "Outdated",
|
|
257
|
+
executing: "Running",
|
|
258
|
+
executed: "Done",
|
|
259
|
+
"execution-failed": "Failed",
|
|
260
|
+
"execution-unknown": "Outcome unknown",
|
|
261
|
+
resolved: "Resolved",
|
|
262
|
+
shadow: "Shadow"
|
|
220
263
|
},
|
|
221
264
|
"waiting-for": "Waiting for {{name}} to decide. The action uses their permissions.",
|
|
222
|
-
"waiting-for-participant": "Waiting for the participant who started this task to decide."
|
|
265
|
+
"waiting-for-participant": "Waiting for the participant who started this task to decide.",
|
|
266
|
+
"waiting-for-approver": "Waiting for an approver to decide.",
|
|
267
|
+
"waiting-for-other-approver": "You started this work, so another approver decides.",
|
|
268
|
+
"give-feedback": "What was wrong?"
|
|
269
|
+
},
|
|
270
|
+
"proposal-feedback": {
|
|
271
|
+
legend: "What was wrong? (optional)",
|
|
272
|
+
bulk: "Applies to the {{count}} proposals you rejected.",
|
|
273
|
+
categories: {
|
|
274
|
+
incorrect: "Incorrect",
|
|
275
|
+
"wrong-value": "Wrong value",
|
|
276
|
+
"not-needed": "Not needed",
|
|
277
|
+
"not-now": "Not now",
|
|
278
|
+
"already-done": "Already done",
|
|
279
|
+
"not-allowed": "Not allowed",
|
|
280
|
+
other: "Other"
|
|
281
|
+
},
|
|
282
|
+
hints: {
|
|
283
|
+
incorrect: "The proposal is factually wrong.",
|
|
284
|
+
"wrong-value": "The right action, with the wrong values.",
|
|
285
|
+
"not-needed": "The change is unnecessary.",
|
|
286
|
+
"not-now": "Right, but not at this moment.",
|
|
287
|
+
"already-done": "Already done or in progress elsewhere.",
|
|
288
|
+
"not-allowed": "Against policy, or beyond what the agent should do.",
|
|
289
|
+
other: "Explain it in the text below."
|
|
290
|
+
},
|
|
291
|
+
groups: {
|
|
292
|
+
wrong: "The agent was wrong",
|
|
293
|
+
"right-but-not-now": "Right, but not now",
|
|
294
|
+
"not-allowed": "Not allowed",
|
|
295
|
+
other: "Other"
|
|
296
|
+
},
|
|
297
|
+
"text-label": "Anything to add? (optional)",
|
|
298
|
+
"text-hint": "The agent's administrators read this to improve it.",
|
|
299
|
+
characters: "{{count}}/{{max}}",
|
|
300
|
+
send: "Send",
|
|
301
|
+
sending: "Sending…",
|
|
302
|
+
skip: "Skip",
|
|
303
|
+
cancel: "Cancel",
|
|
304
|
+
thanks: "Thanks for your feedback",
|
|
305
|
+
partial: "Saved for {{stored}} of {{total}} proposals; the others changed in the meantime."
|
|
223
306
|
},
|
|
224
307
|
"result-codes": {
|
|
225
308
|
"api-bridge-validation": "The request was not valid.",
|
|
@@ -228,7 +311,13 @@ var en = {
|
|
|
228
311
|
"api-bridge-not-found": "The item was not found.",
|
|
229
312
|
"api-bridge-conflict": "The item was changed in the meantime.",
|
|
230
313
|
"api-bridge-upstream": "The service is not available.",
|
|
231
|
-
"api-bridge-timeout": "The request took too long."
|
|
314
|
+
"api-bridge-timeout": "The request took too long.",
|
|
315
|
+
"assistant-proposal-recorded": "Proposed; waits for a person's approval",
|
|
316
|
+
"assistant-proposal-rejected": "Not run: the proposal was rejected.",
|
|
317
|
+
"assistant-proposal-expired": "Not run: nobody decided before the proposal expired.",
|
|
318
|
+
"assistant-proposal-withdrawn": "Not run: the proposal was withdrawn.",
|
|
319
|
+
"assistant-proposal-stale": "Not run: the subject changed since the proposal.",
|
|
320
|
+
"assistant-proposal-execution-failed": "The approved proposal could not be run."
|
|
232
321
|
},
|
|
233
322
|
errors: {
|
|
234
323
|
generic: "Something went wrong. Please try again.",
|
|
@@ -287,7 +376,38 @@ var en = {
|
|
|
287
376
|
"assistant-directory-unavailable": "Inviting colleagues directly is not available.",
|
|
288
377
|
"assistant-push-unavailable": "Desktop notifications are not available.",
|
|
289
378
|
"assistant-push-subscription-invalid": "This browser could not be registered for notifications.",
|
|
290
|
-
"assistant-conversation-removed": "This conversation is no longer available to you."
|
|
379
|
+
"assistant-conversation-removed": "This conversation is no longer available to you.",
|
|
380
|
+
"assistant-subject-not-found": "The subject no longer exists.",
|
|
381
|
+
"assistant-subject-type-unknown": "This kind of subject is not known to the assistant.",
|
|
382
|
+
"assistant-subject-access-denied": "Your role on this subject does not allow this.",
|
|
383
|
+
"assistant-no-accountable-owner": "Nobody answers for this agent on this subject.",
|
|
384
|
+
"assistant-agent-disabled": "This agent has been stopped.",
|
|
385
|
+
"assistant-agent-not-configured": "This agent cannot run on its own.",
|
|
386
|
+
"assistant-run-not-found": "The run was not found.",
|
|
387
|
+
"assistant-run-not-retryable": "This run cannot be retried.",
|
|
388
|
+
"assistant-approval-policy-denied": "You may not decide this proposal.",
|
|
389
|
+
"assistant-four-eyes-required": "Someone other than the person who started this must decide it.",
|
|
390
|
+
"assistant-execution-not-uncertain": "This execution is not waiting to be resolved.",
|
|
391
|
+
"assistant-proposal-stale": "The subject changed since this proposal; it was not executed.",
|
|
392
|
+
"assistant-trigger-not-found": "The trigger was not found.",
|
|
393
|
+
"assistant-webhook-signature-invalid": "The webhook call has no valid signature.",
|
|
394
|
+
"assistant-webhook-payload-invalid": "The webhook call could not be read.",
|
|
395
|
+
"assistant-memory-not-configured": "This agent keeps no working memory.",
|
|
396
|
+
"assistant-memory-invalid": "The working memory change does not fit its schema.",
|
|
397
|
+
"assistant-memory-conflict": "The working memory changed at the same time.",
|
|
398
|
+
"assistant-artifact-invalid": "The file is not valid or too large.",
|
|
399
|
+
"assistant-artifact-outside-turn": "Files can only be saved while the agent works.",
|
|
400
|
+
"assistant-artifact-not-found": "The file was not found.",
|
|
401
|
+
"assistant-feedback-invalid": "Choose one of the listed reasons and keep the explanation under 1,000 characters.",
|
|
402
|
+
"assistant-feedback-not-accepted": "Feedback is possible only after a proposal was rejected or closed.",
|
|
403
|
+
"assistant-consult-subject-unavailable": "The subject does not exist or you may not consult the agent about it.",
|
|
404
|
+
"assistant-consult-agent-unavailable": "The agent cannot be consulted right now.",
|
|
405
|
+
"assistant-consult-depth-exceeded": "This consultation would ask agents in a loop or nest too deeply.",
|
|
406
|
+
"assistant-consult-budget-exhausted": "The agent's daily budget is spent.",
|
|
407
|
+
"assistant-consult-limit-reached": "You reached the daily number of consultations.",
|
|
408
|
+
"assistant-consult-timeout": "The agent did not answer in time.",
|
|
409
|
+
"assistant-consult-failed": "The agent could not answer.",
|
|
410
|
+
"assistant-consult-read-only": "A consultation cannot change anything."
|
|
291
411
|
},
|
|
292
412
|
notices: {
|
|
293
413
|
"no-answer": "The assistant finished without an answer. Try again or rephrase your question.",
|
|
@@ -401,7 +521,8 @@ var en = {
|
|
|
401
521
|
propose: "Propose",
|
|
402
522
|
simulate: "Simulate",
|
|
403
523
|
execute: "Execute"
|
|
404
|
-
}
|
|
524
|
+
},
|
|
525
|
+
feedback: "Feedback"
|
|
405
526
|
},
|
|
406
527
|
"agent-editor": {
|
|
407
528
|
"new-title": "New agent",
|
|
@@ -515,6 +636,57 @@ var en = {
|
|
|
515
636
|
"missing-warning": "The server no longer lists this tool.",
|
|
516
637
|
save: "Save tool",
|
|
517
638
|
back: "Back to the servers"
|
|
639
|
+
},
|
|
640
|
+
feedback: {
|
|
641
|
+
title: "Feedback on proposals",
|
|
642
|
+
description: "What people said when they rejected this agent's proposals. Compare agent versions to see whether a change to the prompt, tools or policy helped.",
|
|
643
|
+
agent: "{{name}} ({{id}})",
|
|
644
|
+
back: "Back to agents",
|
|
645
|
+
refresh: "Refresh",
|
|
646
|
+
"export": "Export (JSON Lines)",
|
|
647
|
+
exporting: "Exporting…",
|
|
648
|
+
"export-hint": "The export holds subject data and the proposed tool input, and is recorded in the audit log.",
|
|
649
|
+
period: "Period",
|
|
650
|
+
periods: {
|
|
651
|
+
"last-7-days": "Last 7 days",
|
|
652
|
+
"last-30-days": "Last 30 days",
|
|
653
|
+
"last-90-days": "Last 90 days"
|
|
654
|
+
},
|
|
655
|
+
tool: "Tool",
|
|
656
|
+
"all-tools": "All tools",
|
|
657
|
+
category: "Category",
|
|
658
|
+
"all-categories": "All categories",
|
|
659
|
+
"category-hint": "The category narrows the feedback and the export.",
|
|
660
|
+
"summary-title": "Per tool and agent version",
|
|
661
|
+
"summary-empty": "The agent made no proposals in this period.",
|
|
662
|
+
columns: {
|
|
663
|
+
tool: "Tool",
|
|
664
|
+
version: "Agent version",
|
|
665
|
+
approved: "Approved",
|
|
666
|
+
rejected: "Rejected",
|
|
667
|
+
expired: "Expired",
|
|
668
|
+
"rejection-rate": "Rejection rate",
|
|
669
|
+
"with-feedback": "Rejected with feedback",
|
|
670
|
+
people: "People",
|
|
671
|
+
categories: "Top categories"
|
|
672
|
+
},
|
|
673
|
+
version: "v{{version}}",
|
|
674
|
+
consultations: "Consultations",
|
|
675
|
+
"no-decisions": "Not decided yet",
|
|
676
|
+
"no-categories": "None",
|
|
677
|
+
"list-title": "Feedback",
|
|
678
|
+
"list-count": "{{count}} in total",
|
|
679
|
+
"list-empty": "Nobody gave feedback in this period.",
|
|
680
|
+
"list-empty-filtered": "No feedback matches these filters.",
|
|
681
|
+
"no-text": "No explanation given.",
|
|
682
|
+
proposal: "Proposal:",
|
|
683
|
+
author: "By {{actor}}",
|
|
684
|
+
bulk: "Bulk",
|
|
685
|
+
"bulk-hint": "Given once for several proposals rejected together; weigh it lower.",
|
|
686
|
+
previous: "Previous",
|
|
687
|
+
next: "Next",
|
|
688
|
+
page: "Page {{page}} of {{pages}}",
|
|
689
|
+
pages: "Pages"
|
|
518
690
|
}
|
|
519
691
|
},
|
|
520
692
|
"page-context": {
|
|
@@ -561,6 +733,150 @@ var en = {
|
|
|
561
733
|
remove: "Remove {{name}}",
|
|
562
734
|
leave: "Leave conversation",
|
|
563
735
|
"no-conversation": "Start or open a conversation to share it."
|
|
736
|
+
},
|
|
737
|
+
"agent-runs": {
|
|
738
|
+
loading: "Loading…",
|
|
739
|
+
"load-failed": "This could not be loaded.",
|
|
740
|
+
retry: "Try again",
|
|
741
|
+
refresh: "Refresh",
|
|
742
|
+
role: {
|
|
743
|
+
none: "No role on this subject",
|
|
744
|
+
reader: "Reader",
|
|
745
|
+
participant: "Participant",
|
|
746
|
+
approver: "Approver"
|
|
747
|
+
},
|
|
748
|
+
thread: {
|
|
749
|
+
title: "Agent",
|
|
750
|
+
"waiting-inputs": "{{count}} messages wait for the next run",
|
|
751
|
+
"open-conversation": "Open conversation",
|
|
752
|
+
"start-message": "Instruction for the agent (optional)",
|
|
753
|
+
start: "Start the agent",
|
|
754
|
+
starting: "Starting…",
|
|
755
|
+
"no-proposals": "The agent has proposed nothing here yet.",
|
|
756
|
+
"no-thread": "The agent has not worked on this yet.",
|
|
757
|
+
tabs: {
|
|
758
|
+
proposals: "Proposals",
|
|
759
|
+
runs: "Runs",
|
|
760
|
+
consultations: "Consultations",
|
|
761
|
+
memory: "Working memory",
|
|
762
|
+
artifacts: "Files"
|
|
763
|
+
},
|
|
764
|
+
outcome: {
|
|
765
|
+
started: "The agent starts shortly.",
|
|
766
|
+
queued: "The agent starts after the current run.",
|
|
767
|
+
coalesced: "Your request joins the run that is about to start.",
|
|
768
|
+
superseded: "The current run stops and the agent starts again.",
|
|
769
|
+
ignored: "The agent is busy; nothing was started.",
|
|
770
|
+
deduplicated: "This was already started.",
|
|
771
|
+
rejected: "The agent could not be started."
|
|
772
|
+
}
|
|
773
|
+
},
|
|
774
|
+
subject: {
|
|
775
|
+
title: "Agent suggestions",
|
|
776
|
+
description: "Proposals that agents made here and that wait for a decision, the first to expire on top.",
|
|
777
|
+
empty: "No open suggestions from agents.",
|
|
778
|
+
unavailable: "You cannot see agent work on this item.",
|
|
779
|
+
agents: "Agents",
|
|
780
|
+
idle: "Not working now",
|
|
781
|
+
opening: "Opening…",
|
|
782
|
+
"open-conversation-label": "Open conversation with {{agent}}",
|
|
783
|
+
"start-label": "Start the agent: {{agent}}",
|
|
784
|
+
"assistant-unavailable": "The assistant is not available."
|
|
785
|
+
},
|
|
786
|
+
inbox: {
|
|
787
|
+
title: "Proposals to decide",
|
|
788
|
+
description: "Proposals of agents that wait for your decision, the ones that expire first on top.",
|
|
789
|
+
empty: "No proposals wait for you.",
|
|
790
|
+
select: "Select this proposal",
|
|
791
|
+
"select-all": "Select all",
|
|
792
|
+
"approve-selected": "Approve selected",
|
|
793
|
+
"reject-selected": "Reject selected",
|
|
794
|
+
previous: "Previous",
|
|
795
|
+
next: "Next",
|
|
796
|
+
page: "Page {{page}} of {{pages}}",
|
|
797
|
+
pages: "Pages"
|
|
798
|
+
},
|
|
799
|
+
proposal: {
|
|
800
|
+
approve: "Approve",
|
|
801
|
+
reject: "Reject",
|
|
802
|
+
expires: "Expires {{date}}",
|
|
803
|
+
"decided-by": "Decided by {{actor}}",
|
|
804
|
+
"arguments": "Input",
|
|
805
|
+
"re-execute": "Run again",
|
|
806
|
+
"mark-resolved": "Mark as resolved",
|
|
807
|
+
"unknown-hint": "The execution stopped without a known outcome. Check whether it happened before you run it again.",
|
|
808
|
+
mode: {
|
|
809
|
+
inline: "The run waits",
|
|
810
|
+
deferred: "Decide later"
|
|
811
|
+
},
|
|
812
|
+
status: {
|
|
813
|
+
pending: "Waiting",
|
|
814
|
+
approved: "Approved",
|
|
815
|
+
rejected: "Rejected",
|
|
816
|
+
expired: "Expired",
|
|
817
|
+
withdrawn: "Withdrawn",
|
|
818
|
+
stale: "Outdated",
|
|
819
|
+
executing: "Running",
|
|
820
|
+
executed: "Done",
|
|
821
|
+
"execution-failed": "Failed",
|
|
822
|
+
"execution-unknown": "Outcome unknown",
|
|
823
|
+
resolved: "Resolved",
|
|
824
|
+
shadow: "Shadow"
|
|
825
|
+
},
|
|
826
|
+
"from-consultation": "From a consultation",
|
|
827
|
+
"from-consultation-hint": "Another assistant consulted the agent for this person; it runs with that person's rights only.",
|
|
828
|
+
"give-feedback": "Give feedback",
|
|
829
|
+
"change-feedback": "Change feedback",
|
|
830
|
+
"your-feedback": "Your feedback: {{category}}",
|
|
831
|
+
"rejected-one": "You rejected: {{summary}}"
|
|
832
|
+
},
|
|
833
|
+
run: {
|
|
834
|
+
empty: "No runs yet.",
|
|
835
|
+
status: {
|
|
836
|
+
queued: "Queued",
|
|
837
|
+
running: "Running",
|
|
838
|
+
"waiting-for-approval": "Waiting for approval",
|
|
839
|
+
completed: "Completed",
|
|
840
|
+
failed: "Failed",
|
|
841
|
+
cancelled: "Cancelled"
|
|
842
|
+
},
|
|
843
|
+
shadow: "Shadow",
|
|
844
|
+
trigger: "Trigger: {{trigger}}",
|
|
845
|
+
"tool-calls": "{{count}} tool calls",
|
|
846
|
+
tokens: "{{count}} tokens",
|
|
847
|
+
proposals: "{{count}} proposals",
|
|
848
|
+
attempt: "Attempt {{attempt}}",
|
|
849
|
+
cancel: "Cancel",
|
|
850
|
+
resume: "Resume",
|
|
851
|
+
restart: "Start again"
|
|
852
|
+
},
|
|
853
|
+
memory: {
|
|
854
|
+
"not-configured": "This agent keeps no working memory.",
|
|
855
|
+
version: "Schema version {{version}}",
|
|
856
|
+
updated: "Updated {{date}}",
|
|
857
|
+
empty: "The agent has not noted anything yet."
|
|
858
|
+
},
|
|
859
|
+
artifacts: {
|
|
860
|
+
"download-failed": "The file could not be downloaded.",
|
|
861
|
+
empty: "No files yet.",
|
|
862
|
+
version: "v{{version}}",
|
|
863
|
+
restricted: "Shielded (agent rights)",
|
|
864
|
+
size: "{{size}} bytes",
|
|
865
|
+
download: "Download"
|
|
866
|
+
},
|
|
867
|
+
consultation: {
|
|
868
|
+
empty: "No assistant has consulted this agent about this subject yet.",
|
|
869
|
+
"private": "Only who asked and how it ended is shown here; the question and the answer stay in the asking conversation.",
|
|
870
|
+
"asked-by": "Asked by {{name}}",
|
|
871
|
+
through: "through {{agent}}",
|
|
872
|
+
depth: "Nested, level {{depth}}",
|
|
873
|
+
more: "{{count}} older consultations are not shown.",
|
|
874
|
+
status: {
|
|
875
|
+
running: "Running",
|
|
876
|
+
completed: "Answered",
|
|
877
|
+
failed: "Not answered"
|
|
878
|
+
}
|
|
879
|
+
}
|
|
564
880
|
}
|
|
565
881
|
}
|
|
566
882
|
};
|
|
@@ -574,6 +890,11 @@ var nl = {
|
|
|
574
890
|
unread: "{{count}} ongelezen gesprekken",
|
|
575
891
|
running: "{{count}} gesprekken bezig"
|
|
576
892
|
},
|
|
893
|
+
"subject-badge": {
|
|
894
|
+
"to-decide": "Voorstellen om te beslissen: {{toDecide}} van {{open}} open.",
|
|
895
|
+
open: "Openstaande voorstellen: {{open}}.",
|
|
896
|
+
working: "Een agent werkt hieraan."
|
|
897
|
+
},
|
|
577
898
|
panel: {
|
|
578
899
|
title: "Assistent",
|
|
579
900
|
"region-label": "Assistentpaneel",
|
|
@@ -629,7 +950,8 @@ var nl = {
|
|
|
629
950
|
working: "De assistent is bezig",
|
|
630
951
|
loading: "Gesprek laden",
|
|
631
952
|
elapsed: "{{seconds}} s",
|
|
632
|
-
participant: "Deelnemer"
|
|
953
|
+
participant: "Deelnemer",
|
|
954
|
+
trigger: "Gebeurtenis"
|
|
633
955
|
},
|
|
634
956
|
composer: {
|
|
635
957
|
label: "Bericht",
|
|
@@ -676,8 +998,16 @@ var nl = {
|
|
|
676
998
|
succeeded: "Voltooid",
|
|
677
999
|
failed: "Mislukt",
|
|
678
1000
|
"awaiting-approval": "Wacht op goedkeuring",
|
|
679
|
-
rejected: "Afgewezen"
|
|
680
|
-
|
|
1001
|
+
rejected: "Afgewezen",
|
|
1002
|
+
"not-executed": "Niet uitgevoerd"
|
|
1003
|
+
},
|
|
1004
|
+
"grant-source": {
|
|
1005
|
+
delegated: "Namens de starter",
|
|
1006
|
+
agent: "Agentrechten",
|
|
1007
|
+
"delegated-hint": "De agent gebruikte de rechten van de persoon die hem startte.",
|
|
1008
|
+
"agent-hint": "De agent gebruikte een recht dat hij zelf heeft gekregen, boven de rechten van de betrokkenen uit."
|
|
1009
|
+
},
|
|
1010
|
+
restricted: "Afgeschermd: de agent kreeg dit met eigen rechten, die jij niet hebt."
|
|
681
1011
|
},
|
|
682
1012
|
"tool-group": {
|
|
683
1013
|
running: "Roept {{name}} aan…",
|
|
@@ -686,6 +1016,7 @@ var nl = {
|
|
|
686
1016
|
"used-many": "{{count}} tools gebruikt",
|
|
687
1017
|
progress: "{{finished}} van {{total}} klaar",
|
|
688
1018
|
failed: "{{count}} mislukt",
|
|
1019
|
+
"not-executed": "{{count}} niet uitgevoerd",
|
|
689
1020
|
"show-calls": "Tool-aanroepen tonen",
|
|
690
1021
|
"hide-calls": "Tool-aanroepen verbergen"
|
|
691
1022
|
},
|
|
@@ -707,10 +1038,58 @@ var nl = {
|
|
|
707
1038
|
pending: "Open",
|
|
708
1039
|
approved: "Goedgekeurd",
|
|
709
1040
|
rejected: "Afgewezen",
|
|
710
|
-
expired: "Verlopen"
|
|
1041
|
+
expired: "Verlopen",
|
|
1042
|
+
withdrawn: "Ingetrokken",
|
|
1043
|
+
stale: "Verouderd",
|
|
1044
|
+
executing: "Wordt uitgevoerd",
|
|
1045
|
+
executed: "Uitgevoerd",
|
|
1046
|
+
"execution-failed": "Mislukt",
|
|
1047
|
+
"execution-unknown": "Uitkomst onbekend",
|
|
1048
|
+
resolved: "Afgehandeld",
|
|
1049
|
+
shadow: "Schaduw"
|
|
711
1050
|
},
|
|
712
1051
|
"waiting-for": "Wacht op de beslissing van {{name}}. De actie gebruikt diens rechten.",
|
|
713
|
-
"waiting-for-participant": "Wacht op de beslissing van de deelnemer die deze taak startte."
|
|
1052
|
+
"waiting-for-participant": "Wacht op de beslissing van de deelnemer die deze taak startte.",
|
|
1053
|
+
"waiting-for-approver": "Wacht tot een beslisser hierover beslist.",
|
|
1054
|
+
"waiting-for-other-approver": "Jij startte dit werk, dus een andere beslisser beslist hierover.",
|
|
1055
|
+
"give-feedback": "Wat klopte er niet?"
|
|
1056
|
+
},
|
|
1057
|
+
"proposal-feedback": {
|
|
1058
|
+
legend: "Wat klopte er niet? (optioneel)",
|
|
1059
|
+
bulk: "Geldt voor de {{count}} voorstellen die je hebt afgewezen.",
|
|
1060
|
+
categories: {
|
|
1061
|
+
incorrect: "Onjuist",
|
|
1062
|
+
"wrong-value": "Verkeerde waarde",
|
|
1063
|
+
"not-needed": "Niet nodig",
|
|
1064
|
+
"not-now": "Niet nu",
|
|
1065
|
+
"already-done": "Al gedaan",
|
|
1066
|
+
"not-allowed": "Niet toegestaan",
|
|
1067
|
+
other: "Anders"
|
|
1068
|
+
},
|
|
1069
|
+
hints: {
|
|
1070
|
+
incorrect: "Het voorstel klopt inhoudelijk niet.",
|
|
1071
|
+
"wrong-value": "De juiste actie, met de verkeerde waarden.",
|
|
1072
|
+
"not-needed": "De wijziging is overbodig.",
|
|
1073
|
+
"not-now": "Juist, maar niet op dit moment.",
|
|
1074
|
+
"already-done": "Al gedaan of elders in behandeling.",
|
|
1075
|
+
"not-allowed": "In strijd met beleid, of meer dan de agent mag doen.",
|
|
1076
|
+
other: "Licht het toe in de tekst hieronder."
|
|
1077
|
+
},
|
|
1078
|
+
groups: {
|
|
1079
|
+
wrong: "De agent zat ernaast",
|
|
1080
|
+
"right-but-not-now": "Juist, maar niet nu",
|
|
1081
|
+
"not-allowed": "Niet toegestaan",
|
|
1082
|
+
other: "Anders"
|
|
1083
|
+
},
|
|
1084
|
+
"text-label": "Nog iets toe te voegen? (optioneel)",
|
|
1085
|
+
"text-hint": "De beheerders van de agent lezen dit om hem te verbeteren.",
|
|
1086
|
+
characters: "{{count}}/{{max}}",
|
|
1087
|
+
send: "Versturen",
|
|
1088
|
+
sending: "Versturen…",
|
|
1089
|
+
skip: "Overslaan",
|
|
1090
|
+
cancel: "Annuleren",
|
|
1091
|
+
thanks: "Bedankt voor je feedback",
|
|
1092
|
+
partial: "Opgeslagen voor {{stored}} van {{total}} voorstellen; de andere zijn intussen gewijzigd."
|
|
714
1093
|
},
|
|
715
1094
|
"result-codes": {
|
|
716
1095
|
"api-bridge-validation": "Het verzoek was niet geldig.",
|
|
@@ -719,7 +1098,13 @@ var nl = {
|
|
|
719
1098
|
"api-bridge-not-found": "Het item is niet gevonden.",
|
|
720
1099
|
"api-bridge-conflict": "Het item is intussen gewijzigd.",
|
|
721
1100
|
"api-bridge-upstream": "De dienst is niet beschikbaar.",
|
|
722
|
-
"api-bridge-timeout": "Het verzoek duurde te lang."
|
|
1101
|
+
"api-bridge-timeout": "Het verzoek duurde te lang.",
|
|
1102
|
+
"assistant-proposal-recorded": "Voorgesteld; wacht op goedkeuring van een persoon",
|
|
1103
|
+
"assistant-proposal-rejected": "Niet uitgevoerd: het voorstel is afgewezen.",
|
|
1104
|
+
"assistant-proposal-expired": "Niet uitgevoerd: niemand besliste voordat het voorstel verliep.",
|
|
1105
|
+
"assistant-proposal-withdrawn": "Niet uitgevoerd: het voorstel is ingetrokken.",
|
|
1106
|
+
"assistant-proposal-stale": "Niet uitgevoerd: het onderwerp is sinds het voorstel gewijzigd.",
|
|
1107
|
+
"assistant-proposal-execution-failed": "Het goedgekeurde voorstel kon niet worden uitgevoerd."
|
|
723
1108
|
},
|
|
724
1109
|
errors: {
|
|
725
1110
|
generic: "Er ging iets mis. Probeer het opnieuw.",
|
|
@@ -778,7 +1163,38 @@ var nl = {
|
|
|
778
1163
|
"assistant-directory-unavailable": "Collega’s direct uitnodigen is niet beschikbaar.",
|
|
779
1164
|
"assistant-push-unavailable": "Bureaubladmeldingen zijn niet beschikbaar.",
|
|
780
1165
|
"assistant-push-subscription-invalid": "Deze browser kon niet worden aangemeld voor meldingen.",
|
|
781
|
-
"assistant-conversation-removed": "Dit gesprek is niet meer voor je beschikbaar."
|
|
1166
|
+
"assistant-conversation-removed": "Dit gesprek is niet meer voor je beschikbaar.",
|
|
1167
|
+
"assistant-subject-not-found": "Het onderwerp bestaat niet meer.",
|
|
1168
|
+
"assistant-subject-type-unknown": "Dit soort onderwerp kent de assistent niet.",
|
|
1169
|
+
"assistant-subject-access-denied": "Je rol op dit onderwerp staat dit niet toe.",
|
|
1170
|
+
"assistant-no-accountable-owner": "Niemand staat voor deze agent op dit onderwerp in.",
|
|
1171
|
+
"assistant-agent-disabled": "Deze agent is stopgezet.",
|
|
1172
|
+
"assistant-agent-not-configured": "Deze agent kan niet zelfstandig werken.",
|
|
1173
|
+
"assistant-run-not-found": "De run is niet gevonden.",
|
|
1174
|
+
"assistant-run-not-retryable": "Deze run kan niet opnieuw worden gestart.",
|
|
1175
|
+
"assistant-approval-policy-denied": "Je mag over dit voorstel niet beslissen.",
|
|
1176
|
+
"assistant-four-eyes-required": "Iemand anders dan de starter moet hierover beslissen.",
|
|
1177
|
+
"assistant-execution-not-uncertain": "Deze uitvoering wacht niet op afhandeling.",
|
|
1178
|
+
"assistant-proposal-stale": "Het onderwerp is sinds dit voorstel gewijzigd; het is niet uitgevoerd.",
|
|
1179
|
+
"assistant-trigger-not-found": "De trigger is niet gevonden.",
|
|
1180
|
+
"assistant-webhook-signature-invalid": "De webhook-aanroep heeft geen geldige handtekening.",
|
|
1181
|
+
"assistant-webhook-payload-invalid": "De webhook-aanroep kon niet worden gelezen.",
|
|
1182
|
+
"assistant-memory-not-configured": "Deze agent houdt geen werkgeheugen bij.",
|
|
1183
|
+
"assistant-memory-invalid": "De wijziging van het werkgeheugen past niet bij het schema.",
|
|
1184
|
+
"assistant-memory-conflict": "Het werkgeheugen is tegelijk gewijzigd.",
|
|
1185
|
+
"assistant-artifact-invalid": "Het bestand is ongeldig of te groot.",
|
|
1186
|
+
"assistant-artifact-outside-turn": "Bestanden kunnen alleen worden opgeslagen terwijl de agent werkt.",
|
|
1187
|
+
"assistant-artifact-not-found": "Het bestand is niet gevonden.",
|
|
1188
|
+
"assistant-feedback-invalid": "Kies een van de genoemde redenen en houd de toelichting onder de 1.000 tekens.",
|
|
1189
|
+
"assistant-feedback-not-accepted": "Feedback kan pas nadat een voorstel is afgewezen of gesloten.",
|
|
1190
|
+
"assistant-consult-subject-unavailable": "Het onderwerp bestaat niet of je mag de agent er niet over raadplegen.",
|
|
1191
|
+
"assistant-consult-agent-unavailable": "De agent kan nu niet worden geraadpleegd.",
|
|
1192
|
+
"assistant-consult-depth-exceeded": "Deze raadpleging zou agents in een kring of te diep laten vragen.",
|
|
1193
|
+
"assistant-consult-budget-exhausted": "Het dagbudget van de agent is op.",
|
|
1194
|
+
"assistant-consult-limit-reached": "Je hebt het aantal raadplegingen voor vandaag bereikt.",
|
|
1195
|
+
"assistant-consult-timeout": "De agent antwoordde niet op tijd.",
|
|
1196
|
+
"assistant-consult-failed": "De agent kon niet antwoorden.",
|
|
1197
|
+
"assistant-consult-read-only": "Een raadpleging kan niets wijzigen."
|
|
782
1198
|
},
|
|
783
1199
|
notices: {
|
|
784
1200
|
"no-answer": "De assistent is klaar zonder antwoord. Probeer het opnieuw of stel je vraag anders.",
|
|
@@ -892,7 +1308,8 @@ var nl = {
|
|
|
892
1308
|
propose: "Voorstellen",
|
|
893
1309
|
simulate: "Simuleren",
|
|
894
1310
|
execute: "Uitvoeren"
|
|
895
|
-
}
|
|
1311
|
+
},
|
|
1312
|
+
feedback: "Feedback"
|
|
896
1313
|
},
|
|
897
1314
|
"agent-editor": {
|
|
898
1315
|
"new-title": "Nieuwe agent",
|
|
@@ -1006,6 +1423,57 @@ var nl = {
|
|
|
1006
1423
|
"missing-warning": "De server biedt deze tool niet meer aan.",
|
|
1007
1424
|
save: "Tool opslaan",
|
|
1008
1425
|
back: "Terug naar de servers"
|
|
1426
|
+
},
|
|
1427
|
+
feedback: {
|
|
1428
|
+
title: "Feedback op voorstellen",
|
|
1429
|
+
description: "Wat mensen zeiden toen ze voorstellen van deze agent afwezen. Vergelijk agentversies om te zien of een wijziging van de prompt, tools of het beleid hielp.",
|
|
1430
|
+
agent: "{{name}} ({{id}})",
|
|
1431
|
+
back: "Terug naar agents",
|
|
1432
|
+
refresh: "Vernieuwen",
|
|
1433
|
+
"export": "Exporteren (JSON Lines)",
|
|
1434
|
+
exporting: "Exporteren…",
|
|
1435
|
+
"export-hint": "De export bevat gegevens van onderwerpen en de voorgestelde toolinvoer, en wordt vastgelegd in de auditlog.",
|
|
1436
|
+
period: "Periode",
|
|
1437
|
+
periods: {
|
|
1438
|
+
"last-7-days": "Afgelopen 7 dagen",
|
|
1439
|
+
"last-30-days": "Afgelopen 30 dagen",
|
|
1440
|
+
"last-90-days": "Afgelopen 90 dagen"
|
|
1441
|
+
},
|
|
1442
|
+
tool: "Tool",
|
|
1443
|
+
"all-tools": "Alle tools",
|
|
1444
|
+
category: "Categorie",
|
|
1445
|
+
"all-categories": "Alle categorieën",
|
|
1446
|
+
"category-hint": "De categorie beperkt de feedback en de export.",
|
|
1447
|
+
"summary-title": "Per tool en agentversie",
|
|
1448
|
+
"summary-empty": "De agent deed in deze periode geen voorstellen.",
|
|
1449
|
+
columns: {
|
|
1450
|
+
tool: "Tool",
|
|
1451
|
+
version: "Agentversie",
|
|
1452
|
+
approved: "Goedgekeurd",
|
|
1453
|
+
rejected: "Afgewezen",
|
|
1454
|
+
expired: "Verlopen",
|
|
1455
|
+
"rejection-rate": "Afwijzingspercentage",
|
|
1456
|
+
"with-feedback": "Afgewezen met feedback",
|
|
1457
|
+
people: "Personen",
|
|
1458
|
+
categories: "Meest gekozen"
|
|
1459
|
+
},
|
|
1460
|
+
version: "v{{version}}",
|
|
1461
|
+
consultations: "Raadplegingen",
|
|
1462
|
+
"no-decisions": "Nog niet beslist",
|
|
1463
|
+
"no-categories": "Geen",
|
|
1464
|
+
"list-title": "Feedback",
|
|
1465
|
+
"list-count": "{{count}} in totaal",
|
|
1466
|
+
"list-empty": "Niemand gaf in deze periode feedback.",
|
|
1467
|
+
"list-empty-filtered": "Geen feedback past bij deze filters.",
|
|
1468
|
+
"no-text": "Geen toelichting gegeven.",
|
|
1469
|
+
proposal: "Voorstel:",
|
|
1470
|
+
author: "Door {{actor}}",
|
|
1471
|
+
bulk: "In bulk",
|
|
1472
|
+
"bulk-hint": "Eén keer gegeven voor meerdere voorstellen die samen zijn afgewezen; weeg dit lager.",
|
|
1473
|
+
previous: "Vorige",
|
|
1474
|
+
next: "Volgende",
|
|
1475
|
+
page: "Pagina {{page}} van {{pages}}",
|
|
1476
|
+
pages: "Pagina's"
|
|
1009
1477
|
}
|
|
1010
1478
|
},
|
|
1011
1479
|
"page-context": {
|
|
@@ -1052,6 +1520,150 @@ var nl = {
|
|
|
1052
1520
|
remove: "{{name}} verwijderen",
|
|
1053
1521
|
leave: "Gesprek verlaten",
|
|
1054
1522
|
"no-conversation": "Start of open een gesprek om het te delen."
|
|
1523
|
+
},
|
|
1524
|
+
"agent-runs": {
|
|
1525
|
+
loading: "Laden…",
|
|
1526
|
+
"load-failed": "Dit kon niet worden geladen.",
|
|
1527
|
+
retry: "Opnieuw proberen",
|
|
1528
|
+
refresh: "Vernieuwen",
|
|
1529
|
+
role: {
|
|
1530
|
+
none: "Geen rol op dit onderwerp",
|
|
1531
|
+
reader: "Lezer",
|
|
1532
|
+
participant: "Deelnemer",
|
|
1533
|
+
approver: "Beslisser"
|
|
1534
|
+
},
|
|
1535
|
+
thread: {
|
|
1536
|
+
title: "Agent",
|
|
1537
|
+
"waiting-inputs": "{{count}} berichten wachten op de volgende run",
|
|
1538
|
+
"open-conversation": "Gesprek openen",
|
|
1539
|
+
"start-message": "Instructie voor de agent (optioneel)",
|
|
1540
|
+
start: "Agent starten",
|
|
1541
|
+
starting: "Starten…",
|
|
1542
|
+
"no-proposals": "De agent heeft hier nog niets voorgesteld.",
|
|
1543
|
+
"no-thread": "De agent heeft hier nog niet aan gewerkt.",
|
|
1544
|
+
tabs: {
|
|
1545
|
+
proposals: "Voorstellen",
|
|
1546
|
+
runs: "Runs",
|
|
1547
|
+
consultations: "Raadplegingen",
|
|
1548
|
+
memory: "Werkgeheugen",
|
|
1549
|
+
artifacts: "Bestanden"
|
|
1550
|
+
},
|
|
1551
|
+
outcome: {
|
|
1552
|
+
started: "De agent start zo.",
|
|
1553
|
+
queued: "De agent start na de huidige run.",
|
|
1554
|
+
coalesced: "Je verzoek sluit aan bij de run die zo start.",
|
|
1555
|
+
superseded: "De huidige run stopt en de agent start opnieuw.",
|
|
1556
|
+
ignored: "De agent is bezig; er is niets gestart.",
|
|
1557
|
+
deduplicated: "Dit was al gestart.",
|
|
1558
|
+
rejected: "De agent kon niet starten."
|
|
1559
|
+
}
|
|
1560
|
+
},
|
|
1561
|
+
subject: {
|
|
1562
|
+
title: "Voorstellen van agents",
|
|
1563
|
+
description: "Voorstellen die agents hier deden en die op een besluit wachten, de eerste die verloopt bovenaan.",
|
|
1564
|
+
empty: "Geen openstaande voorstellen van agents.",
|
|
1565
|
+
unavailable: "Je kunt het werk van agents hieraan niet zien.",
|
|
1566
|
+
agents: "Agents",
|
|
1567
|
+
idle: "Werkt nu niet",
|
|
1568
|
+
opening: "Openen…",
|
|
1569
|
+
"open-conversation-label": "Gesprek openen met {{agent}}",
|
|
1570
|
+
"start-label": "Agent starten: {{agent}}",
|
|
1571
|
+
"assistant-unavailable": "De assistent is niet beschikbaar."
|
|
1572
|
+
},
|
|
1573
|
+
inbox: {
|
|
1574
|
+
title: "Voorstellen om te beslissen",
|
|
1575
|
+
description: "Voorstellen van agents die op jouw besluit wachten, de eerst verlopende bovenaan.",
|
|
1576
|
+
empty: "Er wachten geen voorstellen op jou.",
|
|
1577
|
+
select: "Dit voorstel selecteren",
|
|
1578
|
+
"select-all": "Alles selecteren",
|
|
1579
|
+
"approve-selected": "Selectie goedkeuren",
|
|
1580
|
+
"reject-selected": "Selectie afwijzen",
|
|
1581
|
+
previous: "Vorige",
|
|
1582
|
+
next: "Volgende",
|
|
1583
|
+
page: "Pagina {{page}} van {{pages}}",
|
|
1584
|
+
pages: "Pagina's"
|
|
1585
|
+
},
|
|
1586
|
+
proposal: {
|
|
1587
|
+
approve: "Goedkeuren",
|
|
1588
|
+
reject: "Afwijzen",
|
|
1589
|
+
expires: "Verloopt {{date}}",
|
|
1590
|
+
"decided-by": "Besloten door {{actor}}",
|
|
1591
|
+
"arguments": "Invoer",
|
|
1592
|
+
"re-execute": "Opnieuw uitvoeren",
|
|
1593
|
+
"mark-resolved": "Als afgehandeld markeren",
|
|
1594
|
+
"unknown-hint": "De uitvoering stopte zonder bekende uitkomst. Controleer of het gebeurd is voordat je het opnieuw uitvoert.",
|
|
1595
|
+
mode: {
|
|
1596
|
+
inline: "De run wacht",
|
|
1597
|
+
deferred: "Later beslissen"
|
|
1598
|
+
},
|
|
1599
|
+
status: {
|
|
1600
|
+
pending: "Wacht",
|
|
1601
|
+
approved: "Goedgekeurd",
|
|
1602
|
+
rejected: "Afgewezen",
|
|
1603
|
+
expired: "Verlopen",
|
|
1604
|
+
withdrawn: "Ingetrokken",
|
|
1605
|
+
stale: "Verouderd",
|
|
1606
|
+
executing: "Wordt uitgevoerd",
|
|
1607
|
+
executed: "Uitgevoerd",
|
|
1608
|
+
"execution-failed": "Mislukt",
|
|
1609
|
+
"execution-unknown": "Uitkomst onbekend",
|
|
1610
|
+
resolved: "Afgehandeld",
|
|
1611
|
+
shadow: "Schaduw"
|
|
1612
|
+
},
|
|
1613
|
+
"from-consultation": "Uit een raadpleging",
|
|
1614
|
+
"from-consultation-hint": "Een andere assistent raadpleegde de agent voor deze persoon; het voorstel draait alleen met de rechten van die persoon.",
|
|
1615
|
+
"give-feedback": "Feedback geven",
|
|
1616
|
+
"change-feedback": "Feedback wijzigen",
|
|
1617
|
+
"your-feedback": "Jouw feedback: {{category}}",
|
|
1618
|
+
"rejected-one": "Je hebt afgewezen: {{summary}}"
|
|
1619
|
+
},
|
|
1620
|
+
run: {
|
|
1621
|
+
empty: "Nog geen runs.",
|
|
1622
|
+
status: {
|
|
1623
|
+
queued: "In de wachtrij",
|
|
1624
|
+
running: "Bezig",
|
|
1625
|
+
"waiting-for-approval": "Wacht op goedkeuring",
|
|
1626
|
+
completed: "Klaar",
|
|
1627
|
+
failed: "Mislukt",
|
|
1628
|
+
cancelled: "Geannuleerd"
|
|
1629
|
+
},
|
|
1630
|
+
shadow: "Schaduw",
|
|
1631
|
+
trigger: "Aanleiding: {{trigger}}",
|
|
1632
|
+
"tool-calls": "{{count}} tool-aanroepen",
|
|
1633
|
+
tokens: "{{count}} tokens",
|
|
1634
|
+
proposals: "{{count}} voorstellen",
|
|
1635
|
+
attempt: "Poging {{attempt}}",
|
|
1636
|
+
cancel: "Annuleren",
|
|
1637
|
+
resume: "Hervatten",
|
|
1638
|
+
restart: "Opnieuw starten"
|
|
1639
|
+
},
|
|
1640
|
+
memory: {
|
|
1641
|
+
"not-configured": "Deze agent houdt geen werkgeheugen bij.",
|
|
1642
|
+
version: "Schemaversie {{version}}",
|
|
1643
|
+
updated: "Bijgewerkt {{date}}",
|
|
1644
|
+
empty: "De agent heeft nog niets genoteerd."
|
|
1645
|
+
},
|
|
1646
|
+
artifacts: {
|
|
1647
|
+
"download-failed": "Het bestand kon niet worden gedownload.",
|
|
1648
|
+
empty: "Nog geen bestanden.",
|
|
1649
|
+
version: "v{{version}}",
|
|
1650
|
+
restricted: "Afgeschermd (agentrechten)",
|
|
1651
|
+
size: "{{size}} bytes",
|
|
1652
|
+
download: "Downloaden"
|
|
1653
|
+
},
|
|
1654
|
+
consultation: {
|
|
1655
|
+
empty: "Nog geen assistent heeft deze agent over dit onderwerp geraadpleegd.",
|
|
1656
|
+
"private": "Hier staat alleen wie vroeg en hoe het afliep; de vraag en het antwoord blijven in het vragende gesprek.",
|
|
1657
|
+
"asked-by": "Gevraagd door {{name}}",
|
|
1658
|
+
through: "via {{agent}}",
|
|
1659
|
+
depth: "Genest, niveau {{depth}}",
|
|
1660
|
+
more: "{{count}} oudere raadplegingen worden niet getoond.",
|
|
1661
|
+
status: {
|
|
1662
|
+
running: "Bezig",
|
|
1663
|
+
completed: "Beantwoord",
|
|
1664
|
+
failed: "Niet beantwoord"
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1055
1667
|
}
|
|
1056
1668
|
}
|
|
1057
1669
|
};
|
|
@@ -1231,7 +1843,8 @@ class NhAssistantTransport {
|
|
|
1231
1843
|
}
|
|
1232
1844
|
}
|
|
1233
1845
|
async createInit(method, accept, body, signal) {
|
|
1234
|
-
const headers =
|
|
1846
|
+
const headers = await nhAssistantRequestHeaders(this.config, this.injector);
|
|
1847
|
+
headers['Accept'] = accept;
|
|
1235
1848
|
const token = await runInInjectionContext(this.injector, () => this.config.getAccessToken());
|
|
1236
1849
|
if (token) {
|
|
1237
1850
|
headers['Authorization'] = `Bearer ${token}`;
|
|
@@ -1264,6 +1877,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImpo
|
|
|
1264
1877
|
function createApiError(status, code) {
|
|
1265
1878
|
return new NhAssistantApiError(status, code, nhAssistantErrorMessageKey(code));
|
|
1266
1879
|
}
|
|
1880
|
+
const reservedRequestHeaders = new Set(['authorization', 'accept', 'accept-language', 'content-type']);
|
|
1881
|
+
/**
|
|
1882
|
+
* The host's extra request headers from `NhAssistantConfig.getRequestHeaders`, without the
|
|
1883
|
+
* headers the library sets itself and without empty values.
|
|
1884
|
+
*/
|
|
1885
|
+
async function nhAssistantRequestHeaders(config, injector) {
|
|
1886
|
+
const headers = {};
|
|
1887
|
+
if (!config.getRequestHeaders) {
|
|
1888
|
+
return headers;
|
|
1889
|
+
}
|
|
1890
|
+
const extra = await runInInjectionContext(injector, () => config.getRequestHeaders());
|
|
1891
|
+
for (const [name, value] of Object.entries(extra ?? {})) {
|
|
1892
|
+
if (typeof value === 'string' && value.length > 0 && !reservedRequestHeaders.has(name.toLowerCase())) {
|
|
1893
|
+
headers[name] = value;
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
return headers;
|
|
1897
|
+
}
|
|
1267
1898
|
|
|
1268
1899
|
/** In the admin endpoints a 409 means a stale `expectedVersion` or a forbidden state change. */
|
|
1269
1900
|
const adminStatusCodes = {
|
|
@@ -1337,6 +1968,37 @@ class NhAssistantAdminApiService {
|
|
|
1337
1968
|
const path = `admin/mcp-servers/${encodeURIComponent(serverId)}/tools/${encodeURIComponent(remoteName)}`;
|
|
1338
1969
|
return this.request('PUT', path, request);
|
|
1339
1970
|
}
|
|
1971
|
+
/**
|
|
1972
|
+
* The agent's proposals per tool and agent version in the period: decisions, the rejection rate,
|
|
1973
|
+
* feedback per category and the number of different people who gave it.
|
|
1974
|
+
*/
|
|
1975
|
+
getAgentFeedbackSummary(agentId, filter = {}) {
|
|
1976
|
+
return this.request('GET', withQuery(`${feedbackPath(agentId)}/summary`, filter));
|
|
1977
|
+
}
|
|
1978
|
+
/** The feedback on the agent's proposals with the proposal as approvers read it, newest change first. */
|
|
1979
|
+
getAgentFeedback(agentId, filter = {}) {
|
|
1980
|
+
return this.request('GET', withQuery(feedbackPath(agentId), filter));
|
|
1981
|
+
}
|
|
1982
|
+
/**
|
|
1983
|
+
* Downloads the feedback as JSON Lines with the tool input of each proposal. The file holds
|
|
1984
|
+
* subject data and the server audits the export. Rejects with `NhAssistantApiError`.
|
|
1985
|
+
*/
|
|
1986
|
+
async exportAgentFeedback(agentId, filter = {}) {
|
|
1987
|
+
const path = withQuery(`${feedbackPath(agentId)}/export`, filter);
|
|
1988
|
+
let response;
|
|
1989
|
+
try {
|
|
1990
|
+
response = await this.transport.fetch('GET', path, 'application/x-ndjson', undefined, new AbortController().signal);
|
|
1991
|
+
}
|
|
1992
|
+
catch {
|
|
1993
|
+
const code = nhAssistantCodeForStatus(0);
|
|
1994
|
+
throw new NhAssistantApiError(0, code, nhAssistantErrorMessageKey(code));
|
|
1995
|
+
}
|
|
1996
|
+
if (!response.ok) {
|
|
1997
|
+
const code = await this.transport.failureCode(response, adminStatusCodes);
|
|
1998
|
+
throw new NhAssistantApiError(response.status, code, nhAssistantErrorMessageKey(code));
|
|
1999
|
+
}
|
|
2000
|
+
return response.blob();
|
|
2001
|
+
}
|
|
1340
2002
|
request(method, path, body) {
|
|
1341
2003
|
return this.transport.json(method, path, body, adminStatusCodes);
|
|
1342
2004
|
}
|
|
@@ -1346,6 +2008,168 @@ class NhAssistantAdminApiService {
|
|
|
1346
2008
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantAdminApiService, decorators: [{
|
|
1347
2009
|
type: Injectable
|
|
1348
2010
|
}] });
|
|
2011
|
+
function feedbackPath(agentId) {
|
|
2012
|
+
return `admin/agents/${encodeURIComponent(agentId)}/feedback`;
|
|
2013
|
+
}
|
|
2014
|
+
/** Appends the filter values that are set as query parameters. */
|
|
2015
|
+
function withQuery(path, filter) {
|
|
2016
|
+
const query = new URLSearchParams();
|
|
2017
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
2018
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
2019
|
+
query.set(key, String(value));
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
const suffix = query.toString();
|
|
2023
|
+
return suffix ? `${path}?${suffix}` : path;
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
/**
|
|
2027
|
+
* Client for the agent-run endpoints: the agent thread about a subject, its runs, proposals,
|
|
2028
|
+
* working memory and artifacts, and the approval inbox. Access follows the caller's role on the
|
|
2029
|
+
* subject, which the server checks on every request; failures error with `NhAssistantApiError`.
|
|
2030
|
+
*/
|
|
2031
|
+
class NhAssistantAgentRunApiService {
|
|
2032
|
+
constructor() {
|
|
2033
|
+
this.transport = inject(NhAssistantTransport);
|
|
2034
|
+
}
|
|
2035
|
+
/** The agent's thread about the subject, the caller's role and whether the caller may start the agent. */
|
|
2036
|
+
getThread(agentId, subjectType, subjectId) {
|
|
2037
|
+
const query = new URLSearchParams({ agentId, subjectType, subjectId });
|
|
2038
|
+
return this.transport.json('GET', `threads?${query.toString()}`);
|
|
2039
|
+
}
|
|
2040
|
+
/**
|
|
2041
|
+
* The agent's thread about the subject, created without starting a run when it is missing, so
|
|
2042
|
+
* the caller can open the conversation and type there. Creating a thread needs the participant
|
|
2043
|
+
* role; readers may open an existing one. Errors with `assistant-subject-access-denied` otherwise.
|
|
2044
|
+
*/
|
|
2045
|
+
openThread(agentId, subjectType, subjectId) {
|
|
2046
|
+
return this.transport.json('POST', 'threads/open', { agentId, subjectType, subjectId });
|
|
2047
|
+
}
|
|
2048
|
+
/** Starts the agent on the subject with the caller as initiator; needs the participant role. */
|
|
2049
|
+
startAgent(request) {
|
|
2050
|
+
return this.transport.json('POST', 'threads/start', request);
|
|
2051
|
+
}
|
|
2052
|
+
/**
|
|
2053
|
+
* All agent work on one subject: the caller's role, every agent with a thread on it plus
|
|
2054
|
+
* `agentId`, and the open proposals of all of them with the caller's live decision right. Errors
|
|
2055
|
+
* with `assistant-subject-not-found` when the caller has no role on the subject.
|
|
2056
|
+
*/
|
|
2057
|
+
getSubjectWork(subjectType, subjectId, agentId) {
|
|
2058
|
+
const path = `subjects/${encodeURIComponent(subjectType)}/${encodeURIComponent(subjectId)}/work`;
|
|
2059
|
+
const query = agentId ? `?${new URLSearchParams({ agentId }).toString()}` : '';
|
|
2060
|
+
return this.transport.json('GET', path + query);
|
|
2061
|
+
}
|
|
2062
|
+
/**
|
|
2063
|
+
* Counts the agent work on at most `NH_ASSISTANT_MAX_SUBJECT_SUMMARY` subjects of one type in one
|
|
2064
|
+
* request, for badges in a list; one item per id in request order. `NhAssistantSubjectSummaryService`
|
|
2065
|
+
* batches and caches these requests for many badges.
|
|
2066
|
+
*/
|
|
2067
|
+
summarizeSubjects(subjectType, subjectIds) {
|
|
2068
|
+
return this.transport.json('POST', `subjects/${encodeURIComponent(subjectType)}/summary`, {
|
|
2069
|
+
subjectIds
|
|
2070
|
+
});
|
|
2071
|
+
}
|
|
2072
|
+
getRuns(conversationId) {
|
|
2073
|
+
return this.transport.json('GET', `threads/${encodeURIComponent(conversationId)}/runs`);
|
|
2074
|
+
}
|
|
2075
|
+
/** Who consulted the thread's agent through another assistant, newest first. */
|
|
2076
|
+
getConsultations(conversationId, page = 1, itemsPerPage = 20) {
|
|
2077
|
+
const query = new URLSearchParams({ page: String(page), itemsPerPage: String(itemsPerPage) });
|
|
2078
|
+
return this.transport.json('GET', `threads/${encodeURIComponent(conversationId)}/consultations?${query.toString()}`);
|
|
2079
|
+
}
|
|
2080
|
+
getProposals(conversationId) {
|
|
2081
|
+
return this.transport.json('GET', `threads/${encodeURIComponent(conversationId)}/proposals`);
|
|
2082
|
+
}
|
|
2083
|
+
getMemory(conversationId) {
|
|
2084
|
+
return this.transport.json('GET', `threads/${encodeURIComponent(conversationId)}/memory`);
|
|
2085
|
+
}
|
|
2086
|
+
getArtifacts(conversationId) {
|
|
2087
|
+
return this.transport.json('GET', `threads/${encodeURIComponent(conversationId)}/artifacts`);
|
|
2088
|
+
}
|
|
2089
|
+
/** Downloads one artifact version with the host's credentials. */
|
|
2090
|
+
downloadArtifact(conversationId, artifactId) {
|
|
2091
|
+
const path = `threads/${encodeURIComponent(conversationId)}/artifacts/${encodeURIComponent(artifactId)}/content`;
|
|
2092
|
+
return this.transport.fetch('GET', path, '*/*', undefined, new AbortController().signal).then(response => {
|
|
2093
|
+
if (!response.ok) {
|
|
2094
|
+
throw new Error(`Artifact download failed with status ${response.status}.`);
|
|
2095
|
+
}
|
|
2096
|
+
return response.blob();
|
|
2097
|
+
});
|
|
2098
|
+
}
|
|
2099
|
+
/** Cancels a run at its next safe point and withdraws its open proposals; needs the participant role. */
|
|
2100
|
+
cancelRun(runId) {
|
|
2101
|
+
return this.transport.json('POST', `runs/${encodeURIComponent(runId)}/cancel`);
|
|
2102
|
+
}
|
|
2103
|
+
/** Resumes or restarts a failed or cancelled run; needs the approver role. */
|
|
2104
|
+
retryRun(runId, mode) {
|
|
2105
|
+
return this.transport.json('POST', `runs/${encodeURIComponent(runId)}/retry`, { mode });
|
|
2106
|
+
}
|
|
2107
|
+
/** Pending proposals the caller may decide, sorted by expiry. */
|
|
2108
|
+
getInbox(filter = {}) {
|
|
2109
|
+
const query = new URLSearchParams();
|
|
2110
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
2111
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
2112
|
+
query.set(key, String(value));
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
const suffix = query.toString();
|
|
2116
|
+
return this.transport.json('GET', suffix ? `approvals/inbox?${suffix}` : 'approvals/inbox');
|
|
2117
|
+
}
|
|
2118
|
+
/**
|
|
2119
|
+
* Decides one proposal, bound to the proposal hash the person saw; the server checks the approval
|
|
2120
|
+
* policy and the tool's approve policy again. Accepts an inbox proposal or the approval part of an
|
|
2121
|
+
* agent thread.
|
|
2122
|
+
*/
|
|
2123
|
+
decide(proposal, decision, reason) {
|
|
2124
|
+
return this.transport.json('POST', `approvals/${encodeURIComponent(proposal.approvalId)}/decisions`, {
|
|
2125
|
+
decision,
|
|
2126
|
+
expectedProposalHash: proposal.proposalHash,
|
|
2127
|
+
reason: reason?.trim() || null
|
|
2128
|
+
});
|
|
2129
|
+
}
|
|
2130
|
+
/** Decides several proposals; each one is checked and decided on its own. */
|
|
2131
|
+
decideMany(approvalIds, decision, reason) {
|
|
2132
|
+
return this.transport.json('POST', 'approvals/decisions', {
|
|
2133
|
+
approvalIds,
|
|
2134
|
+
decision,
|
|
2135
|
+
reason: reason?.trim() || null
|
|
2136
|
+
});
|
|
2137
|
+
}
|
|
2138
|
+
/** Resolves an execution whose outcome is unknown. */
|
|
2139
|
+
resolve(approvalId, action, reason) {
|
|
2140
|
+
return this.transport.json('POST', `approvals/${encodeURIComponent(approvalId)}/resolve`, {
|
|
2141
|
+
action,
|
|
2142
|
+
reason: reason?.trim() || null
|
|
2143
|
+
});
|
|
2144
|
+
}
|
|
2145
|
+
/**
|
|
2146
|
+
* Gives or changes the caller's feedback on a closed proposal (`canGiveFeedback`). Feedback is
|
|
2147
|
+
* optional and separate from the decision; an empty text is sent as `null`. Fails with
|
|
2148
|
+
* `assistant-feedback-not-accepted` while the proposal is pending or after it was approved.
|
|
2149
|
+
*/
|
|
2150
|
+
giveFeedback(approvalId, category, text) {
|
|
2151
|
+
return this.transport.json('PUT', `approvals/${encodeURIComponent(approvalId)}/feedback`, {
|
|
2152
|
+
category,
|
|
2153
|
+
text: text?.trim() || null
|
|
2154
|
+
});
|
|
2155
|
+
}
|
|
2156
|
+
/**
|
|
2157
|
+
* Gives the same feedback on at most `NH_ASSISTANT_MAX_BULK_FEEDBACK` proposals that were
|
|
2158
|
+
* rejected together. Each proposal is checked on its own and stored as bulk feedback.
|
|
2159
|
+
*/
|
|
2160
|
+
giveFeedbackMany(approvalIds, category, text) {
|
|
2161
|
+
return this.transport.json('POST', 'approvals/feedback', {
|
|
2162
|
+
approvalIds,
|
|
2163
|
+
category,
|
|
2164
|
+
text: text?.trim() || null
|
|
2165
|
+
});
|
|
2166
|
+
}
|
|
2167
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantAgentRunApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2168
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantAgentRunApiService }); }
|
|
2169
|
+
}
|
|
2170
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantAgentRunApiService, decorators: [{
|
|
2171
|
+
type: Injectable
|
|
2172
|
+
}] });
|
|
1349
2173
|
|
|
1350
2174
|
/**
|
|
1351
2175
|
* Incremental parser for the `text/event-stream` format.
|
|
@@ -1614,10 +2438,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImpo
|
|
|
1614
2438
|
|
|
1615
2439
|
const reconnectDelaysMs = [0, 2_000, 5_000, 10_000, 30_000];
|
|
1616
2440
|
const restartDelayMs = 30_000;
|
|
2441
|
+
/** Query parameters the library and SignalR set themselves. */
|
|
2442
|
+
const reservedHubQueryParameters = new Set(['access_token', 'id', 'negotiateversion']);
|
|
1617
2443
|
/**
|
|
1618
|
-
* Live updates of the assistant hub: conversation changes, read positions, removals
|
|
1619
|
-
*
|
|
1620
|
-
* own and emits `resync` after every (re)connect,
|
|
2444
|
+
* Live updates of the assistant hub: conversation changes, read positions, removals, the turn
|
|
2445
|
+
* events of other sessions and changes to the work of agents that run on their own. Connects only
|
|
2446
|
+
* while the store asks for it, reconnects on its own and emits `resync` after every (re)connect,
|
|
2447
|
+
* because updates sent while disconnected are lost.
|
|
1621
2448
|
*/
|
|
1622
2449
|
class NhAssistantLiveService {
|
|
1623
2450
|
constructor() {
|
|
@@ -1628,6 +2455,7 @@ class NhAssistantLiveService {
|
|
|
1628
2455
|
this.readSubject = new Subject();
|
|
1629
2456
|
this.removedSubject = new Subject();
|
|
1630
2457
|
this.eventSubject = new Subject();
|
|
2458
|
+
this.agentWorkSubject = new Subject();
|
|
1631
2459
|
this.resyncSubject = new Subject();
|
|
1632
2460
|
this.stateSignal = signal('off', ...(ngDevMode ? [{ debugName: "stateSignal" }] : []));
|
|
1633
2461
|
this.hubPath = null;
|
|
@@ -1637,6 +2465,11 @@ class NhAssistantLiveService {
|
|
|
1637
2465
|
this.read$ = this.readSubject.asObservable();
|
|
1638
2466
|
this.removed$ = this.removedSubject.asObservable();
|
|
1639
2467
|
this.events$ = this.eventSubject.asObservable();
|
|
2468
|
+
/**
|
|
2469
|
+
* A run, proposal or feedback of an agent thread changed. It carries no content; components
|
|
2470
|
+
* that show the thread reload it.
|
|
2471
|
+
*/
|
|
2472
|
+
this.agentWork$ = this.agentWorkSubject.asObservable();
|
|
1640
2473
|
/** Reload snapshots: emitted after every (re)connect. */
|
|
1641
2474
|
this.resync$ = this.resyncSubject.asObservable();
|
|
1642
2475
|
this.state = this.stateSignal.asReadonly();
|
|
@@ -1646,6 +2479,7 @@ class NhAssistantLiveService {
|
|
|
1646
2479
|
this.readSubject.complete();
|
|
1647
2480
|
this.removedSubject.complete();
|
|
1648
2481
|
this.eventSubject.complete();
|
|
2482
|
+
this.agentWorkSubject.complete();
|
|
1649
2483
|
this.resyncSubject.complete();
|
|
1650
2484
|
});
|
|
1651
2485
|
}
|
|
@@ -1701,9 +2535,12 @@ class NhAssistantLiveService {
|
|
|
1701
2535
|
}
|
|
1702
2536
|
async build(url) {
|
|
1703
2537
|
const signalR = await import('@microsoft/signalr');
|
|
2538
|
+
const headers = await nhAssistantRequestHeaders(this.config, this.injector);
|
|
2539
|
+
const hubUrl = await this.withQueryParameters(url);
|
|
1704
2540
|
const connection = new signalR.HubConnectionBuilder()
|
|
1705
|
-
.withUrl(
|
|
1706
|
-
accessTokenFactory: async () => (await this.token()) ?? ''
|
|
2541
|
+
.withUrl(hubUrl, {
|
|
2542
|
+
accessTokenFactory: async () => (await this.token()) ?? '',
|
|
2543
|
+
headers
|
|
1707
2544
|
})
|
|
1708
2545
|
.withAutomaticReconnect(reconnectDelaysMs)
|
|
1709
2546
|
.configureLogging(signalR.LogLevel.None)
|
|
@@ -1712,6 +2549,7 @@ class NhAssistantLiveService {
|
|
|
1712
2549
|
connection.on('ConversationRead', (message) => this.readSubject.next(message));
|
|
1713
2550
|
connection.on('ConversationRemoved', (message) => this.removedSubject.next(message));
|
|
1714
2551
|
connection.on('ConversationEvent', (message) => this.eventSubject.next(message));
|
|
2552
|
+
connection.on('AgentWorkChanged', (message) => this.agentWorkSubject.next(message));
|
|
1715
2553
|
connection.onreconnecting(() => this.stateSignal.set('reconnecting'));
|
|
1716
2554
|
connection.onreconnected(() => {
|
|
1717
2555
|
this.stateSignal.set('connected');
|
|
@@ -1743,6 +2581,30 @@ class NhAssistantLiveService {
|
|
|
1743
2581
|
return null;
|
|
1744
2582
|
}
|
|
1745
2583
|
}
|
|
2584
|
+
/**
|
|
2585
|
+
* The hub URL with the host's `getHubQueryParameters`, which reach the hub on every transport,
|
|
2586
|
+
* unlike headers on WebSockets and server-sent events.
|
|
2587
|
+
*/
|
|
2588
|
+
async withQueryParameters(url) {
|
|
2589
|
+
const getter = this.config.getHubQueryParameters;
|
|
2590
|
+
if (!getter) {
|
|
2591
|
+
return url;
|
|
2592
|
+
}
|
|
2593
|
+
let parameters;
|
|
2594
|
+
try {
|
|
2595
|
+
parameters = await runInInjectionContext(this.injector, () => getter());
|
|
2596
|
+
}
|
|
2597
|
+
catch {
|
|
2598
|
+
return url;
|
|
2599
|
+
}
|
|
2600
|
+
const target = new URL(url);
|
|
2601
|
+
for (const [name, value] of Object.entries(parameters ?? {})) {
|
|
2602
|
+
if (typeof value === 'string' && value.length > 0 && !reservedHubQueryParameters.has(name.toLowerCase())) {
|
|
2603
|
+
target.searchParams.set(name, value);
|
|
2604
|
+
}
|
|
2605
|
+
}
|
|
2606
|
+
return target.toString();
|
|
2607
|
+
}
|
|
1746
2608
|
/** `hubBaseUrl` + `hubPath`, or the hub path on the origin of `apiBaseUrl`. */
|
|
1747
2609
|
resolveUrl(hubPath) {
|
|
1748
2610
|
const page = globalThis.location?.href ?? 'http://localhost';
|
|
@@ -2399,6 +3261,7 @@ function validId(value) {
|
|
|
2399
3261
|
|
|
2400
3262
|
/** Notice code for a completed turn that produced no answer text and no server code. */
|
|
2401
3263
|
const NH_ASSISTANT_NO_ANSWER_NOTICE_CODE = 'assistant-no-answer';
|
|
3264
|
+
const fourEyesRequiredCode = 'assistant-four-eyes-required';
|
|
2402
3265
|
/** True when a conversation has messages the caller has not read. A running turn is not unread yet. */
|
|
2403
3266
|
function nhAssistantIsUnread(summary) {
|
|
2404
3267
|
if (summary.status === 'running' || summary.lastReadSequence === undefined) {
|
|
@@ -2420,6 +3283,7 @@ const newConversationKey = '';
|
|
|
2420
3283
|
class NhAssistantStore {
|
|
2421
3284
|
constructor() {
|
|
2422
3285
|
this.api = inject(NhAssistantApiService);
|
|
3286
|
+
this.agentRuns = inject(NhAssistantAgentRunApiService);
|
|
2423
3287
|
this.config = inject(NH_ASSISTANT_CONFIG);
|
|
2424
3288
|
this.injector = inject(EnvironmentInjector);
|
|
2425
3289
|
this.accessPolicy = inject(NH_ASSISTANT_ACCESS_POLICY);
|
|
@@ -2452,9 +3316,13 @@ class NhAssistantStore {
|
|
|
2452
3316
|
this.queueDispatchState = signal(null, ...(ngDevMode ? [{ debugName: "queueDispatchState" }] : []));
|
|
2453
3317
|
this.steeringState = signal(null, ...(ngDevMode ? [{ debugName: "steeringState" }] : []));
|
|
2454
3318
|
this.submittingState = signal(false, ...(ngDevMode ? [{ debugName: "submittingState" }] : []));
|
|
3319
|
+
/** Agent proposals the current user rejected in this session and may give feedback on. */
|
|
3320
|
+
this.feedbackApprovalsState = signal(new Set(), ...(ngDevMode ? [{ debugName: "feedbackApprovalsState" }] : []));
|
|
2455
3321
|
this.destroyed = false;
|
|
2456
3322
|
this.streams = new Map();
|
|
2457
3323
|
this.cancelTimers = new Map();
|
|
3324
|
+
/** Pending reloads of agent threads after live agent-work updates, per conversation. */
|
|
3325
|
+
this.agentWorkTimers = new Map();
|
|
2458
3326
|
this.pendingSendResolves = new Map();
|
|
2459
3327
|
this.markingRead = new Set();
|
|
2460
3328
|
this.liveSubscription = new Subscription();
|
|
@@ -2530,13 +3398,61 @@ class NhAssistantStore {
|
|
|
2530
3398
|
this.members = computed(() => this.activeConversation()?.members ?? [], ...(ngDevMode ? [{ debugName: "members" }] : []));
|
|
2531
3399
|
/** True when the current user owns the open conversation (or it is new). */
|
|
2532
3400
|
this.isOwner = computed(() => (this.activeConversation()?.role ?? 'owner') === 'owner', ...(ngDevMode ? [{ debugName: "isOwner" }] : []));
|
|
2533
|
-
/** True when the
|
|
2534
|
-
this.
|
|
3401
|
+
/** True when the open conversation is the durable thread of an agent about a subject. */
|
|
3402
|
+
this.isAgentThread = computed(() => this.activeConversation()?.kind === 'thread', ...(ngDevMode ? [{ debugName: "isAgentThread" }] : []));
|
|
3403
|
+
/**
|
|
3404
|
+
* The current user's right to decide each pending approval of the open conversation, by approval
|
|
3405
|
+
* id. In a chat only the author of the waiting turn decides. In an agent thread the subject's
|
|
3406
|
+
* approvers decide, as the server reports per proposal: the approval policy, four-eyes and the
|
|
3407
|
+
* tool's approve policy, checked live. Proposals the user rejected in this session carry
|
|
3408
|
+
* `canGiveFeedback`.
|
|
3409
|
+
*/
|
|
3410
|
+
this.approvalRights = computed(() => {
|
|
2535
3411
|
const conversation = this.activeConversation();
|
|
2536
|
-
|
|
2537
|
-
|
|
3412
|
+
const rights = new Map();
|
|
3413
|
+
if (!conversation) {
|
|
3414
|
+
return rights;
|
|
3415
|
+
}
|
|
3416
|
+
if (conversation.kind === 'thread') {
|
|
3417
|
+
const decisions = new Map((conversation.thread?.decisions ?? []).map(decision => [decision.approvalId, decision]));
|
|
3418
|
+
for (const approval of pendingApprovalParts(conversation)) {
|
|
3419
|
+
const decision = decisions.get(approval.approvalId);
|
|
3420
|
+
rights.set(approval.approvalId, {
|
|
3421
|
+
canDecide: decision?.canDecide === true,
|
|
3422
|
+
waitingFor: decision?.reasonCode === fourEyesRequiredCode ? 'other-approver' : 'approver',
|
|
3423
|
+
decisionOwner: null
|
|
3424
|
+
});
|
|
3425
|
+
}
|
|
3426
|
+
const feedbackApprovals = this.feedbackApprovalsState();
|
|
3427
|
+
for (const approval of approvalParts(conversation)) {
|
|
3428
|
+
if (approval.status === 'rejected' && feedbackApprovals.has(approval.approvalId)) {
|
|
3429
|
+
rights.set(approval.approvalId, {
|
|
3430
|
+
canDecide: false,
|
|
3431
|
+
waitingFor: 'approver',
|
|
3432
|
+
decisionOwner: null,
|
|
3433
|
+
canGiveFeedback: true
|
|
3434
|
+
});
|
|
3435
|
+
}
|
|
3436
|
+
}
|
|
3437
|
+
return rights;
|
|
2538
3438
|
}
|
|
2539
|
-
|
|
3439
|
+
if (conversation.pendingApproval) {
|
|
3440
|
+
rights.set(conversation.pendingApproval.approvalId, {
|
|
3441
|
+
canDecide: !conversation.activeActorId || !conversation.currentActorId
|
|
3442
|
+
|| conversation.activeActorId === conversation.currentActorId,
|
|
3443
|
+
waitingFor: 'turn',
|
|
3444
|
+
decisionOwner: this.activeMember()?.displayName ?? null
|
|
3445
|
+
});
|
|
3446
|
+
}
|
|
3447
|
+
return rights;
|
|
3448
|
+
}, ...(ngDevMode ? [{ debugName: "approvalRights" }] : []));
|
|
3449
|
+
/**
|
|
3450
|
+
* True when the current user may decide the pending approval: in a chat it belongs to their own
|
|
3451
|
+
* turn, in an agent thread the server allows them as an approver.
|
|
3452
|
+
*/
|
|
3453
|
+
this.canDecide = computed(() => {
|
|
3454
|
+
const approval = this.activeConversation()?.pendingApproval;
|
|
3455
|
+
return approval ? this.approvalRights().get(approval.approvalId)?.canDecide === true : false;
|
|
2540
3456
|
}, ...(ngDevMode ? [{ debugName: "canDecide" }] : []));
|
|
2541
3457
|
/** The member whose turn runs or waits in the open conversation, when it is someone else. */
|
|
2542
3458
|
this.activeMember = computed(() => {
|
|
@@ -2577,6 +3493,7 @@ class NhAssistantStore {
|
|
|
2577
3493
|
this.accessSubscription?.unsubscribe();
|
|
2578
3494
|
this.liveSubscription.unsubscribe();
|
|
2579
3495
|
this.stopAllTurns();
|
|
3496
|
+
this.clearAgentWorkTimers();
|
|
2580
3497
|
clearTimeout(this.refreshTimer);
|
|
2581
3498
|
});
|
|
2582
3499
|
effect(() => {
|
|
@@ -2607,6 +3524,7 @@ class NhAssistantStore {
|
|
|
2607
3524
|
this.liveSubscription.add(this.live.read$.subscribe(read => this.onLiveRead(read)));
|
|
2608
3525
|
this.liveSubscription.add(this.live.removed$.subscribe(removed => this.onLiveRemoved(removed)));
|
|
2609
3526
|
this.liveSubscription.add(this.live.events$.subscribe(event => this.onLiveEvent(event)));
|
|
3527
|
+
this.liveSubscription.add(this.live.agentWork$.subscribe(change => this.onLiveAgentWork(change)));
|
|
2610
3528
|
this.liveSubscription.add(this.live.resync$.subscribe(() => this.resync()));
|
|
2611
3529
|
if (this.browser) {
|
|
2612
3530
|
const update = () => this.pageVisibleState.set(this.document.visibilityState === 'visible' && this.document.hasFocus());
|
|
@@ -2964,12 +3882,31 @@ class NhAssistantStore {
|
|
|
2964
3882
|
this.sendingState.set(null);
|
|
2965
3883
|
});
|
|
2966
3884
|
}
|
|
2967
|
-
/**
|
|
2968
|
-
|
|
3885
|
+
/**
|
|
3886
|
+
* Approves or rejects an approval of the open conversation: `approvalId`, or the pending approval
|
|
3887
|
+
* when it is left out. A proposal of an agent's run is decided with its proposal hash through the
|
|
3888
|
+
* agent-run endpoint; other approvals resume their turn as a stream. Ignores calls the current
|
|
3889
|
+
* user may not make and repeated calls while a decision is in flight.
|
|
3890
|
+
*/
|
|
3891
|
+
decide(decision, reason, approvalId) {
|
|
2969
3892
|
const session = this.activeSession();
|
|
2970
3893
|
const conversation = session?.conversation;
|
|
2971
|
-
|
|
2972
|
-
|
|
3894
|
+
if (!session || !conversation || session.deciding || session.streaming) {
|
|
3895
|
+
return;
|
|
3896
|
+
}
|
|
3897
|
+
const approval = approvalId
|
|
3898
|
+
? pendingApprovalParts(conversation).find(part => part.approvalId === approvalId) ?? null
|
|
3899
|
+
: conversation.pendingApproval;
|
|
3900
|
+
if (!approval || this.approvalRights().get(approval.approvalId)?.canDecide !== true) {
|
|
3901
|
+
return;
|
|
3902
|
+
}
|
|
3903
|
+
const proposal = conversation.thread?.decisions.find(item => item.approvalId === approval.approvalId);
|
|
3904
|
+
if (proposal?.runId) {
|
|
3905
|
+
void this.decideProposal(conversation.id, approval, decision, reason);
|
|
3906
|
+
return;
|
|
3907
|
+
}
|
|
3908
|
+
if (conversation.pendingApproval?.approvalId !== approval.approvalId) {
|
|
3909
|
+
// Only the approval the conversation waits for resumes a turn.
|
|
2973
3910
|
return;
|
|
2974
3911
|
}
|
|
2975
3912
|
const conversationId = conversation.id;
|
|
@@ -3001,6 +3938,38 @@ class NhAssistantStore {
|
|
|
3001
3938
|
}
|
|
3002
3939
|
});
|
|
3003
3940
|
}
|
|
3941
|
+
/**
|
|
3942
|
+
* Decides a proposal of an agent's run with `POST approvals/{id}/decisions`. An approved deferred
|
|
3943
|
+
* proposal executes later as a background operation; an inline one wakes the waiting run. The
|
|
3944
|
+
* reloaded snapshot shows the outcome and the proposals that remain open.
|
|
3945
|
+
*/
|
|
3946
|
+
async decideProposal(conversationId, approval, decision, reason) {
|
|
3947
|
+
const revision = this.accountRevision;
|
|
3948
|
+
this.patchSession(conversationId, session => ({ ...session, deciding: true, error: null, notice: null }));
|
|
3949
|
+
try {
|
|
3950
|
+
const proposal = await firstValueFrom(this.agentRuns.decide(approval, decision, reason));
|
|
3951
|
+
if (revision === this.accountRevision) {
|
|
3952
|
+
if (decision === 'reject' && proposal.status === 'rejected' && proposal.canGiveFeedback === true) {
|
|
3953
|
+
this.feedbackApprovalsState.update(approvals => new Set(approvals).add(approval.approvalId));
|
|
3954
|
+
}
|
|
3955
|
+
this.patchSession(conversationId, session => ({
|
|
3956
|
+
...session,
|
|
3957
|
+
conversation: applyProposalDecision(session.conversation, approval.approvalId, proposal.status)
|
|
3958
|
+
}));
|
|
3959
|
+
}
|
|
3960
|
+
}
|
|
3961
|
+
catch (error) {
|
|
3962
|
+
if (revision === this.accountRevision) {
|
|
3963
|
+
this.setError(conversationId, error);
|
|
3964
|
+
}
|
|
3965
|
+
}
|
|
3966
|
+
finally {
|
|
3967
|
+
if (revision === this.accountRevision) {
|
|
3968
|
+
this.patchSession(conversationId, session => ({ ...session, deciding: false }));
|
|
3969
|
+
await this.reloadSession(conversationId);
|
|
3970
|
+
}
|
|
3971
|
+
}
|
|
3972
|
+
}
|
|
3004
3973
|
/** Asks the server to stop the running turn or to abandon a waiting approval of the open conversation. */
|
|
3005
3974
|
async cancel() {
|
|
3006
3975
|
const conversationId = this.activeIdState();
|
|
@@ -3333,6 +4302,8 @@ class NhAssistantStore {
|
|
|
3333
4302
|
this.queueDispatchState.set(null);
|
|
3334
4303
|
this.steeringState.set(null);
|
|
3335
4304
|
this.submittingState.set(false);
|
|
4305
|
+
this.feedbackApprovalsState.set(new Set());
|
|
4306
|
+
this.clearAgentWorkTimers();
|
|
3336
4307
|
void this.live.stop();
|
|
3337
4308
|
void this.push.deactivate().catch(() => undefined);
|
|
3338
4309
|
}
|
|
@@ -3537,6 +4508,33 @@ class NhAssistantStore {
|
|
|
3537
4508
|
conversation: applyNhAssistantLiveEvent(current.conversation, event)
|
|
3538
4509
|
}));
|
|
3539
4510
|
}
|
|
4511
|
+
/**
|
|
4512
|
+
* A run or proposal of an agent thread changed, for example a colleague decided a proposal in the
|
|
4513
|
+
* inbox: reload the thread when this tab has it open, so its approval cards show the decision.
|
|
4514
|
+
* A burst of updates reloads once. Feedback does not change the thread.
|
|
4515
|
+
*/
|
|
4516
|
+
onLiveAgentWork(change) {
|
|
4517
|
+
const conversationId = change.conversationId;
|
|
4518
|
+
const session = this.sessionsState().get(conversationId);
|
|
4519
|
+
if (change.reason === 'feedback' || !session || session.streaming) {
|
|
4520
|
+
return;
|
|
4521
|
+
}
|
|
4522
|
+
clearTimeout(this.agentWorkTimers.get(conversationId));
|
|
4523
|
+
this.agentWorkTimers.set(conversationId, setTimeout(() => {
|
|
4524
|
+
this.agentWorkTimers.delete(conversationId);
|
|
4525
|
+
const current = this.sessionsState().get(conversationId);
|
|
4526
|
+
// A decision of this tab reloads the thread itself when it ends.
|
|
4527
|
+
if (current && !current.streaming && !current.deciding) {
|
|
4528
|
+
void this.reloadSession(conversationId);
|
|
4529
|
+
}
|
|
4530
|
+
}, refreshDelayMs));
|
|
4531
|
+
}
|
|
4532
|
+
clearAgentWorkTimers() {
|
|
4533
|
+
for (const timer of this.agentWorkTimers.values()) {
|
|
4534
|
+
clearTimeout(timer);
|
|
4535
|
+
}
|
|
4536
|
+
this.agentWorkTimers.clear();
|
|
4537
|
+
}
|
|
3540
4538
|
/** After a reconnect: updates sent while disconnected are lost, so reload what is shown. */
|
|
3541
4539
|
resync() {
|
|
3542
4540
|
if (!this.enabled()) {
|
|
@@ -3688,6 +4686,48 @@ function completionNotice(conversation, errorCode) {
|
|
|
3688
4686
|
}
|
|
3689
4687
|
return null;
|
|
3690
4688
|
}
|
|
4689
|
+
/** The approvals of a conversation that still wait for a decision, each once. */
|
|
4690
|
+
/** Every approval of the conversation once, by approval id; later parts win. */
|
|
4691
|
+
function approvalParts(conversation) {
|
|
4692
|
+
const approvals = new Map();
|
|
4693
|
+
for (const message of conversation.messages) {
|
|
4694
|
+
for (const part of message.parts) {
|
|
4695
|
+
if (part.type === 'approval') {
|
|
4696
|
+
approvals.set(part.approvalId, part);
|
|
4697
|
+
}
|
|
4698
|
+
}
|
|
4699
|
+
}
|
|
4700
|
+
return [...approvals.values()];
|
|
4701
|
+
}
|
|
4702
|
+
function pendingApprovalParts(conversation) {
|
|
4703
|
+
const approvals = new Map();
|
|
4704
|
+
for (const message of conversation.messages) {
|
|
4705
|
+
for (const part of message.parts) {
|
|
4706
|
+
if (part.type === 'approval' && part.status === 'pending') {
|
|
4707
|
+
approvals.set(part.approvalId, part);
|
|
4708
|
+
}
|
|
4709
|
+
}
|
|
4710
|
+
}
|
|
4711
|
+
if (conversation.pendingApproval?.status === 'pending') {
|
|
4712
|
+
approvals.set(conversation.pendingApproval.approvalId, conversation.pendingApproval);
|
|
4713
|
+
}
|
|
4714
|
+
return [...approvals.values()];
|
|
4715
|
+
}
|
|
4716
|
+
/**
|
|
4717
|
+
* Shows the decision of an agent's proposal until the reloaded snapshot arrives: the approval's new
|
|
4718
|
+
* status, no open decision for it and, for the approval the conversation waited for, no pending one.
|
|
4719
|
+
*/
|
|
4720
|
+
function applyProposalDecision(conversation, approvalId, status) {
|
|
4721
|
+
const messages = conversation.messages.map(message => ({
|
|
4722
|
+
...message,
|
|
4723
|
+
parts: message.parts.map(part => part.type === 'approval' && part.approvalId === approvalId ? { ...part, status } : part)
|
|
4724
|
+
}));
|
|
4725
|
+
const thread = conversation.thread
|
|
4726
|
+
? { ...conversation.thread, decisions: conversation.thread.decisions.filter(item => item.approvalId !== approvalId) }
|
|
4727
|
+
: conversation.thread;
|
|
4728
|
+
const pendingApproval = conversation.pendingApproval?.approvalId === approvalId ? null : conversation.pendingApproval;
|
|
4729
|
+
return { ...conversation, messages, thread, pendingApproval };
|
|
4730
|
+
}
|
|
3691
4731
|
function toSummary(conversation) {
|
|
3692
4732
|
return {
|
|
3693
4733
|
id: conversation.id,
|
|
@@ -3736,6 +4776,7 @@ class NhAssistantPanelService {
|
|
|
3736
4776
|
constructor() {
|
|
3737
4777
|
this.overlay = inject(Overlay);
|
|
3738
4778
|
this.store = inject(NhAssistantStore);
|
|
4779
|
+
this.agentRuns = inject(NhAssistantAgentRunApiService);
|
|
3739
4780
|
this.injector = inject(Injector);
|
|
3740
4781
|
this.document = inject(DOCUMENT);
|
|
3741
4782
|
this.breakpoints = inject(BreakpointObserver);
|
|
@@ -3768,6 +4809,26 @@ class NhAssistantPanelService {
|
|
|
3768
4809
|
}
|
|
3769
4810
|
});
|
|
3770
4811
|
}
|
|
4812
|
+
/**
|
|
4813
|
+
* Opens the panel on the conversation with an agent about a subject, for example the intake
|
|
4814
|
+
* reviewer about one project. The server returns the agent's thread about the subject and creates
|
|
4815
|
+
* it without starting a run when it is missing; people then type in the conversation, which runs
|
|
4816
|
+
* a turn. Creating needs the participant role, readers may open an existing thread.
|
|
4817
|
+
*
|
|
4818
|
+
* Resolves with the lookup once the panel opens the conversation. A failure, such as
|
|
4819
|
+
* `assistant-subject-access-denied`, rejects with `NhAssistantApiError` and leaves the panel as
|
|
4820
|
+
* it is, so the caller shows the localized `messageKey` where the person asked.
|
|
4821
|
+
*/
|
|
4822
|
+
async openSubject(agentId, subject) {
|
|
4823
|
+
const lookup = await firstValueFrom(this.agentRuns.openThread(agentId, subject.type, subject.id));
|
|
4824
|
+
const conversationId = lookup.thread?.conversationId;
|
|
4825
|
+
if (!conversationId) {
|
|
4826
|
+
const code = NhAssistantClientErrorCodes.invalidResponse;
|
|
4827
|
+
throw new NhAssistantApiError(200, code, nhAssistantErrorMessageKey(code));
|
|
4828
|
+
}
|
|
4829
|
+
this.open(conversationId);
|
|
4830
|
+
return lookup;
|
|
4831
|
+
}
|
|
3771
4832
|
showPanel() {
|
|
3772
4833
|
if (this.openState()) {
|
|
3773
4834
|
return;
|
|
@@ -3871,6 +4932,324 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImpo
|
|
|
3871
4932
|
type: Injectable
|
|
3872
4933
|
}], ctorParameters: () => [] });
|
|
3873
4934
|
|
|
4935
|
+
/** How long a view waits for more agent-work updates before it reloads once. */
|
|
4936
|
+
const NH_ASSISTANT_AGENT_WORK_RELOAD_DELAY_MS = 300;
|
|
4937
|
+
/**
|
|
4938
|
+
* Calls `reload` once a burst of live agent-work updates that `matches` has settled, and after
|
|
4939
|
+
* every (re)connect of the hub, because updates sent while it was disconnected are lost. Without a
|
|
4940
|
+
* live service or a connected hub nothing arrives and the view keeps its manual refresh. Call it in
|
|
4941
|
+
* an injection context, for example a component constructor; it stops when that context ends.
|
|
4942
|
+
*/
|
|
4943
|
+
function nhAssistantReloadOnAgentWork(matches, reload) {
|
|
4944
|
+
const live = inject(NhAssistantLiveService, { optional: true });
|
|
4945
|
+
if (!live) {
|
|
4946
|
+
return;
|
|
4947
|
+
}
|
|
4948
|
+
const subscription = merge(live.agentWork$.pipe(filter(matches)), live.resync$)
|
|
4949
|
+
.pipe(debounceTime(NH_ASSISTANT_AGENT_WORK_RELOAD_DELAY_MS))
|
|
4950
|
+
.subscribe(() => reload());
|
|
4951
|
+
inject(DestroyRef).onDestroy(() => subscription.unsubscribe());
|
|
4952
|
+
}
|
|
4953
|
+
|
|
4954
|
+
/** Summaries kept for subjects that nobody shows any more, so a list that re-renders needs no request. */
|
|
4955
|
+
const maxUnwatchedSummaries = 500;
|
|
4956
|
+
/**
|
|
4957
|
+
* Counts of agent work per subject for badges in lists and on pages. Every summary asked for in the
|
|
4958
|
+
* same tick becomes one `POST subjects/{type}/summary` per subject type, in chunks of
|
|
4959
|
+
* `NH_ASSISTANT_MAX_SUBJECT_SUMMARY`. Results are cached per subject and shared by everyone who
|
|
4960
|
+
* shows it. Live agent-work updates refetch only the subjects they name, batched; a reconnect of
|
|
4961
|
+
* the hub refetches every subject shown. Nothing is requested on the server or while the assistant
|
|
4962
|
+
* is disabled or denied, and the cache clears when it becomes unavailable, for example on sign-out.
|
|
4963
|
+
*/
|
|
4964
|
+
class NhAssistantSubjectSummaryService {
|
|
4965
|
+
constructor() {
|
|
4966
|
+
this.api = inject(NhAssistantAgentRunApiService);
|
|
4967
|
+
this.store = inject(NhAssistantStore);
|
|
4968
|
+
this.live = inject(NhAssistantLiveService, { optional: true });
|
|
4969
|
+
this.browser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
4970
|
+
this.entries = new Map();
|
|
4971
|
+
/** Subject ids per type that wait for the next request. */
|
|
4972
|
+
this.queue = new Map();
|
|
4973
|
+
/** Subjects named by live updates that wait for the end of the burst. */
|
|
4974
|
+
this.changed = new Set();
|
|
4975
|
+
this.requests = new Subscription();
|
|
4976
|
+
this.liveSubscription = new Subscription();
|
|
4977
|
+
this.flushScheduled = false;
|
|
4978
|
+
this.requestRevision = 0;
|
|
4979
|
+
this.destroyed = false;
|
|
4980
|
+
effect(() => {
|
|
4981
|
+
const enabled = this.store.enabled();
|
|
4982
|
+
untracked(() => this.onAvailabilityChanged(enabled));
|
|
4983
|
+
});
|
|
4984
|
+
if (this.live) {
|
|
4985
|
+
this.liveSubscription.add(this.live.agentWork$.subscribe(change => this.onAgentWork(change)));
|
|
4986
|
+
this.liveSubscription.add(this.live.resync$.subscribe(() => this.refetchShown()));
|
|
4987
|
+
}
|
|
4988
|
+
inject(DestroyRef).onDestroy(() => {
|
|
4989
|
+
this.destroyed = true;
|
|
4990
|
+
this.liveSubscription.unsubscribe();
|
|
4991
|
+
this.requests.unsubscribe();
|
|
4992
|
+
clearTimeout(this.changedTimer);
|
|
4993
|
+
for (const entry of this.entries.values()) {
|
|
4994
|
+
entry.value.complete();
|
|
4995
|
+
}
|
|
4996
|
+
this.entries.clear();
|
|
4997
|
+
this.queue.clear();
|
|
4998
|
+
});
|
|
4999
|
+
}
|
|
5000
|
+
/**
|
|
5001
|
+
* The agent work on one subject: `null` until it is known, then every update. Subscribing shows
|
|
5002
|
+
* the subject; summaries of subjects that are shown stay current with live updates. The request
|
|
5003
|
+
* starts in the next microtask, together with every other summary asked for until then.
|
|
5004
|
+
*/
|
|
5005
|
+
summary(subjectType, subjectId) {
|
|
5006
|
+
return new Observable(subscriber => {
|
|
5007
|
+
const entry = this.watch(subjectType, subjectId);
|
|
5008
|
+
const subscription = entry.value.subscribe(subscriber);
|
|
5009
|
+
return () => {
|
|
5010
|
+
subscription.unsubscribe();
|
|
5011
|
+
this.unwatch(entry);
|
|
5012
|
+
};
|
|
5013
|
+
});
|
|
5014
|
+
}
|
|
5015
|
+
/**
|
|
5016
|
+
* Fetches the summaries of these subjects again, or of every subject of the type that is shown,
|
|
5017
|
+
* for example after the application changed them without an agent.
|
|
5018
|
+
*/
|
|
5019
|
+
refresh(subjectType, subjectIds) {
|
|
5020
|
+
const ids = subjectIds ? new Set(subjectIds) : null;
|
|
5021
|
+
for (const entry of [...this.entries.values()]) {
|
|
5022
|
+
if (entry.subjectType !== subjectType || (ids && !ids.has(entry.subjectId))) {
|
|
5023
|
+
continue;
|
|
5024
|
+
}
|
|
5025
|
+
if (entry.watchers > 0) {
|
|
5026
|
+
this.enqueue(entry);
|
|
5027
|
+
}
|
|
5028
|
+
else {
|
|
5029
|
+
this.forget(entry);
|
|
5030
|
+
}
|
|
5031
|
+
}
|
|
5032
|
+
}
|
|
5033
|
+
watch(subjectType, subjectId) {
|
|
5034
|
+
const key = summaryKey(subjectType, subjectId);
|
|
5035
|
+
let entry = this.entries.get(key);
|
|
5036
|
+
if (entry) {
|
|
5037
|
+
// The most recently shown summaries stay longest.
|
|
5038
|
+
this.entries.delete(key);
|
|
5039
|
+
}
|
|
5040
|
+
else {
|
|
5041
|
+
entry = {
|
|
5042
|
+
subjectType,
|
|
5043
|
+
subjectId,
|
|
5044
|
+
value: new BehaviorSubject(null),
|
|
5045
|
+
watchers: 0,
|
|
5046
|
+
queued: false,
|
|
5047
|
+
loaded: false,
|
|
5048
|
+
failed: false,
|
|
5049
|
+
request: 0
|
|
5050
|
+
};
|
|
5051
|
+
}
|
|
5052
|
+
this.entries.set(key, entry);
|
|
5053
|
+
entry.watchers++;
|
|
5054
|
+
if (!entry.loaded || entry.failed) {
|
|
5055
|
+
this.enqueue(entry);
|
|
5056
|
+
}
|
|
5057
|
+
if (this.browser) {
|
|
5058
|
+
void this.store.initialize();
|
|
5059
|
+
}
|
|
5060
|
+
return entry;
|
|
5061
|
+
}
|
|
5062
|
+
unwatch(entry) {
|
|
5063
|
+
entry.watchers = Math.max(0, entry.watchers - 1);
|
|
5064
|
+
if (entry.watchers > 0) {
|
|
5065
|
+
return;
|
|
5066
|
+
}
|
|
5067
|
+
let unwatched = 0;
|
|
5068
|
+
for (const item of this.entries.values()) {
|
|
5069
|
+
if (item.watchers === 0) {
|
|
5070
|
+
unwatched++;
|
|
5071
|
+
}
|
|
5072
|
+
}
|
|
5073
|
+
for (const item of [...this.entries.values()]) {
|
|
5074
|
+
if (unwatched <= maxUnwatchedSummaries) {
|
|
5075
|
+
break;
|
|
5076
|
+
}
|
|
5077
|
+
if (item.watchers === 0) {
|
|
5078
|
+
this.forget(item);
|
|
5079
|
+
unwatched--;
|
|
5080
|
+
}
|
|
5081
|
+
}
|
|
5082
|
+
}
|
|
5083
|
+
isCurrent(entry) {
|
|
5084
|
+
return this.entries.get(summaryKey(entry.subjectType, entry.subjectId)) === entry;
|
|
5085
|
+
}
|
|
5086
|
+
/** Drops a summary nobody shows; the next subscriber fetches it again. */
|
|
5087
|
+
forget(entry) {
|
|
5088
|
+
if (this.isCurrent(entry)) {
|
|
5089
|
+
this.entries.delete(summaryKey(entry.subjectType, entry.subjectId));
|
|
5090
|
+
}
|
|
5091
|
+
this.queue.get(entry.subjectType)?.delete(entry.subjectId);
|
|
5092
|
+
entry.value.complete();
|
|
5093
|
+
}
|
|
5094
|
+
enqueue(entry) {
|
|
5095
|
+
if (entry.queued) {
|
|
5096
|
+
return;
|
|
5097
|
+
}
|
|
5098
|
+
entry.queued = true;
|
|
5099
|
+
let ids = this.queue.get(entry.subjectType);
|
|
5100
|
+
if (!ids) {
|
|
5101
|
+
ids = new Set();
|
|
5102
|
+
this.queue.set(entry.subjectType, ids);
|
|
5103
|
+
}
|
|
5104
|
+
ids.add(entry.subjectId);
|
|
5105
|
+
this.scheduleFlush();
|
|
5106
|
+
}
|
|
5107
|
+
scheduleFlush() {
|
|
5108
|
+
if (this.flushScheduled) {
|
|
5109
|
+
return;
|
|
5110
|
+
}
|
|
5111
|
+
this.flushScheduled = true;
|
|
5112
|
+
queueMicrotask(() => {
|
|
5113
|
+
this.flushScheduled = false;
|
|
5114
|
+
this.flush();
|
|
5115
|
+
});
|
|
5116
|
+
}
|
|
5117
|
+
/** Sends the queued subjects: one request per type and chunk. Waits while the assistant is unavailable. */
|
|
5118
|
+
flush() {
|
|
5119
|
+
if (this.destroyed || !this.browser || !this.store.enabled()) {
|
|
5120
|
+
return;
|
|
5121
|
+
}
|
|
5122
|
+
for (const [subjectType, ids] of [...this.queue]) {
|
|
5123
|
+
this.queue.delete(subjectType);
|
|
5124
|
+
const subjectIds = [...ids];
|
|
5125
|
+
for (let start = 0; start < subjectIds.length; start += NH_ASSISTANT_MAX_SUBJECT_SUMMARY) {
|
|
5126
|
+
this.send(subjectType, subjectIds.slice(start, start + NH_ASSISTANT_MAX_SUBJECT_SUMMARY));
|
|
5127
|
+
}
|
|
5128
|
+
}
|
|
5129
|
+
}
|
|
5130
|
+
send(subjectType, subjectIds) {
|
|
5131
|
+
const revision = ++this.requestRevision;
|
|
5132
|
+
const entries = [];
|
|
5133
|
+
for (const subjectId of subjectIds) {
|
|
5134
|
+
const entry = this.entries.get(summaryKey(subjectType, subjectId));
|
|
5135
|
+
if (entry) {
|
|
5136
|
+
entry.queued = false;
|
|
5137
|
+
entry.request = revision;
|
|
5138
|
+
entries.push(entry);
|
|
5139
|
+
}
|
|
5140
|
+
}
|
|
5141
|
+
if (entries.length === 0) {
|
|
5142
|
+
return;
|
|
5143
|
+
}
|
|
5144
|
+
const subscription = this.api.summarizeSubjects(subjectType, entries.map(entry => entry.subjectId)).subscribe({
|
|
5145
|
+
next: list => {
|
|
5146
|
+
const items = new Map(list.items.map(item => [item.subjectId, item]));
|
|
5147
|
+
for (const entry of entries) {
|
|
5148
|
+
if (entry.request !== revision || !this.isCurrent(entry)) {
|
|
5149
|
+
continue;
|
|
5150
|
+
}
|
|
5151
|
+
entry.loaded = true;
|
|
5152
|
+
entry.failed = false;
|
|
5153
|
+
entry.value.next(items.get(entry.subjectId) ?? emptySummary(entry.subjectId));
|
|
5154
|
+
}
|
|
5155
|
+
},
|
|
5156
|
+
error: () => {
|
|
5157
|
+
// A badge without a count is better than an error in a list; the next live update,
|
|
5158
|
+
// reconnect or subscriber tries again.
|
|
5159
|
+
for (const entry of entries) {
|
|
5160
|
+
if (entry.request === revision) {
|
|
5161
|
+
entry.failed = true;
|
|
5162
|
+
}
|
|
5163
|
+
}
|
|
5164
|
+
}
|
|
5165
|
+
});
|
|
5166
|
+
this.requests.add(subscription);
|
|
5167
|
+
}
|
|
5168
|
+
onAgentWork(change) {
|
|
5169
|
+
if (!change.subjectType || !change.subjectId) {
|
|
5170
|
+
return;
|
|
5171
|
+
}
|
|
5172
|
+
const key = summaryKey(change.subjectType, change.subjectId);
|
|
5173
|
+
const entry = this.entries.get(key);
|
|
5174
|
+
if (!entry) {
|
|
5175
|
+
return;
|
|
5176
|
+
}
|
|
5177
|
+
if (entry.watchers === 0) {
|
|
5178
|
+
this.forget(entry);
|
|
5179
|
+
return;
|
|
5180
|
+
}
|
|
5181
|
+
this.changed.add(key);
|
|
5182
|
+
if (this.changedTimer === undefined) {
|
|
5183
|
+
this.changedTimer = setTimeout(() => this.refetchChanged(), NH_ASSISTANT_AGENT_WORK_RELOAD_DELAY_MS);
|
|
5184
|
+
}
|
|
5185
|
+
}
|
|
5186
|
+
/** Refetches the shown subjects of a burst of live updates, together. */
|
|
5187
|
+
refetchChanged() {
|
|
5188
|
+
this.changedTimer = undefined;
|
|
5189
|
+
const keys = [...this.changed];
|
|
5190
|
+
this.changed.clear();
|
|
5191
|
+
for (const key of keys) {
|
|
5192
|
+
const entry = this.entries.get(key);
|
|
5193
|
+
if (entry && entry.watchers > 0) {
|
|
5194
|
+
this.enqueue(entry);
|
|
5195
|
+
}
|
|
5196
|
+
}
|
|
5197
|
+
}
|
|
5198
|
+
/** After a reconnect updates may have been missed: refetch what is shown, forget the rest. */
|
|
5199
|
+
refetchShown() {
|
|
5200
|
+
for (const entry of [...this.entries.values()]) {
|
|
5201
|
+
if (entry.watchers > 0) {
|
|
5202
|
+
this.enqueue(entry);
|
|
5203
|
+
}
|
|
5204
|
+
else {
|
|
5205
|
+
this.forget(entry);
|
|
5206
|
+
}
|
|
5207
|
+
}
|
|
5208
|
+
}
|
|
5209
|
+
onAvailabilityChanged(enabled) {
|
|
5210
|
+
if (enabled) {
|
|
5211
|
+
for (const entry of this.entries.values()) {
|
|
5212
|
+
if (entry.watchers > 0 && (!entry.loaded || entry.failed)) {
|
|
5213
|
+
this.enqueue(entry);
|
|
5214
|
+
}
|
|
5215
|
+
}
|
|
5216
|
+
this.scheduleFlush();
|
|
5217
|
+
return;
|
|
5218
|
+
}
|
|
5219
|
+
// Signed out, denied or disabled: no counts of a previous account may remain.
|
|
5220
|
+
this.requests.unsubscribe();
|
|
5221
|
+
this.requests = new Subscription();
|
|
5222
|
+
clearTimeout(this.changedTimer);
|
|
5223
|
+
this.changedTimer = undefined;
|
|
5224
|
+
this.changed.clear();
|
|
5225
|
+
this.queue.clear();
|
|
5226
|
+
for (const entry of [...this.entries.values()]) {
|
|
5227
|
+
if (entry.watchers === 0) {
|
|
5228
|
+
this.forget(entry);
|
|
5229
|
+
continue;
|
|
5230
|
+
}
|
|
5231
|
+
entry.queued = false;
|
|
5232
|
+
entry.loaded = false;
|
|
5233
|
+
entry.failed = false;
|
|
5234
|
+
entry.request = 0;
|
|
5235
|
+
if (entry.value.value !== null) {
|
|
5236
|
+
entry.value.next(null);
|
|
5237
|
+
}
|
|
5238
|
+
}
|
|
5239
|
+
}
|
|
5240
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantSubjectSummaryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
5241
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantSubjectSummaryService }); }
|
|
5242
|
+
}
|
|
5243
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantSubjectSummaryService, decorators: [{
|
|
5244
|
+
type: Injectable
|
|
5245
|
+
}], ctorParameters: () => [] });
|
|
5246
|
+
function summaryKey(subjectType, subjectId) {
|
|
5247
|
+
return `${subjectType}\u0000${subjectId}`;
|
|
5248
|
+
}
|
|
5249
|
+
function emptySummary(subjectId) {
|
|
5250
|
+
return { subjectId, openProposals: 0, proposalsToDecide: 0, runActive: false };
|
|
5251
|
+
}
|
|
5252
|
+
|
|
3874
5253
|
/**
|
|
3875
5254
|
* Registers the assistant client, store and panel for one application or route scope.
|
|
3876
5255
|
* Call it once in the root providers; a route may call it again to host an isolated
|
|
@@ -3883,11 +5262,13 @@ function provideNhAssistant(config) {
|
|
|
3883
5262
|
NhAssistantTransport,
|
|
3884
5263
|
NhAssistantApiService,
|
|
3885
5264
|
NhAssistantAdminApiService,
|
|
5265
|
+
NhAssistantAgentRunApiService,
|
|
3886
5266
|
NhAssistantLiveService,
|
|
3887
5267
|
NhAssistantPushService,
|
|
3888
5268
|
NhAssistantStore,
|
|
3889
5269
|
NhAssistantUiState,
|
|
3890
5270
|
NhAssistantPanelService,
|
|
5271
|
+
NhAssistantSubjectSummaryService,
|
|
3891
5272
|
...(config.translations === 'host'
|
|
3892
5273
|
? []
|
|
3893
5274
|
: [provideEnvironmentInitializer(() => inject(NhAssistantTranslationMerger).start())])
|
|
@@ -4189,7 +5570,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImpo
|
|
|
4189
5570
|
`, 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"] }]
|
|
4190
5571
|
}], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }], conversationSelect: [{ type: i0.Output, args: ["conversationSelect"] }] } });
|
|
4191
5572
|
|
|
4192
|
-
let nextId$
|
|
5573
|
+
let nextId$8 = 0;
|
|
4193
5574
|
/**
|
|
4194
5575
|
* Chooses the agent for new conversations. Agent names come from the host's translation
|
|
4195
5576
|
* keys; agents created by an administrator carry literal text, which is shown as is.
|
|
@@ -4202,7 +5583,7 @@ class NhAssistantAgentPickerComponent {
|
|
|
4202
5583
|
/** Compact prompt-bar control; capability information remains in the tooltip and accessible description. */
|
|
4203
5584
|
this.compact = input(false, ...(ngDevMode ? [{ debugName: "compact" }] : []));
|
|
4204
5585
|
this.agentChange = output();
|
|
4205
|
-
this.selectId = `nh-assistant-agent-picker-${nextId$
|
|
5586
|
+
this.selectId = `nh-assistant-agent-picker-${nextId$8++}`;
|
|
4206
5587
|
this.selected = computed(() => this.agents().find(agent => agent.id === this.selectedAgentId()) ?? null, ...(ngDevMode ? [{ debugName: "selected" }] : []));
|
|
4207
5588
|
}
|
|
4208
5589
|
onChange(event) {
|
|
@@ -4254,7 +5635,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImpo
|
|
|
4254
5635
|
`, 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"] }]
|
|
4255
5636
|
}], 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"] }] } });
|
|
4256
5637
|
|
|
4257
|
-
let nextId$
|
|
5638
|
+
let nextId$7 = 0;
|
|
4258
5639
|
/**
|
|
4259
5640
|
* Message input. Enter sends when sending is available; Shift+Enter inserts a new line.
|
|
4260
5641
|
* Drafting remains available while the assistant runs or waits for approval.
|
|
@@ -4285,7 +5666,7 @@ class NhAssistantComposerComponent {
|
|
|
4285
5666
|
this.textarea = viewChild.required('textarea');
|
|
4286
5667
|
this.ownedFocus = false;
|
|
4287
5668
|
this.previousBusy = false;
|
|
4288
|
-
this.inputId = `nh-assistant-composer-${nextId$
|
|
5669
|
+
this.inputId = `nh-assistant-composer-${nextId$7++}`;
|
|
4289
5670
|
this.hintId = `${this.inputId}-hint`;
|
|
4290
5671
|
this.text = signal('', ...(ngDevMode ? [{ debugName: "text" }] : []));
|
|
4291
5672
|
this.length = computed(() => this.text().trim().length, ...(ngDevMode ? [{ debugName: "length" }] : []));
|
|
@@ -4409,7 +5790,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImpo
|
|
|
4409
5790
|
|
|
4410
5791
|
/** Maximum length of the user's own instructions. */
|
|
4411
5792
|
const NH_ASSISTANT_MAX_CUSTOM_INSTRUCTIONS = 1_000;
|
|
4412
|
-
let nextId$
|
|
5793
|
+
let nextId$6 = 0;
|
|
4413
5794
|
/**
|
|
4414
5795
|
* The user's own assistant preferences: desktop notifications, style, form of address,
|
|
4415
5796
|
* answer length and optional instructions. Notifications apply immediately; the other
|
|
@@ -4424,7 +5805,7 @@ class NhAssistantPreferencesComponent {
|
|
|
4424
5805
|
this.closed = output();
|
|
4425
5806
|
/** Emitted with the stored preferences after a successful save. */
|
|
4426
5807
|
this.saved = output();
|
|
4427
|
-
this.id = `nh-assistant-preferences-${nextId$
|
|
5808
|
+
this.id = `nh-assistant-preferences-${nextId$6++}`;
|
|
4428
5809
|
this.styles = ['default', 'direct', 'personal', 'detailed'];
|
|
4429
5810
|
this.addressForms = ['informal', 'formal'];
|
|
4430
5811
|
this.responseLengths = ['short', 'normal', 'long'];
|
|
@@ -4535,7 +5916,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImpo
|
|
|
4535
5916
|
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"] }]
|
|
4536
5917
|
}], ctorParameters: () => [], propDecorators: { closed: [{ type: i0.Output, args: ["closed"] }], saved: [{ type: i0.Output, args: ["saved"] }] } });
|
|
4537
5918
|
|
|
4538
|
-
let nextId$
|
|
5919
|
+
let nextId$5 = 0;
|
|
4539
5920
|
const searchDelayMs = 250;
|
|
4540
5921
|
/**
|
|
4541
5922
|
* Sharing of the open conversation. The owner creates, copies, renews or stops an invitation
|
|
@@ -4551,7 +5932,7 @@ class NhAssistantShareComponent {
|
|
|
4551
5932
|
this.closed = output();
|
|
4552
5933
|
/** Show the invitation link, participants, or both (the default for custom hosts). */
|
|
4553
5934
|
this.section = input('all', ...(ngDevMode ? [{ debugName: "section" }] : []));
|
|
4554
|
-
this.id = `nh-assistant-share-${nextId$
|
|
5935
|
+
this.id = `nh-assistant-share-${nextId$5++}`;
|
|
4555
5936
|
this.conversation = this.store.activeConversation;
|
|
4556
5937
|
this.isOwner = this.store.isOwner;
|
|
4557
5938
|
this.busy = signal(false, ...(ngDevMode ? [{ debugName: "busy" }] : []));
|
|
@@ -4796,6 +6177,134 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImpo
|
|
|
4796
6177
|
args: [{ name: 'nhAssistantPartGroups', standalone: true }]
|
|
4797
6178
|
}] });
|
|
4798
6179
|
|
|
6180
|
+
/** Converts any failure into a user-facing code and translation key, never raw text. */
|
|
6181
|
+
function toNhAssistantError(error) {
|
|
6182
|
+
if (error instanceof NhAssistantApiError) {
|
|
6183
|
+
return { code: error.code, messageKey: error.messageKey };
|
|
6184
|
+
}
|
|
6185
|
+
return { code: NhAssistantClientErrorCodes.server, messageKey: nhAssistantErrorMessageKey(NhAssistantClientErrorCodes.server) };
|
|
6186
|
+
}
|
|
6187
|
+
/** Translation keys for an error, from the most to the least specific. */
|
|
6188
|
+
function nhAssistantErrorKeys(error) {
|
|
6189
|
+
return error ? [error.messageKey, nhAssistantErrorMessageKey(error.code), 'nh-assistant.errors.generic'] : [];
|
|
6190
|
+
}
|
|
6191
|
+
/** Dash-case identifiers as the contract requires for agent and MCP server ids. */
|
|
6192
|
+
const NH_ASSISTANT_DASH_CASE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
6193
|
+
|
|
6194
|
+
const notAcceptedCode = 'assistant-feedback-not-accepted';
|
|
6195
|
+
let nextId$4 = 0;
|
|
6196
|
+
/**
|
|
6197
|
+
* Asks, after a rejection, what was wrong with one or more agent proposals: a category from a fixed
|
|
6198
|
+
* set and an optional text. It is optional and never blocks the decision; people skip it with one
|
|
6199
|
+
* action. One proposal is sent with `PUT approvals/{id}/feedback`, several rejected together with
|
|
6200
|
+
* `POST approvals/feedback` as bulk feedback. With `initial` it changes the caller's own feedback.
|
|
6201
|
+
* After saving it thanks the person in place of the form; hosts remove it after `dismissed`.
|
|
6202
|
+
*/
|
|
6203
|
+
class NhAssistantProposalFeedbackComponent {
|
|
6204
|
+
constructor() {
|
|
6205
|
+
this.api = inject(NhAssistantAgentRunApiService);
|
|
6206
|
+
this.host = inject(ElementRef);
|
|
6207
|
+
this.document = inject(DOCUMENT);
|
|
6208
|
+
this.injector = inject(Injector);
|
|
6209
|
+
this.thanks = viewChild('thanks', ...(ngDevMode ? [{ debugName: "thanks" }] : []));
|
|
6210
|
+
/** The proposals the feedback is about; several when they were rejected together. */
|
|
6211
|
+
this.approvalIds = input.required(...(ngDevMode ? [{ debugName: "approvalIds" }] : []));
|
|
6212
|
+
/** The caller's current feedback, to change it; `null` for new feedback. */
|
|
6213
|
+
this.initial = input(null, ...(ngDevMode ? [{ debugName: "initial" }] : []));
|
|
6214
|
+
/** Emits after the server stored the feedback. */
|
|
6215
|
+
this.saved = output();
|
|
6216
|
+
/** Emits when the person skips the question. */
|
|
6217
|
+
this.dismissed = output();
|
|
6218
|
+
this.id = `nh-assistant-proposal-feedback-${nextId$4++}`;
|
|
6219
|
+
this.categories = NH_ASSISTANT_FEEDBACK_CATEGORIES;
|
|
6220
|
+
this.maxText = NH_ASSISTANT_MAX_FEEDBACK_TEXT;
|
|
6221
|
+
/** A new question when the host passes other proposals or other initial feedback. */
|
|
6222
|
+
this.question = computed(() => ({ approvalIds: this.approvalIds(), initial: this.initial() }), ...(ngDevMode ? [{ debugName: "question" }] : []));
|
|
6223
|
+
this.category = linkedSignal(() => this.question().initial?.category ?? null, ...(ngDevMode ? [{ debugName: "category" }] : []));
|
|
6224
|
+
this.text = linkedSignal(() => this.question().initial?.text ?? '', ...(ngDevMode ? [{ debugName: "text" }] : []));
|
|
6225
|
+
this.busy = signal(false, ...(ngDevMode ? [{ debugName: "busy" }] : []));
|
|
6226
|
+
this.done = linkedSignal(...(ngDevMode ? [{ debugName: "done", source: this.question, computation: () => false }] : [{ source: this.question, computation: () => false }]));
|
|
6227
|
+
/** Set when a bulk request stored the feedback for only part of the proposals. */
|
|
6228
|
+
this.partial = linkedSignal(...(ngDevMode ? [{ debugName: "partial", source: this.question,
|
|
6229
|
+
computation: () => null }] : [{
|
|
6230
|
+
source: this.question,
|
|
6231
|
+
computation: () => null
|
|
6232
|
+
}]));
|
|
6233
|
+
this.error = linkedSignal(...(ngDevMode ? [{ debugName: "error", source: this.question, computation: () => null }] : [{ source: this.question, computation: () => null }]));
|
|
6234
|
+
this.errorKeys = computed(() => nhAssistantErrorKeys(this.error()), ...(ngDevMode ? [{ debugName: "errorKeys" }] : []));
|
|
6235
|
+
this.count = computed(() => new Set(this.approvalIds()).size, ...(ngDevMode ? [{ debugName: "count" }] : []));
|
|
6236
|
+
this.textLength = computed(() => this.text().length, ...(ngDevMode ? [{ debugName: "textLength" }] : []));
|
|
6237
|
+
this.tooLong = computed(() => this.textLength() > this.maxText, ...(ngDevMode ? [{ debugName: "tooLong" }] : []));
|
|
6238
|
+
this.canSend = computed(() => this.category() !== null && this.count() > 0 && !this.busy() && !this.tooLong(), ...(ngDevMode ? [{ debugName: "canSend" }] : []));
|
|
6239
|
+
inject(DestroyRef).onDestroy(() => this.subscription?.unsubscribe());
|
|
6240
|
+
}
|
|
6241
|
+
choose(category) {
|
|
6242
|
+
this.category.set(category);
|
|
6243
|
+
this.error.set(null);
|
|
6244
|
+
}
|
|
6245
|
+
onText(event) {
|
|
6246
|
+
this.text.set(event.target.value);
|
|
6247
|
+
}
|
|
6248
|
+
dismiss() {
|
|
6249
|
+
this.subscription?.unsubscribe();
|
|
6250
|
+
this.busy.set(false);
|
|
6251
|
+
this.dismissed.emit();
|
|
6252
|
+
}
|
|
6253
|
+
send() {
|
|
6254
|
+
const category = this.category();
|
|
6255
|
+
if (!this.canSend() || category === null) {
|
|
6256
|
+
return;
|
|
6257
|
+
}
|
|
6258
|
+
const approvalIds = [...new Set(this.approvalIds())];
|
|
6259
|
+
const text = this.text().trim() || null;
|
|
6260
|
+
this.busy.set(true);
|
|
6261
|
+
this.error.set(null);
|
|
6262
|
+
this.subscription?.unsubscribe();
|
|
6263
|
+
this.subscription = this.request(approvalIds, category, text).subscribe({
|
|
6264
|
+
next: outcomes => {
|
|
6265
|
+
this.busy.set(false);
|
|
6266
|
+
const stored = outcomes.filter(outcome => outcome.succeeded).map(outcome => outcome.approvalId);
|
|
6267
|
+
if (stored.length === 0) {
|
|
6268
|
+
const code = outcomes.find(outcome => outcome.code)?.code ?? notAcceptedCode;
|
|
6269
|
+
this.error.set({ code, messageKey: nhAssistantErrorMessageKey(code) });
|
|
6270
|
+
return;
|
|
6271
|
+
}
|
|
6272
|
+
// The form disappears; keep keyboard focus on the confirmation instead of losing it.
|
|
6273
|
+
const focused = this.host.nativeElement.contains(this.document.activeElement);
|
|
6274
|
+
this.partial.set(stored.length < approvalIds.length ? { stored: stored.length, total: approvalIds.length } : null);
|
|
6275
|
+
this.done.set(true);
|
|
6276
|
+
if (focused) {
|
|
6277
|
+
afterNextRender(() => this.thanks()?.nativeElement.focus(), { injector: this.injector });
|
|
6278
|
+
}
|
|
6279
|
+
this.saved.emit({ approvalIds: stored, category, text });
|
|
6280
|
+
},
|
|
6281
|
+
error: failure => {
|
|
6282
|
+
this.busy.set(false);
|
|
6283
|
+
this.error.set(toNhAssistantError(failure));
|
|
6284
|
+
}
|
|
6285
|
+
});
|
|
6286
|
+
}
|
|
6287
|
+
/** One proposal through its own endpoint; several in requests of at most the bulk limit. */
|
|
6288
|
+
request(approvalIds, category, text) {
|
|
6289
|
+
if (approvalIds.length === 1) {
|
|
6290
|
+
const [approvalId] = approvalIds;
|
|
6291
|
+
return this.api.giveFeedback(approvalId, category, text).pipe(map(() => [{ approvalId, succeeded: true, code: null }]));
|
|
6292
|
+
}
|
|
6293
|
+
const chunks = [];
|
|
6294
|
+
for (let index = 0; index < approvalIds.length; index += NH_ASSISTANT_MAX_BULK_FEEDBACK) {
|
|
6295
|
+
chunks.push(approvalIds.slice(index, index + NH_ASSISTANT_MAX_BULK_FEEDBACK));
|
|
6296
|
+
}
|
|
6297
|
+
return forkJoin(chunks.map(chunk => this.api.giveFeedbackMany(chunk, category, text))).pipe(map(responses => responses.flatMap(response => response.results)));
|
|
6298
|
+
}
|
|
6299
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantProposalFeedbackComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
6300
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.33", type: NhAssistantProposalFeedbackComponent, isStandalone: true, selector: "nh-assistant-proposal-feedback", inputs: { approvalIds: { classPropertyName: "approvalIds", publicName: "approvalIds", isSignal: true, isRequired: true, transformFunction: null }, initial: { classPropertyName: "initial", publicName: "initial", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { saved: "saved", dismissed: "dismissed" }, viewQueries: [{ propertyName: "thanks", first: true, predicate: ["thanks"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (done()) {\n <div class=\"feedback done\" role=\"status\" tabindex=\"-1\" #thanks>\n <p class=\"thanks\"><nh-assistant-icon name=\"check\" />{{ 'nh-assistant.proposal-feedback.thanks' | translate }}</p>\n @if (partial(); as stored) {\n <p class=\"scope\">{{ 'nh-assistant.proposal-feedback.partial' | translate: { stored: stored.stored, total: stored.total } }}</p>\n }\n </div>\n} @else {\n <form class=\"feedback\" [attr.aria-busy]=\"busy()\" (submit)=\"$event.preventDefault(); send()\">\n <fieldset class=\"categories\" [disabled]=\"busy()\">\n <legend>{{ 'nh-assistant.proposal-feedback.legend' | translate }}</legend>\n @if (count() > 1) {\n <p class=\"scope\">{{ 'nh-assistant.proposal-feedback.bulk' | translate: { count: count() } }}</p>\n }\n <div class=\"chips\">\n @for (option of categories; track option) {\n <label class=\"chip\" [class.selected]=\"category() === option\">\n <input\n type=\"radio\"\n [name]=\"id + '-category'\"\n [value]=\"option\"\n [checked]=\"category() === option\"\n [attr.aria-describedby]=\"id + '-hint-' + option\"\n (change)=\"choose(option)\" />\n @if (category() === option) {\n <nh-assistant-icon name=\"check\" />\n }\n <span>{{ 'nh-assistant.proposal-feedback.categories.' + option | translate }}</span>\n </label>\n }\n </div>\n @for (option of categories; track option) {\n <span class=\"visually-hidden\" [id]=\"id + '-hint-' + option\">{{ 'nh-assistant.proposal-feedback.hints.' + option | translate }}</span>\n }\n @if (category(); as selected) {\n <p class=\"selected-hint\" aria-hidden=\"true\">{{ 'nh-assistant.proposal-feedback.hints.' + selected | translate }}</p>\n }\n\n <div class=\"field\">\n <label [for]=\"id + '-text'\">{{ 'nh-assistant.proposal-feedback.text-label' | translate }}</label>\n <textarea\n rows=\"2\"\n [id]=\"id + '-text'\"\n [attr.maxlength]=\"maxText\"\n [value]=\"text()\"\n [attr.aria-describedby]=\"id + '-text-hint'\"\n [attr.aria-invalid]=\"tooLong() ? true : null\"\n (input)=\"onText($event)\"></textarea>\n <p class=\"hint\" [id]=\"id + '-text-hint'\">\n <span>{{ 'nh-assistant.proposal-feedback.text-hint' | translate }}</span>\n <span class=\"counter\" [class.error-text]=\"tooLong()\">\n {{ 'nh-assistant.proposal-feedback.characters' | translate: { count: textLength(), max: maxText } }}\n </span>\n </p>\n </div>\n </fieldset>\n\n @if (error(); as failure) {\n <p class=\"message error\" role=\"alert\"><nh-assistant-icon name=\"warning\" />{{ errorKeys() | nhAssistantTranslate: failure.code }}</p>\n }\n\n <div class=\"actions\">\n <button class=\"text-button\" type=\"button\" (click)=\"dismiss()\">\n {{ (initial() ? 'nh-assistant.proposal-feedback.cancel' : 'nh-assistant.proposal-feedback.skip') | translate }}\n </button>\n <button class=\"primary\" type=\"submit\" [disabled]=\"!canSend()\">\n {{ (busy() ? 'nh-assistant.proposal-feedback.sending' : 'nh-assistant.proposal-feedback.send') | translate }}\n </button>\n </div>\n </form>\n}\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;min-width:0;color:var(--nha-text);font-family:var(--nha-font);font-size:13px}: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}.feedback{display:flex;flex-direction:column;gap:10px;min-width:0;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:10px 12px;background:var(--nha-surface)}.feedback.done{gap:4px}.feedback.done:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.categories{display:flex;flex-direction:column;gap:8px}.chips{display:flex;flex-wrap:wrap;gap:6px}.chip{position:relative;display:inline-flex;align-items:center;gap:4px;min-height:30px;max-width:100%;border:1px solid var(--nha-border);border-radius:999px;padding:4px 12px;color:var(--nha-text);background:var(--nha-background);font-size:12px;font-weight:650;overflow-wrap:anywhere;cursor:pointer}.chip:hover{background:var(--nha-surface-strong)}.chip.selected{border-color:var(--nha-highlight);box-shadow:inset 0 0 0 1px var(--nha-highlight)}.chip:has(input:focus-visible){outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.chip input{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}.chip nh-assistant-icon{color:var(--nha-highlight)}fieldset:disabled .chip{cursor:not-allowed;opacity:.6}.scope,.selected-hint{margin:0;color:var(--nha-text-muted);font-size:12px}.thanks{display:flex;align-items:center;gap:6px;margin:0;font-size:13px}.thanks nh-assistant-icon{color:var(--nha-success)}textarea{min-height:56px}.text-button{min-height:36px;border:0;border-radius:var(--nha-radius-control);padding:6px 10px;color:var(--nha-text-muted);background:transparent;font:inherit;font-size:13px;font-weight:650;cursor:pointer}.text-button:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.text-button:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}.text-button:disabled{cursor:not-allowed;opacity:.55}\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 }); }
|
|
6301
|
+
}
|
|
6302
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantProposalFeedbackComponent, decorators: [{
|
|
6303
|
+
type: Component,
|
|
6304
|
+
args: [{ selector: 'nh-assistant-proposal-feedback', standalone: true, imports: [TranslatePipe, NhAssistantTranslatePipe, NhAssistantIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (done()) {\n <div class=\"feedback done\" role=\"status\" tabindex=\"-1\" #thanks>\n <p class=\"thanks\"><nh-assistant-icon name=\"check\" />{{ 'nh-assistant.proposal-feedback.thanks' | translate }}</p>\n @if (partial(); as stored) {\n <p class=\"scope\">{{ 'nh-assistant.proposal-feedback.partial' | translate: { stored: stored.stored, total: stored.total } }}</p>\n }\n </div>\n} @else {\n <form class=\"feedback\" [attr.aria-busy]=\"busy()\" (submit)=\"$event.preventDefault(); send()\">\n <fieldset class=\"categories\" [disabled]=\"busy()\">\n <legend>{{ 'nh-assistant.proposal-feedback.legend' | translate }}</legend>\n @if (count() > 1) {\n <p class=\"scope\">{{ 'nh-assistant.proposal-feedback.bulk' | translate: { count: count() } }}</p>\n }\n <div class=\"chips\">\n @for (option of categories; track option) {\n <label class=\"chip\" [class.selected]=\"category() === option\">\n <input\n type=\"radio\"\n [name]=\"id + '-category'\"\n [value]=\"option\"\n [checked]=\"category() === option\"\n [attr.aria-describedby]=\"id + '-hint-' + option\"\n (change)=\"choose(option)\" />\n @if (category() === option) {\n <nh-assistant-icon name=\"check\" />\n }\n <span>{{ 'nh-assistant.proposal-feedback.categories.' + option | translate }}</span>\n </label>\n }\n </div>\n @for (option of categories; track option) {\n <span class=\"visually-hidden\" [id]=\"id + '-hint-' + option\">{{ 'nh-assistant.proposal-feedback.hints.' + option | translate }}</span>\n }\n @if (category(); as selected) {\n <p class=\"selected-hint\" aria-hidden=\"true\">{{ 'nh-assistant.proposal-feedback.hints.' + selected | translate }}</p>\n }\n\n <div class=\"field\">\n <label [for]=\"id + '-text'\">{{ 'nh-assistant.proposal-feedback.text-label' | translate }}</label>\n <textarea\n rows=\"2\"\n [id]=\"id + '-text'\"\n [attr.maxlength]=\"maxText\"\n [value]=\"text()\"\n [attr.aria-describedby]=\"id + '-text-hint'\"\n [attr.aria-invalid]=\"tooLong() ? true : null\"\n (input)=\"onText($event)\"></textarea>\n <p class=\"hint\" [id]=\"id + '-text-hint'\">\n <span>{{ 'nh-assistant.proposal-feedback.text-hint' | translate }}</span>\n <span class=\"counter\" [class.error-text]=\"tooLong()\">\n {{ 'nh-assistant.proposal-feedback.characters' | translate: { count: textLength(), max: maxText } }}\n </span>\n </p>\n </div>\n </fieldset>\n\n @if (error(); as failure) {\n <p class=\"message error\" role=\"alert\"><nh-assistant-icon name=\"warning\" />{{ errorKeys() | nhAssistantTranslate: failure.code }}</p>\n }\n\n <div class=\"actions\">\n <button class=\"text-button\" type=\"button\" (click)=\"dismiss()\">\n {{ (initial() ? 'nh-assistant.proposal-feedback.cancel' : 'nh-assistant.proposal-feedback.skip') | translate }}\n </button>\n <button class=\"primary\" type=\"submit\" [disabled]=\"!canSend()\">\n {{ (busy() ? 'nh-assistant.proposal-feedback.sending' : 'nh-assistant.proposal-feedback.send') | translate }}\n </button>\n </div>\n </form>\n}\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;min-width:0;color:var(--nha-text);font-family:var(--nha-font);font-size:13px}: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}.feedback{display:flex;flex-direction:column;gap:10px;min-width:0;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:10px 12px;background:var(--nha-surface)}.feedback.done{gap:4px}.feedback.done:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.categories{display:flex;flex-direction:column;gap:8px}.chips{display:flex;flex-wrap:wrap;gap:6px}.chip{position:relative;display:inline-flex;align-items:center;gap:4px;min-height:30px;max-width:100%;border:1px solid var(--nha-border);border-radius:999px;padding:4px 12px;color:var(--nha-text);background:var(--nha-background);font-size:12px;font-weight:650;overflow-wrap:anywhere;cursor:pointer}.chip:hover{background:var(--nha-surface-strong)}.chip.selected{border-color:var(--nha-highlight);box-shadow:inset 0 0 0 1px var(--nha-highlight)}.chip:has(input:focus-visible){outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.chip input{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}.chip nh-assistant-icon{color:var(--nha-highlight)}fieldset:disabled .chip{cursor:not-allowed;opacity:.6}.scope,.selected-hint{margin:0;color:var(--nha-text-muted);font-size:12px}.thanks{display:flex;align-items:center;gap:6px;margin:0;font-size:13px}.thanks nh-assistant-icon{color:var(--nha-success)}textarea{min-height:56px}.text-button{min-height:36px;border:0;border-radius:var(--nha-radius-control);padding:6px 10px;color:var(--nha-text-muted);background:transparent;font:inherit;font-size:13px;font-weight:650;cursor:pointer}.text-button:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.text-button:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}.text-button:disabled{cursor:not-allowed;opacity:.55}\n"] }]
|
|
6305
|
+
}], ctorParameters: () => [], propDecorators: { thanks: [{ type: i0.ViewChild, args: ['thanks', { isSignal: true }] }], approvalIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "approvalIds", required: true }] }], initial: [{ type: i0.Input, args: [{ isSignal: true, alias: "initial", required: false }] }], saved: [{ type: i0.Output, args: ["saved"] }], dismissed: [{ type: i0.Output, args: ["dismissed"] }] } });
|
|
6306
|
+
|
|
6307
|
+
let nextId$3 = 0;
|
|
4799
6308
|
/** Formats remaining milliseconds as `m:ss`, or `h:mm:ss` above one hour. */
|
|
4800
6309
|
function formatNhAssistantCountdown(milliseconds) {
|
|
4801
6310
|
const totalSeconds = Math.max(0, Math.ceil(milliseconds / 1000));
|
|
@@ -4810,7 +6319,8 @@ function formatNhAssistantCountdown(milliseconds) {
|
|
|
4810
6319
|
/**
|
|
4811
6320
|
* Asks the user to approve or reject a proposed mutation. Shows the summary, targets,
|
|
4812
6321
|
* proposed arguments and a countdown to expiry. A decision is emitted at most once until
|
|
4813
|
-
* the host reports that it is no longer deciding.
|
|
6322
|
+
* the host reports that it is no longer deciding. After the user rejected an agent's proposal
|
|
6323
|
+
* (`canGiveFeedback`), a low-key action asks what was wrong.
|
|
4814
6324
|
*/
|
|
4815
6325
|
class NhAssistantApprovalCardComponent {
|
|
4816
6326
|
constructor() {
|
|
@@ -4818,23 +6328,54 @@ class NhAssistantApprovalCardComponent {
|
|
|
4818
6328
|
/** True while the host submits a decision; disables both buttons. */
|
|
4819
6329
|
this.deciding = input(false, ...(ngDevMode ? [{ debugName: "deciding" }] : []));
|
|
4820
6330
|
/**
|
|
4821
|
-
* False when the
|
|
4822
|
-
*
|
|
6331
|
+
* False when the current user may not decide: in a chat the approval belongs to another
|
|
6332
|
+
* participant's turn, in an agent thread the user is no approver of the proposal.
|
|
4823
6333
|
*/
|
|
4824
6334
|
this.canDecide = input(true, ...(ngDevMode ? [{ debugName: "canDecide" }] : []));
|
|
4825
|
-
/** Name of the participant who decides, shown when the current user cannot. */
|
|
6335
|
+
/** Name of the participant who decides a `turn` approval, shown when the current user cannot. */
|
|
4826
6336
|
this.decisionOwner = input(null, ...(ngDevMode ? [{ debugName: "decisionOwner" }] : []));
|
|
6337
|
+
/**
|
|
6338
|
+
* Who decides when the current user cannot: the author of the turn (`turn`, the default), an
|
|
6339
|
+
* approver of the agent thread's subject (`approver`), or another approver because the current
|
|
6340
|
+
* user started the work (`other-approver`).
|
|
6341
|
+
*/
|
|
6342
|
+
this.waitingFor = input('turn', ...(ngDevMode ? [{ debugName: "waitingFor" }] : []));
|
|
6343
|
+
/**
|
|
6344
|
+
* True when the current user rejected this agent proposal and may say what was wrong, for example
|
|
6345
|
+
* from `NhAssistantStore.approvalRights`. Chat approvals never offer feedback.
|
|
6346
|
+
*/
|
|
6347
|
+
this.canGiveFeedback = input(false, ...(ngDevMode ? [{ debugName: "canGiveFeedback" }] : []));
|
|
4827
6348
|
this.decide = output();
|
|
4828
6349
|
this.now = signal(Date.now(), ...(ngDevMode ? [{ debugName: "now" }] : []));
|
|
4829
6350
|
this.submitted = signal(false, ...(ngDevMode ? [{ debugName: "submitted" }] : []));
|
|
6351
|
+
/** The feedback question is expanded. */
|
|
6352
|
+
this.feedbackOpen = signal(false, ...(ngDevMode ? [{ debugName: "feedbackOpen" }] : []));
|
|
6353
|
+
/** The feedback was stored; the question thanks the person and the action disappears. */
|
|
6354
|
+
this.feedbackSaved = signal(false, ...(ngDevMode ? [{ debugName: "feedbackSaved" }] : []));
|
|
6355
|
+
this.feedbackToggle = viewChild('feedbackToggle', ...(ngDevMode ? [{ debugName: "feedbackToggle" }] : []));
|
|
4830
6356
|
this.remainingMs = computed(() => Date.parse(this.approval().expiresAt) - this.now(), ...(ngDevMode ? [{ debugName: "remainingMs" }] : []));
|
|
4831
6357
|
this.expired = computed(() => this.approval().status === 'expired' ||
|
|
4832
6358
|
(this.approval().status === 'pending' && this.remainingMs() <= 0), ...(ngDevMode ? [{ debugName: "expired" }] : []));
|
|
4833
6359
|
this.effectiveStatus = computed(() => this.expired() ? 'expired' : this.approval().status, ...(ngDevMode ? [{ debugName: "effectiveStatus" }] : []));
|
|
4834
6360
|
this.actionable = computed(() => this.effectiveStatus() === 'pending', ...(ngDevMode ? [{ debugName: "actionable" }] : []));
|
|
6361
|
+
this.succeeded = computed(() => ['approved', 'executing', 'executed', 'resolved'].includes(this.effectiveStatus()), ...(ngDevMode ? [{ debugName: "succeeded" }] : []));
|
|
4835
6362
|
this.disabled = computed(() => !this.actionable() || !this.canDecide() || this.deciding() || this.submitted(), ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
4836
6363
|
this.countdown = computed(() => formatNhAssistantCountdown(this.remainingMs()), ...(ngDevMode ? [{ debugName: "countdown" }] : []));
|
|
4837
6364
|
this.presentedSummary = computed(() => this.approval().presentation?.summary ?? null, ...(ngDevMode ? [{ debugName: "presentedSummary" }] : []));
|
|
6365
|
+
this.feedbackAvailable = computed(() => this.canGiveFeedback() && this.effectiveStatus() === 'rejected', ...(ngDevMode ? [{ debugName: "feedbackAvailable" }] : []));
|
|
6366
|
+
this.feedbackIds = computed(() => [this.approval().approvalId], ...(ngDevMode ? [{ debugName: "feedbackIds" }] : []));
|
|
6367
|
+
this.feedbackPanelId = `nh-assistant-approval-feedback-${nextId$3++}`;
|
|
6368
|
+
/** Translation key of the text shown instead of the buttons when the current user cannot decide. */
|
|
6369
|
+
this.waitingKey = computed(() => {
|
|
6370
|
+
switch (this.waitingFor()) {
|
|
6371
|
+
case 'approver':
|
|
6372
|
+
return 'nh-assistant.approval.waiting-for-approver';
|
|
6373
|
+
case 'other-approver':
|
|
6374
|
+
return 'nh-assistant.approval.waiting-for-other-approver';
|
|
6375
|
+
default:
|
|
6376
|
+
return this.decisionOwner() ? 'nh-assistant.approval.waiting-for' : 'nh-assistant.approval.waiting-for-participant';
|
|
6377
|
+
}
|
|
6378
|
+
}, ...(ngDevMode ? [{ debugName: "waitingKey" }] : []));
|
|
4838
6379
|
// Tick only while the approval is pending and not yet expired.
|
|
4839
6380
|
effect(onCleanup => {
|
|
4840
6381
|
if (this.approval().status !== 'pending') {
|
|
@@ -4863,15 +6404,26 @@ class NhAssistantApprovalCardComponent {
|
|
|
4863
6404
|
this.submitted.set(true);
|
|
4864
6405
|
this.decide.emit(decision);
|
|
4865
6406
|
}
|
|
6407
|
+
toggleFeedback() {
|
|
6408
|
+
this.feedbackOpen.update(open => !open);
|
|
6409
|
+
}
|
|
6410
|
+
onFeedbackSaved() {
|
|
6411
|
+
this.feedbackSaved.set(true);
|
|
6412
|
+
}
|
|
6413
|
+
/** Collapses the question and returns focus to the action that opened it. */
|
|
6414
|
+
onFeedbackDismissed() {
|
|
6415
|
+
this.feedbackOpen.set(false);
|
|
6416
|
+
this.feedbackToggle()?.nativeElement.focus();
|
|
6417
|
+
}
|
|
4866
6418
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantApprovalCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4867
|
-
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 }); }
|
|
6419
|
+
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 }, waitingFor: { classPropertyName: "waitingFor", publicName: "waitingFor", isSignal: true, isRequired: false, transformFunction: null }, canGiveFeedback: { classPropertyName: "canGiveFeedback", publicName: "canGiveFeedback", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { decide: "decide" }, host: { properties: { "attr.data-status": "effectiveStatus()" } }, viewQueries: [{ propertyName: "feedbackToggle", first: true, predicate: ["feedbackToggle"], descendants: true, isSignal: true }], 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' : succeeded() ? '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 {{ waitingKey() | translate: { name: decisionOwner() } }}\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\n @if (feedbackAvailable()) {\n @if (!feedbackSaved()) {\n <button\n #feedbackToggle\n class=\"feedback-toggle\"\n type=\"button\"\n [attr.aria-expanded]=\"feedbackOpen()\"\n [attr.aria-controls]=\"feedbackOpen() ? feedbackPanelId : null\"\n (click)=\"toggleFeedback()\">\n {{ 'nh-assistant.approval.give-feedback' | translate }}\n </button>\n }\n @if (feedbackOpen()) {\n <nh-assistant-proposal-feedback\n class=\"feedback\"\n [attr.id]=\"feedbackPanelId\"\n [approvalIds]=\"feedbackIds()\"\n (saved)=\"onFeedbackSaved()\"\n (dismissed)=\"onFeedbackDismissed()\" />\n }\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;overflow-wrap:anywhere}.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)}.feedback-toggle{display:inline-flex;min-height:0;margin-top:10px;border:0;border-radius:6px;padding:2px 0;color:var(--nha-text-muted);background:transparent;font-size:12px;font-weight:650;text-decoration:underline;text-underline-offset:2px}.feedback-toggle:hover:not(:disabled){color:var(--nha-text)}.feedback{display:block;margin-top:10px}\n"], dependencies: [{ kind: "component", type: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }, { kind: "component", type: NhAssistantProposalFeedbackComponent, selector: "nh-assistant-proposal-feedback", inputs: ["approvalIds", "initial"], outputs: ["saved", "dismissed"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
4868
6420
|
}
|
|
4869
6421
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantApprovalCardComponent, decorators: [{
|
|
4870
6422
|
type: Component,
|
|
4871
|
-
args: [{ selector: 'nh-assistant-approval-card', standalone: true, imports: [TranslatePipe, NhAssistantIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
6423
|
+
args: [{ selector: 'nh-assistant-approval-card', standalone: true, imports: [TranslatePipe, NhAssistantIconComponent, NhAssistantProposalFeedbackComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
4872
6424
|
'[attr.data-status]': 'effectiveStatus()'
|
|
4873
|
-
}, template: "<section class=\"card\" [attr.aria-label]=\"'nh-assistant.approval.title' | translate\">\n <header class=\"header\">\n <nh-assistant-icon [name]=\"actionable() ? 'warning' :
|
|
4874
|
-
}], 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"] }] } });
|
|
6425
|
+
}, template: "<section class=\"card\" [attr.aria-label]=\"'nh-assistant.approval.title' | translate\">\n <header class=\"header\">\n <nh-assistant-icon [name]=\"actionable() ? 'warning' : succeeded() ? '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 {{ waitingKey() | translate: { name: decisionOwner() } }}\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\n @if (feedbackAvailable()) {\n @if (!feedbackSaved()) {\n <button\n #feedbackToggle\n class=\"feedback-toggle\"\n type=\"button\"\n [attr.aria-expanded]=\"feedbackOpen()\"\n [attr.aria-controls]=\"feedbackOpen() ? feedbackPanelId : null\"\n (click)=\"toggleFeedback()\">\n {{ 'nh-assistant.approval.give-feedback' | translate }}\n </button>\n }\n @if (feedbackOpen()) {\n <nh-assistant-proposal-feedback\n class=\"feedback\"\n [attr.id]=\"feedbackPanelId\"\n [approvalIds]=\"feedbackIds()\"\n (saved)=\"onFeedbackSaved()\"\n (dismissed)=\"onFeedbackDismissed()\" />\n }\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;overflow-wrap:anywhere}.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)}.feedback-toggle{display:inline-flex;min-height:0;margin-top:10px;border:0;border-radius:6px;padding:2px 0;color:var(--nha-text-muted);background:transparent;font-size:12px;font-weight:650;text-decoration:underline;text-underline-offset:2px}.feedback-toggle:hover:not(:disabled){color:var(--nha-text)}.feedback{display:block;margin-top:10px}\n"] }]
|
|
6426
|
+
}], 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 }] }], waitingFor: [{ type: i0.Input, args: [{ isSignal: true, alias: "waitingFor", required: false }] }], canGiveFeedback: [{ type: i0.Input, args: [{ isSignal: true, alias: "canGiveFeedback", required: false }] }], decide: [{ type: i0.Output, args: ["decide"] }], feedbackToggle: [{ type: i0.ViewChild, args: ['feedbackToggle', { isSignal: true }] }] } });
|
|
4875
6427
|
|
|
4876
6428
|
let nextId$2 = 0;
|
|
4877
6429
|
const statusIcons = {
|
|
@@ -4879,7 +6431,9 @@ const statusIcons = {
|
|
|
4879
6431
|
succeeded: 'check',
|
|
4880
6432
|
failed: 'warning',
|
|
4881
6433
|
'awaiting-approval': 'clock',
|
|
4882
|
-
rejected: 'x'
|
|
6434
|
+
rejected: 'x',
|
|
6435
|
+
// Ended without running: its proposal expired, was withdrawn or became stale.
|
|
6436
|
+
'not-executed': 'stop'
|
|
4883
6437
|
};
|
|
4884
6438
|
/** Shows one tool invocation with its status and expandable input and result previews. */
|
|
4885
6439
|
class NhAssistantToolCallCardComponent {
|
|
@@ -4894,13 +6448,13 @@ class NhAssistantToolCallCardComponent {
|
|
|
4894
6448
|
this.expanded.update(expanded => !expanded);
|
|
4895
6449
|
}
|
|
4896
6450
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantToolCallCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4897
|
-
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 }); }
|
|
6451
|
+
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 @if (part().grantSource; as grantSource) {\n <span class=\"grant\" [attr.title]=\"'nh-assistant.tool-call.grant-source.' + grantSource + '-hint' | translate\">\n {{ 'nh-assistant.tool-call.grant-source.' + grantSource | translate }}\n </span>\n }\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 } @else if (part().restricted) {\n <p class=\"label\">{{ 'nh-assistant.tool-call.result' | translate }}</p>\n <p class=\"restricted\">{{ 'nh-assistant.tool-call.restricted' | translate }}</p>\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)}:host([data-status=not-executed]){--nha-status-color: var(--nha-text-muted)}:host([data-status=not-executed]) .card{border-left-style:dashed}.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}}.grant{flex:none;padding:1px 6px;border:1px solid var(--nh-assistant-border, currentColor);border-radius:999px;font-size:.75em;opacity:.85}.restricted{margin:0;font-style:italic;opacity:.8}\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 }); }
|
|
4898
6452
|
}
|
|
4899
6453
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantToolCallCardComponent, decorators: [{
|
|
4900
6454
|
type: Component,
|
|
4901
6455
|
args: [{ selector: 'nh-assistant-tool-call-card', standalone: true, imports: [TranslatePipe, NhAssistantTranslatePipe, NhAssistantIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
4902
6456
|
'[attr.data-status]': 'part().status'
|
|
4903
|
-
}, 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"] }]
|
|
6457
|
+
}, 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 @if (part().grantSource; as grantSource) {\n <span class=\"grant\" [attr.title]=\"'nh-assistant.tool-call.grant-source.' + grantSource + '-hint' | translate\">\n {{ 'nh-assistant.tool-call.grant-source.' + grantSource | translate }}\n </span>\n }\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 } @else if (part().restricted) {\n <p class=\"label\">{{ 'nh-assistant.tool-call.result' | translate }}</p>\n <p class=\"restricted\">{{ 'nh-assistant.tool-call.restricted' | translate }}</p>\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)}:host([data-status=not-executed]){--nha-status-color: var(--nha-text-muted)}:host([data-status=not-executed]) .card{border-left-style:dashed}.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}}.grant{flex:none;padding:1px 6px;border:1px solid var(--nh-assistant-border, currentColor);border-radius:999px;font-size:.75em;opacity:.85}.restricted{margin:0;font-style:italic;opacity:.8}\n"] }]
|
|
4904
6458
|
}], propDecorators: { part: [{ type: i0.Input, args: [{ isSignal: true, alias: "part", required: true }] }] } });
|
|
4905
6459
|
|
|
4906
6460
|
let nextId$1 = 0;
|
|
@@ -4908,6 +6462,7 @@ const stateIcons = {
|
|
|
4908
6462
|
'awaiting-approval': 'clock',
|
|
4909
6463
|
running: 'tool',
|
|
4910
6464
|
failed: 'warning',
|
|
6465
|
+
'not-executed': 'stop',
|
|
4911
6466
|
succeeded: 'check'
|
|
4912
6467
|
};
|
|
4913
6468
|
/**
|
|
@@ -4930,12 +6485,17 @@ class NhAssistantToolCallGroupComponent {
|
|
|
4930
6485
|
this.total = computed(() => this.calls().length, ...(ngDevMode ? [{ debugName: "total" }] : []));
|
|
4931
6486
|
this.finished = computed(() => this.calls().filter(call => !isActive(call)).length, ...(ngDevMode ? [{ debugName: "finished" }] : []));
|
|
4932
6487
|
this.failed = computed(() => this.calls().filter(call => call.status === 'failed').length, ...(ngDevMode ? [{ debugName: "failed" }] : []));
|
|
6488
|
+
/** Calls that finished without running; they never wait. */
|
|
6489
|
+
this.notExecuted = computed(() => this.calls().filter(call => call.status === 'not-executed').length, ...(ngDevMode ? [{ debugName: "notExecuted" }] : []));
|
|
4933
6490
|
this.state = computed(() => {
|
|
4934
6491
|
const current = this.current();
|
|
4935
6492
|
if (current) {
|
|
4936
6493
|
return current.status === 'awaiting-approval' ? 'awaiting-approval' : 'running';
|
|
4937
6494
|
}
|
|
4938
|
-
|
|
6495
|
+
if (this.failed() > 0) {
|
|
6496
|
+
return 'failed';
|
|
6497
|
+
}
|
|
6498
|
+
return this.notExecuted() > 0 ? 'not-executed' : 'succeeded';
|
|
4939
6499
|
}, ...(ngDevMode ? [{ debugName: "state" }] : []));
|
|
4940
6500
|
this.stateIcon = computed(() => stateIcons[this.state()], ...(ngDevMode ? [{ debugName: "stateIcon" }] : []));
|
|
4941
6501
|
}
|
|
@@ -4943,13 +6503,13 @@ class NhAssistantToolCallGroupComponent {
|
|
|
4943
6503
|
this.expanded.update(expanded => !expanded);
|
|
4944
6504
|
}
|
|
4945
6505
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantToolCallGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4946
|
-
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
|
|
6506
|
+
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 {\n @if (failed() > 0) {\n <span class=\"count failed\">{{ 'nh-assistant.tool-group.failed' | translate: { count: failed() } }}</span>\n }\n @if (notExecuted() > 0) {\n <span class=\"count not-executed\">{{ 'nh-assistant.tool-group.not-executed' | translate: { count: notExecuted() } }}</span>\n }\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)}:host([data-status=not-executed]){--nha-status-color: var(--nha-text-muted)}:host([data-status=not-executed]) .group{border-left-style:dashed}.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,.count.not-executed{border-radius:999px;padding:2px 8px;font-size:11px;font-weight:700}.count.failed{color:var(--nha-danger);background:var(--nha-danger-soft)}.count.not-executed{color:var(--nha-text-muted);background:var(--nha-surface-strong)}.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 }); }
|
|
4947
6507
|
}
|
|
4948
6508
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantToolCallGroupComponent, decorators: [{
|
|
4949
6509
|
type: Component,
|
|
4950
6510
|
args: [{ selector: 'nh-assistant-tool-call-group', standalone: true, imports: [TranslatePipe, NhAssistantIconComponent, NhAssistantToolCallCardComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
4951
6511
|
'[attr.data-status]': 'state()'
|
|
4952
|
-
}, 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
|
|
6512
|
+
}, 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 {\n @if (failed() > 0) {\n <span class=\"count failed\">{{ 'nh-assistant.tool-group.failed' | translate: { count: failed() } }}</span>\n }\n @if (notExecuted() > 0) {\n <span class=\"count not-executed\">{{ 'nh-assistant.tool-group.not-executed' | translate: { count: notExecuted() } }}</span>\n }\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)}:host([data-status=not-executed]){--nha-status-color: var(--nha-text-muted)}:host([data-status=not-executed]) .group{border-left-style:dashed}.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,.count.not-executed{border-radius:999px;padding:2px 8px;font-size:11px;font-weight:700}.count.failed{color:var(--nha-danger);background:var(--nha-danger-soft)}.count.not-executed{color:var(--nha-text-muted);background:var(--nha-surface-strong)}.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"] }]
|
|
4953
6513
|
}], propDecorators: { calls: [{ type: i0.Input, args: [{ isSignal: true, alias: "calls", required: true }] }] } });
|
|
4954
6514
|
function isActive(call) {
|
|
4955
6515
|
return call.status === 'running' || call.status === 'awaiting-approval';
|
|
@@ -4970,8 +6530,9 @@ const stickToBottomDistance = 120;
|
|
|
4970
6530
|
/**
|
|
4971
6531
|
* The messages of a conversation as a live log. Assistant text renders as sanitized
|
|
4972
6532
|
* Markdown, consecutive tool calls as one collapsible group that counts along, and
|
|
4973
|
-
* approvals as cards.
|
|
4974
|
-
*
|
|
6533
|
+
* approvals as cards. Messages a trigger wrote into an agent thread read as events, not as
|
|
6534
|
+
* a person. While a turn runs without visible progress, a working indicator shows the
|
|
6535
|
+
* elapsed time. The thread follows new content while the user is at the bottom.
|
|
4975
6536
|
*/
|
|
4976
6537
|
class NhAssistantThreadComponent {
|
|
4977
6538
|
constructor() {
|
|
@@ -4988,7 +6549,17 @@ class NhAssistantThreadComponent {
|
|
|
4988
6549
|
this.canDecide = input(true, ...(ngDevMode ? [{ debugName: "canDecide" }] : []));
|
|
4989
6550
|
/** Name of the participant who decides the pending approval, when it is someone else. */
|
|
4990
6551
|
this.decisionOwner = input(null, ...(ngDevMode ? [{ debugName: "decisionOwner" }] : []));
|
|
6552
|
+
/**
|
|
6553
|
+
* Who may decide each pending approval, by approval id, for example `NhAssistantStore.approvalRights`.
|
|
6554
|
+
* An agent thread can hold several open proposals that the subject's approvers decide. Approvals
|
|
6555
|
+
* without an entry use `canDecide` and `decisionOwner`. An entry with `canGiveFeedback` lets the
|
|
6556
|
+
* card of a rejected proposal ask what was wrong.
|
|
6557
|
+
*/
|
|
6558
|
+
this.approvalRights = input(null, ...(ngDevMode ? [{ debugName: "approvalRights" }] : []));
|
|
6559
|
+
/** The decision about an approval card, without the approval it belongs to. */
|
|
4991
6560
|
this.approvalDecision = output();
|
|
6561
|
+
/** The decision about an approval card together with its approval id. */
|
|
6562
|
+
this.approvalChoice = output();
|
|
4992
6563
|
this.names = computed(() => new Map(this.members().map(member => [member.actorId, member.displayName])), ...(ngDevMode ? [{ debugName: "names" }] : []));
|
|
4993
6564
|
this.virtualThreshold = NH_ASSISTANT_VIRTUAL_SCROLL_THRESHOLD;
|
|
4994
6565
|
this.virtual = computed(() => this.messages().length > NH_ASSISTANT_VIRTUAL_SCROLL_THRESHOLD, ...(ngDevMode ? [{ debugName: "virtual" }] : []));
|
|
@@ -5047,13 +6618,30 @@ class NhAssistantThreadComponent {
|
|
|
5047
6618
|
trackMessage(_index, message) {
|
|
5048
6619
|
return message.id;
|
|
5049
6620
|
}
|
|
6621
|
+
/** True for a user message a trigger wrote into an agent thread. */
|
|
6622
|
+
isTrigger(message) {
|
|
6623
|
+
return message.role === 'user' && message.authorKind === 'trigger';
|
|
6624
|
+
}
|
|
5050
6625
|
/** True for a user message written by another person in a shared conversation. */
|
|
5051
6626
|
isOthers(message) {
|
|
5052
6627
|
return message.role === 'user'
|
|
6628
|
+
&& !this.isTrigger(message)
|
|
5053
6629
|
&& !!message.authorActorId
|
|
5054
6630
|
&& !!this.currentActorId()
|
|
5055
6631
|
&& message.authorActorId !== this.currentActorId();
|
|
5056
6632
|
}
|
|
6633
|
+
/** Whether and why the current user may decide an approval card. */
|
|
6634
|
+
rightFor(approval) {
|
|
6635
|
+
return this.approvalRights()?.get(approval.approvalId) ?? {
|
|
6636
|
+
canDecide: this.canDecide(),
|
|
6637
|
+
waitingFor: 'turn',
|
|
6638
|
+
decisionOwner: this.decisionOwner()
|
|
6639
|
+
};
|
|
6640
|
+
}
|
|
6641
|
+
choose(approval, decision) {
|
|
6642
|
+
this.approvalDecision.emit(decision);
|
|
6643
|
+
this.approvalChoice.emit({ approvalId: approval.approvalId, decision });
|
|
6644
|
+
}
|
|
5057
6645
|
/** Display name of another person's message, or `null` when the application provides none. */
|
|
5058
6646
|
authorName(message) {
|
|
5059
6647
|
return message.authorActorId ? this.names().get(message.authorActorId) ?? null : null;
|
|
@@ -5074,7 +6662,7 @@ class NhAssistantThreadComponent {
|
|
|
5074
6662
|
}
|
|
5075
6663
|
}
|
|
5076
6664
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantThreadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
5077
|
-
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 }); }
|
|
6665
|
+
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 }, approvalRights: { classPropertyName: "approvalRights", publicName: "approvalRights", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { approvalDecision: "approvalDecision", approvalChoice: "approvalChoice" }, 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\n class=\"message\"\n [attr.data-role]=\"message.role\"\n [attr.data-others]=\"isOthers(message) ? 'true' : null\"\n [attr.data-author]=\"isTrigger(message) ? 'trigger' : null\">\n @if (isTrigger(message)) {\n <p class=\"author\">{{ 'nh-assistant.thread.trigger' | translate }}</p>\n } @else 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]=\"rightFor(group.part).canDecide\"\n [decisionOwner]=\"rightFor(group.part).decisionOwner\"\n [waitingFor]=\"rightFor(group.part).waitingFor\"\n [canGiveFeedback]=\"rightFor(group.part).canGiveFeedback === true\"\n (decide)=\"choose(group.part, $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-author=trigger]{align-items:stretch}.message[data-author=trigger] .bubble{max-width:none;color:var(--nha-text-muted);background:transparent;border:1px dashed var(--nha-border)}.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", "waitingFor", "canGiveFeedback"], outputs: ["decide"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: NhAssistantMarkdownPipe, name: "nhAssistantMarkdown" }, { kind: "pipe", type: NhAssistantPartGroupsPipe, name: "nhAssistantPartGroups" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
5078
6666
|
}
|
|
5079
6667
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantThreadComponent, decorators: [{
|
|
5080
6668
|
type: Component,
|
|
@@ -5086,8 +6674,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImpo
|
|
|
5086
6674
|
NhAssistantPartGroupsPipe,
|
|
5087
6675
|
NhAssistantToolCallGroupComponent,
|
|
5088
6676
|
NhAssistantApprovalCardComponent
|
|
5089
|
-
], 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
|
|
5090
|
-
}], 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 }] }] } });
|
|
6677
|
+
], 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\n class=\"message\"\n [attr.data-role]=\"message.role\"\n [attr.data-others]=\"isOthers(message) ? 'true' : null\"\n [attr.data-author]=\"isTrigger(message) ? 'trigger' : null\">\n @if (isTrigger(message)) {\n <p class=\"author\">{{ 'nh-assistant.thread.trigger' | translate }}</p>\n } @else 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]=\"rightFor(group.part).canDecide\"\n [decisionOwner]=\"rightFor(group.part).decisionOwner\"\n [waitingFor]=\"rightFor(group.part).waitingFor\"\n [canGiveFeedback]=\"rightFor(group.part).canGiveFeedback === true\"\n (decide)=\"choose(group.part, $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-author=trigger]{align-items:stretch}.message[data-author=trigger] .bubble{max-width:none;color:var(--nha-text-muted);background:transparent;border:1px dashed var(--nha-border)}.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"] }]
|
|
6678
|
+
}], 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 }] }], approvalRights: [{ type: i0.Input, args: [{ isSignal: true, alias: "approvalRights", required: false }] }], approvalDecision: [{ type: i0.Output, args: ["approvalDecision"] }], approvalChoice: [{ type: i0.Output, args: ["approvalChoice"] }], scroller: [{ type: i0.ViewChild, args: ['scroller', { isSignal: true }] }], viewport: [{ type: i0.ViewChild, args: [i0.forwardRef(() => CdkVirtualScrollViewport), { isSignal: true }] }] } });
|
|
5091
6679
|
|
|
5092
6680
|
let nextId = 0;
|
|
5093
6681
|
/**
|
|
@@ -5300,11 +6888,15 @@ class NhAssistantPanelComponent {
|
|
|
5300
6888
|
decide(decision) {
|
|
5301
6889
|
this.store.decide(decision);
|
|
5302
6890
|
}
|
|
6891
|
+
/** Decides the approval of a card; in an agent thread several proposals can be open. */
|
|
6892
|
+
decideApproval(choice) {
|
|
6893
|
+
this.store.decide(choice.decision, undefined, choice.approvalId);
|
|
6894
|
+
}
|
|
5303
6895
|
cancel() {
|
|
5304
6896
|
void this.store.cancel();
|
|
5305
6897
|
}
|
|
5306
6898
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
5307
|
-
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 @if (store.queuedMessages().length > 0) {\n <section class=\"message-queue\" [attr.aria-label]=\"'nh-assistant.queue.title' | translate: { count: store.queuedMessages().length }\">\n <div class=\"queue-heading\">\n <span aria-live=\"polite\">{{ 'nh-assistant.queue.title' | translate: { count: store.queuedMessages().length } }}</span>\n @if (store.queuePaused()) {\n <button type=\"button\" class=\"queue-resume\" [disabled]=\"editingQueuedId() !== null\" (click)=\"store.resumeQueue()\">\n {{ 'nh-assistant.queue.resume' | translate }}\n </button>\n }\n </div>\n <ol class=\"queue-items\">\n @for (item of store.queuedMessages(); track item.id) {\n <li class=\"queue-item\" [class.failed]=\"item.state === 'failed'\">\n @if (editingQueuedId() === item.id && item.state !== 'sending') {\n <div class=\"queue-edit\">\n <textarea rows=\"2\" [value]=\"queuedDraft()\" (input)=\"changeQueuedDraft($event)\"\n [attr.aria-label]=\"'nh-assistant.queue.edit' | translate\"></textarea>\n <div class=\"queue-buttons\">\n <button type=\"button\" class=\"icon-button\" [disabled]=\"!validQueuedDraft()\"\n [attr.aria-label]=\"'nh-assistant.queue.save' | translate\" [attr.title]=\"'nh-assistant.queue.save' | translate\"\n (click)=\"saveQueuedMessage(item.id)\"><nh-assistant-icon name=\"check\" /></button>\n <button type=\"button\" class=\"icon-button\" [attr.aria-label]=\"'nh-assistant.queue.cancel-edit' | translate\"\n [attr.title]=\"'nh-assistant.queue.cancel-edit' | translate\"\n (click)=\"cancelQueueEdit()\"><nh-assistant-icon name=\"close\" /></button>\n </div>\n </div>\n } @else {\n <div class=\"queue-message\">\n <p [title]=\"item.text\">{{ item.text }}</p>\n @if (item.state !== 'queued') {\n <span class=\"queue-state\">{{ 'nh-assistant.queue.state.' + item.state | translate }}</span>\n }\n </div>\n <div class=\"queue-buttons\">\n @if (store.canSteer() && item.state === 'queued') {\n <button type=\"button\" class=\"queue-steer\" [disabled]=\"editingQueuedId() !== null\"\n [attr.title]=\"'nh-assistant.queue.steer-hint' | translate\"\n (click)=\"store.steerQueuedMessage(item.id)\">{{ 'nh-assistant.queue.steer' | translate }}</button>\n }\n <button type=\"button\" class=\"icon-button\" [disabled]=\"item.state === 'sending'\"\n [attr.aria-label]=\"'nh-assistant.queue.edit' | translate\" [attr.title]=\"'nh-assistant.queue.edit' | translate\"\n (click)=\"editQueuedMessage(item.id, item.text)\"><nh-assistant-icon name=\"edit\" /></button>\n <button type=\"button\" class=\"icon-button\" [disabled]=\"item.state === 'sending'\"\n [attr.aria-label]=\"'nh-assistant.queue.remove' | translate\" [attr.title]=\"'nh-assistant.queue.remove' | translate\"\n (click)=\"store.removeQueuedMessage(item.id)\"><nh-assistant-icon name=\"close\" /></button>\n </div>\n }\n </li>\n }\n </ol>\n </section>\n }\n <nh-assistant-composer\n [disabled]=\"closed()\"\n [sendDisabled]=\"composerDisabled()\"\n [busy]=\"busy()\"\n [queueWhileBusy]=\"true\"\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 @if (store.agents().length > 1) {\n <nh-assistant-agent-picker\n nhAssistantComposerControls\n [compact]=\"true\"\n [agents]=\"store.agents()\"\n [selectedAgentId]=\"store.selectedAgentId()\"\n (agentChange)=\"store.selectAgent($event)\" />\n }\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}.message-queue{margin-bottom:10px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);background:var(--nha-background);overflow:hidden;font-size:12px}.queue-heading{display:flex;justify-content:space-between;align-items:center;gap:8px;padding:8px 10px;color:var(--nha-text-muted)}.queue-items{max-height:180px;overflow-y:auto;margin:0;padding:0;list-style:none}.queue-item{display:flex;align-items:center;gap:8px;border-top:1px solid var(--nha-border);padding:6px 8px 6px 10px}.queue-item.failed{border-left:2px solid var(--nha-danger)}.queue-message{flex:1;min-width:0}.queue-message p{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere;margin:0}.queue-state{color:var(--nha-text-muted);font-size:11px}.queue-buttons{display:flex;flex-shrink:0;align-items:center;gap:2px}.queue-buttons .icon-button{width:28px;height:30px}.queue-resume,.queue-steer{border:0;border-radius:6px;padding:6px;color:var(--nha-highlight);background:transparent;font:inherit;cursor:pointer}.queue-resume:hover,.queue-steer:hover{background:var(--nha-surface)}.queue-resume:focus-visible,.queue-steer:focus-visible{outline:2px solid var(--nha-focus)}.queue-resume:disabled,.queue-steer:disabled{cursor:not-allowed;opacity:.5}.queue-edit{width:100%}.queue-edit textarea{box-sizing:border-box;width:100%;border:1px solid var(--nha-border);border-radius:6px;padding:8px;color:var(--nha-text);background:var(--nha-surface);font:inherit;resize:vertical}.queue-edit .queue-buttons{justify-content:flex-end}.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", "queueWhileBusy", "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 }); }
|
|
6899
|
+
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 [approvalRights]=\"store.approvalRights()\"\n (approvalChoice)=\"decideApproval($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 @if (store.queuedMessages().length > 0) {\n <section class=\"message-queue\" [attr.aria-label]=\"'nh-assistant.queue.title' | translate: { count: store.queuedMessages().length }\">\n <div class=\"queue-heading\">\n <span aria-live=\"polite\">{{ 'nh-assistant.queue.title' | translate: { count: store.queuedMessages().length } }}</span>\n @if (store.queuePaused()) {\n <button type=\"button\" class=\"queue-resume\" [disabled]=\"editingQueuedId() !== null\" (click)=\"store.resumeQueue()\">\n {{ 'nh-assistant.queue.resume' | translate }}\n </button>\n }\n </div>\n <ol class=\"queue-items\">\n @for (item of store.queuedMessages(); track item.id) {\n <li class=\"queue-item\" [class.failed]=\"item.state === 'failed'\">\n @if (editingQueuedId() === item.id && item.state !== 'sending') {\n <div class=\"queue-edit\">\n <textarea rows=\"2\" [value]=\"queuedDraft()\" (input)=\"changeQueuedDraft($event)\"\n [attr.aria-label]=\"'nh-assistant.queue.edit' | translate\"></textarea>\n <div class=\"queue-buttons\">\n <button type=\"button\" class=\"icon-button\" [disabled]=\"!validQueuedDraft()\"\n [attr.aria-label]=\"'nh-assistant.queue.save' | translate\" [attr.title]=\"'nh-assistant.queue.save' | translate\"\n (click)=\"saveQueuedMessage(item.id)\"><nh-assistant-icon name=\"check\" /></button>\n <button type=\"button\" class=\"icon-button\" [attr.aria-label]=\"'nh-assistant.queue.cancel-edit' | translate\"\n [attr.title]=\"'nh-assistant.queue.cancel-edit' | translate\"\n (click)=\"cancelQueueEdit()\"><nh-assistant-icon name=\"close\" /></button>\n </div>\n </div>\n } @else {\n <div class=\"queue-message\">\n <p [title]=\"item.text\">{{ item.text }}</p>\n @if (item.state !== 'queued') {\n <span class=\"queue-state\">{{ 'nh-assistant.queue.state.' + item.state | translate }}</span>\n }\n </div>\n <div class=\"queue-buttons\">\n @if (store.canSteer() && item.state === 'queued') {\n <button type=\"button\" class=\"queue-steer\" [disabled]=\"editingQueuedId() !== null\"\n [attr.title]=\"'nh-assistant.queue.steer-hint' | translate\"\n (click)=\"store.steerQueuedMessage(item.id)\">{{ 'nh-assistant.queue.steer' | translate }}</button>\n }\n <button type=\"button\" class=\"icon-button\" [disabled]=\"item.state === 'sending'\"\n [attr.aria-label]=\"'nh-assistant.queue.edit' | translate\" [attr.title]=\"'nh-assistant.queue.edit' | translate\"\n (click)=\"editQueuedMessage(item.id, item.text)\"><nh-assistant-icon name=\"edit\" /></button>\n <button type=\"button\" class=\"icon-button\" [disabled]=\"item.state === 'sending'\"\n [attr.aria-label]=\"'nh-assistant.queue.remove' | translate\" [attr.title]=\"'nh-assistant.queue.remove' | translate\"\n (click)=\"store.removeQueuedMessage(item.id)\"><nh-assistant-icon name=\"close\" /></button>\n </div>\n }\n </li>\n }\n </ol>\n </section>\n }\n <nh-assistant-composer\n [disabled]=\"closed()\"\n [sendDisabled]=\"composerDisabled()\"\n [busy]=\"busy()\"\n [queueWhileBusy]=\"true\"\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 @if (store.agents().length > 1) {\n <nh-assistant-agent-picker\n nhAssistantComposerControls\n [compact]=\"true\"\n [agents]=\"store.agents()\"\n [selectedAgentId]=\"store.selectedAgentId()\"\n (agentChange)=\"store.selectAgent($event)\" />\n }\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}.message-queue{margin-bottom:10px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);background:var(--nha-background);overflow:hidden;font-size:12px}.queue-heading{display:flex;justify-content:space-between;align-items:center;gap:8px;padding:8px 10px;color:var(--nha-text-muted)}.queue-items{max-height:180px;overflow-y:auto;margin:0;padding:0;list-style:none}.queue-item{display:flex;align-items:center;gap:8px;border-top:1px solid var(--nha-border);padding:6px 8px 6px 10px}.queue-item.failed{border-left:2px solid var(--nha-danger)}.queue-message{flex:1;min-width:0}.queue-message p{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere;margin:0}.queue-state{color:var(--nha-text-muted);font-size:11px}.queue-buttons{display:flex;flex-shrink:0;align-items:center;gap:2px}.queue-buttons .icon-button{width:28px;height:30px}.queue-resume,.queue-steer{border:0;border-radius:6px;padding:6px;color:var(--nha-highlight);background:transparent;font:inherit;cursor:pointer}.queue-resume:hover,.queue-steer:hover{background:var(--nha-surface)}.queue-resume:focus-visible,.queue-steer:focus-visible{outline:2px solid var(--nha-focus)}.queue-resume:disabled,.queue-steer:disabled{cursor:not-allowed;opacity:.5}.queue-edit{width:100%}.queue-edit textarea{box-sizing:border-box;width:100%;border:1px solid var(--nha-border);border-radius:6px;padding:8px;color:var(--nha-text);background:var(--nha-surface);font:inherit;resize:vertical}.queue-edit .queue-buttons{justify-content:flex-end}.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", "queueWhileBusy", "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", "approvalRights"], outputs: ["approvalDecision", "approvalChoice"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: NhAssistantTranslatePipe, name: "nhAssistantTranslate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
5308
6900
|
}
|
|
5309
6901
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantPanelComponent, decorators: [{
|
|
5310
6902
|
type: Component,
|
|
@@ -5320,22 +6912,96 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImpo
|
|
|
5320
6912
|
NhAssistantPreferencesComponent,
|
|
5321
6913
|
NhAssistantShareComponent,
|
|
5322
6914
|
NhAssistantThreadComponent
|
|
5323
|
-
], 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 @if (store.queuedMessages().length > 0) {\n <section class=\"message-queue\" [attr.aria-label]=\"'nh-assistant.queue.title' | translate: { count: store.queuedMessages().length }\">\n <div class=\"queue-heading\">\n <span aria-live=\"polite\">{{ 'nh-assistant.queue.title' | translate: { count: store.queuedMessages().length } }}</span>\n @if (store.queuePaused()) {\n <button type=\"button\" class=\"queue-resume\" [disabled]=\"editingQueuedId() !== null\" (click)=\"store.resumeQueue()\">\n {{ 'nh-assistant.queue.resume' | translate }}\n </button>\n }\n </div>\n <ol class=\"queue-items\">\n @for (item of store.queuedMessages(); track item.id) {\n <li class=\"queue-item\" [class.failed]=\"item.state === 'failed'\">\n @if (editingQueuedId() === item.id && item.state !== 'sending') {\n <div class=\"queue-edit\">\n <textarea rows=\"2\" [value]=\"queuedDraft()\" (input)=\"changeQueuedDraft($event)\"\n [attr.aria-label]=\"'nh-assistant.queue.edit' | translate\"></textarea>\n <div class=\"queue-buttons\">\n <button type=\"button\" class=\"icon-button\" [disabled]=\"!validQueuedDraft()\"\n [attr.aria-label]=\"'nh-assistant.queue.save' | translate\" [attr.title]=\"'nh-assistant.queue.save' | translate\"\n (click)=\"saveQueuedMessage(item.id)\"><nh-assistant-icon name=\"check\" /></button>\n <button type=\"button\" class=\"icon-button\" [attr.aria-label]=\"'nh-assistant.queue.cancel-edit' | translate\"\n [attr.title]=\"'nh-assistant.queue.cancel-edit' | translate\"\n (click)=\"cancelQueueEdit()\"><nh-assistant-icon name=\"close\" /></button>\n </div>\n </div>\n } @else {\n <div class=\"queue-message\">\n <p [title]=\"item.text\">{{ item.text }}</p>\n @if (item.state !== 'queued') {\n <span class=\"queue-state\">{{ 'nh-assistant.queue.state.' + item.state | translate }}</span>\n }\n </div>\n <div class=\"queue-buttons\">\n @if (store.canSteer() && item.state === 'queued') {\n <button type=\"button\" class=\"queue-steer\" [disabled]=\"editingQueuedId() !== null\"\n [attr.title]=\"'nh-assistant.queue.steer-hint' | translate\"\n (click)=\"store.steerQueuedMessage(item.id)\">{{ 'nh-assistant.queue.steer' | translate }}</button>\n }\n <button type=\"button\" class=\"icon-button\" [disabled]=\"item.state === 'sending'\"\n [attr.aria-label]=\"'nh-assistant.queue.edit' | translate\" [attr.title]=\"'nh-assistant.queue.edit' | translate\"\n (click)=\"editQueuedMessage(item.id, item.text)\"><nh-assistant-icon name=\"edit\" /></button>\n <button type=\"button\" class=\"icon-button\" [disabled]=\"item.state === 'sending'\"\n [attr.aria-label]=\"'nh-assistant.queue.remove' | translate\" [attr.title]=\"'nh-assistant.queue.remove' | translate\"\n (click)=\"store.removeQueuedMessage(item.id)\"><nh-assistant-icon name=\"close\" /></button>\n </div>\n }\n </li>\n }\n </ol>\n </section>\n }\n <nh-assistant-composer\n [disabled]=\"closed()\"\n [sendDisabled]=\"composerDisabled()\"\n [busy]=\"busy()\"\n [queueWhileBusy]=\"true\"\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 @if (store.agents().length > 1) {\n <nh-assistant-agent-picker\n nhAssistantComposerControls\n [compact]=\"true\"\n [agents]=\"store.agents()\"\n [selectedAgentId]=\"store.selectedAgentId()\"\n (agentChange)=\"store.selectAgent($event)\" />\n }\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}.message-queue{margin-bottom:10px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);background:var(--nha-background);overflow:hidden;font-size:12px}.queue-heading{display:flex;justify-content:space-between;align-items:center;gap:8px;padding:8px 10px;color:var(--nha-text-muted)}.queue-items{max-height:180px;overflow-y:auto;margin:0;padding:0;list-style:none}.queue-item{display:flex;align-items:center;gap:8px;border-top:1px solid var(--nha-border);padding:6px 8px 6px 10px}.queue-item.failed{border-left:2px solid var(--nha-danger)}.queue-message{flex:1;min-width:0}.queue-message p{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere;margin:0}.queue-state{color:var(--nha-text-muted);font-size:11px}.queue-buttons{display:flex;flex-shrink:0;align-items:center;gap:2px}.queue-buttons .icon-button{width:28px;height:30px}.queue-resume,.queue-steer{border:0;border-radius:6px;padding:6px;color:var(--nha-highlight);background:transparent;font:inherit;cursor:pointer}.queue-resume:hover,.queue-steer:hover{background:var(--nha-surface)}.queue-resume:focus-visible,.queue-steer:focus-visible{outline:2px solid var(--nha-focus)}.queue-resume:disabled,.queue-steer:disabled{cursor:not-allowed;opacity:.5}.queue-edit{width:100%}.queue-edit textarea{box-sizing:border-box;width:100%;border:1px solid var(--nha-border);border-radius:6px;padding:8px;color:var(--nha-text);background:var(--nha-surface);font:inherit;resize:vertical}.queue-edit .queue-buttons{justify-content:flex-end}.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"] }]
|
|
6915
|
+
], 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 [approvalRights]=\"store.approvalRights()\"\n (approvalChoice)=\"decideApproval($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 @if (store.queuedMessages().length > 0) {\n <section class=\"message-queue\" [attr.aria-label]=\"'nh-assistant.queue.title' | translate: { count: store.queuedMessages().length }\">\n <div class=\"queue-heading\">\n <span aria-live=\"polite\">{{ 'nh-assistant.queue.title' | translate: { count: store.queuedMessages().length } }}</span>\n @if (store.queuePaused()) {\n <button type=\"button\" class=\"queue-resume\" [disabled]=\"editingQueuedId() !== null\" (click)=\"store.resumeQueue()\">\n {{ 'nh-assistant.queue.resume' | translate }}\n </button>\n }\n </div>\n <ol class=\"queue-items\">\n @for (item of store.queuedMessages(); track item.id) {\n <li class=\"queue-item\" [class.failed]=\"item.state === 'failed'\">\n @if (editingQueuedId() === item.id && item.state !== 'sending') {\n <div class=\"queue-edit\">\n <textarea rows=\"2\" [value]=\"queuedDraft()\" (input)=\"changeQueuedDraft($event)\"\n [attr.aria-label]=\"'nh-assistant.queue.edit' | translate\"></textarea>\n <div class=\"queue-buttons\">\n <button type=\"button\" class=\"icon-button\" [disabled]=\"!validQueuedDraft()\"\n [attr.aria-label]=\"'nh-assistant.queue.save' | translate\" [attr.title]=\"'nh-assistant.queue.save' | translate\"\n (click)=\"saveQueuedMessage(item.id)\"><nh-assistant-icon name=\"check\" /></button>\n <button type=\"button\" class=\"icon-button\" [attr.aria-label]=\"'nh-assistant.queue.cancel-edit' | translate\"\n [attr.title]=\"'nh-assistant.queue.cancel-edit' | translate\"\n (click)=\"cancelQueueEdit()\"><nh-assistant-icon name=\"close\" /></button>\n </div>\n </div>\n } @else {\n <div class=\"queue-message\">\n <p [title]=\"item.text\">{{ item.text }}</p>\n @if (item.state !== 'queued') {\n <span class=\"queue-state\">{{ 'nh-assistant.queue.state.' + item.state | translate }}</span>\n }\n </div>\n <div class=\"queue-buttons\">\n @if (store.canSteer() && item.state === 'queued') {\n <button type=\"button\" class=\"queue-steer\" [disabled]=\"editingQueuedId() !== null\"\n [attr.title]=\"'nh-assistant.queue.steer-hint' | translate\"\n (click)=\"store.steerQueuedMessage(item.id)\">{{ 'nh-assistant.queue.steer' | translate }}</button>\n }\n <button type=\"button\" class=\"icon-button\" [disabled]=\"item.state === 'sending'\"\n [attr.aria-label]=\"'nh-assistant.queue.edit' | translate\" [attr.title]=\"'nh-assistant.queue.edit' | translate\"\n (click)=\"editQueuedMessage(item.id, item.text)\"><nh-assistant-icon name=\"edit\" /></button>\n <button type=\"button\" class=\"icon-button\" [disabled]=\"item.state === 'sending'\"\n [attr.aria-label]=\"'nh-assistant.queue.remove' | translate\" [attr.title]=\"'nh-assistant.queue.remove' | translate\"\n (click)=\"store.removeQueuedMessage(item.id)\"><nh-assistant-icon name=\"close\" /></button>\n </div>\n }\n </li>\n }\n </ol>\n </section>\n }\n <nh-assistant-composer\n [disabled]=\"closed()\"\n [sendDisabled]=\"composerDisabled()\"\n [busy]=\"busy()\"\n [queueWhileBusy]=\"true\"\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 @if (store.agents().length > 1) {\n <nh-assistant-agent-picker\n nhAssistantComposerControls\n [compact]=\"true\"\n [agents]=\"store.agents()\"\n [selectedAgentId]=\"store.selectedAgentId()\"\n (agentChange)=\"store.selectAgent($event)\" />\n }\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}.message-queue{margin-bottom:10px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);background:var(--nha-background);overflow:hidden;font-size:12px}.queue-heading{display:flex;justify-content:space-between;align-items:center;gap:8px;padding:8px 10px;color:var(--nha-text-muted)}.queue-items{max-height:180px;overflow-y:auto;margin:0;padding:0;list-style:none}.queue-item{display:flex;align-items:center;gap:8px;border-top:1px solid var(--nha-border);padding:6px 8px 6px 10px}.queue-item.failed{border-left:2px solid var(--nha-danger)}.queue-message{flex:1;min-width:0}.queue-message p{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere;margin:0}.queue-state{color:var(--nha-text-muted);font-size:11px}.queue-buttons{display:flex;flex-shrink:0;align-items:center;gap:2px}.queue-buttons .icon-button{width:28px;height:30px}.queue-resume,.queue-steer{border:0;border-radius:6px;padding:6px;color:var(--nha-highlight);background:transparent;font:inherit;cursor:pointer}.queue-resume:hover,.queue-steer:hover{background:var(--nha-surface)}.queue-resume:focus-visible,.queue-steer:focus-visible{outline:2px solid var(--nha-focus)}.queue-resume:disabled,.queue-steer:disabled{cursor:not-allowed;opacity:.5}.queue-edit{width:100%}.queue-edit textarea{box-sizing:border-box;width:100%;border:1px solid var(--nha-border);border-radius:6px;padding:8px;color:var(--nha-text);background:var(--nha-surface);font:inherit;resize:vertical}.queue-edit .queue-buttons{justify-content:flex-end}.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"] }]
|
|
5324
6916
|
}], ctorParameters: () => [], propDecorators: { drawer: [{ type: i0.ViewChild, args: ['drawer', { isSignal: true }] }] } });
|
|
5325
6917
|
|
|
5326
|
-
/**
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
6918
|
+
/**
|
|
6919
|
+
* A compact count of the agent work on one subject, for example on each row of a project list.
|
|
6920
|
+
* It shows nothing while the subject has no work. Otherwise it shows the number of proposals the
|
|
6921
|
+
* viewer may decide in the accent color, or, when they may decide none, a neutral count of the
|
|
6922
|
+
* open proposals, and a small working indicator while an agent works on the subject. Its tooltip
|
|
6923
|
+
* and accessible name say both counts. Badges on one page share one summary request per tick and
|
|
6924
|
+
* refresh on live agent-work updates of their subject; see `NhAssistantSubjectSummaryService`.
|
|
6925
|
+
*/
|
|
6926
|
+
class NhAssistantSubjectBadgeComponent {
|
|
6927
|
+
constructor() {
|
|
6928
|
+
this.summaries = inject(NhAssistantSubjectSummaryService);
|
|
6929
|
+
this.summary = signal(null, ...(ngDevMode ? [{ debugName: "summary" }] : []));
|
|
6930
|
+
/** The application's kind of subject, for example `project`. */
|
|
6931
|
+
this.subjectType = input.required(...(ngDevMode ? [{ debugName: "subjectType" }] : []));
|
|
6932
|
+
this.subjectId = input.required(...(ngDevMode ? [{ debugName: "subjectId" }] : []));
|
|
6933
|
+
/** The summary while the subject has work to show; `null` otherwise. */
|
|
6934
|
+
this.work = computed(() => {
|
|
6935
|
+
const summary = this.summary();
|
|
6936
|
+
return summary && (summary.openProposals > 0 || summary.runActive) ? summary : null;
|
|
6937
|
+
}, ...(ngDevMode ? [{ debugName: "work" }] : []));
|
|
6938
|
+
this.count = computed(() => {
|
|
6939
|
+
const summary = this.work();
|
|
6940
|
+
if (!summary) {
|
|
6941
|
+
return 0;
|
|
6942
|
+
}
|
|
6943
|
+
return summary.proposalsToDecide > 0 ? summary.proposalsToDecide : summary.openProposals;
|
|
6944
|
+
}, ...(ngDevMode ? [{ debugName: "count" }] : []));
|
|
6945
|
+
this.countLabel = computed(() => this.count() > 99 ? '99+' : String(this.count()), ...(ngDevMode ? [{ debugName: "countLabel" }] : []));
|
|
6946
|
+
this.proposalsKey = computed(() => {
|
|
6947
|
+
const summary = this.work();
|
|
6948
|
+
if (!summary || summary.openProposals === 0) {
|
|
6949
|
+
return null;
|
|
6950
|
+
}
|
|
6951
|
+
return summary.proposalsToDecide > 0 ? 'nh-assistant.subject-badge.to-decide' : 'nh-assistant.subject-badge.open';
|
|
6952
|
+
}, ...(ngDevMode ? [{ debugName: "proposalsKey" }] : []));
|
|
6953
|
+
effect(onCleanup => {
|
|
6954
|
+
const subjectType = this.subjectType();
|
|
6955
|
+
const subjectId = this.subjectId();
|
|
6956
|
+
const subscription = untracked(() => this.summaries.summary(subjectType, subjectId).subscribe(summary => this.summary.set(summary)));
|
|
6957
|
+
onCleanup(() => subscription.unsubscribe());
|
|
6958
|
+
});
|
|
5330
6959
|
}
|
|
5331
|
-
|
|
5332
|
-
}
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
6960
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantSubjectBadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
6961
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.33", type: NhAssistantSubjectBadgeComponent, isStandalone: true, selector: "nh-assistant-subject-badge", inputs: { subjectType: { classPropertyName: "subjectType", publicName: "subjectType", isSignal: true, isRequired: true, transformFunction: null }, subjectId: { classPropertyName: "subjectId", publicName: "subjectId", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
|
|
6962
|
+
@if (work(); as summary) {
|
|
6963
|
+
@let proposals = proposalsKey() ? (proposalsKey()! | translate: { toDecide: summary.proposalsToDecide, open: summary.openProposals }) : '';
|
|
6964
|
+
@let working = summary.runActive ? ('nh-assistant.subject-badge.working' | translate) : '';
|
|
6965
|
+
@let label = proposals && working ? proposals + ' ' + working : proposals || working;
|
|
6966
|
+
<span
|
|
6967
|
+
class="subject-badge"
|
|
6968
|
+
[attr.data-state]="summary.proposalsToDecide > 0 ? 'decide' : summary.openProposals > 0 ? 'open' : 'working'"
|
|
6969
|
+
[attr.title]="label">
|
|
6970
|
+
<nh-assistant-icon name="assistant" />
|
|
6971
|
+
@if (count() > 0) {
|
|
6972
|
+
<span class="count" aria-hidden="true">{{ countLabel() }}</span>
|
|
6973
|
+
}
|
|
6974
|
+
@if (summary.runActive) {
|
|
6975
|
+
<span class="working" aria-hidden="true"></span>
|
|
6976
|
+
}
|
|
6977
|
+
<span class="visually-hidden">{{ label }}</span>
|
|
6978
|
+
</span>
|
|
6979
|
+
}
|
|
6980
|
+
`, 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;vertical-align:middle}.subject-badge{position:relative;display:inline-flex;align-items:center;gap:4px;min-height:22px;border:1px solid var(--nha-border);border-radius:999px;padding:1px 8px 1px 6px;color:var(--nha-text-muted);background:var(--nha-surface-strong);font-family:var(--nha-font);font-size:12px;font-weight:700;line-height:1;white-space:nowrap}.subject-badge nh-assistant-icon{font-size:13px}.subject-badge[data-state=decide]{border-color:var(--nha-accent);color:var(--nha-accent-contrast);background:var(--nha-accent)}.subject-badge[data-state=working]{padding-right:6px}.count{font-variant-numeric:tabular-nums}.working{width:8px;height:8px;border:2px solid color-mix(in srgb,var(--nha-highlight) 30%,transparent);border-top-color:var(--nha-highlight);border-radius:50%;animation:nh-assistant-subject-spin .9s linear infinite}.subject-badge[data-state=decide] .working{border-color:color-mix(in srgb,var(--nha-accent-contrast) 35%,transparent);border-top-color:var(--nha-accent-contrast)}@media(prefers-reduced-motion:reduce){.working{border-color:var(--nha-highlight);animation:none}.subject-badge[data-state=decide] .working{border-color:var(--nha-accent-contrast)}}@keyframes nh-assistant-subject-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 }); }
|
|
5336
6981
|
}
|
|
5337
|
-
|
|
5338
|
-
|
|
6982
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantSubjectBadgeComponent, decorators: [{
|
|
6983
|
+
type: Component,
|
|
6984
|
+
args: [{ selector: 'nh-assistant-subject-badge', standalone: true, imports: [TranslatePipe, NhAssistantIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
6985
|
+
@if (work(); as summary) {
|
|
6986
|
+
@let proposals = proposalsKey() ? (proposalsKey()! | translate: { toDecide: summary.proposalsToDecide, open: summary.openProposals }) : '';
|
|
6987
|
+
@let working = summary.runActive ? ('nh-assistant.subject-badge.working' | translate) : '';
|
|
6988
|
+
@let label = proposals && working ? proposals + ' ' + working : proposals || working;
|
|
6989
|
+
<span
|
|
6990
|
+
class="subject-badge"
|
|
6991
|
+
[attr.data-state]="summary.proposalsToDecide > 0 ? 'decide' : summary.openProposals > 0 ? 'open' : 'working'"
|
|
6992
|
+
[attr.title]="label">
|
|
6993
|
+
<nh-assistant-icon name="assistant" />
|
|
6994
|
+
@if (count() > 0) {
|
|
6995
|
+
<span class="count" aria-hidden="true">{{ countLabel() }}</span>
|
|
6996
|
+
}
|
|
6997
|
+
@if (summary.runActive) {
|
|
6998
|
+
<span class="working" aria-hidden="true"></span>
|
|
6999
|
+
}
|
|
7000
|
+
<span class="visually-hidden">{{ label }}</span>
|
|
7001
|
+
</span>
|
|
7002
|
+
}
|
|
7003
|
+
`, 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;vertical-align:middle}.subject-badge{position:relative;display:inline-flex;align-items:center;gap:4px;min-height:22px;border:1px solid var(--nha-border);border-radius:999px;padding:1px 8px 1px 6px;color:var(--nha-text-muted);background:var(--nha-surface-strong);font-family:var(--nha-font);font-size:12px;font-weight:700;line-height:1;white-space:nowrap}.subject-badge nh-assistant-icon{font-size:13px}.subject-badge[data-state=decide]{border-color:var(--nha-accent);color:var(--nha-accent-contrast);background:var(--nha-accent)}.subject-badge[data-state=working]{padding-right:6px}.count{font-variant-numeric:tabular-nums}.working{width:8px;height:8px;border:2px solid color-mix(in srgb,var(--nha-highlight) 30%,transparent);border-top-color:var(--nha-highlight);border-radius:50%;animation:nh-assistant-subject-spin .9s linear infinite}.subject-badge[data-state=decide] .working{border-color:color-mix(in srgb,var(--nha-accent-contrast) 35%,transparent);border-top-color:var(--nha-accent-contrast)}@media(prefers-reduced-motion:reduce){.working{border-color:var(--nha-highlight);animation:none}.subject-badge[data-state=decide] .working{border-color:var(--nha-accent-contrast)}}@keyframes nh-assistant-subject-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"] }]
|
|
7004
|
+
}], ctorParameters: () => [], propDecorators: { subjectType: [{ type: i0.Input, args: [{ isSignal: true, alias: "subjectType", required: true }] }], subjectId: [{ type: i0.Input, args: [{ isSignal: true, alias: "subjectId", required: true }] }] } });
|
|
5339
7005
|
|
|
5340
7006
|
/*
|
|
5341
7007
|
* Public API surface of @newheap/platform-ai-chat.
|
|
@@ -5345,5 +7011,5 @@ const NH_ASSISTANT_DASH_CASE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
|
5345
7011
|
* Generated bundle index. Do not edit.
|
|
5346
7012
|
*/
|
|
5347
7013
|
|
|
5348
|
-
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 };
|
|
7014
|
+
export { NH_ASSISTANT_ACCESS_POLICY, NH_ASSISTANT_CONFIG, NH_ASSISTANT_CONVERSATION_FRAGMENT, NH_ASSISTANT_FEEDBACK_CATEGORIES, NH_ASSISTANT_FEEDBACK_GROUPS, NH_ASSISTANT_FETCH, NH_ASSISTANT_ICONS, NH_ASSISTANT_JOIN_FRAGMENT, NH_ASSISTANT_MAX_BULK_FEEDBACK, NH_ASSISTANT_MAX_CUSTOM_INSTRUCTIONS, NH_ASSISTANT_MAX_FEEDBACK_TEXT, NH_ASSISTANT_MAX_SUBJECT_SUMMARY, 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, NhAssistantAgentRunApiService, NhAssistantAllowAllAccessPolicy, NhAssistantApiError, NhAssistantApiService, NhAssistantApprovalCardComponent, NhAssistantClientErrorCodes, NhAssistantComposerComponent, NhAssistantConversationListComponent, NhAssistantLauncherComponent, NhAssistantLiveService, NhAssistantPanelComponent, NhAssistantPanelService, NhAssistantPreferencesComponent, NhAssistantProposalFeedbackComponent, NhAssistantPushService, NhAssistantShareComponent, NhAssistantSseParser, NhAssistantStore, NhAssistantSubjectBadgeComponent, NhAssistantSubjectSummaryService, 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, nhAssistantReloadOnAgentWork as ɵnhAssistantReloadOnAgentWork, toNhAssistantError as ɵtoNhAssistantError };
|
|
5349
7015
|
//# sourceMappingURL=newheap-platform-ai-chat.mjs.map
|