@newheap/platform-ai-chat 0.8.1 → 0.10.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 +85 -4
- package/agent-runs/index.d.ts +384 -0
- package/fesm2022/newheap-platform-ai-chat-admin.mjs +251 -12
- package/fesm2022/newheap-platform-ai-chat-admin.mjs.map +1 -1
- package/fesm2022/newheap-platform-ai-chat-agent-runs.mjs +1029 -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 +1837 -80
- package/fesm2022/newheap-platform-ai-chat.mjs.map +1 -1
- package/i18n/en.json +330 -8
- package/i18n/nl.json +330 -8
- package/index.d.ts +1013 -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,34 @@ const NhAssistantClientErrorCodes = {
|
|
|
48
48
|
conversationRemoved: 'assistant-conversation-removed'
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
/** The most proposals one `POST approvals/lookup` reads. */
|
|
52
|
+
const NH_ASSISTANT_MAX_PROPOSAL_LOOKUP = 100;
|
|
53
|
+
/** Maximum number of subjects one `POST subjects/{type}/summary` request accepts. */
|
|
54
|
+
const NH_ASSISTANT_MAX_SUBJECT_SUMMARY = 200;
|
|
55
|
+
/** The feedback categories in the order people choose them. */
|
|
56
|
+
const NH_ASSISTANT_FEEDBACK_CATEGORIES = [
|
|
57
|
+
'incorrect',
|
|
58
|
+
'wrong-value',
|
|
59
|
+
'not-needed',
|
|
60
|
+
'not-now',
|
|
61
|
+
'already-done',
|
|
62
|
+
'not-allowed',
|
|
63
|
+
'other'
|
|
64
|
+
];
|
|
65
|
+
/** The group of each feedback category, as the server reports it. */
|
|
66
|
+
const NH_ASSISTANT_FEEDBACK_GROUPS = {
|
|
67
|
+
incorrect: 'wrong',
|
|
68
|
+
'wrong-value': 'wrong',
|
|
69
|
+
'not-needed': 'wrong',
|
|
70
|
+
'not-now': 'right-but-not-now',
|
|
71
|
+
'already-done': 'right-but-not-now',
|
|
72
|
+
'not-allowed': 'not-allowed',
|
|
73
|
+
other: 'other'
|
|
74
|
+
};
|
|
75
|
+
/** Maximum length of the text of a feedback. */
|
|
76
|
+
const NH_ASSISTANT_MAX_FEEDBACK_TEXT = 1_000;
|
|
77
|
+
/** Maximum number of proposals one `POST approvals/feedback` request accepts. */
|
|
78
|
+
const NH_ASSISTANT_MAX_BULK_FEEDBACK = 50;
|
|
57
79
|
|
|
58
80
|
const NH_ASSISTANT_CONFIG = new InjectionToken('NH_ASSISTANT_CONFIG');
|
|
59
81
|
const NH_ASSISTANT_ACCESS_POLICY = new InjectionToken('NH_ASSISTANT_ACCESS_POLICY');
|
|
@@ -83,6 +105,11 @@ var en = {
|
|
|
83
105
|
unread: "{{count}} unread conversations",
|
|
84
106
|
running: "{{count}} conversations working"
|
|
85
107
|
},
|
|
108
|
+
"subject-badge": {
|
|
109
|
+
"to-decide": "Proposals to decide: {{toDecide}} of {{open}} open.",
|
|
110
|
+
open: "Open proposals: {{open}}.",
|
|
111
|
+
working: "An agent is working on this."
|
|
112
|
+
},
|
|
86
113
|
panel: {
|
|
87
114
|
title: "Assistant",
|
|
88
115
|
"region-label": "Assistant panel",
|
|
@@ -138,7 +165,9 @@ var en = {
|
|
|
138
165
|
working: "The assistant is working",
|
|
139
166
|
loading: "Loading conversation",
|
|
140
167
|
elapsed: "{{seconds}}s",
|
|
141
|
-
participant: "Participant"
|
|
168
|
+
participant: "Participant",
|
|
169
|
+
trigger: "Event",
|
|
170
|
+
rule: "Automatic rule"
|
|
142
171
|
},
|
|
143
172
|
composer: {
|
|
144
173
|
label: "Message",
|
|
@@ -185,8 +214,16 @@ var en = {
|
|
|
185
214
|
succeeded: "Completed",
|
|
186
215
|
failed: "Failed",
|
|
187
216
|
"awaiting-approval": "Awaiting approval",
|
|
188
|
-
rejected: "Rejected"
|
|
189
|
-
|
|
217
|
+
rejected: "Rejected",
|
|
218
|
+
"not-executed": "Not run"
|
|
219
|
+
},
|
|
220
|
+
"grant-source": {
|
|
221
|
+
delegated: "On behalf of the initiator",
|
|
222
|
+
agent: "Agent rights",
|
|
223
|
+
"delegated-hint": "The agent used the rights of the person who started it.",
|
|
224
|
+
"agent-hint": "The agent used a right it was granted itself, beyond the rights of the people involved."
|
|
225
|
+
},
|
|
226
|
+
restricted: "Shielded: the agent obtained this with its own rights, which you do not have."
|
|
190
227
|
},
|
|
191
228
|
"tool-group": {
|
|
192
229
|
running: "Calling {{name}}…",
|
|
@@ -195,6 +232,7 @@ var en = {
|
|
|
195
232
|
"used-many": "Used {{count}} tools",
|
|
196
233
|
progress: "{{finished}} of {{total}} done",
|
|
197
234
|
failed: "{{count}} failed",
|
|
235
|
+
"not-executed": "{{count}} not run",
|
|
198
236
|
"show-calls": "Show tool calls",
|
|
199
237
|
"hide-calls": "Hide tool calls"
|
|
200
238
|
},
|
|
@@ -216,10 +254,65 @@ var en = {
|
|
|
216
254
|
pending: "Pending",
|
|
217
255
|
approved: "Approved",
|
|
218
256
|
rejected: "Rejected",
|
|
219
|
-
expired: "Expired"
|
|
257
|
+
expired: "Expired",
|
|
258
|
+
withdrawn: "Withdrawn",
|
|
259
|
+
stale: "Outdated",
|
|
260
|
+
executing: "Running",
|
|
261
|
+
executed: "Done",
|
|
262
|
+
"execution-failed": "Failed",
|
|
263
|
+
"execution-unknown": "Outcome unknown",
|
|
264
|
+
resolved: "Resolved",
|
|
265
|
+
shadow: "Shadow"
|
|
220
266
|
},
|
|
221
267
|
"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."
|
|
268
|
+
"waiting-for-participant": "Waiting for the participant who started this task to decide.",
|
|
269
|
+
"waiting-for-approver": "Waiting for an approver to decide.",
|
|
270
|
+
"waiting-for-other-approver": "You started this work, so another approver decides.",
|
|
271
|
+
"give-feedback": "What was wrong?",
|
|
272
|
+
"stale-kind": {
|
|
273
|
+
"already-done": "Already done",
|
|
274
|
+
changed: "Changed",
|
|
275
|
+
"not-applicable": "No longer needed"
|
|
276
|
+
},
|
|
277
|
+
rationale: "Why: {{rationale}}",
|
|
278
|
+
superseded: "Replaced by a newer proposal"
|
|
279
|
+
},
|
|
280
|
+
"proposal-feedback": {
|
|
281
|
+
legend: "What was wrong? (optional)",
|
|
282
|
+
bulk: "Applies to the {{count}} proposals you rejected.",
|
|
283
|
+
categories: {
|
|
284
|
+
incorrect: "Incorrect",
|
|
285
|
+
"wrong-value": "Wrong value",
|
|
286
|
+
"not-needed": "Not needed",
|
|
287
|
+
"not-now": "Not now",
|
|
288
|
+
"already-done": "Already done",
|
|
289
|
+
"not-allowed": "Not allowed",
|
|
290
|
+
other: "Other"
|
|
291
|
+
},
|
|
292
|
+
hints: {
|
|
293
|
+
incorrect: "The proposal is factually wrong.",
|
|
294
|
+
"wrong-value": "The right action, with the wrong values.",
|
|
295
|
+
"not-needed": "The change is unnecessary.",
|
|
296
|
+
"not-now": "Right, but not at this moment.",
|
|
297
|
+
"already-done": "Already done or in progress elsewhere.",
|
|
298
|
+
"not-allowed": "Against policy, or beyond what the agent should do.",
|
|
299
|
+
other: "Explain it in the text below."
|
|
300
|
+
},
|
|
301
|
+
groups: {
|
|
302
|
+
wrong: "The agent was wrong",
|
|
303
|
+
"right-but-not-now": "Right, but not now",
|
|
304
|
+
"not-allowed": "Not allowed",
|
|
305
|
+
other: "Other"
|
|
306
|
+
},
|
|
307
|
+
"text-label": "Anything to add? (optional)",
|
|
308
|
+
"text-hint": "The agent's administrators read this to improve it.",
|
|
309
|
+
characters: "{{count}}/{{max}}",
|
|
310
|
+
send: "Send",
|
|
311
|
+
sending: "Sending…",
|
|
312
|
+
skip: "Skip",
|
|
313
|
+
cancel: "Cancel",
|
|
314
|
+
thanks: "Thanks for your feedback",
|
|
315
|
+
partial: "Saved for {{stored}} of {{total}} proposals; the others changed in the meantime."
|
|
223
316
|
},
|
|
224
317
|
"result-codes": {
|
|
225
318
|
"api-bridge-validation": "The request was not valid.",
|
|
@@ -228,7 +321,15 @@ var en = {
|
|
|
228
321
|
"api-bridge-not-found": "The item was not found.",
|
|
229
322
|
"api-bridge-conflict": "The item was changed in the meantime.",
|
|
230
323
|
"api-bridge-upstream": "The service is not available.",
|
|
231
|
-
"api-bridge-timeout": "The request took too long."
|
|
324
|
+
"api-bridge-timeout": "The request took too long.",
|
|
325
|
+
"assistant-proposal-recorded": "Proposed; waits for a person's approval",
|
|
326
|
+
"assistant-proposal-rejected": "Not run: the proposal was rejected.",
|
|
327
|
+
"assistant-proposal-expired": "Not run: nobody decided before the proposal expired.",
|
|
328
|
+
"assistant-proposal-withdrawn": "Not run: the proposal was withdrawn.",
|
|
329
|
+
"assistant-proposal-stale": "Not run: the proposal no longer applied.",
|
|
330
|
+
"assistant-proposal-execution-failed": "The approved proposal could not be run.",
|
|
331
|
+
"assistant-proposal-already-open": "Not proposed again: the same proposal already waits.",
|
|
332
|
+
"assistant-proposal-recently-rejected": "Not proposed again: people rejected it recently."
|
|
232
333
|
},
|
|
233
334
|
errors: {
|
|
234
335
|
generic: "Something went wrong. Please try again.",
|
|
@@ -287,7 +388,43 @@ var en = {
|
|
|
287
388
|
"assistant-directory-unavailable": "Inviting colleagues directly is not available.",
|
|
288
389
|
"assistant-push-unavailable": "Desktop notifications are not available.",
|
|
289
390
|
"assistant-push-subscription-invalid": "This browser could not be registered for notifications.",
|
|
290
|
-
"assistant-conversation-removed": "This conversation is no longer available to you."
|
|
391
|
+
"assistant-conversation-removed": "This conversation is no longer available to you.",
|
|
392
|
+
"assistant-subject-not-found": "The subject no longer exists.",
|
|
393
|
+
"assistant-subject-type-unknown": "This kind of subject is not known to the assistant.",
|
|
394
|
+
"assistant-subject-access-denied": "Your role on this subject does not allow this.",
|
|
395
|
+
"assistant-no-accountable-owner": "Nobody answers for this agent on this subject.",
|
|
396
|
+
"assistant-agent-disabled": "This agent has been stopped.",
|
|
397
|
+
"assistant-agent-not-configured": "This agent cannot run on its own.",
|
|
398
|
+
"assistant-run-not-found": "The run was not found.",
|
|
399
|
+
"assistant-run-not-retryable": "This run cannot be retried.",
|
|
400
|
+
"assistant-approval-policy-denied": "You may not decide this proposal.",
|
|
401
|
+
"assistant-four-eyes-required": "Someone other than the person who started this must decide it.",
|
|
402
|
+
"assistant-execution-not-uncertain": "This execution is not waiting to be resolved.",
|
|
403
|
+
"assistant-proposal-stale": "This proposal no longer applies, for example because the work was already done. Nothing was changed.",
|
|
404
|
+
"assistant-proposal-check-failed": "Whether this proposal still applies could not be checked, so nothing was changed. Try again later.",
|
|
405
|
+
"assistant-proposal-already-open": "The same proposal already waits for a decision.",
|
|
406
|
+
"assistant-proposal-recently-rejected": "People rejected the same proposal recently.",
|
|
407
|
+
"assistant-proposal-superseded": "A newer proposal replaced this one.",
|
|
408
|
+
"assistant-proposal-refused": "This action cannot be proposed by an automatic rule.",
|
|
409
|
+
"assistant-trigger-not-found": "The trigger was not found.",
|
|
410
|
+
"assistant-webhook-signature-invalid": "The webhook call has no valid signature.",
|
|
411
|
+
"assistant-webhook-payload-invalid": "The webhook call could not be read.",
|
|
412
|
+
"assistant-memory-not-configured": "This agent keeps no working memory.",
|
|
413
|
+
"assistant-memory-invalid": "The working memory change does not fit its schema.",
|
|
414
|
+
"assistant-memory-conflict": "The working memory changed at the same time.",
|
|
415
|
+
"assistant-artifact-invalid": "The file is not valid or too large.",
|
|
416
|
+
"assistant-artifact-outside-turn": "Files can only be saved while the agent works.",
|
|
417
|
+
"assistant-artifact-not-found": "The file was not found.",
|
|
418
|
+
"assistant-feedback-invalid": "Choose one of the listed reasons and keep the explanation under 1,000 characters.",
|
|
419
|
+
"assistant-feedback-not-accepted": "Feedback is possible only after a proposal was rejected or closed.",
|
|
420
|
+
"assistant-consult-subject-unavailable": "The subject does not exist or you may not consult the agent about it.",
|
|
421
|
+
"assistant-consult-agent-unavailable": "The agent cannot be consulted right now.",
|
|
422
|
+
"assistant-consult-depth-exceeded": "This consultation would ask agents in a loop or nest too deeply.",
|
|
423
|
+
"assistant-consult-budget-exhausted": "The agent's daily budget is spent.",
|
|
424
|
+
"assistant-consult-limit-reached": "You reached the daily number of consultations.",
|
|
425
|
+
"assistant-consult-timeout": "The agent did not answer in time.",
|
|
426
|
+
"assistant-consult-failed": "The agent could not answer.",
|
|
427
|
+
"assistant-consult-read-only": "A consultation cannot change anything."
|
|
291
428
|
},
|
|
292
429
|
notices: {
|
|
293
430
|
"no-answer": "The assistant finished without an answer. Try again or rephrase your question.",
|
|
@@ -401,7 +538,8 @@ var en = {
|
|
|
401
538
|
propose: "Propose",
|
|
402
539
|
simulate: "Simulate",
|
|
403
540
|
execute: "Execute"
|
|
404
|
-
}
|
|
541
|
+
},
|
|
542
|
+
feedback: "Feedback"
|
|
405
543
|
},
|
|
406
544
|
"agent-editor": {
|
|
407
545
|
"new-title": "New agent",
|
|
@@ -515,6 +653,59 @@ var en = {
|
|
|
515
653
|
"missing-warning": "The server no longer lists this tool.",
|
|
516
654
|
save: "Save tool",
|
|
517
655
|
back: "Back to the servers"
|
|
656
|
+
},
|
|
657
|
+
feedback: {
|
|
658
|
+
title: "Feedback on proposals",
|
|
659
|
+
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.",
|
|
660
|
+
agent: "{{name}} ({{id}})",
|
|
661
|
+
back: "Back to agents",
|
|
662
|
+
refresh: "Refresh",
|
|
663
|
+
"export": "Export (JSON Lines)",
|
|
664
|
+
exporting: "Exporting…",
|
|
665
|
+
"export-hint": "The export holds subject data and the proposed tool input, and is recorded in the audit log.",
|
|
666
|
+
period: "Period",
|
|
667
|
+
periods: {
|
|
668
|
+
"last-7-days": "Last 7 days",
|
|
669
|
+
"last-30-days": "Last 30 days",
|
|
670
|
+
"last-90-days": "Last 90 days"
|
|
671
|
+
},
|
|
672
|
+
tool: "Tool",
|
|
673
|
+
"all-tools": "All tools",
|
|
674
|
+
category: "Category",
|
|
675
|
+
"all-categories": "All categories",
|
|
676
|
+
"category-hint": "The category narrows the feedback and the export.",
|
|
677
|
+
"summary-title": "Per tool and agent version",
|
|
678
|
+
"summary-empty": "The agent made no proposals in this period.",
|
|
679
|
+
columns: {
|
|
680
|
+
tool: "Tool",
|
|
681
|
+
version: "Agent version",
|
|
682
|
+
approved: "Approved",
|
|
683
|
+
rejected: "Rejected",
|
|
684
|
+
expired: "Expired",
|
|
685
|
+
"rejection-rate": "Rejection rate",
|
|
686
|
+
"with-feedback": "Rejected with feedback",
|
|
687
|
+
invalidated: "No longer needed",
|
|
688
|
+
people: "People",
|
|
689
|
+
categories: "Top categories"
|
|
690
|
+
},
|
|
691
|
+
version: "v{{version}}",
|
|
692
|
+
consultations: "Consultations",
|
|
693
|
+
"no-decisions": "Not decided yet",
|
|
694
|
+
"no-categories": "None",
|
|
695
|
+
"invalidated-hint": "Open proposals that closed before anyone decided, because the work was already done or no longer applied. They do not count as rejected.",
|
|
696
|
+
"list-title": "Feedback",
|
|
697
|
+
"list-count": "{{count}} in total",
|
|
698
|
+
"list-empty": "Nobody gave feedback in this period.",
|
|
699
|
+
"list-empty-filtered": "No feedback matches these filters.",
|
|
700
|
+
"no-text": "No explanation given.",
|
|
701
|
+
proposal: "Proposal:",
|
|
702
|
+
author: "By {{actor}}",
|
|
703
|
+
bulk: "Bulk",
|
|
704
|
+
"bulk-hint": "Given once for several proposals rejected together; weigh it lower.",
|
|
705
|
+
previous: "Previous",
|
|
706
|
+
next: "Next",
|
|
707
|
+
page: "Page {{page}} of {{pages}}",
|
|
708
|
+
pages: "Pages"
|
|
518
709
|
}
|
|
519
710
|
},
|
|
520
711
|
"page-context": {
|
|
@@ -561,6 +752,159 @@ var en = {
|
|
|
561
752
|
remove: "Remove {{name}}",
|
|
562
753
|
leave: "Leave conversation",
|
|
563
754
|
"no-conversation": "Start or open a conversation to share it."
|
|
755
|
+
},
|
|
756
|
+
"agent-runs": {
|
|
757
|
+
loading: "Loading…",
|
|
758
|
+
"load-failed": "This could not be loaded.",
|
|
759
|
+
retry: "Try again",
|
|
760
|
+
refresh: "Refresh",
|
|
761
|
+
role: {
|
|
762
|
+
none: "No role on this subject",
|
|
763
|
+
reader: "Reader",
|
|
764
|
+
participant: "Participant",
|
|
765
|
+
approver: "Approver"
|
|
766
|
+
},
|
|
767
|
+
thread: {
|
|
768
|
+
title: "Agent",
|
|
769
|
+
"waiting-inputs": "{{count}} messages wait for the next run",
|
|
770
|
+
"open-conversation": "Open conversation",
|
|
771
|
+
"start-message": "Instruction for the agent (optional)",
|
|
772
|
+
start: "Start the agent",
|
|
773
|
+
starting: "Starting…",
|
|
774
|
+
"no-proposals": "The agent has proposed nothing here yet.",
|
|
775
|
+
"no-thread": "The agent has not worked on this yet.",
|
|
776
|
+
tabs: {
|
|
777
|
+
proposals: "Proposals",
|
|
778
|
+
runs: "Runs",
|
|
779
|
+
consultations: "Consultations",
|
|
780
|
+
memory: "Working memory",
|
|
781
|
+
artifacts: "Files"
|
|
782
|
+
},
|
|
783
|
+
outcome: {
|
|
784
|
+
started: "The agent starts shortly.",
|
|
785
|
+
queued: "The agent starts after the current run.",
|
|
786
|
+
coalesced: "Your request joins the run that is about to start.",
|
|
787
|
+
superseded: "The current run stops and the agent starts again.",
|
|
788
|
+
ignored: "The agent is busy; nothing was started.",
|
|
789
|
+
deduplicated: "This was already started.",
|
|
790
|
+
rejected: "The agent could not be started."
|
|
791
|
+
}
|
|
792
|
+
},
|
|
793
|
+
subject: {
|
|
794
|
+
title: "Agent suggestions",
|
|
795
|
+
description: "Proposals that agents made here and that wait for a decision, the first to expire on top.",
|
|
796
|
+
empty: "No open suggestions from agents.",
|
|
797
|
+
unavailable: "You cannot see agent work on this item.",
|
|
798
|
+
agents: "Agents",
|
|
799
|
+
idle: "Not working now",
|
|
800
|
+
opening: "Opening…",
|
|
801
|
+
"open-conversation-label": "Open conversation with {{agent}}",
|
|
802
|
+
"start-label": "Start the agent: {{agent}}",
|
|
803
|
+
"assistant-unavailable": "The assistant is not available."
|
|
804
|
+
},
|
|
805
|
+
inbox: {
|
|
806
|
+
title: "Proposals to decide",
|
|
807
|
+
description: "Proposals of agents that wait for your decision, the ones that expire first on top.",
|
|
808
|
+
empty: "No proposals wait for you.",
|
|
809
|
+
select: "Select this proposal",
|
|
810
|
+
"select-all": "Select all",
|
|
811
|
+
"approve-selected": "Approve selected",
|
|
812
|
+
"reject-selected": "Reject selected",
|
|
813
|
+
previous: "Previous",
|
|
814
|
+
next: "Next",
|
|
815
|
+
page: "Page {{page}} of {{pages}}",
|
|
816
|
+
pages: "Pages"
|
|
817
|
+
},
|
|
818
|
+
proposal: {
|
|
819
|
+
approve: "Approve",
|
|
820
|
+
reject: "Reject",
|
|
821
|
+
expires: "Expires {{date}}",
|
|
822
|
+
"decided-by": "Decided by {{actor}}",
|
|
823
|
+
"arguments": "Input",
|
|
824
|
+
"re-execute": "Run again",
|
|
825
|
+
"mark-resolved": "Mark as resolved",
|
|
826
|
+
"unknown-hint": "The execution stopped without a known outcome. Check whether it happened before you run it again.",
|
|
827
|
+
mode: {
|
|
828
|
+
inline: "The run waits",
|
|
829
|
+
deferred: "Decide later"
|
|
830
|
+
},
|
|
831
|
+
status: {
|
|
832
|
+
pending: "Waiting",
|
|
833
|
+
approved: "Approved",
|
|
834
|
+
rejected: "Rejected",
|
|
835
|
+
expired: "Expired",
|
|
836
|
+
withdrawn: "Withdrawn",
|
|
837
|
+
stale: "Outdated",
|
|
838
|
+
executing: "Running",
|
|
839
|
+
executed: "Done",
|
|
840
|
+
"execution-failed": "Failed",
|
|
841
|
+
"execution-unknown": "Outcome unknown",
|
|
842
|
+
resolved: "Resolved",
|
|
843
|
+
shadow: "Shadow"
|
|
844
|
+
},
|
|
845
|
+
"from-consultation": "From a consultation",
|
|
846
|
+
"from-consultation-hint": "Another assistant consulted the agent for this person; it runs with that person's rights only.",
|
|
847
|
+
"give-feedback": "Give feedback",
|
|
848
|
+
"change-feedback": "Change feedback",
|
|
849
|
+
"your-feedback": "Your feedback: {{category}}",
|
|
850
|
+
"rejected-one": "You rejected: {{summary}}",
|
|
851
|
+
closed: "Closed in the meantime",
|
|
852
|
+
dismiss: "Dismiss",
|
|
853
|
+
"dismiss-label": "Dismiss the closed proposal: {{summary}}",
|
|
854
|
+
"show-successor": "Show the newer proposal",
|
|
855
|
+
"stale-by": "Overtaken by {{actor}}"
|
|
856
|
+
},
|
|
857
|
+
run: {
|
|
858
|
+
empty: "No runs yet.",
|
|
859
|
+
status: {
|
|
860
|
+
queued: "Queued",
|
|
861
|
+
running: "Running",
|
|
862
|
+
"waiting-for-approval": "Waiting for approval",
|
|
863
|
+
completed: "Completed",
|
|
864
|
+
failed: "Failed",
|
|
865
|
+
cancelled: "Cancelled"
|
|
866
|
+
},
|
|
867
|
+
shadow: "Shadow",
|
|
868
|
+
kind: {
|
|
869
|
+
rule: "Automatic rule",
|
|
870
|
+
"rule-then-model": "Rule, then the model"
|
|
871
|
+
},
|
|
872
|
+
trigger: "Trigger: {{trigger}}",
|
|
873
|
+
"tool-calls": "{{count}} tool calls",
|
|
874
|
+
tokens: "{{count}} tokens",
|
|
875
|
+
proposals: "{{count}} proposals",
|
|
876
|
+
attempt: "Attempt {{attempt}}",
|
|
877
|
+
cancel: "Cancel",
|
|
878
|
+
resume: "Resume",
|
|
879
|
+
restart: "Start again"
|
|
880
|
+
},
|
|
881
|
+
memory: {
|
|
882
|
+
"not-configured": "This agent keeps no working memory.",
|
|
883
|
+
version: "Schema version {{version}}",
|
|
884
|
+
updated: "Updated {{date}}",
|
|
885
|
+
empty: "The agent has not noted anything yet."
|
|
886
|
+
},
|
|
887
|
+
artifacts: {
|
|
888
|
+
"download-failed": "The file could not be downloaded.",
|
|
889
|
+
empty: "No files yet.",
|
|
890
|
+
version: "v{{version}}",
|
|
891
|
+
restricted: "Shielded (agent rights)",
|
|
892
|
+
size: "{{size}} bytes",
|
|
893
|
+
download: "Download"
|
|
894
|
+
},
|
|
895
|
+
consultation: {
|
|
896
|
+
empty: "No assistant has consulted this agent about this subject yet.",
|
|
897
|
+
"private": "Only who asked and how it ended is shown here; the question and the answer stay in the asking conversation.",
|
|
898
|
+
"asked-by": "Asked by {{name}}",
|
|
899
|
+
through: "through {{agent}}",
|
|
900
|
+
depth: "Nested, level {{depth}}",
|
|
901
|
+
more: "{{count}} older consultations are not shown.",
|
|
902
|
+
status: {
|
|
903
|
+
running: "Running",
|
|
904
|
+
completed: "Answered",
|
|
905
|
+
failed: "Not answered"
|
|
906
|
+
}
|
|
907
|
+
}
|
|
564
908
|
}
|
|
565
909
|
}
|
|
566
910
|
};
|
|
@@ -574,6 +918,11 @@ var nl = {
|
|
|
574
918
|
unread: "{{count}} ongelezen gesprekken",
|
|
575
919
|
running: "{{count}} gesprekken bezig"
|
|
576
920
|
},
|
|
921
|
+
"subject-badge": {
|
|
922
|
+
"to-decide": "Voorstellen om te beslissen: {{toDecide}} van {{open}} open.",
|
|
923
|
+
open: "Openstaande voorstellen: {{open}}.",
|
|
924
|
+
working: "Een agent werkt hieraan."
|
|
925
|
+
},
|
|
577
926
|
panel: {
|
|
578
927
|
title: "Assistent",
|
|
579
928
|
"region-label": "Assistentpaneel",
|
|
@@ -629,7 +978,9 @@ var nl = {
|
|
|
629
978
|
working: "De assistent is bezig",
|
|
630
979
|
loading: "Gesprek laden",
|
|
631
980
|
elapsed: "{{seconds}} s",
|
|
632
|
-
participant: "Deelnemer"
|
|
981
|
+
participant: "Deelnemer",
|
|
982
|
+
trigger: "Gebeurtenis",
|
|
983
|
+
rule: "Automatische regel"
|
|
633
984
|
},
|
|
634
985
|
composer: {
|
|
635
986
|
label: "Bericht",
|
|
@@ -676,8 +1027,16 @@ var nl = {
|
|
|
676
1027
|
succeeded: "Voltooid",
|
|
677
1028
|
failed: "Mislukt",
|
|
678
1029
|
"awaiting-approval": "Wacht op goedkeuring",
|
|
679
|
-
rejected: "Afgewezen"
|
|
680
|
-
|
|
1030
|
+
rejected: "Afgewezen",
|
|
1031
|
+
"not-executed": "Niet uitgevoerd"
|
|
1032
|
+
},
|
|
1033
|
+
"grant-source": {
|
|
1034
|
+
delegated: "Namens de starter",
|
|
1035
|
+
agent: "Agentrechten",
|
|
1036
|
+
"delegated-hint": "De agent gebruikte de rechten van de persoon die hem startte.",
|
|
1037
|
+
"agent-hint": "De agent gebruikte een recht dat hij zelf heeft gekregen, boven de rechten van de betrokkenen uit."
|
|
1038
|
+
},
|
|
1039
|
+
restricted: "Afgeschermd: de agent kreeg dit met eigen rechten, die jij niet hebt."
|
|
681
1040
|
},
|
|
682
1041
|
"tool-group": {
|
|
683
1042
|
running: "Roept {{name}} aan…",
|
|
@@ -686,6 +1045,7 @@ var nl = {
|
|
|
686
1045
|
"used-many": "{{count}} tools gebruikt",
|
|
687
1046
|
progress: "{{finished}} van {{total}} klaar",
|
|
688
1047
|
failed: "{{count}} mislukt",
|
|
1048
|
+
"not-executed": "{{count}} niet uitgevoerd",
|
|
689
1049
|
"show-calls": "Tool-aanroepen tonen",
|
|
690
1050
|
"hide-calls": "Tool-aanroepen verbergen"
|
|
691
1051
|
},
|
|
@@ -707,10 +1067,65 @@ var nl = {
|
|
|
707
1067
|
pending: "Open",
|
|
708
1068
|
approved: "Goedgekeurd",
|
|
709
1069
|
rejected: "Afgewezen",
|
|
710
|
-
expired: "Verlopen"
|
|
1070
|
+
expired: "Verlopen",
|
|
1071
|
+
withdrawn: "Ingetrokken",
|
|
1072
|
+
stale: "Verouderd",
|
|
1073
|
+
executing: "Wordt uitgevoerd",
|
|
1074
|
+
executed: "Uitgevoerd",
|
|
1075
|
+
"execution-failed": "Mislukt",
|
|
1076
|
+
"execution-unknown": "Uitkomst onbekend",
|
|
1077
|
+
resolved: "Afgehandeld",
|
|
1078
|
+
shadow: "Schaduw"
|
|
711
1079
|
},
|
|
712
1080
|
"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."
|
|
1081
|
+
"waiting-for-participant": "Wacht op de beslissing van de deelnemer die deze taak startte.",
|
|
1082
|
+
"waiting-for-approver": "Wacht tot een beslisser hierover beslist.",
|
|
1083
|
+
"waiting-for-other-approver": "Jij startte dit werk, dus een andere beslisser beslist hierover.",
|
|
1084
|
+
"give-feedback": "Wat klopte er niet?",
|
|
1085
|
+
"stale-kind": {
|
|
1086
|
+
"already-done": "Al gedaan",
|
|
1087
|
+
changed: "Gewijzigd",
|
|
1088
|
+
"not-applicable": "Niet meer nodig"
|
|
1089
|
+
},
|
|
1090
|
+
rationale: "Waarom: {{rationale}}",
|
|
1091
|
+
superseded: "Vervangen door een nieuwer voorstel"
|
|
1092
|
+
},
|
|
1093
|
+
"proposal-feedback": {
|
|
1094
|
+
legend: "Wat klopte er niet? (optioneel)",
|
|
1095
|
+
bulk: "Geldt voor de {{count}} voorstellen die je hebt afgewezen.",
|
|
1096
|
+
categories: {
|
|
1097
|
+
incorrect: "Onjuist",
|
|
1098
|
+
"wrong-value": "Verkeerde waarde",
|
|
1099
|
+
"not-needed": "Niet nodig",
|
|
1100
|
+
"not-now": "Niet nu",
|
|
1101
|
+
"already-done": "Al gedaan",
|
|
1102
|
+
"not-allowed": "Niet toegestaan",
|
|
1103
|
+
other: "Anders"
|
|
1104
|
+
},
|
|
1105
|
+
hints: {
|
|
1106
|
+
incorrect: "Het voorstel klopt inhoudelijk niet.",
|
|
1107
|
+
"wrong-value": "De juiste actie, met de verkeerde waarden.",
|
|
1108
|
+
"not-needed": "De wijziging is overbodig.",
|
|
1109
|
+
"not-now": "Juist, maar niet op dit moment.",
|
|
1110
|
+
"already-done": "Al gedaan of elders in behandeling.",
|
|
1111
|
+
"not-allowed": "In strijd met beleid, of meer dan de agent mag doen.",
|
|
1112
|
+
other: "Licht het toe in de tekst hieronder."
|
|
1113
|
+
},
|
|
1114
|
+
groups: {
|
|
1115
|
+
wrong: "De agent zat ernaast",
|
|
1116
|
+
"right-but-not-now": "Juist, maar niet nu",
|
|
1117
|
+
"not-allowed": "Niet toegestaan",
|
|
1118
|
+
other: "Anders"
|
|
1119
|
+
},
|
|
1120
|
+
"text-label": "Nog iets toe te voegen? (optioneel)",
|
|
1121
|
+
"text-hint": "De beheerders van de agent lezen dit om hem te verbeteren.",
|
|
1122
|
+
characters: "{{count}}/{{max}}",
|
|
1123
|
+
send: "Versturen",
|
|
1124
|
+
sending: "Versturen…",
|
|
1125
|
+
skip: "Overslaan",
|
|
1126
|
+
cancel: "Annuleren",
|
|
1127
|
+
thanks: "Bedankt voor je feedback",
|
|
1128
|
+
partial: "Opgeslagen voor {{stored}} van {{total}} voorstellen; de andere zijn intussen gewijzigd."
|
|
714
1129
|
},
|
|
715
1130
|
"result-codes": {
|
|
716
1131
|
"api-bridge-validation": "Het verzoek was niet geldig.",
|
|
@@ -719,7 +1134,15 @@ var nl = {
|
|
|
719
1134
|
"api-bridge-not-found": "Het item is niet gevonden.",
|
|
720
1135
|
"api-bridge-conflict": "Het item is intussen gewijzigd.",
|
|
721
1136
|
"api-bridge-upstream": "De dienst is niet beschikbaar.",
|
|
722
|
-
"api-bridge-timeout": "Het verzoek duurde te lang."
|
|
1137
|
+
"api-bridge-timeout": "Het verzoek duurde te lang.",
|
|
1138
|
+
"assistant-proposal-recorded": "Voorgesteld; wacht op goedkeuring van een persoon",
|
|
1139
|
+
"assistant-proposal-rejected": "Niet uitgevoerd: het voorstel is afgewezen.",
|
|
1140
|
+
"assistant-proposal-expired": "Niet uitgevoerd: niemand besliste voordat het voorstel verliep.",
|
|
1141
|
+
"assistant-proposal-withdrawn": "Niet uitgevoerd: het voorstel is ingetrokken.",
|
|
1142
|
+
"assistant-proposal-stale": "Niet uitgevoerd: het voorstel was niet meer van toepassing.",
|
|
1143
|
+
"assistant-proposal-execution-failed": "Het goedgekeurde voorstel kon niet worden uitgevoerd.",
|
|
1144
|
+
"assistant-proposal-already-open": "Niet opnieuw voorgesteld: hetzelfde voorstel wacht al.",
|
|
1145
|
+
"assistant-proposal-recently-rejected": "Niet opnieuw voorgesteld: het is kort geleden afgewezen."
|
|
723
1146
|
},
|
|
724
1147
|
errors: {
|
|
725
1148
|
generic: "Er ging iets mis. Probeer het opnieuw.",
|
|
@@ -778,7 +1201,43 @@ var nl = {
|
|
|
778
1201
|
"assistant-directory-unavailable": "Collega’s direct uitnodigen is niet beschikbaar.",
|
|
779
1202
|
"assistant-push-unavailable": "Bureaubladmeldingen zijn niet beschikbaar.",
|
|
780
1203
|
"assistant-push-subscription-invalid": "Deze browser kon niet worden aangemeld voor meldingen.",
|
|
781
|
-
"assistant-conversation-removed": "Dit gesprek is niet meer voor je beschikbaar."
|
|
1204
|
+
"assistant-conversation-removed": "Dit gesprek is niet meer voor je beschikbaar.",
|
|
1205
|
+
"assistant-subject-not-found": "Het onderwerp bestaat niet meer.",
|
|
1206
|
+
"assistant-subject-type-unknown": "Dit soort onderwerp kent de assistent niet.",
|
|
1207
|
+
"assistant-subject-access-denied": "Je rol op dit onderwerp staat dit niet toe.",
|
|
1208
|
+
"assistant-no-accountable-owner": "Niemand staat voor deze agent op dit onderwerp in.",
|
|
1209
|
+
"assistant-agent-disabled": "Deze agent is stopgezet.",
|
|
1210
|
+
"assistant-agent-not-configured": "Deze agent kan niet zelfstandig werken.",
|
|
1211
|
+
"assistant-run-not-found": "De run is niet gevonden.",
|
|
1212
|
+
"assistant-run-not-retryable": "Deze run kan niet opnieuw worden gestart.",
|
|
1213
|
+
"assistant-approval-policy-denied": "Je mag over dit voorstel niet beslissen.",
|
|
1214
|
+
"assistant-four-eyes-required": "Iemand anders dan de starter moet hierover beslissen.",
|
|
1215
|
+
"assistant-execution-not-uncertain": "Deze uitvoering wacht niet op afhandeling.",
|
|
1216
|
+
"assistant-proposal-stale": "Dit voorstel is niet meer van toepassing, bijvoorbeeld omdat het werk al gedaan is. Er is niets gewijzigd.",
|
|
1217
|
+
"assistant-proposal-check-failed": "Kon niet controleren of dit voorstel nog van toepassing is, dus er is niets gewijzigd. Probeer het later opnieuw.",
|
|
1218
|
+
"assistant-proposal-already-open": "Hetzelfde voorstel wacht al op een besluit.",
|
|
1219
|
+
"assistant-proposal-recently-rejected": "Hetzelfde voorstel is kort geleden afgewezen.",
|
|
1220
|
+
"assistant-proposal-superseded": "Een nieuwer voorstel heeft dit voorstel vervangen.",
|
|
1221
|
+
"assistant-proposal-refused": "Deze actie kan niet door een automatische regel worden voorgesteld.",
|
|
1222
|
+
"assistant-trigger-not-found": "De trigger is niet gevonden.",
|
|
1223
|
+
"assistant-webhook-signature-invalid": "De webhook-aanroep heeft geen geldige handtekening.",
|
|
1224
|
+
"assistant-webhook-payload-invalid": "De webhook-aanroep kon niet worden gelezen.",
|
|
1225
|
+
"assistant-memory-not-configured": "Deze agent houdt geen werkgeheugen bij.",
|
|
1226
|
+
"assistant-memory-invalid": "De wijziging van het werkgeheugen past niet bij het schema.",
|
|
1227
|
+
"assistant-memory-conflict": "Het werkgeheugen is tegelijk gewijzigd.",
|
|
1228
|
+
"assistant-artifact-invalid": "Het bestand is ongeldig of te groot.",
|
|
1229
|
+
"assistant-artifact-outside-turn": "Bestanden kunnen alleen worden opgeslagen terwijl de agent werkt.",
|
|
1230
|
+
"assistant-artifact-not-found": "Het bestand is niet gevonden.",
|
|
1231
|
+
"assistant-feedback-invalid": "Kies een van de genoemde redenen en houd de toelichting onder de 1.000 tekens.",
|
|
1232
|
+
"assistant-feedback-not-accepted": "Feedback kan pas nadat een voorstel is afgewezen of gesloten.",
|
|
1233
|
+
"assistant-consult-subject-unavailable": "Het onderwerp bestaat niet of je mag de agent er niet over raadplegen.",
|
|
1234
|
+
"assistant-consult-agent-unavailable": "De agent kan nu niet worden geraadpleegd.",
|
|
1235
|
+
"assistant-consult-depth-exceeded": "Deze raadpleging zou agents in een kring of te diep laten vragen.",
|
|
1236
|
+
"assistant-consult-budget-exhausted": "Het dagbudget van de agent is op.",
|
|
1237
|
+
"assistant-consult-limit-reached": "Je hebt het aantal raadplegingen voor vandaag bereikt.",
|
|
1238
|
+
"assistant-consult-timeout": "De agent antwoordde niet op tijd.",
|
|
1239
|
+
"assistant-consult-failed": "De agent kon niet antwoorden.",
|
|
1240
|
+
"assistant-consult-read-only": "Een raadpleging kan niets wijzigen."
|
|
782
1241
|
},
|
|
783
1242
|
notices: {
|
|
784
1243
|
"no-answer": "De assistent is klaar zonder antwoord. Probeer het opnieuw of stel je vraag anders.",
|
|
@@ -892,7 +1351,8 @@ var nl = {
|
|
|
892
1351
|
propose: "Voorstellen",
|
|
893
1352
|
simulate: "Simuleren",
|
|
894
1353
|
execute: "Uitvoeren"
|
|
895
|
-
}
|
|
1354
|
+
},
|
|
1355
|
+
feedback: "Feedback"
|
|
896
1356
|
},
|
|
897
1357
|
"agent-editor": {
|
|
898
1358
|
"new-title": "Nieuwe agent",
|
|
@@ -1006,6 +1466,59 @@ var nl = {
|
|
|
1006
1466
|
"missing-warning": "De server biedt deze tool niet meer aan.",
|
|
1007
1467
|
save: "Tool opslaan",
|
|
1008
1468
|
back: "Terug naar de servers"
|
|
1469
|
+
},
|
|
1470
|
+
feedback: {
|
|
1471
|
+
title: "Feedback op voorstellen",
|
|
1472
|
+
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.",
|
|
1473
|
+
agent: "{{name}} ({{id}})",
|
|
1474
|
+
back: "Terug naar agents",
|
|
1475
|
+
refresh: "Vernieuwen",
|
|
1476
|
+
"export": "Exporteren (JSON Lines)",
|
|
1477
|
+
exporting: "Exporteren…",
|
|
1478
|
+
"export-hint": "De export bevat gegevens van onderwerpen en de voorgestelde toolinvoer, en wordt vastgelegd in de auditlog.",
|
|
1479
|
+
period: "Periode",
|
|
1480
|
+
periods: {
|
|
1481
|
+
"last-7-days": "Afgelopen 7 dagen",
|
|
1482
|
+
"last-30-days": "Afgelopen 30 dagen",
|
|
1483
|
+
"last-90-days": "Afgelopen 90 dagen"
|
|
1484
|
+
},
|
|
1485
|
+
tool: "Tool",
|
|
1486
|
+
"all-tools": "Alle tools",
|
|
1487
|
+
category: "Categorie",
|
|
1488
|
+
"all-categories": "Alle categorieën",
|
|
1489
|
+
"category-hint": "De categorie beperkt de feedback en de export.",
|
|
1490
|
+
"summary-title": "Per tool en agentversie",
|
|
1491
|
+
"summary-empty": "De agent deed in deze periode geen voorstellen.",
|
|
1492
|
+
columns: {
|
|
1493
|
+
tool: "Tool",
|
|
1494
|
+
version: "Agentversie",
|
|
1495
|
+
approved: "Goedgekeurd",
|
|
1496
|
+
rejected: "Afgewezen",
|
|
1497
|
+
expired: "Verlopen",
|
|
1498
|
+
"rejection-rate": "Afwijzingspercentage",
|
|
1499
|
+
"with-feedback": "Afgewezen met feedback",
|
|
1500
|
+
invalidated: "Niet meer nodig",
|
|
1501
|
+
people: "Personen",
|
|
1502
|
+
categories: "Meest gekozen"
|
|
1503
|
+
},
|
|
1504
|
+
version: "v{{version}}",
|
|
1505
|
+
consultations: "Raadplegingen",
|
|
1506
|
+
"no-decisions": "Nog niet beslist",
|
|
1507
|
+
"no-categories": "Geen",
|
|
1508
|
+
"invalidated-hint": "Open voorstellen die sloten voordat iemand besliste, omdat het werk al gedaan was of niet meer van toepassing was. Ze tellen niet als afgewezen.",
|
|
1509
|
+
"list-title": "Feedback",
|
|
1510
|
+
"list-count": "{{count}} in totaal",
|
|
1511
|
+
"list-empty": "Niemand gaf in deze periode feedback.",
|
|
1512
|
+
"list-empty-filtered": "Geen feedback past bij deze filters.",
|
|
1513
|
+
"no-text": "Geen toelichting gegeven.",
|
|
1514
|
+
proposal: "Voorstel:",
|
|
1515
|
+
author: "Door {{actor}}",
|
|
1516
|
+
bulk: "In bulk",
|
|
1517
|
+
"bulk-hint": "Eén keer gegeven voor meerdere voorstellen die samen zijn afgewezen; weeg dit lager.",
|
|
1518
|
+
previous: "Vorige",
|
|
1519
|
+
next: "Volgende",
|
|
1520
|
+
page: "Pagina {{page}} van {{pages}}",
|
|
1521
|
+
pages: "Pagina's"
|
|
1009
1522
|
}
|
|
1010
1523
|
},
|
|
1011
1524
|
"page-context": {
|
|
@@ -1052,6 +1565,159 @@ var nl = {
|
|
|
1052
1565
|
remove: "{{name}} verwijderen",
|
|
1053
1566
|
leave: "Gesprek verlaten",
|
|
1054
1567
|
"no-conversation": "Start of open een gesprek om het te delen."
|
|
1568
|
+
},
|
|
1569
|
+
"agent-runs": {
|
|
1570
|
+
loading: "Laden…",
|
|
1571
|
+
"load-failed": "Dit kon niet worden geladen.",
|
|
1572
|
+
retry: "Opnieuw proberen",
|
|
1573
|
+
refresh: "Vernieuwen",
|
|
1574
|
+
role: {
|
|
1575
|
+
none: "Geen rol op dit onderwerp",
|
|
1576
|
+
reader: "Lezer",
|
|
1577
|
+
participant: "Deelnemer",
|
|
1578
|
+
approver: "Beslisser"
|
|
1579
|
+
},
|
|
1580
|
+
thread: {
|
|
1581
|
+
title: "Agent",
|
|
1582
|
+
"waiting-inputs": "{{count}} berichten wachten op de volgende run",
|
|
1583
|
+
"open-conversation": "Gesprek openen",
|
|
1584
|
+
"start-message": "Instructie voor de agent (optioneel)",
|
|
1585
|
+
start: "Agent starten",
|
|
1586
|
+
starting: "Starten…",
|
|
1587
|
+
"no-proposals": "De agent heeft hier nog niets voorgesteld.",
|
|
1588
|
+
"no-thread": "De agent heeft hier nog niet aan gewerkt.",
|
|
1589
|
+
tabs: {
|
|
1590
|
+
proposals: "Voorstellen",
|
|
1591
|
+
runs: "Runs",
|
|
1592
|
+
consultations: "Raadplegingen",
|
|
1593
|
+
memory: "Werkgeheugen",
|
|
1594
|
+
artifacts: "Bestanden"
|
|
1595
|
+
},
|
|
1596
|
+
outcome: {
|
|
1597
|
+
started: "De agent start zo.",
|
|
1598
|
+
queued: "De agent start na de huidige run.",
|
|
1599
|
+
coalesced: "Je verzoek sluit aan bij de run die zo start.",
|
|
1600
|
+
superseded: "De huidige run stopt en de agent start opnieuw.",
|
|
1601
|
+
ignored: "De agent is bezig; er is niets gestart.",
|
|
1602
|
+
deduplicated: "Dit was al gestart.",
|
|
1603
|
+
rejected: "De agent kon niet starten."
|
|
1604
|
+
}
|
|
1605
|
+
},
|
|
1606
|
+
subject: {
|
|
1607
|
+
title: "Voorstellen van agents",
|
|
1608
|
+
description: "Voorstellen die agents hier deden en die op een besluit wachten, de eerste die verloopt bovenaan.",
|
|
1609
|
+
empty: "Geen openstaande voorstellen van agents.",
|
|
1610
|
+
unavailable: "Je kunt het werk van agents hieraan niet zien.",
|
|
1611
|
+
agents: "Agents",
|
|
1612
|
+
idle: "Werkt nu niet",
|
|
1613
|
+
opening: "Openen…",
|
|
1614
|
+
"open-conversation-label": "Gesprek openen met {{agent}}",
|
|
1615
|
+
"start-label": "Agent starten: {{agent}}",
|
|
1616
|
+
"assistant-unavailable": "De assistent is niet beschikbaar."
|
|
1617
|
+
},
|
|
1618
|
+
inbox: {
|
|
1619
|
+
title: "Voorstellen om te beslissen",
|
|
1620
|
+
description: "Voorstellen van agents die op jouw besluit wachten, de eerst verlopende bovenaan.",
|
|
1621
|
+
empty: "Er wachten geen voorstellen op jou.",
|
|
1622
|
+
select: "Dit voorstel selecteren",
|
|
1623
|
+
"select-all": "Alles selecteren",
|
|
1624
|
+
"approve-selected": "Selectie goedkeuren",
|
|
1625
|
+
"reject-selected": "Selectie afwijzen",
|
|
1626
|
+
previous: "Vorige",
|
|
1627
|
+
next: "Volgende",
|
|
1628
|
+
page: "Pagina {{page}} van {{pages}}",
|
|
1629
|
+
pages: "Pagina's"
|
|
1630
|
+
},
|
|
1631
|
+
proposal: {
|
|
1632
|
+
approve: "Goedkeuren",
|
|
1633
|
+
reject: "Afwijzen",
|
|
1634
|
+
expires: "Verloopt {{date}}",
|
|
1635
|
+
"decided-by": "Besloten door {{actor}}",
|
|
1636
|
+
"arguments": "Invoer",
|
|
1637
|
+
"re-execute": "Opnieuw uitvoeren",
|
|
1638
|
+
"mark-resolved": "Als afgehandeld markeren",
|
|
1639
|
+
"unknown-hint": "De uitvoering stopte zonder bekende uitkomst. Controleer of het gebeurd is voordat je het opnieuw uitvoert.",
|
|
1640
|
+
mode: {
|
|
1641
|
+
inline: "De run wacht",
|
|
1642
|
+
deferred: "Later beslissen"
|
|
1643
|
+
},
|
|
1644
|
+
status: {
|
|
1645
|
+
pending: "Wacht",
|
|
1646
|
+
approved: "Goedgekeurd",
|
|
1647
|
+
rejected: "Afgewezen",
|
|
1648
|
+
expired: "Verlopen",
|
|
1649
|
+
withdrawn: "Ingetrokken",
|
|
1650
|
+
stale: "Verouderd",
|
|
1651
|
+
executing: "Wordt uitgevoerd",
|
|
1652
|
+
executed: "Uitgevoerd",
|
|
1653
|
+
"execution-failed": "Mislukt",
|
|
1654
|
+
"execution-unknown": "Uitkomst onbekend",
|
|
1655
|
+
resolved: "Afgehandeld",
|
|
1656
|
+
shadow: "Schaduw"
|
|
1657
|
+
},
|
|
1658
|
+
"from-consultation": "Uit een raadpleging",
|
|
1659
|
+
"from-consultation-hint": "Een andere assistent raadpleegde de agent voor deze persoon; het voorstel draait alleen met de rechten van die persoon.",
|
|
1660
|
+
"give-feedback": "Feedback geven",
|
|
1661
|
+
"change-feedback": "Feedback wijzigen",
|
|
1662
|
+
"your-feedback": "Jouw feedback: {{category}}",
|
|
1663
|
+
"rejected-one": "Je hebt afgewezen: {{summary}}",
|
|
1664
|
+
closed: "Intussen gesloten",
|
|
1665
|
+
dismiss: "Wegklikken",
|
|
1666
|
+
"dismiss-label": "Gesloten voorstel wegklikken: {{summary}}",
|
|
1667
|
+
"show-successor": "Toon het nieuwere voorstel",
|
|
1668
|
+
"stale-by": "Ingehaald door {{actor}}"
|
|
1669
|
+
},
|
|
1670
|
+
run: {
|
|
1671
|
+
empty: "Nog geen runs.",
|
|
1672
|
+
status: {
|
|
1673
|
+
queued: "In de wachtrij",
|
|
1674
|
+
running: "Bezig",
|
|
1675
|
+
"waiting-for-approval": "Wacht op goedkeuring",
|
|
1676
|
+
completed: "Klaar",
|
|
1677
|
+
failed: "Mislukt",
|
|
1678
|
+
cancelled: "Geannuleerd"
|
|
1679
|
+
},
|
|
1680
|
+
shadow: "Schaduw",
|
|
1681
|
+
kind: {
|
|
1682
|
+
rule: "Automatische regel",
|
|
1683
|
+
"rule-then-model": "Regel, daarna het model"
|
|
1684
|
+
},
|
|
1685
|
+
trigger: "Aanleiding: {{trigger}}",
|
|
1686
|
+
"tool-calls": "{{count}} tool-aanroepen",
|
|
1687
|
+
tokens: "{{count}} tokens",
|
|
1688
|
+
proposals: "{{count}} voorstellen",
|
|
1689
|
+
attempt: "Poging {{attempt}}",
|
|
1690
|
+
cancel: "Annuleren",
|
|
1691
|
+
resume: "Hervatten",
|
|
1692
|
+
restart: "Opnieuw starten"
|
|
1693
|
+
},
|
|
1694
|
+
memory: {
|
|
1695
|
+
"not-configured": "Deze agent houdt geen werkgeheugen bij.",
|
|
1696
|
+
version: "Schemaversie {{version}}",
|
|
1697
|
+
updated: "Bijgewerkt {{date}}",
|
|
1698
|
+
empty: "De agent heeft nog niets genoteerd."
|
|
1699
|
+
},
|
|
1700
|
+
artifacts: {
|
|
1701
|
+
"download-failed": "Het bestand kon niet worden gedownload.",
|
|
1702
|
+
empty: "Nog geen bestanden.",
|
|
1703
|
+
version: "v{{version}}",
|
|
1704
|
+
restricted: "Afgeschermd (agentrechten)",
|
|
1705
|
+
size: "{{size}} bytes",
|
|
1706
|
+
download: "Downloaden"
|
|
1707
|
+
},
|
|
1708
|
+
consultation: {
|
|
1709
|
+
empty: "Nog geen assistent heeft deze agent over dit onderwerp geraadpleegd.",
|
|
1710
|
+
"private": "Hier staat alleen wie vroeg en hoe het afliep; de vraag en het antwoord blijven in het vragende gesprek.",
|
|
1711
|
+
"asked-by": "Gevraagd door {{name}}",
|
|
1712
|
+
through: "via {{agent}}",
|
|
1713
|
+
depth: "Genest, niveau {{depth}}",
|
|
1714
|
+
more: "{{count}} oudere raadplegingen worden niet getoond.",
|
|
1715
|
+
status: {
|
|
1716
|
+
running: "Bezig",
|
|
1717
|
+
completed: "Beantwoord",
|
|
1718
|
+
failed: "Niet beantwoord"
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1055
1721
|
}
|
|
1056
1722
|
}
|
|
1057
1723
|
};
|
|
@@ -1231,7 +1897,8 @@ class NhAssistantTransport {
|
|
|
1231
1897
|
}
|
|
1232
1898
|
}
|
|
1233
1899
|
async createInit(method, accept, body, signal) {
|
|
1234
|
-
const headers =
|
|
1900
|
+
const headers = await nhAssistantRequestHeaders(this.config, this.injector);
|
|
1901
|
+
headers['Accept'] = accept;
|
|
1235
1902
|
const token = await runInInjectionContext(this.injector, () => this.config.getAccessToken());
|
|
1236
1903
|
if (token) {
|
|
1237
1904
|
headers['Authorization'] = `Bearer ${token}`;
|
|
@@ -1264,6 +1931,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImpo
|
|
|
1264
1931
|
function createApiError(status, code) {
|
|
1265
1932
|
return new NhAssistantApiError(status, code, nhAssistantErrorMessageKey(code));
|
|
1266
1933
|
}
|
|
1934
|
+
const reservedRequestHeaders = new Set(['authorization', 'accept', 'accept-language', 'content-type']);
|
|
1935
|
+
/**
|
|
1936
|
+
* The host's extra request headers from `NhAssistantConfig.getRequestHeaders`, without the
|
|
1937
|
+
* headers the library sets itself and without empty values.
|
|
1938
|
+
*/
|
|
1939
|
+
async function nhAssistantRequestHeaders(config, injector) {
|
|
1940
|
+
const headers = {};
|
|
1941
|
+
if (!config.getRequestHeaders) {
|
|
1942
|
+
return headers;
|
|
1943
|
+
}
|
|
1944
|
+
const extra = await runInInjectionContext(injector, () => config.getRequestHeaders());
|
|
1945
|
+
for (const [name, value] of Object.entries(extra ?? {})) {
|
|
1946
|
+
if (typeof value === 'string' && value.length > 0 && !reservedRequestHeaders.has(name.toLowerCase())) {
|
|
1947
|
+
headers[name] = value;
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
return headers;
|
|
1951
|
+
}
|
|
1267
1952
|
|
|
1268
1953
|
/** In the admin endpoints a 409 means a stale `expectedVersion` or a forbidden state change. */
|
|
1269
1954
|
const adminStatusCodes = {
|
|
@@ -1337,6 +2022,37 @@ class NhAssistantAdminApiService {
|
|
|
1337
2022
|
const path = `admin/mcp-servers/${encodeURIComponent(serverId)}/tools/${encodeURIComponent(remoteName)}`;
|
|
1338
2023
|
return this.request('PUT', path, request);
|
|
1339
2024
|
}
|
|
2025
|
+
/**
|
|
2026
|
+
* The agent's proposals per tool and agent version in the period: decisions, the rejection rate,
|
|
2027
|
+
* feedback per category and the number of different people who gave it.
|
|
2028
|
+
*/
|
|
2029
|
+
getAgentFeedbackSummary(agentId, filter = {}) {
|
|
2030
|
+
return this.request('GET', withQuery(`${feedbackPath(agentId)}/summary`, filter));
|
|
2031
|
+
}
|
|
2032
|
+
/** The feedback on the agent's proposals with the proposal as approvers read it, newest change first. */
|
|
2033
|
+
getAgentFeedback(agentId, filter = {}) {
|
|
2034
|
+
return this.request('GET', withQuery(feedbackPath(agentId), filter));
|
|
2035
|
+
}
|
|
2036
|
+
/**
|
|
2037
|
+
* Downloads the feedback as JSON Lines with the tool input of each proposal. The file holds
|
|
2038
|
+
* subject data and the server audits the export. Rejects with `NhAssistantApiError`.
|
|
2039
|
+
*/
|
|
2040
|
+
async exportAgentFeedback(agentId, filter = {}) {
|
|
2041
|
+
const path = withQuery(`${feedbackPath(agentId)}/export`, filter);
|
|
2042
|
+
let response;
|
|
2043
|
+
try {
|
|
2044
|
+
response = await this.transport.fetch('GET', path, 'application/x-ndjson', undefined, new AbortController().signal);
|
|
2045
|
+
}
|
|
2046
|
+
catch {
|
|
2047
|
+
const code = nhAssistantCodeForStatus(0);
|
|
2048
|
+
throw new NhAssistantApiError(0, code, nhAssistantErrorMessageKey(code));
|
|
2049
|
+
}
|
|
2050
|
+
if (!response.ok) {
|
|
2051
|
+
const code = await this.transport.failureCode(response, adminStatusCodes);
|
|
2052
|
+
throw new NhAssistantApiError(response.status, code, nhAssistantErrorMessageKey(code));
|
|
2053
|
+
}
|
|
2054
|
+
return response.blob();
|
|
2055
|
+
}
|
|
1340
2056
|
request(method, path, body) {
|
|
1341
2057
|
return this.transport.json(method, path, body, adminStatusCodes);
|
|
1342
2058
|
}
|
|
@@ -1346,6 +2062,183 @@ class NhAssistantAdminApiService {
|
|
|
1346
2062
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantAdminApiService, decorators: [{
|
|
1347
2063
|
type: Injectable
|
|
1348
2064
|
}] });
|
|
2065
|
+
function feedbackPath(agentId) {
|
|
2066
|
+
return `admin/agents/${encodeURIComponent(agentId)}/feedback`;
|
|
2067
|
+
}
|
|
2068
|
+
/** Appends the filter values that are set as query parameters. */
|
|
2069
|
+
function withQuery(path, filter) {
|
|
2070
|
+
const query = new URLSearchParams();
|
|
2071
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
2072
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
2073
|
+
query.set(key, String(value));
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
const suffix = query.toString();
|
|
2077
|
+
return suffix ? `${path}?${suffix}` : path;
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
/**
|
|
2081
|
+
* Client for the agent-run endpoints: the agent thread about a subject, its runs, proposals,
|
|
2082
|
+
* working memory and artifacts, and the approval inbox. Access follows the caller's role on the
|
|
2083
|
+
* subject, which the server checks on every request; failures error with `NhAssistantApiError`.
|
|
2084
|
+
*/
|
|
2085
|
+
class NhAssistantAgentRunApiService {
|
|
2086
|
+
constructor() {
|
|
2087
|
+
this.transport = inject(NhAssistantTransport);
|
|
2088
|
+
}
|
|
2089
|
+
/** The agent's thread about the subject, the caller's role and whether the caller may start the agent. */
|
|
2090
|
+
getThread(agentId, subjectType, subjectId) {
|
|
2091
|
+
const query = new URLSearchParams({ agentId, subjectType, subjectId });
|
|
2092
|
+
return this.transport.json('GET', `threads?${query.toString()}`);
|
|
2093
|
+
}
|
|
2094
|
+
/**
|
|
2095
|
+
* The agent's thread about the subject, created without starting a run when it is missing, so
|
|
2096
|
+
* the caller can open the conversation and type there. Creating a thread needs the participant
|
|
2097
|
+
* role; readers may open an existing one. Errors with `assistant-subject-access-denied` otherwise.
|
|
2098
|
+
*/
|
|
2099
|
+
openThread(agentId, subjectType, subjectId) {
|
|
2100
|
+
return this.transport.json('POST', 'threads/open', { agentId, subjectType, subjectId });
|
|
2101
|
+
}
|
|
2102
|
+
/** Starts the agent on the subject with the caller as initiator; needs the participant role. */
|
|
2103
|
+
startAgent(request) {
|
|
2104
|
+
return this.transport.json('POST', 'threads/start', request);
|
|
2105
|
+
}
|
|
2106
|
+
/**
|
|
2107
|
+
* All agent work on one subject: the caller's role, every agent with a thread on it plus
|
|
2108
|
+
* `agentId`, and the open proposals of all of them with the caller's live decision right. Errors
|
|
2109
|
+
* with `assistant-subject-not-found` when the caller has no role on the subject.
|
|
2110
|
+
*/
|
|
2111
|
+
getSubjectWork(subjectType, subjectId, agentId) {
|
|
2112
|
+
const path = `subjects/${encodeURIComponent(subjectType)}/${encodeURIComponent(subjectId)}/work`;
|
|
2113
|
+
const query = agentId ? `?${new URLSearchParams({ agentId }).toString()}` : '';
|
|
2114
|
+
return this.transport.json('GET', path + query);
|
|
2115
|
+
}
|
|
2116
|
+
/**
|
|
2117
|
+
* Counts the agent work on at most `NH_ASSISTANT_MAX_SUBJECT_SUMMARY` subjects of one type in one
|
|
2118
|
+
* request, for badges in a list; one item per id in request order. `NhAssistantSubjectSummaryService`
|
|
2119
|
+
* batches and caches these requests for many badges.
|
|
2120
|
+
*/
|
|
2121
|
+
summarizeSubjects(subjectType, subjectIds) {
|
|
2122
|
+
return this.transport.json('POST', `subjects/${encodeURIComponent(subjectType)}/summary`, {
|
|
2123
|
+
subjectIds
|
|
2124
|
+
});
|
|
2125
|
+
}
|
|
2126
|
+
getRuns(conversationId) {
|
|
2127
|
+
return this.transport.json('GET', `threads/${encodeURIComponent(conversationId)}/runs`);
|
|
2128
|
+
}
|
|
2129
|
+
/** Who consulted the thread's agent through another assistant, newest first. */
|
|
2130
|
+
getConsultations(conversationId, page = 1, itemsPerPage = 20) {
|
|
2131
|
+
const query = new URLSearchParams({ page: String(page), itemsPerPage: String(itemsPerPage) });
|
|
2132
|
+
return this.transport.json('GET', `threads/${encodeURIComponent(conversationId)}/consultations?${query.toString()}`);
|
|
2133
|
+
}
|
|
2134
|
+
getProposals(conversationId) {
|
|
2135
|
+
return this.transport.json('GET', `threads/${encodeURIComponent(conversationId)}/proposals`);
|
|
2136
|
+
}
|
|
2137
|
+
/**
|
|
2138
|
+
* One proposal of an agent thread in any status, with the caller's live right to decide it, for
|
|
2139
|
+
* example the one an `AgentWorkChanged` update names. Fails with `assistant-approval-not-found`
|
|
2140
|
+
* for an unknown proposal and for one the caller may not see.
|
|
2141
|
+
*/
|
|
2142
|
+
getProposal(approvalId) {
|
|
2143
|
+
return this.transport.json('GET', `approvals/${encodeURIComponent(approvalId)}`);
|
|
2144
|
+
}
|
|
2145
|
+
/**
|
|
2146
|
+
* The proposals among at most `NH_ASSISTANT_MAX_PROPOSAL_LOOKUP` ids that the caller may see, in
|
|
2147
|
+
* the order of the ids, each with the caller's live right to decide it. The others are left out.
|
|
2148
|
+
*/
|
|
2149
|
+
lookupProposals(approvalIds) {
|
|
2150
|
+
return this.transport.json('POST', 'approvals/lookup', { approvalIds });
|
|
2151
|
+
}
|
|
2152
|
+
getMemory(conversationId) {
|
|
2153
|
+
return this.transport.json('GET', `threads/${encodeURIComponent(conversationId)}/memory`);
|
|
2154
|
+
}
|
|
2155
|
+
getArtifacts(conversationId) {
|
|
2156
|
+
return this.transport.json('GET', `threads/${encodeURIComponent(conversationId)}/artifacts`);
|
|
2157
|
+
}
|
|
2158
|
+
/** Downloads one artifact version with the host's credentials. */
|
|
2159
|
+
downloadArtifact(conversationId, artifactId) {
|
|
2160
|
+
const path = `threads/${encodeURIComponent(conversationId)}/artifacts/${encodeURIComponent(artifactId)}/content`;
|
|
2161
|
+
return this.transport.fetch('GET', path, '*/*', undefined, new AbortController().signal).then(response => {
|
|
2162
|
+
if (!response.ok) {
|
|
2163
|
+
throw new Error(`Artifact download failed with status ${response.status}.`);
|
|
2164
|
+
}
|
|
2165
|
+
return response.blob();
|
|
2166
|
+
});
|
|
2167
|
+
}
|
|
2168
|
+
/** Cancels a run at its next safe point and withdraws its open proposals; needs the participant role. */
|
|
2169
|
+
cancelRun(runId) {
|
|
2170
|
+
return this.transport.json('POST', `runs/${encodeURIComponent(runId)}/cancel`);
|
|
2171
|
+
}
|
|
2172
|
+
/** Resumes or restarts a failed or cancelled run; needs the approver role. */
|
|
2173
|
+
retryRun(runId, mode) {
|
|
2174
|
+
return this.transport.json('POST', `runs/${encodeURIComponent(runId)}/retry`, { mode });
|
|
2175
|
+
}
|
|
2176
|
+
/** Pending proposals the caller may decide, sorted by expiry. */
|
|
2177
|
+
getInbox(filter = {}) {
|
|
2178
|
+
const query = new URLSearchParams();
|
|
2179
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
2180
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
2181
|
+
query.set(key, String(value));
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
const suffix = query.toString();
|
|
2185
|
+
return this.transport.json('GET', suffix ? `approvals/inbox?${suffix}` : 'approvals/inbox');
|
|
2186
|
+
}
|
|
2187
|
+
/**
|
|
2188
|
+
* Decides one proposal, bound to the proposal hash the person saw; the server checks the approval
|
|
2189
|
+
* policy and the tool's approve policy again. Accepts an inbox proposal or the approval part of an
|
|
2190
|
+
* agent thread.
|
|
2191
|
+
*/
|
|
2192
|
+
decide(proposal, decision, reason) {
|
|
2193
|
+
return this.transport.json('POST', `approvals/${encodeURIComponent(proposal.approvalId)}/decisions`, {
|
|
2194
|
+
decision,
|
|
2195
|
+
expectedProposalHash: proposal.proposalHash,
|
|
2196
|
+
reason: reason?.trim() || null
|
|
2197
|
+
});
|
|
2198
|
+
}
|
|
2199
|
+
/** Decides several proposals; each one is checked and decided on its own. */
|
|
2200
|
+
decideMany(approvalIds, decision, reason) {
|
|
2201
|
+
return this.transport.json('POST', 'approvals/decisions', {
|
|
2202
|
+
approvalIds,
|
|
2203
|
+
decision,
|
|
2204
|
+
reason: reason?.trim() || null
|
|
2205
|
+
});
|
|
2206
|
+
}
|
|
2207
|
+
/** Resolves an execution whose outcome is unknown. */
|
|
2208
|
+
resolve(approvalId, action, reason) {
|
|
2209
|
+
return this.transport.json('POST', `approvals/${encodeURIComponent(approvalId)}/resolve`, {
|
|
2210
|
+
action,
|
|
2211
|
+
reason: reason?.trim() || null
|
|
2212
|
+
});
|
|
2213
|
+
}
|
|
2214
|
+
/**
|
|
2215
|
+
* Gives or changes the caller's feedback on a closed proposal (`canGiveFeedback`). Feedback is
|
|
2216
|
+
* optional and separate from the decision; an empty text is sent as `null`. Fails with
|
|
2217
|
+
* `assistant-feedback-not-accepted` while the proposal is pending or after it was approved.
|
|
2218
|
+
*/
|
|
2219
|
+
giveFeedback(approvalId, category, text) {
|
|
2220
|
+
return this.transport.json('PUT', `approvals/${encodeURIComponent(approvalId)}/feedback`, {
|
|
2221
|
+
category,
|
|
2222
|
+
text: text?.trim() || null
|
|
2223
|
+
});
|
|
2224
|
+
}
|
|
2225
|
+
/**
|
|
2226
|
+
* Gives the same feedback on at most `NH_ASSISTANT_MAX_BULK_FEEDBACK` proposals that were
|
|
2227
|
+
* rejected together. Each proposal is checked on its own and stored as bulk feedback.
|
|
2228
|
+
*/
|
|
2229
|
+
giveFeedbackMany(approvalIds, category, text) {
|
|
2230
|
+
return this.transport.json('POST', 'approvals/feedback', {
|
|
2231
|
+
approvalIds,
|
|
2232
|
+
category,
|
|
2233
|
+
text: text?.trim() || null
|
|
2234
|
+
});
|
|
2235
|
+
}
|
|
2236
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantAgentRunApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2237
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantAgentRunApiService }); }
|
|
2238
|
+
}
|
|
2239
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantAgentRunApiService, decorators: [{
|
|
2240
|
+
type: Injectable
|
|
2241
|
+
}] });
|
|
1349
2242
|
|
|
1350
2243
|
/**
|
|
1351
2244
|
* Incremental parser for the `text/event-stream` format.
|
|
@@ -1614,10 +2507,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImpo
|
|
|
1614
2507
|
|
|
1615
2508
|
const reconnectDelaysMs = [0, 2_000, 5_000, 10_000, 30_000];
|
|
1616
2509
|
const restartDelayMs = 30_000;
|
|
2510
|
+
/** Query parameters the library and SignalR set themselves. */
|
|
2511
|
+
const reservedHubQueryParameters = new Set(['access_token', 'id', 'negotiateversion']);
|
|
1617
2512
|
/**
|
|
1618
|
-
* Live updates of the assistant hub: conversation changes, read positions, removals
|
|
1619
|
-
*
|
|
1620
|
-
* own and emits `resync` after every (re)connect,
|
|
2513
|
+
* Live updates of the assistant hub: conversation changes, read positions, removals, the turn
|
|
2514
|
+
* events of other sessions and changes to the work of agents that run on their own. Connects only
|
|
2515
|
+
* while the store asks for it, reconnects on its own and emits `resync` after every (re)connect,
|
|
2516
|
+
* because updates sent while disconnected are lost.
|
|
1621
2517
|
*/
|
|
1622
2518
|
class NhAssistantLiveService {
|
|
1623
2519
|
constructor() {
|
|
@@ -1628,6 +2524,7 @@ class NhAssistantLiveService {
|
|
|
1628
2524
|
this.readSubject = new Subject();
|
|
1629
2525
|
this.removedSubject = new Subject();
|
|
1630
2526
|
this.eventSubject = new Subject();
|
|
2527
|
+
this.agentWorkSubject = new Subject();
|
|
1631
2528
|
this.resyncSubject = new Subject();
|
|
1632
2529
|
this.stateSignal = signal('off', ...(ngDevMode ? [{ debugName: "stateSignal" }] : []));
|
|
1633
2530
|
this.hubPath = null;
|
|
@@ -1637,6 +2534,11 @@ class NhAssistantLiveService {
|
|
|
1637
2534
|
this.read$ = this.readSubject.asObservable();
|
|
1638
2535
|
this.removed$ = this.removedSubject.asObservable();
|
|
1639
2536
|
this.events$ = this.eventSubject.asObservable();
|
|
2537
|
+
/**
|
|
2538
|
+
* A run, proposal or feedback of an agent thread changed. It carries no content; components
|
|
2539
|
+
* that show the thread reload it.
|
|
2540
|
+
*/
|
|
2541
|
+
this.agentWork$ = this.agentWorkSubject.asObservable();
|
|
1640
2542
|
/** Reload snapshots: emitted after every (re)connect. */
|
|
1641
2543
|
this.resync$ = this.resyncSubject.asObservable();
|
|
1642
2544
|
this.state = this.stateSignal.asReadonly();
|
|
@@ -1646,6 +2548,7 @@ class NhAssistantLiveService {
|
|
|
1646
2548
|
this.readSubject.complete();
|
|
1647
2549
|
this.removedSubject.complete();
|
|
1648
2550
|
this.eventSubject.complete();
|
|
2551
|
+
this.agentWorkSubject.complete();
|
|
1649
2552
|
this.resyncSubject.complete();
|
|
1650
2553
|
});
|
|
1651
2554
|
}
|
|
@@ -1701,9 +2604,12 @@ class NhAssistantLiveService {
|
|
|
1701
2604
|
}
|
|
1702
2605
|
async build(url) {
|
|
1703
2606
|
const signalR = await import('@microsoft/signalr');
|
|
2607
|
+
const headers = await nhAssistantRequestHeaders(this.config, this.injector);
|
|
2608
|
+
const hubUrl = await this.withQueryParameters(url);
|
|
1704
2609
|
const connection = new signalR.HubConnectionBuilder()
|
|
1705
|
-
.withUrl(
|
|
1706
|
-
accessTokenFactory: async () => (await this.token()) ?? ''
|
|
2610
|
+
.withUrl(hubUrl, {
|
|
2611
|
+
accessTokenFactory: async () => (await this.token()) ?? '',
|
|
2612
|
+
headers
|
|
1707
2613
|
})
|
|
1708
2614
|
.withAutomaticReconnect(reconnectDelaysMs)
|
|
1709
2615
|
.configureLogging(signalR.LogLevel.None)
|
|
@@ -1712,6 +2618,7 @@ class NhAssistantLiveService {
|
|
|
1712
2618
|
connection.on('ConversationRead', (message) => this.readSubject.next(message));
|
|
1713
2619
|
connection.on('ConversationRemoved', (message) => this.removedSubject.next(message));
|
|
1714
2620
|
connection.on('ConversationEvent', (message) => this.eventSubject.next(message));
|
|
2621
|
+
connection.on('AgentWorkChanged', (message) => this.agentWorkSubject.next(message));
|
|
1715
2622
|
connection.onreconnecting(() => this.stateSignal.set('reconnecting'));
|
|
1716
2623
|
connection.onreconnected(() => {
|
|
1717
2624
|
this.stateSignal.set('connected');
|
|
@@ -1743,6 +2650,30 @@ class NhAssistantLiveService {
|
|
|
1743
2650
|
return null;
|
|
1744
2651
|
}
|
|
1745
2652
|
}
|
|
2653
|
+
/**
|
|
2654
|
+
* The hub URL with the host's `getHubQueryParameters`, which reach the hub on every transport,
|
|
2655
|
+
* unlike headers on WebSockets and server-sent events.
|
|
2656
|
+
*/
|
|
2657
|
+
async withQueryParameters(url) {
|
|
2658
|
+
const getter = this.config.getHubQueryParameters;
|
|
2659
|
+
if (!getter) {
|
|
2660
|
+
return url;
|
|
2661
|
+
}
|
|
2662
|
+
let parameters;
|
|
2663
|
+
try {
|
|
2664
|
+
parameters = await runInInjectionContext(this.injector, () => getter());
|
|
2665
|
+
}
|
|
2666
|
+
catch {
|
|
2667
|
+
return url;
|
|
2668
|
+
}
|
|
2669
|
+
const target = new URL(url);
|
|
2670
|
+
for (const [name, value] of Object.entries(parameters ?? {})) {
|
|
2671
|
+
if (typeof value === 'string' && value.length > 0 && !reservedHubQueryParameters.has(name.toLowerCase())) {
|
|
2672
|
+
target.searchParams.set(name, value);
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
return target.toString();
|
|
2676
|
+
}
|
|
1746
2677
|
/** `hubBaseUrl` + `hubPath`, or the hub path on the origin of `apiBaseUrl`. */
|
|
1747
2678
|
resolveUrl(hubPath) {
|
|
1748
2679
|
const page = globalThis.location?.href ?? 'http://localhost';
|
|
@@ -2399,6 +3330,7 @@ function validId(value) {
|
|
|
2399
3330
|
|
|
2400
3331
|
/** Notice code for a completed turn that produced no answer text and no server code. */
|
|
2401
3332
|
const NH_ASSISTANT_NO_ANSWER_NOTICE_CODE = 'assistant-no-answer';
|
|
3333
|
+
const fourEyesRequiredCode = 'assistant-four-eyes-required';
|
|
2402
3334
|
/** True when a conversation has messages the caller has not read. A running turn is not unread yet. */
|
|
2403
3335
|
function nhAssistantIsUnread(summary) {
|
|
2404
3336
|
if (summary.status === 'running' || summary.lastReadSequence === undefined) {
|
|
@@ -2420,6 +3352,7 @@ const newConversationKey = '';
|
|
|
2420
3352
|
class NhAssistantStore {
|
|
2421
3353
|
constructor() {
|
|
2422
3354
|
this.api = inject(NhAssistantApiService);
|
|
3355
|
+
this.agentRuns = inject(NhAssistantAgentRunApiService);
|
|
2423
3356
|
this.config = inject(NH_ASSISTANT_CONFIG);
|
|
2424
3357
|
this.injector = inject(EnvironmentInjector);
|
|
2425
3358
|
this.accessPolicy = inject(NH_ASSISTANT_ACCESS_POLICY);
|
|
@@ -2452,9 +3385,13 @@ class NhAssistantStore {
|
|
|
2452
3385
|
this.queueDispatchState = signal(null, ...(ngDevMode ? [{ debugName: "queueDispatchState" }] : []));
|
|
2453
3386
|
this.steeringState = signal(null, ...(ngDevMode ? [{ debugName: "steeringState" }] : []));
|
|
2454
3387
|
this.submittingState = signal(false, ...(ngDevMode ? [{ debugName: "submittingState" }] : []));
|
|
3388
|
+
/** Agent proposals the current user rejected in this session and may give feedback on. */
|
|
3389
|
+
this.feedbackApprovalsState = signal(new Set(), ...(ngDevMode ? [{ debugName: "feedbackApprovalsState" }] : []));
|
|
2455
3390
|
this.destroyed = false;
|
|
2456
3391
|
this.streams = new Map();
|
|
2457
3392
|
this.cancelTimers = new Map();
|
|
3393
|
+
/** Pending reloads of agent threads after live agent-work updates, per conversation. */
|
|
3394
|
+
this.agentWorkTimers = new Map();
|
|
2458
3395
|
this.pendingSendResolves = new Map();
|
|
2459
3396
|
this.markingRead = new Set();
|
|
2460
3397
|
this.liveSubscription = new Subscription();
|
|
@@ -2530,13 +3467,61 @@ class NhAssistantStore {
|
|
|
2530
3467
|
this.members = computed(() => this.activeConversation()?.members ?? [], ...(ngDevMode ? [{ debugName: "members" }] : []));
|
|
2531
3468
|
/** True when the current user owns the open conversation (or it is new). */
|
|
2532
3469
|
this.isOwner = computed(() => (this.activeConversation()?.role ?? 'owner') === 'owner', ...(ngDevMode ? [{ debugName: "isOwner" }] : []));
|
|
2533
|
-
/** True when the
|
|
2534
|
-
this.
|
|
3470
|
+
/** True when the open conversation is the durable thread of an agent about a subject. */
|
|
3471
|
+
this.isAgentThread = computed(() => this.activeConversation()?.kind === 'thread', ...(ngDevMode ? [{ debugName: "isAgentThread" }] : []));
|
|
3472
|
+
/**
|
|
3473
|
+
* The current user's right to decide each pending approval of the open conversation, by approval
|
|
3474
|
+
* id. In a chat only the author of the waiting turn decides. In an agent thread the subject's
|
|
3475
|
+
* approvers decide, as the server reports per proposal: the approval policy, four-eyes and the
|
|
3476
|
+
* tool's approve policy, checked live. Proposals the user rejected in this session carry
|
|
3477
|
+
* `canGiveFeedback`.
|
|
3478
|
+
*/
|
|
3479
|
+
this.approvalRights = computed(() => {
|
|
2535
3480
|
const conversation = this.activeConversation();
|
|
2536
|
-
|
|
2537
|
-
|
|
3481
|
+
const rights = new Map();
|
|
3482
|
+
if (!conversation) {
|
|
3483
|
+
return rights;
|
|
2538
3484
|
}
|
|
2539
|
-
|
|
3485
|
+
if (conversation.kind === 'thread') {
|
|
3486
|
+
const decisions = new Map((conversation.thread?.decisions ?? []).map(decision => [decision.approvalId, decision]));
|
|
3487
|
+
for (const approval of pendingApprovalParts(conversation)) {
|
|
3488
|
+
const decision = decisions.get(approval.approvalId);
|
|
3489
|
+
rights.set(approval.approvalId, {
|
|
3490
|
+
canDecide: decision?.canDecide === true,
|
|
3491
|
+
waitingFor: decision?.reasonCode === fourEyesRequiredCode ? 'other-approver' : 'approver',
|
|
3492
|
+
decisionOwner: null
|
|
3493
|
+
});
|
|
3494
|
+
}
|
|
3495
|
+
const feedbackApprovals = this.feedbackApprovalsState();
|
|
3496
|
+
for (const approval of approvalParts(conversation)) {
|
|
3497
|
+
if (approval.status === 'rejected' && feedbackApprovals.has(approval.approvalId)) {
|
|
3498
|
+
rights.set(approval.approvalId, {
|
|
3499
|
+
canDecide: false,
|
|
3500
|
+
waitingFor: 'approver',
|
|
3501
|
+
decisionOwner: null,
|
|
3502
|
+
canGiveFeedback: true
|
|
3503
|
+
});
|
|
3504
|
+
}
|
|
3505
|
+
}
|
|
3506
|
+
return rights;
|
|
3507
|
+
}
|
|
3508
|
+
if (conversation.pendingApproval) {
|
|
3509
|
+
rights.set(conversation.pendingApproval.approvalId, {
|
|
3510
|
+
canDecide: !conversation.activeActorId || !conversation.currentActorId
|
|
3511
|
+
|| conversation.activeActorId === conversation.currentActorId,
|
|
3512
|
+
waitingFor: 'turn',
|
|
3513
|
+
decisionOwner: this.activeMember()?.displayName ?? null
|
|
3514
|
+
});
|
|
3515
|
+
}
|
|
3516
|
+
return rights;
|
|
3517
|
+
}, ...(ngDevMode ? [{ debugName: "approvalRights" }] : []));
|
|
3518
|
+
/**
|
|
3519
|
+
* True when the current user may decide the pending approval: in a chat it belongs to their own
|
|
3520
|
+
* turn, in an agent thread the server allows them as an approver.
|
|
3521
|
+
*/
|
|
3522
|
+
this.canDecide = computed(() => {
|
|
3523
|
+
const approval = this.activeConversation()?.pendingApproval;
|
|
3524
|
+
return approval ? this.approvalRights().get(approval.approvalId)?.canDecide === true : false;
|
|
2540
3525
|
}, ...(ngDevMode ? [{ debugName: "canDecide" }] : []));
|
|
2541
3526
|
/** The member whose turn runs or waits in the open conversation, when it is someone else. */
|
|
2542
3527
|
this.activeMember = computed(() => {
|
|
@@ -2577,6 +3562,7 @@ class NhAssistantStore {
|
|
|
2577
3562
|
this.accessSubscription?.unsubscribe();
|
|
2578
3563
|
this.liveSubscription.unsubscribe();
|
|
2579
3564
|
this.stopAllTurns();
|
|
3565
|
+
this.clearAgentWorkTimers();
|
|
2580
3566
|
clearTimeout(this.refreshTimer);
|
|
2581
3567
|
});
|
|
2582
3568
|
effect(() => {
|
|
@@ -2607,6 +3593,7 @@ class NhAssistantStore {
|
|
|
2607
3593
|
this.liveSubscription.add(this.live.read$.subscribe(read => this.onLiveRead(read)));
|
|
2608
3594
|
this.liveSubscription.add(this.live.removed$.subscribe(removed => this.onLiveRemoved(removed)));
|
|
2609
3595
|
this.liveSubscription.add(this.live.events$.subscribe(event => this.onLiveEvent(event)));
|
|
3596
|
+
this.liveSubscription.add(this.live.agentWork$.subscribe(change => this.onLiveAgentWork(change)));
|
|
2610
3597
|
this.liveSubscription.add(this.live.resync$.subscribe(() => this.resync()));
|
|
2611
3598
|
if (this.browser) {
|
|
2612
3599
|
const update = () => this.pageVisibleState.set(this.document.visibilityState === 'visible' && this.document.hasFocus());
|
|
@@ -2964,12 +3951,31 @@ class NhAssistantStore {
|
|
|
2964
3951
|
this.sendingState.set(null);
|
|
2965
3952
|
});
|
|
2966
3953
|
}
|
|
2967
|
-
/**
|
|
2968
|
-
|
|
3954
|
+
/**
|
|
3955
|
+
* Approves or rejects an approval of the open conversation: `approvalId`, or the pending approval
|
|
3956
|
+
* when it is left out. A proposal of an agent's run is decided with its proposal hash through the
|
|
3957
|
+
* agent-run endpoint; other approvals resume their turn as a stream. Ignores calls the current
|
|
3958
|
+
* user may not make and repeated calls while a decision is in flight.
|
|
3959
|
+
*/
|
|
3960
|
+
decide(decision, reason, approvalId) {
|
|
2969
3961
|
const session = this.activeSession();
|
|
2970
3962
|
const conversation = session?.conversation;
|
|
2971
|
-
|
|
2972
|
-
|
|
3963
|
+
if (!session || !conversation || session.deciding || session.streaming) {
|
|
3964
|
+
return;
|
|
3965
|
+
}
|
|
3966
|
+
const approval = approvalId
|
|
3967
|
+
? pendingApprovalParts(conversation).find(part => part.approvalId === approvalId) ?? null
|
|
3968
|
+
: conversation.pendingApproval;
|
|
3969
|
+
if (!approval || this.approvalRights().get(approval.approvalId)?.canDecide !== true) {
|
|
3970
|
+
return;
|
|
3971
|
+
}
|
|
3972
|
+
const proposal = conversation.thread?.decisions.find(item => item.approvalId === approval.approvalId);
|
|
3973
|
+
if (proposal?.runId) {
|
|
3974
|
+
void this.decideProposal(conversation.id, approval, decision, reason);
|
|
3975
|
+
return;
|
|
3976
|
+
}
|
|
3977
|
+
if (conversation.pendingApproval?.approvalId !== approval.approvalId) {
|
|
3978
|
+
// Only the approval the conversation waits for resumes a turn.
|
|
2973
3979
|
return;
|
|
2974
3980
|
}
|
|
2975
3981
|
const conversationId = conversation.id;
|
|
@@ -3001,6 +4007,38 @@ class NhAssistantStore {
|
|
|
3001
4007
|
}
|
|
3002
4008
|
});
|
|
3003
4009
|
}
|
|
4010
|
+
/**
|
|
4011
|
+
* Decides a proposal of an agent's run with `POST approvals/{id}/decisions`. An approved deferred
|
|
4012
|
+
* proposal executes later as a background operation; an inline one wakes the waiting run. The
|
|
4013
|
+
* reloaded snapshot shows the outcome and the proposals that remain open.
|
|
4014
|
+
*/
|
|
4015
|
+
async decideProposal(conversationId, approval, decision, reason) {
|
|
4016
|
+
const revision = this.accountRevision;
|
|
4017
|
+
this.patchSession(conversationId, session => ({ ...session, deciding: true, error: null, notice: null }));
|
|
4018
|
+
try {
|
|
4019
|
+
const proposal = await firstValueFrom(this.agentRuns.decide(approval, decision, reason));
|
|
4020
|
+
if (revision === this.accountRevision) {
|
|
4021
|
+
if (decision === 'reject' && proposal.status === 'rejected' && proposal.canGiveFeedback === true) {
|
|
4022
|
+
this.feedbackApprovalsState.update(approvals => new Set(approvals).add(approval.approvalId));
|
|
4023
|
+
}
|
|
4024
|
+
this.patchSession(conversationId, session => ({
|
|
4025
|
+
...session,
|
|
4026
|
+
conversation: applyProposalDecision(session.conversation, approval.approvalId, proposal.status)
|
|
4027
|
+
}));
|
|
4028
|
+
}
|
|
4029
|
+
}
|
|
4030
|
+
catch (error) {
|
|
4031
|
+
if (revision === this.accountRevision) {
|
|
4032
|
+
this.setError(conversationId, error);
|
|
4033
|
+
}
|
|
4034
|
+
}
|
|
4035
|
+
finally {
|
|
4036
|
+
if (revision === this.accountRevision) {
|
|
4037
|
+
this.patchSession(conversationId, session => ({ ...session, deciding: false }));
|
|
4038
|
+
await this.reloadSession(conversationId);
|
|
4039
|
+
}
|
|
4040
|
+
}
|
|
4041
|
+
}
|
|
3004
4042
|
/** Asks the server to stop the running turn or to abandon a waiting approval of the open conversation. */
|
|
3005
4043
|
async cancel() {
|
|
3006
4044
|
const conversationId = this.activeIdState();
|
|
@@ -3333,6 +4371,8 @@ class NhAssistantStore {
|
|
|
3333
4371
|
this.queueDispatchState.set(null);
|
|
3334
4372
|
this.steeringState.set(null);
|
|
3335
4373
|
this.submittingState.set(false);
|
|
4374
|
+
this.feedbackApprovalsState.set(new Set());
|
|
4375
|
+
this.clearAgentWorkTimers();
|
|
3336
4376
|
void this.live.stop();
|
|
3337
4377
|
void this.push.deactivate().catch(() => undefined);
|
|
3338
4378
|
}
|
|
@@ -3537,6 +4577,33 @@ class NhAssistantStore {
|
|
|
3537
4577
|
conversation: applyNhAssistantLiveEvent(current.conversation, event)
|
|
3538
4578
|
}));
|
|
3539
4579
|
}
|
|
4580
|
+
/**
|
|
4581
|
+
* A run or proposal of an agent thread changed, for example a colleague decided a proposal in the
|
|
4582
|
+
* inbox: reload the thread when this tab has it open, so its approval cards show the decision.
|
|
4583
|
+
* A burst of updates reloads once. Feedback does not change the thread.
|
|
4584
|
+
*/
|
|
4585
|
+
onLiveAgentWork(change) {
|
|
4586
|
+
const conversationId = change.conversationId;
|
|
4587
|
+
const session = this.sessionsState().get(conversationId);
|
|
4588
|
+
if (change.reason === 'feedback' || !session || session.streaming) {
|
|
4589
|
+
return;
|
|
4590
|
+
}
|
|
4591
|
+
clearTimeout(this.agentWorkTimers.get(conversationId));
|
|
4592
|
+
this.agentWorkTimers.set(conversationId, setTimeout(() => {
|
|
4593
|
+
this.agentWorkTimers.delete(conversationId);
|
|
4594
|
+
const current = this.sessionsState().get(conversationId);
|
|
4595
|
+
// A decision of this tab reloads the thread itself when it ends.
|
|
4596
|
+
if (current && !current.streaming && !current.deciding) {
|
|
4597
|
+
void this.reloadSession(conversationId);
|
|
4598
|
+
}
|
|
4599
|
+
}, refreshDelayMs));
|
|
4600
|
+
}
|
|
4601
|
+
clearAgentWorkTimers() {
|
|
4602
|
+
for (const timer of this.agentWorkTimers.values()) {
|
|
4603
|
+
clearTimeout(timer);
|
|
4604
|
+
}
|
|
4605
|
+
this.agentWorkTimers.clear();
|
|
4606
|
+
}
|
|
3540
4607
|
/** After a reconnect: updates sent while disconnected are lost, so reload what is shown. */
|
|
3541
4608
|
resync() {
|
|
3542
4609
|
if (!this.enabled()) {
|
|
@@ -3688,6 +4755,48 @@ function completionNotice(conversation, errorCode) {
|
|
|
3688
4755
|
}
|
|
3689
4756
|
return null;
|
|
3690
4757
|
}
|
|
4758
|
+
/** The approvals of a conversation that still wait for a decision, each once. */
|
|
4759
|
+
/** Every approval of the conversation once, by approval id; later parts win. */
|
|
4760
|
+
function approvalParts(conversation) {
|
|
4761
|
+
const approvals = new Map();
|
|
4762
|
+
for (const message of conversation.messages) {
|
|
4763
|
+
for (const part of message.parts) {
|
|
4764
|
+
if (part.type === 'approval') {
|
|
4765
|
+
approvals.set(part.approvalId, part);
|
|
4766
|
+
}
|
|
4767
|
+
}
|
|
4768
|
+
}
|
|
4769
|
+
return [...approvals.values()];
|
|
4770
|
+
}
|
|
4771
|
+
function pendingApprovalParts(conversation) {
|
|
4772
|
+
const approvals = new Map();
|
|
4773
|
+
for (const message of conversation.messages) {
|
|
4774
|
+
for (const part of message.parts) {
|
|
4775
|
+
if (part.type === 'approval' && part.status === 'pending') {
|
|
4776
|
+
approvals.set(part.approvalId, part);
|
|
4777
|
+
}
|
|
4778
|
+
}
|
|
4779
|
+
}
|
|
4780
|
+
if (conversation.pendingApproval?.status === 'pending') {
|
|
4781
|
+
approvals.set(conversation.pendingApproval.approvalId, conversation.pendingApproval);
|
|
4782
|
+
}
|
|
4783
|
+
return [...approvals.values()];
|
|
4784
|
+
}
|
|
4785
|
+
/**
|
|
4786
|
+
* Shows the decision of an agent's proposal until the reloaded snapshot arrives: the approval's new
|
|
4787
|
+
* status, no open decision for it and, for the approval the conversation waited for, no pending one.
|
|
4788
|
+
*/
|
|
4789
|
+
function applyProposalDecision(conversation, approvalId, status) {
|
|
4790
|
+
const messages = conversation.messages.map(message => ({
|
|
4791
|
+
...message,
|
|
4792
|
+
parts: message.parts.map(part => part.type === 'approval' && part.approvalId === approvalId ? { ...part, status } : part)
|
|
4793
|
+
}));
|
|
4794
|
+
const thread = conversation.thread
|
|
4795
|
+
? { ...conversation.thread, decisions: conversation.thread.decisions.filter(item => item.approvalId !== approvalId) }
|
|
4796
|
+
: conversation.thread;
|
|
4797
|
+
const pendingApproval = conversation.pendingApproval?.approvalId === approvalId ? null : conversation.pendingApproval;
|
|
4798
|
+
return { ...conversation, messages, thread, pendingApproval };
|
|
4799
|
+
}
|
|
3691
4800
|
function toSummary(conversation) {
|
|
3692
4801
|
return {
|
|
3693
4802
|
id: conversation.id,
|
|
@@ -3736,6 +4845,7 @@ class NhAssistantPanelService {
|
|
|
3736
4845
|
constructor() {
|
|
3737
4846
|
this.overlay = inject(Overlay);
|
|
3738
4847
|
this.store = inject(NhAssistantStore);
|
|
4848
|
+
this.agentRuns = inject(NhAssistantAgentRunApiService);
|
|
3739
4849
|
this.injector = inject(Injector);
|
|
3740
4850
|
this.document = inject(DOCUMENT);
|
|
3741
4851
|
this.breakpoints = inject(BreakpointObserver);
|
|
@@ -3768,6 +4878,26 @@ class NhAssistantPanelService {
|
|
|
3768
4878
|
}
|
|
3769
4879
|
});
|
|
3770
4880
|
}
|
|
4881
|
+
/**
|
|
4882
|
+
* Opens the panel on the conversation with an agent about a subject, for example the intake
|
|
4883
|
+
* reviewer about one project. The server returns the agent's thread about the subject and creates
|
|
4884
|
+
* it without starting a run when it is missing; people then type in the conversation, which runs
|
|
4885
|
+
* a turn. Creating needs the participant role, readers may open an existing thread.
|
|
4886
|
+
*
|
|
4887
|
+
* Resolves with the lookup once the panel opens the conversation. A failure, such as
|
|
4888
|
+
* `assistant-subject-access-denied`, rejects with `NhAssistantApiError` and leaves the panel as
|
|
4889
|
+
* it is, so the caller shows the localized `messageKey` where the person asked.
|
|
4890
|
+
*/
|
|
4891
|
+
async openSubject(agentId, subject) {
|
|
4892
|
+
const lookup = await firstValueFrom(this.agentRuns.openThread(agentId, subject.type, subject.id));
|
|
4893
|
+
const conversationId = lookup.thread?.conversationId;
|
|
4894
|
+
if (!conversationId) {
|
|
4895
|
+
const code = NhAssistantClientErrorCodes.invalidResponse;
|
|
4896
|
+
throw new NhAssistantApiError(200, code, nhAssistantErrorMessageKey(code));
|
|
4897
|
+
}
|
|
4898
|
+
this.open(conversationId);
|
|
4899
|
+
return lookup;
|
|
4900
|
+
}
|
|
3771
4901
|
showPanel() {
|
|
3772
4902
|
if (this.openState()) {
|
|
3773
4903
|
return;
|
|
@@ -3871,6 +5001,324 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImpo
|
|
|
3871
5001
|
type: Injectable
|
|
3872
5002
|
}], ctorParameters: () => [] });
|
|
3873
5003
|
|
|
5004
|
+
/** How long a view waits for more agent-work updates before it reloads once. */
|
|
5005
|
+
const NH_ASSISTANT_AGENT_WORK_RELOAD_DELAY_MS = 300;
|
|
5006
|
+
/**
|
|
5007
|
+
* Calls `reload` once a burst of live agent-work updates that `matches` has settled, and after
|
|
5008
|
+
* every (re)connect of the hub, because updates sent while it was disconnected are lost. Without a
|
|
5009
|
+
* live service or a connected hub nothing arrives and the view keeps its manual refresh. Call it in
|
|
5010
|
+
* an injection context, for example a component constructor; it stops when that context ends.
|
|
5011
|
+
*/
|
|
5012
|
+
function nhAssistantReloadOnAgentWork(matches, reload) {
|
|
5013
|
+
const live = inject(NhAssistantLiveService, { optional: true });
|
|
5014
|
+
if (!live) {
|
|
5015
|
+
return;
|
|
5016
|
+
}
|
|
5017
|
+
const subscription = merge(live.agentWork$.pipe(filter(matches)), live.resync$)
|
|
5018
|
+
.pipe(debounceTime(NH_ASSISTANT_AGENT_WORK_RELOAD_DELAY_MS))
|
|
5019
|
+
.subscribe(() => reload());
|
|
5020
|
+
inject(DestroyRef).onDestroy(() => subscription.unsubscribe());
|
|
5021
|
+
}
|
|
5022
|
+
|
|
5023
|
+
/** Summaries kept for subjects that nobody shows any more, so a list that re-renders needs no request. */
|
|
5024
|
+
const maxUnwatchedSummaries = 500;
|
|
5025
|
+
/**
|
|
5026
|
+
* Counts of agent work per subject for badges in lists and on pages. Every summary asked for in the
|
|
5027
|
+
* same tick becomes one `POST subjects/{type}/summary` per subject type, in chunks of
|
|
5028
|
+
* `NH_ASSISTANT_MAX_SUBJECT_SUMMARY`. Results are cached per subject and shared by everyone who
|
|
5029
|
+
* shows it. Live agent-work updates refetch only the subjects they name, batched; a reconnect of
|
|
5030
|
+
* the hub refetches every subject shown. Nothing is requested on the server or while the assistant
|
|
5031
|
+
* is disabled or denied, and the cache clears when it becomes unavailable, for example on sign-out.
|
|
5032
|
+
*/
|
|
5033
|
+
class NhAssistantSubjectSummaryService {
|
|
5034
|
+
constructor() {
|
|
5035
|
+
this.api = inject(NhAssistantAgentRunApiService);
|
|
5036
|
+
this.store = inject(NhAssistantStore);
|
|
5037
|
+
this.live = inject(NhAssistantLiveService, { optional: true });
|
|
5038
|
+
this.browser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
5039
|
+
this.entries = new Map();
|
|
5040
|
+
/** Subject ids per type that wait for the next request. */
|
|
5041
|
+
this.queue = new Map();
|
|
5042
|
+
/** Subjects named by live updates that wait for the end of the burst. */
|
|
5043
|
+
this.changed = new Set();
|
|
5044
|
+
this.requests = new Subscription();
|
|
5045
|
+
this.liveSubscription = new Subscription();
|
|
5046
|
+
this.flushScheduled = false;
|
|
5047
|
+
this.requestRevision = 0;
|
|
5048
|
+
this.destroyed = false;
|
|
5049
|
+
effect(() => {
|
|
5050
|
+
const enabled = this.store.enabled();
|
|
5051
|
+
untracked(() => this.onAvailabilityChanged(enabled));
|
|
5052
|
+
});
|
|
5053
|
+
if (this.live) {
|
|
5054
|
+
this.liveSubscription.add(this.live.agentWork$.subscribe(change => this.onAgentWork(change)));
|
|
5055
|
+
this.liveSubscription.add(this.live.resync$.subscribe(() => this.refetchShown()));
|
|
5056
|
+
}
|
|
5057
|
+
inject(DestroyRef).onDestroy(() => {
|
|
5058
|
+
this.destroyed = true;
|
|
5059
|
+
this.liveSubscription.unsubscribe();
|
|
5060
|
+
this.requests.unsubscribe();
|
|
5061
|
+
clearTimeout(this.changedTimer);
|
|
5062
|
+
for (const entry of this.entries.values()) {
|
|
5063
|
+
entry.value.complete();
|
|
5064
|
+
}
|
|
5065
|
+
this.entries.clear();
|
|
5066
|
+
this.queue.clear();
|
|
5067
|
+
});
|
|
5068
|
+
}
|
|
5069
|
+
/**
|
|
5070
|
+
* The agent work on one subject: `null` until it is known, then every update. Subscribing shows
|
|
5071
|
+
* the subject; summaries of subjects that are shown stay current with live updates. The request
|
|
5072
|
+
* starts in the next microtask, together with every other summary asked for until then.
|
|
5073
|
+
*/
|
|
5074
|
+
summary(subjectType, subjectId) {
|
|
5075
|
+
return new Observable(subscriber => {
|
|
5076
|
+
const entry = this.watch(subjectType, subjectId);
|
|
5077
|
+
const subscription = entry.value.subscribe(subscriber);
|
|
5078
|
+
return () => {
|
|
5079
|
+
subscription.unsubscribe();
|
|
5080
|
+
this.unwatch(entry);
|
|
5081
|
+
};
|
|
5082
|
+
});
|
|
5083
|
+
}
|
|
5084
|
+
/**
|
|
5085
|
+
* Fetches the summaries of these subjects again, or of every subject of the type that is shown,
|
|
5086
|
+
* for example after the application changed them without an agent.
|
|
5087
|
+
*/
|
|
5088
|
+
refresh(subjectType, subjectIds) {
|
|
5089
|
+
const ids = subjectIds ? new Set(subjectIds) : null;
|
|
5090
|
+
for (const entry of [...this.entries.values()]) {
|
|
5091
|
+
if (entry.subjectType !== subjectType || (ids && !ids.has(entry.subjectId))) {
|
|
5092
|
+
continue;
|
|
5093
|
+
}
|
|
5094
|
+
if (entry.watchers > 0) {
|
|
5095
|
+
this.enqueue(entry);
|
|
5096
|
+
}
|
|
5097
|
+
else {
|
|
5098
|
+
this.forget(entry);
|
|
5099
|
+
}
|
|
5100
|
+
}
|
|
5101
|
+
}
|
|
5102
|
+
watch(subjectType, subjectId) {
|
|
5103
|
+
const key = summaryKey(subjectType, subjectId);
|
|
5104
|
+
let entry = this.entries.get(key);
|
|
5105
|
+
if (entry) {
|
|
5106
|
+
// The most recently shown summaries stay longest.
|
|
5107
|
+
this.entries.delete(key);
|
|
5108
|
+
}
|
|
5109
|
+
else {
|
|
5110
|
+
entry = {
|
|
5111
|
+
subjectType,
|
|
5112
|
+
subjectId,
|
|
5113
|
+
value: new BehaviorSubject(null),
|
|
5114
|
+
watchers: 0,
|
|
5115
|
+
queued: false,
|
|
5116
|
+
loaded: false,
|
|
5117
|
+
failed: false,
|
|
5118
|
+
request: 0
|
|
5119
|
+
};
|
|
5120
|
+
}
|
|
5121
|
+
this.entries.set(key, entry);
|
|
5122
|
+
entry.watchers++;
|
|
5123
|
+
if (!entry.loaded || entry.failed) {
|
|
5124
|
+
this.enqueue(entry);
|
|
5125
|
+
}
|
|
5126
|
+
if (this.browser) {
|
|
5127
|
+
void this.store.initialize();
|
|
5128
|
+
}
|
|
5129
|
+
return entry;
|
|
5130
|
+
}
|
|
5131
|
+
unwatch(entry) {
|
|
5132
|
+
entry.watchers = Math.max(0, entry.watchers - 1);
|
|
5133
|
+
if (entry.watchers > 0) {
|
|
5134
|
+
return;
|
|
5135
|
+
}
|
|
5136
|
+
let unwatched = 0;
|
|
5137
|
+
for (const item of this.entries.values()) {
|
|
5138
|
+
if (item.watchers === 0) {
|
|
5139
|
+
unwatched++;
|
|
5140
|
+
}
|
|
5141
|
+
}
|
|
5142
|
+
for (const item of [...this.entries.values()]) {
|
|
5143
|
+
if (unwatched <= maxUnwatchedSummaries) {
|
|
5144
|
+
break;
|
|
5145
|
+
}
|
|
5146
|
+
if (item.watchers === 0) {
|
|
5147
|
+
this.forget(item);
|
|
5148
|
+
unwatched--;
|
|
5149
|
+
}
|
|
5150
|
+
}
|
|
5151
|
+
}
|
|
5152
|
+
isCurrent(entry) {
|
|
5153
|
+
return this.entries.get(summaryKey(entry.subjectType, entry.subjectId)) === entry;
|
|
5154
|
+
}
|
|
5155
|
+
/** Drops a summary nobody shows; the next subscriber fetches it again. */
|
|
5156
|
+
forget(entry) {
|
|
5157
|
+
if (this.isCurrent(entry)) {
|
|
5158
|
+
this.entries.delete(summaryKey(entry.subjectType, entry.subjectId));
|
|
5159
|
+
}
|
|
5160
|
+
this.queue.get(entry.subjectType)?.delete(entry.subjectId);
|
|
5161
|
+
entry.value.complete();
|
|
5162
|
+
}
|
|
5163
|
+
enqueue(entry) {
|
|
5164
|
+
if (entry.queued) {
|
|
5165
|
+
return;
|
|
5166
|
+
}
|
|
5167
|
+
entry.queued = true;
|
|
5168
|
+
let ids = this.queue.get(entry.subjectType);
|
|
5169
|
+
if (!ids) {
|
|
5170
|
+
ids = new Set();
|
|
5171
|
+
this.queue.set(entry.subjectType, ids);
|
|
5172
|
+
}
|
|
5173
|
+
ids.add(entry.subjectId);
|
|
5174
|
+
this.scheduleFlush();
|
|
5175
|
+
}
|
|
5176
|
+
scheduleFlush() {
|
|
5177
|
+
if (this.flushScheduled) {
|
|
5178
|
+
return;
|
|
5179
|
+
}
|
|
5180
|
+
this.flushScheduled = true;
|
|
5181
|
+
queueMicrotask(() => {
|
|
5182
|
+
this.flushScheduled = false;
|
|
5183
|
+
this.flush();
|
|
5184
|
+
});
|
|
5185
|
+
}
|
|
5186
|
+
/** Sends the queued subjects: one request per type and chunk. Waits while the assistant is unavailable. */
|
|
5187
|
+
flush() {
|
|
5188
|
+
if (this.destroyed || !this.browser || !this.store.enabled()) {
|
|
5189
|
+
return;
|
|
5190
|
+
}
|
|
5191
|
+
for (const [subjectType, ids] of [...this.queue]) {
|
|
5192
|
+
this.queue.delete(subjectType);
|
|
5193
|
+
const subjectIds = [...ids];
|
|
5194
|
+
for (let start = 0; start < subjectIds.length; start += NH_ASSISTANT_MAX_SUBJECT_SUMMARY) {
|
|
5195
|
+
this.send(subjectType, subjectIds.slice(start, start + NH_ASSISTANT_MAX_SUBJECT_SUMMARY));
|
|
5196
|
+
}
|
|
5197
|
+
}
|
|
5198
|
+
}
|
|
5199
|
+
send(subjectType, subjectIds) {
|
|
5200
|
+
const revision = ++this.requestRevision;
|
|
5201
|
+
const entries = [];
|
|
5202
|
+
for (const subjectId of subjectIds) {
|
|
5203
|
+
const entry = this.entries.get(summaryKey(subjectType, subjectId));
|
|
5204
|
+
if (entry) {
|
|
5205
|
+
entry.queued = false;
|
|
5206
|
+
entry.request = revision;
|
|
5207
|
+
entries.push(entry);
|
|
5208
|
+
}
|
|
5209
|
+
}
|
|
5210
|
+
if (entries.length === 0) {
|
|
5211
|
+
return;
|
|
5212
|
+
}
|
|
5213
|
+
const subscription = this.api.summarizeSubjects(subjectType, entries.map(entry => entry.subjectId)).subscribe({
|
|
5214
|
+
next: list => {
|
|
5215
|
+
const items = new Map(list.items.map(item => [item.subjectId, item]));
|
|
5216
|
+
for (const entry of entries) {
|
|
5217
|
+
if (entry.request !== revision || !this.isCurrent(entry)) {
|
|
5218
|
+
continue;
|
|
5219
|
+
}
|
|
5220
|
+
entry.loaded = true;
|
|
5221
|
+
entry.failed = false;
|
|
5222
|
+
entry.value.next(items.get(entry.subjectId) ?? emptySummary(entry.subjectId));
|
|
5223
|
+
}
|
|
5224
|
+
},
|
|
5225
|
+
error: () => {
|
|
5226
|
+
// A badge without a count is better than an error in a list; the next live update,
|
|
5227
|
+
// reconnect or subscriber tries again.
|
|
5228
|
+
for (const entry of entries) {
|
|
5229
|
+
if (entry.request === revision) {
|
|
5230
|
+
entry.failed = true;
|
|
5231
|
+
}
|
|
5232
|
+
}
|
|
5233
|
+
}
|
|
5234
|
+
});
|
|
5235
|
+
this.requests.add(subscription);
|
|
5236
|
+
}
|
|
5237
|
+
onAgentWork(change) {
|
|
5238
|
+
if (!change.subjectType || !change.subjectId) {
|
|
5239
|
+
return;
|
|
5240
|
+
}
|
|
5241
|
+
const key = summaryKey(change.subjectType, change.subjectId);
|
|
5242
|
+
const entry = this.entries.get(key);
|
|
5243
|
+
if (!entry) {
|
|
5244
|
+
return;
|
|
5245
|
+
}
|
|
5246
|
+
if (entry.watchers === 0) {
|
|
5247
|
+
this.forget(entry);
|
|
5248
|
+
return;
|
|
5249
|
+
}
|
|
5250
|
+
this.changed.add(key);
|
|
5251
|
+
if (this.changedTimer === undefined) {
|
|
5252
|
+
this.changedTimer = setTimeout(() => this.refetchChanged(), NH_ASSISTANT_AGENT_WORK_RELOAD_DELAY_MS);
|
|
5253
|
+
}
|
|
5254
|
+
}
|
|
5255
|
+
/** Refetches the shown subjects of a burst of live updates, together. */
|
|
5256
|
+
refetchChanged() {
|
|
5257
|
+
this.changedTimer = undefined;
|
|
5258
|
+
const keys = [...this.changed];
|
|
5259
|
+
this.changed.clear();
|
|
5260
|
+
for (const key of keys) {
|
|
5261
|
+
const entry = this.entries.get(key);
|
|
5262
|
+
if (entry && entry.watchers > 0) {
|
|
5263
|
+
this.enqueue(entry);
|
|
5264
|
+
}
|
|
5265
|
+
}
|
|
5266
|
+
}
|
|
5267
|
+
/** After a reconnect updates may have been missed: refetch what is shown, forget the rest. */
|
|
5268
|
+
refetchShown() {
|
|
5269
|
+
for (const entry of [...this.entries.values()]) {
|
|
5270
|
+
if (entry.watchers > 0) {
|
|
5271
|
+
this.enqueue(entry);
|
|
5272
|
+
}
|
|
5273
|
+
else {
|
|
5274
|
+
this.forget(entry);
|
|
5275
|
+
}
|
|
5276
|
+
}
|
|
5277
|
+
}
|
|
5278
|
+
onAvailabilityChanged(enabled) {
|
|
5279
|
+
if (enabled) {
|
|
5280
|
+
for (const entry of this.entries.values()) {
|
|
5281
|
+
if (entry.watchers > 0 && (!entry.loaded || entry.failed)) {
|
|
5282
|
+
this.enqueue(entry);
|
|
5283
|
+
}
|
|
5284
|
+
}
|
|
5285
|
+
this.scheduleFlush();
|
|
5286
|
+
return;
|
|
5287
|
+
}
|
|
5288
|
+
// Signed out, denied or disabled: no counts of a previous account may remain.
|
|
5289
|
+
this.requests.unsubscribe();
|
|
5290
|
+
this.requests = new Subscription();
|
|
5291
|
+
clearTimeout(this.changedTimer);
|
|
5292
|
+
this.changedTimer = undefined;
|
|
5293
|
+
this.changed.clear();
|
|
5294
|
+
this.queue.clear();
|
|
5295
|
+
for (const entry of [...this.entries.values()]) {
|
|
5296
|
+
if (entry.watchers === 0) {
|
|
5297
|
+
this.forget(entry);
|
|
5298
|
+
continue;
|
|
5299
|
+
}
|
|
5300
|
+
entry.queued = false;
|
|
5301
|
+
entry.loaded = false;
|
|
5302
|
+
entry.failed = false;
|
|
5303
|
+
entry.request = 0;
|
|
5304
|
+
if (entry.value.value !== null) {
|
|
5305
|
+
entry.value.next(null);
|
|
5306
|
+
}
|
|
5307
|
+
}
|
|
5308
|
+
}
|
|
5309
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantSubjectSummaryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
5310
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantSubjectSummaryService }); }
|
|
5311
|
+
}
|
|
5312
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantSubjectSummaryService, decorators: [{
|
|
5313
|
+
type: Injectable
|
|
5314
|
+
}], ctorParameters: () => [] });
|
|
5315
|
+
function summaryKey(subjectType, subjectId) {
|
|
5316
|
+
return `${subjectType}\u0000${subjectId}`;
|
|
5317
|
+
}
|
|
5318
|
+
function emptySummary(subjectId) {
|
|
5319
|
+
return { subjectId, openProposals: 0, proposalsToDecide: 0, runActive: false };
|
|
5320
|
+
}
|
|
5321
|
+
|
|
3874
5322
|
/**
|
|
3875
5323
|
* Registers the assistant client, store and panel for one application or route scope.
|
|
3876
5324
|
* Call it once in the root providers; a route may call it again to host an isolated
|
|
@@ -3883,11 +5331,13 @@ function provideNhAssistant(config) {
|
|
|
3883
5331
|
NhAssistantTransport,
|
|
3884
5332
|
NhAssistantApiService,
|
|
3885
5333
|
NhAssistantAdminApiService,
|
|
5334
|
+
NhAssistantAgentRunApiService,
|
|
3886
5335
|
NhAssistantLiveService,
|
|
3887
5336
|
NhAssistantPushService,
|
|
3888
5337
|
NhAssistantStore,
|
|
3889
5338
|
NhAssistantUiState,
|
|
3890
5339
|
NhAssistantPanelService,
|
|
5340
|
+
NhAssistantSubjectSummaryService,
|
|
3891
5341
|
...(config.translations === 'host'
|
|
3892
5342
|
? []
|
|
3893
5343
|
: [provideEnvironmentInitializer(() => inject(NhAssistantTranslationMerger).start())])
|
|
@@ -4189,7 +5639,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImpo
|
|
|
4189
5639
|
`, 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
5640
|
}], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: true }] }], conversationSelect: [{ type: i0.Output, args: ["conversationSelect"] }] } });
|
|
4191
5641
|
|
|
4192
|
-
let nextId$
|
|
5642
|
+
let nextId$8 = 0;
|
|
4193
5643
|
/**
|
|
4194
5644
|
* Chooses the agent for new conversations. Agent names come from the host's translation
|
|
4195
5645
|
* keys; agents created by an administrator carry literal text, which is shown as is.
|
|
@@ -4202,7 +5652,7 @@ class NhAssistantAgentPickerComponent {
|
|
|
4202
5652
|
/** Compact prompt-bar control; capability information remains in the tooltip and accessible description. */
|
|
4203
5653
|
this.compact = input(false, ...(ngDevMode ? [{ debugName: "compact" }] : []));
|
|
4204
5654
|
this.agentChange = output();
|
|
4205
|
-
this.selectId = `nh-assistant-agent-picker-${nextId$
|
|
5655
|
+
this.selectId = `nh-assistant-agent-picker-${nextId$8++}`;
|
|
4206
5656
|
this.selected = computed(() => this.agents().find(agent => agent.id === this.selectedAgentId()) ?? null, ...(ngDevMode ? [{ debugName: "selected" }] : []));
|
|
4207
5657
|
}
|
|
4208
5658
|
onChange(event) {
|
|
@@ -4254,7 +5704,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImpo
|
|
|
4254
5704
|
`, 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
5705
|
}], 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
5706
|
|
|
4257
|
-
let nextId$
|
|
5707
|
+
let nextId$7 = 0;
|
|
4258
5708
|
/**
|
|
4259
5709
|
* Message input. Enter sends when sending is available; Shift+Enter inserts a new line.
|
|
4260
5710
|
* Drafting remains available while the assistant runs or waits for approval.
|
|
@@ -4285,7 +5735,7 @@ class NhAssistantComposerComponent {
|
|
|
4285
5735
|
this.textarea = viewChild.required('textarea');
|
|
4286
5736
|
this.ownedFocus = false;
|
|
4287
5737
|
this.previousBusy = false;
|
|
4288
|
-
this.inputId = `nh-assistant-composer-${nextId$
|
|
5738
|
+
this.inputId = `nh-assistant-composer-${nextId$7++}`;
|
|
4289
5739
|
this.hintId = `${this.inputId}-hint`;
|
|
4290
5740
|
this.text = signal('', ...(ngDevMode ? [{ debugName: "text" }] : []));
|
|
4291
5741
|
this.length = computed(() => this.text().trim().length, ...(ngDevMode ? [{ debugName: "length" }] : []));
|
|
@@ -4409,7 +5859,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImpo
|
|
|
4409
5859
|
|
|
4410
5860
|
/** Maximum length of the user's own instructions. */
|
|
4411
5861
|
const NH_ASSISTANT_MAX_CUSTOM_INSTRUCTIONS = 1_000;
|
|
4412
|
-
let nextId$
|
|
5862
|
+
let nextId$6 = 0;
|
|
4413
5863
|
/**
|
|
4414
5864
|
* The user's own assistant preferences: desktop notifications, style, form of address,
|
|
4415
5865
|
* answer length and optional instructions. Notifications apply immediately; the other
|
|
@@ -4424,7 +5874,7 @@ class NhAssistantPreferencesComponent {
|
|
|
4424
5874
|
this.closed = output();
|
|
4425
5875
|
/** Emitted with the stored preferences after a successful save. */
|
|
4426
5876
|
this.saved = output();
|
|
4427
|
-
this.id = `nh-assistant-preferences-${nextId$
|
|
5877
|
+
this.id = `nh-assistant-preferences-${nextId$6++}`;
|
|
4428
5878
|
this.styles = ['default', 'direct', 'personal', 'detailed'];
|
|
4429
5879
|
this.addressForms = ['informal', 'formal'];
|
|
4430
5880
|
this.responseLengths = ['short', 'normal', 'long'];
|
|
@@ -4535,7 +5985,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImpo
|
|
|
4535
5985
|
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
5986
|
}], ctorParameters: () => [], propDecorators: { closed: [{ type: i0.Output, args: ["closed"] }], saved: [{ type: i0.Output, args: ["saved"] }] } });
|
|
4537
5987
|
|
|
4538
|
-
let nextId$
|
|
5988
|
+
let nextId$5 = 0;
|
|
4539
5989
|
const searchDelayMs = 250;
|
|
4540
5990
|
/**
|
|
4541
5991
|
* Sharing of the open conversation. The owner creates, copies, renews or stops an invitation
|
|
@@ -4551,7 +6001,7 @@ class NhAssistantShareComponent {
|
|
|
4551
6001
|
this.closed = output();
|
|
4552
6002
|
/** Show the invitation link, participants, or both (the default for custom hosts). */
|
|
4553
6003
|
this.section = input('all', ...(ngDevMode ? [{ debugName: "section" }] : []));
|
|
4554
|
-
this.id = `nh-assistant-share-${nextId$
|
|
6004
|
+
this.id = `nh-assistant-share-${nextId$5++}`;
|
|
4555
6005
|
this.conversation = this.store.activeConversation;
|
|
4556
6006
|
this.isOwner = this.store.isOwner;
|
|
4557
6007
|
this.busy = signal(false, ...(ngDevMode ? [{ debugName: "busy" }] : []));
|
|
@@ -4796,6 +6246,134 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImpo
|
|
|
4796
6246
|
args: [{ name: 'nhAssistantPartGroups', standalone: true }]
|
|
4797
6247
|
}] });
|
|
4798
6248
|
|
|
6249
|
+
/** Converts any failure into a user-facing code and translation key, never raw text. */
|
|
6250
|
+
function toNhAssistantError(error) {
|
|
6251
|
+
if (error instanceof NhAssistantApiError) {
|
|
6252
|
+
return { code: error.code, messageKey: error.messageKey };
|
|
6253
|
+
}
|
|
6254
|
+
return { code: NhAssistantClientErrorCodes.server, messageKey: nhAssistantErrorMessageKey(NhAssistantClientErrorCodes.server) };
|
|
6255
|
+
}
|
|
6256
|
+
/** Translation keys for an error, from the most to the least specific. */
|
|
6257
|
+
function nhAssistantErrorKeys(error) {
|
|
6258
|
+
return error ? [error.messageKey, nhAssistantErrorMessageKey(error.code), 'nh-assistant.errors.generic'] : [];
|
|
6259
|
+
}
|
|
6260
|
+
/** Dash-case identifiers as the contract requires for agent and MCP server ids. */
|
|
6261
|
+
const NH_ASSISTANT_DASH_CASE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
6262
|
+
|
|
6263
|
+
const notAcceptedCode = 'assistant-feedback-not-accepted';
|
|
6264
|
+
let nextId$4 = 0;
|
|
6265
|
+
/**
|
|
6266
|
+
* Asks, after a rejection, what was wrong with one or more agent proposals: a category from a fixed
|
|
6267
|
+
* set and an optional text. It is optional and never blocks the decision; people skip it with one
|
|
6268
|
+
* action. One proposal is sent with `PUT approvals/{id}/feedback`, several rejected together with
|
|
6269
|
+
* `POST approvals/feedback` as bulk feedback. With `initial` it changes the caller's own feedback.
|
|
6270
|
+
* After saving it thanks the person in place of the form; hosts remove it after `dismissed`.
|
|
6271
|
+
*/
|
|
6272
|
+
class NhAssistantProposalFeedbackComponent {
|
|
6273
|
+
constructor() {
|
|
6274
|
+
this.api = inject(NhAssistantAgentRunApiService);
|
|
6275
|
+
this.host = inject(ElementRef);
|
|
6276
|
+
this.document = inject(DOCUMENT);
|
|
6277
|
+
this.injector = inject(Injector);
|
|
6278
|
+
this.thanks = viewChild('thanks', ...(ngDevMode ? [{ debugName: "thanks" }] : []));
|
|
6279
|
+
/** The proposals the feedback is about; several when they were rejected together. */
|
|
6280
|
+
this.approvalIds = input.required(...(ngDevMode ? [{ debugName: "approvalIds" }] : []));
|
|
6281
|
+
/** The caller's current feedback, to change it; `null` for new feedback. */
|
|
6282
|
+
this.initial = input(null, ...(ngDevMode ? [{ debugName: "initial" }] : []));
|
|
6283
|
+
/** Emits after the server stored the feedback. */
|
|
6284
|
+
this.saved = output();
|
|
6285
|
+
/** Emits when the person skips the question. */
|
|
6286
|
+
this.dismissed = output();
|
|
6287
|
+
this.id = `nh-assistant-proposal-feedback-${nextId$4++}`;
|
|
6288
|
+
this.categories = NH_ASSISTANT_FEEDBACK_CATEGORIES;
|
|
6289
|
+
this.maxText = NH_ASSISTANT_MAX_FEEDBACK_TEXT;
|
|
6290
|
+
/** A new question when the host passes other proposals or other initial feedback. */
|
|
6291
|
+
this.question = computed(() => ({ approvalIds: this.approvalIds(), initial: this.initial() }), ...(ngDevMode ? [{ debugName: "question" }] : []));
|
|
6292
|
+
this.category = linkedSignal(() => this.question().initial?.category ?? null, ...(ngDevMode ? [{ debugName: "category" }] : []));
|
|
6293
|
+
this.text = linkedSignal(() => this.question().initial?.text ?? '', ...(ngDevMode ? [{ debugName: "text" }] : []));
|
|
6294
|
+
this.busy = signal(false, ...(ngDevMode ? [{ debugName: "busy" }] : []));
|
|
6295
|
+
this.done = linkedSignal(...(ngDevMode ? [{ debugName: "done", source: this.question, computation: () => false }] : [{ source: this.question, computation: () => false }]));
|
|
6296
|
+
/** Set when a bulk request stored the feedback for only part of the proposals. */
|
|
6297
|
+
this.partial = linkedSignal(...(ngDevMode ? [{ debugName: "partial", source: this.question,
|
|
6298
|
+
computation: () => null }] : [{
|
|
6299
|
+
source: this.question,
|
|
6300
|
+
computation: () => null
|
|
6301
|
+
}]));
|
|
6302
|
+
this.error = linkedSignal(...(ngDevMode ? [{ debugName: "error", source: this.question, computation: () => null }] : [{ source: this.question, computation: () => null }]));
|
|
6303
|
+
this.errorKeys = computed(() => nhAssistantErrorKeys(this.error()), ...(ngDevMode ? [{ debugName: "errorKeys" }] : []));
|
|
6304
|
+
this.count = computed(() => new Set(this.approvalIds()).size, ...(ngDevMode ? [{ debugName: "count" }] : []));
|
|
6305
|
+
this.textLength = computed(() => this.text().length, ...(ngDevMode ? [{ debugName: "textLength" }] : []));
|
|
6306
|
+
this.tooLong = computed(() => this.textLength() > this.maxText, ...(ngDevMode ? [{ debugName: "tooLong" }] : []));
|
|
6307
|
+
this.canSend = computed(() => this.category() !== null && this.count() > 0 && !this.busy() && !this.tooLong(), ...(ngDevMode ? [{ debugName: "canSend" }] : []));
|
|
6308
|
+
inject(DestroyRef).onDestroy(() => this.subscription?.unsubscribe());
|
|
6309
|
+
}
|
|
6310
|
+
choose(category) {
|
|
6311
|
+
this.category.set(category);
|
|
6312
|
+
this.error.set(null);
|
|
6313
|
+
}
|
|
6314
|
+
onText(event) {
|
|
6315
|
+
this.text.set(event.target.value);
|
|
6316
|
+
}
|
|
6317
|
+
dismiss() {
|
|
6318
|
+
this.subscription?.unsubscribe();
|
|
6319
|
+
this.busy.set(false);
|
|
6320
|
+
this.dismissed.emit();
|
|
6321
|
+
}
|
|
6322
|
+
send() {
|
|
6323
|
+
const category = this.category();
|
|
6324
|
+
if (!this.canSend() || category === null) {
|
|
6325
|
+
return;
|
|
6326
|
+
}
|
|
6327
|
+
const approvalIds = [...new Set(this.approvalIds())];
|
|
6328
|
+
const text = this.text().trim() || null;
|
|
6329
|
+
this.busy.set(true);
|
|
6330
|
+
this.error.set(null);
|
|
6331
|
+
this.subscription?.unsubscribe();
|
|
6332
|
+
this.subscription = this.request(approvalIds, category, text).subscribe({
|
|
6333
|
+
next: outcomes => {
|
|
6334
|
+
this.busy.set(false);
|
|
6335
|
+
const stored = outcomes.filter(outcome => outcome.succeeded).map(outcome => outcome.approvalId);
|
|
6336
|
+
if (stored.length === 0) {
|
|
6337
|
+
const code = outcomes.find(outcome => outcome.code)?.code ?? notAcceptedCode;
|
|
6338
|
+
this.error.set({ code, messageKey: nhAssistantErrorMessageKey(code) });
|
|
6339
|
+
return;
|
|
6340
|
+
}
|
|
6341
|
+
// The form disappears; keep keyboard focus on the confirmation instead of losing it.
|
|
6342
|
+
const focused = this.host.nativeElement.contains(this.document.activeElement);
|
|
6343
|
+
this.partial.set(stored.length < approvalIds.length ? { stored: stored.length, total: approvalIds.length } : null);
|
|
6344
|
+
this.done.set(true);
|
|
6345
|
+
if (focused) {
|
|
6346
|
+
afterNextRender(() => this.thanks()?.nativeElement.focus(), { injector: this.injector });
|
|
6347
|
+
}
|
|
6348
|
+
this.saved.emit({ approvalIds: stored, category, text });
|
|
6349
|
+
},
|
|
6350
|
+
error: failure => {
|
|
6351
|
+
this.busy.set(false);
|
|
6352
|
+
this.error.set(toNhAssistantError(failure));
|
|
6353
|
+
}
|
|
6354
|
+
});
|
|
6355
|
+
}
|
|
6356
|
+
/** One proposal through its own endpoint; several in requests of at most the bulk limit. */
|
|
6357
|
+
request(approvalIds, category, text) {
|
|
6358
|
+
if (approvalIds.length === 1) {
|
|
6359
|
+
const [approvalId] = approvalIds;
|
|
6360
|
+
return this.api.giveFeedback(approvalId, category, text).pipe(map(() => [{ approvalId, succeeded: true, code: null }]));
|
|
6361
|
+
}
|
|
6362
|
+
const chunks = [];
|
|
6363
|
+
for (let index = 0; index < approvalIds.length; index += NH_ASSISTANT_MAX_BULK_FEEDBACK) {
|
|
6364
|
+
chunks.push(approvalIds.slice(index, index + NH_ASSISTANT_MAX_BULK_FEEDBACK));
|
|
6365
|
+
}
|
|
6366
|
+
return forkJoin(chunks.map(chunk => this.api.giveFeedbackMany(chunk, category, text))).pipe(map(responses => responses.flatMap(response => response.results)));
|
|
6367
|
+
}
|
|
6368
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantProposalFeedbackComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
6369
|
+
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 }); }
|
|
6370
|
+
}
|
|
6371
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantProposalFeedbackComponent, decorators: [{
|
|
6372
|
+
type: Component,
|
|
6373
|
+
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"] }]
|
|
6374
|
+
}], 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"] }] } });
|
|
6375
|
+
|
|
6376
|
+
let nextId$3 = 0;
|
|
4799
6377
|
/** Formats remaining milliseconds as `m:ss`, or `h:mm:ss` above one hour. */
|
|
4800
6378
|
function formatNhAssistantCountdown(milliseconds) {
|
|
4801
6379
|
const totalSeconds = Math.max(0, Math.ceil(milliseconds / 1000));
|
|
@@ -4810,7 +6388,9 @@ function formatNhAssistantCountdown(milliseconds) {
|
|
|
4810
6388
|
/**
|
|
4811
6389
|
* Asks the user to approve or reject a proposed mutation. Shows the summary, targets,
|
|
4812
6390
|
* 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.
|
|
6391
|
+
* the host reports that it is no longer deciding. After the user rejected an agent's proposal
|
|
6392
|
+
* (`canGiveFeedback`), a low-key action asks what was wrong. A proposal that closed because it no
|
|
6393
|
+
* longer fits shows why, such as "Already done", with the application's explanation.
|
|
4814
6394
|
*/
|
|
4815
6395
|
class NhAssistantApprovalCardComponent {
|
|
4816
6396
|
constructor() {
|
|
@@ -4818,23 +6398,71 @@ class NhAssistantApprovalCardComponent {
|
|
|
4818
6398
|
/** True while the host submits a decision; disables both buttons. */
|
|
4819
6399
|
this.deciding = input(false, ...(ngDevMode ? [{ debugName: "deciding" }] : []));
|
|
4820
6400
|
/**
|
|
4821
|
-
* False when the
|
|
4822
|
-
*
|
|
6401
|
+
* False when the current user may not decide: in a chat the approval belongs to another
|
|
6402
|
+
* participant's turn, in an agent thread the user is no approver of the proposal.
|
|
4823
6403
|
*/
|
|
4824
6404
|
this.canDecide = input(true, ...(ngDevMode ? [{ debugName: "canDecide" }] : []));
|
|
4825
|
-
/** Name of the participant who decides, shown when the current user cannot. */
|
|
6405
|
+
/** Name of the participant who decides a `turn` approval, shown when the current user cannot. */
|
|
4826
6406
|
this.decisionOwner = input(null, ...(ngDevMode ? [{ debugName: "decisionOwner" }] : []));
|
|
6407
|
+
/**
|
|
6408
|
+
* Who decides when the current user cannot: the author of the turn (`turn`, the default), an
|
|
6409
|
+
* approver of the agent thread's subject (`approver`), or another approver because the current
|
|
6410
|
+
* user started the work (`other-approver`).
|
|
6411
|
+
*/
|
|
6412
|
+
this.waitingFor = input('turn', ...(ngDevMode ? [{ debugName: "waitingFor" }] : []));
|
|
6413
|
+
/**
|
|
6414
|
+
* True when the current user rejected this agent proposal and may say what was wrong, for example
|
|
6415
|
+
* from `NhAssistantStore.approvalRights`. Chat approvals never offer feedback.
|
|
6416
|
+
*/
|
|
6417
|
+
this.canGiveFeedback = input(false, ...(ngDevMode ? [{ debugName: "canGiveFeedback" }] : []));
|
|
4827
6418
|
this.decide = output();
|
|
4828
6419
|
this.now = signal(Date.now(), ...(ngDevMode ? [{ debugName: "now" }] : []));
|
|
4829
6420
|
this.submitted = signal(false, ...(ngDevMode ? [{ debugName: "submitted" }] : []));
|
|
6421
|
+
/** The feedback question is expanded. */
|
|
6422
|
+
this.feedbackOpen = signal(false, ...(ngDevMode ? [{ debugName: "feedbackOpen" }] : []));
|
|
6423
|
+
/** The feedback was stored; the question thanks the person and the action disappears. */
|
|
6424
|
+
this.feedbackSaved = signal(false, ...(ngDevMode ? [{ debugName: "feedbackSaved" }] : []));
|
|
6425
|
+
this.feedbackToggle = viewChild('feedbackToggle', ...(ngDevMode ? [{ debugName: "feedbackToggle" }] : []));
|
|
4830
6426
|
this.remainingMs = computed(() => Date.parse(this.approval().expiresAt) - this.now(), ...(ngDevMode ? [{ debugName: "remainingMs" }] : []));
|
|
4831
6427
|
this.expired = computed(() => this.approval().status === 'expired' ||
|
|
4832
6428
|
(this.approval().status === 'pending' && this.remainingMs() <= 0), ...(ngDevMode ? [{ debugName: "expired" }] : []));
|
|
4833
6429
|
this.effectiveStatus = computed(() => this.expired() ? 'expired' : this.approval().status, ...(ngDevMode ? [{ debugName: "effectiveStatus" }] : []));
|
|
6430
|
+
/**
|
|
6431
|
+
* Translation key of the status: the kind of a stale proposal, "replaced" for a superseded one,
|
|
6432
|
+
* otherwise the status itself.
|
|
6433
|
+
*/
|
|
6434
|
+
this.statusKey = computed(() => {
|
|
6435
|
+
const kind = this.approval().staleKind;
|
|
6436
|
+
if (this.effectiveStatus() === 'stale' && kind) {
|
|
6437
|
+
return `nh-assistant.approval.stale-kind.${kind}`;
|
|
6438
|
+
}
|
|
6439
|
+
return this.effectiveStatus() === 'withdrawn' && this.approval().supersededByApprovalId
|
|
6440
|
+
? 'nh-assistant.approval.superseded'
|
|
6441
|
+
: `nh-assistant.approval.status.${this.effectiveStatus()}`;
|
|
6442
|
+
}, ...(ngDevMode ? [{ debugName: "statusKey" }] : []));
|
|
6443
|
+
/** Why the agent's rules proposed it; model proposals have none. */
|
|
6444
|
+
this.rationale = computed(() => this.approval().rationale ?? null, ...(ngDevMode ? [{ debugName: "rationale" }] : []));
|
|
6445
|
+
/** The application's explanation of a stale proposal, such as who already did the work. */
|
|
6446
|
+
this.staleExplanation = computed(() => this.effectiveStatus() === 'stale' ? this.approval().staleExplanation ?? null : null, ...(ngDevMode ? [{ debugName: "staleExplanation" }] : []));
|
|
4834
6447
|
this.actionable = computed(() => this.effectiveStatus() === 'pending', ...(ngDevMode ? [{ debugName: "actionable" }] : []));
|
|
6448
|
+
this.succeeded = computed(() => ['approved', 'executing', 'executed', 'resolved'].includes(this.effectiveStatus()), ...(ngDevMode ? [{ debugName: "succeeded" }] : []));
|
|
4835
6449
|
this.disabled = computed(() => !this.actionable() || !this.canDecide() || this.deciding() || this.submitted(), ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
4836
6450
|
this.countdown = computed(() => formatNhAssistantCountdown(this.remainingMs()), ...(ngDevMode ? [{ debugName: "countdown" }] : []));
|
|
4837
6451
|
this.presentedSummary = computed(() => this.approval().presentation?.summary ?? null, ...(ngDevMode ? [{ debugName: "presentedSummary" }] : []));
|
|
6452
|
+
this.feedbackAvailable = computed(() => this.canGiveFeedback() && this.effectiveStatus() === 'rejected', ...(ngDevMode ? [{ debugName: "feedbackAvailable" }] : []));
|
|
6453
|
+
this.feedbackIds = computed(() => [this.approval().approvalId], ...(ngDevMode ? [{ debugName: "feedbackIds" }] : []));
|
|
6454
|
+
this.feedbackPanelId = `nh-assistant-approval-feedback-${nextId$3++}`;
|
|
6455
|
+
/** Translation key of the text shown instead of the buttons when the current user cannot decide. */
|
|
6456
|
+
this.waitingKey = computed(() => {
|
|
6457
|
+
switch (this.waitingFor()) {
|
|
6458
|
+
case 'approver':
|
|
6459
|
+
return 'nh-assistant.approval.waiting-for-approver';
|
|
6460
|
+
case 'other-approver':
|
|
6461
|
+
return 'nh-assistant.approval.waiting-for-other-approver';
|
|
6462
|
+
default:
|
|
6463
|
+
return this.decisionOwner() ? 'nh-assistant.approval.waiting-for' : 'nh-assistant.approval.waiting-for-participant';
|
|
6464
|
+
}
|
|
6465
|
+
}, ...(ngDevMode ? [{ debugName: "waitingKey" }] : []));
|
|
4838
6466
|
// Tick only while the approval is pending and not yet expired.
|
|
4839
6467
|
effect(onCleanup => {
|
|
4840
6468
|
if (this.approval().status !== 'pending') {
|
|
@@ -4863,15 +6491,26 @@ class NhAssistantApprovalCardComponent {
|
|
|
4863
6491
|
this.submitted.set(true);
|
|
4864
6492
|
this.decide.emit(decision);
|
|
4865
6493
|
}
|
|
6494
|
+
toggleFeedback() {
|
|
6495
|
+
this.feedbackOpen.update(open => !open);
|
|
6496
|
+
}
|
|
6497
|
+
onFeedbackSaved() {
|
|
6498
|
+
this.feedbackSaved.set(true);
|
|
6499
|
+
}
|
|
6500
|
+
/** Collapses the question and returns focus to the action that opened it. */
|
|
6501
|
+
onFeedbackDismissed() {
|
|
6502
|
+
this.feedbackOpen.set(false);
|
|
6503
|
+
this.feedbackToggle()?.nativeElement.focus();
|
|
6504
|
+
}
|
|
4866
6505
|
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 }); }
|
|
6506
|
+
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\">{{ statusKey() | translate }}</span>\n </header>\n\n @if (staleExplanation(); as explanation) {\n <p class=\"stale-explanation\">{{ explanation }}</p>\n }\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 @if (rationale(); as why) {\n <p class=\"rationale\">{{ 'nh-assistant.approval.rationale' | translate: { rationale: why } }}</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]),:host([data-status=stale]){--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}.stale-explanation{margin:8px 0 0;border-radius:8px;padding:8px 10px;color:var(--nha-text);background:var(--nha-surface-strong);font-size:13px;overflow-wrap:anywhere}.rationale{margin:8px 0 0;color:var(--nha-text-muted);font-size:13px;overflow-wrap:anywhere}.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
6507
|
}
|
|
4869
6508
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantApprovalCardComponent, decorators: [{
|
|
4870
6509
|
type: Component,
|
|
4871
|
-
args: [{ selector: 'nh-assistant-approval-card', standalone: true, imports: [TranslatePipe, NhAssistantIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
6510
|
+
args: [{ selector: 'nh-assistant-approval-card', standalone: true, imports: [TranslatePipe, NhAssistantIconComponent, NhAssistantProposalFeedbackComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
4872
6511
|
'[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' : 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"] }]
|
|
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"] }] } });
|
|
6512
|
+
}, 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\">{{ statusKey() | translate }}</span>\n </header>\n\n @if (staleExplanation(); as explanation) {\n <p class=\"stale-explanation\">{{ explanation }}</p>\n }\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 @if (rationale(); as why) {\n <p class=\"rationale\">{{ 'nh-assistant.approval.rationale' | translate: { rationale: why } }}</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]),:host([data-status=stale]){--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}.stale-explanation{margin:8px 0 0;border-radius:8px;padding:8px 10px;color:var(--nha-text);background:var(--nha-surface-strong);font-size:13px;overflow-wrap:anywhere}.rationale{margin:8px 0 0;color:var(--nha-text-muted);font-size:13px;overflow-wrap:anywhere}.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"] }]
|
|
6513
|
+
}], 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
6514
|
|
|
4876
6515
|
let nextId$2 = 0;
|
|
4877
6516
|
const statusIcons = {
|
|
@@ -4879,7 +6518,9 @@ const statusIcons = {
|
|
|
4879
6518
|
succeeded: 'check',
|
|
4880
6519
|
failed: 'warning',
|
|
4881
6520
|
'awaiting-approval': 'clock',
|
|
4882
|
-
rejected: 'x'
|
|
6521
|
+
rejected: 'x',
|
|
6522
|
+
// Ended without running: its proposal expired, was withdrawn or became stale.
|
|
6523
|
+
'not-executed': 'stop'
|
|
4883
6524
|
};
|
|
4884
6525
|
/** Shows one tool invocation with its status and expandable input and result previews. */
|
|
4885
6526
|
class NhAssistantToolCallCardComponent {
|
|
@@ -4894,13 +6535,13 @@ class NhAssistantToolCallCardComponent {
|
|
|
4894
6535
|
this.expanded.update(expanded => !expanded);
|
|
4895
6536
|
}
|
|
4896
6537
|
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 }); }
|
|
6538
|
+
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
6539
|
}
|
|
4899
6540
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantToolCallCardComponent, decorators: [{
|
|
4900
6541
|
type: Component,
|
|
4901
6542
|
args: [{ selector: 'nh-assistant-tool-call-card', standalone: true, imports: [TranslatePipe, NhAssistantTranslatePipe, NhAssistantIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
4902
6543
|
'[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"] }]
|
|
6544
|
+
}, 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
6545
|
}], propDecorators: { part: [{ type: i0.Input, args: [{ isSignal: true, alias: "part", required: true }] }] } });
|
|
4905
6546
|
|
|
4906
6547
|
let nextId$1 = 0;
|
|
@@ -4908,6 +6549,7 @@ const stateIcons = {
|
|
|
4908
6549
|
'awaiting-approval': 'clock',
|
|
4909
6550
|
running: 'tool',
|
|
4910
6551
|
failed: 'warning',
|
|
6552
|
+
'not-executed': 'stop',
|
|
4911
6553
|
succeeded: 'check'
|
|
4912
6554
|
};
|
|
4913
6555
|
/**
|
|
@@ -4930,12 +6572,17 @@ class NhAssistantToolCallGroupComponent {
|
|
|
4930
6572
|
this.total = computed(() => this.calls().length, ...(ngDevMode ? [{ debugName: "total" }] : []));
|
|
4931
6573
|
this.finished = computed(() => this.calls().filter(call => !isActive(call)).length, ...(ngDevMode ? [{ debugName: "finished" }] : []));
|
|
4932
6574
|
this.failed = computed(() => this.calls().filter(call => call.status === 'failed').length, ...(ngDevMode ? [{ debugName: "failed" }] : []));
|
|
6575
|
+
/** Calls that finished without running; they never wait. */
|
|
6576
|
+
this.notExecuted = computed(() => this.calls().filter(call => call.status === 'not-executed').length, ...(ngDevMode ? [{ debugName: "notExecuted" }] : []));
|
|
4933
6577
|
this.state = computed(() => {
|
|
4934
6578
|
const current = this.current();
|
|
4935
6579
|
if (current) {
|
|
4936
6580
|
return current.status === 'awaiting-approval' ? 'awaiting-approval' : 'running';
|
|
4937
6581
|
}
|
|
4938
|
-
|
|
6582
|
+
if (this.failed() > 0) {
|
|
6583
|
+
return 'failed';
|
|
6584
|
+
}
|
|
6585
|
+
return this.notExecuted() > 0 ? 'not-executed' : 'succeeded';
|
|
4939
6586
|
}, ...(ngDevMode ? [{ debugName: "state" }] : []));
|
|
4940
6587
|
this.stateIcon = computed(() => stateIcons[this.state()], ...(ngDevMode ? [{ debugName: "stateIcon" }] : []));
|
|
4941
6588
|
}
|
|
@@ -4943,13 +6590,13 @@ class NhAssistantToolCallGroupComponent {
|
|
|
4943
6590
|
this.expanded.update(expanded => !expanded);
|
|
4944
6591
|
}
|
|
4945
6592
|
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
|
|
6593
|
+
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
6594
|
}
|
|
4948
6595
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantToolCallGroupComponent, decorators: [{
|
|
4949
6596
|
type: Component,
|
|
4950
6597
|
args: [{ selector: 'nh-assistant-tool-call-group', standalone: true, imports: [TranslatePipe, NhAssistantIconComponent, NhAssistantToolCallCardComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
4951
6598
|
'[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
|
|
6599
|
+
}, 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
6600
|
}], propDecorators: { calls: [{ type: i0.Input, args: [{ isSignal: true, alias: "calls", required: true }] }] } });
|
|
4954
6601
|
function isActive(call) {
|
|
4955
6602
|
return call.status === 'running' || call.status === 'awaiting-approval';
|
|
@@ -4970,8 +6617,9 @@ const stickToBottomDistance = 120;
|
|
|
4970
6617
|
/**
|
|
4971
6618
|
* The messages of a conversation as a live log. Assistant text renders as sanitized
|
|
4972
6619
|
* Markdown, consecutive tool calls as one collapsible group that counts along, and
|
|
4973
|
-
* approvals as cards.
|
|
4974
|
-
*
|
|
6620
|
+
* approvals as cards. Messages a trigger wrote into an agent thread read as events, not as
|
|
6621
|
+
* a person. While a turn runs without visible progress, a working indicator shows the
|
|
6622
|
+
* elapsed time. The thread follows new content while the user is at the bottom.
|
|
4975
6623
|
*/
|
|
4976
6624
|
class NhAssistantThreadComponent {
|
|
4977
6625
|
constructor() {
|
|
@@ -4988,7 +6636,17 @@ class NhAssistantThreadComponent {
|
|
|
4988
6636
|
this.canDecide = input(true, ...(ngDevMode ? [{ debugName: "canDecide" }] : []));
|
|
4989
6637
|
/** Name of the participant who decides the pending approval, when it is someone else. */
|
|
4990
6638
|
this.decisionOwner = input(null, ...(ngDevMode ? [{ debugName: "decisionOwner" }] : []));
|
|
6639
|
+
/**
|
|
6640
|
+
* Who may decide each pending approval, by approval id, for example `NhAssistantStore.approvalRights`.
|
|
6641
|
+
* An agent thread can hold several open proposals that the subject's approvers decide. Approvals
|
|
6642
|
+
* without an entry use `canDecide` and `decisionOwner`. An entry with `canGiveFeedback` lets the
|
|
6643
|
+
* card of a rejected proposal ask what was wrong.
|
|
6644
|
+
*/
|
|
6645
|
+
this.approvalRights = input(null, ...(ngDevMode ? [{ debugName: "approvalRights" }] : []));
|
|
6646
|
+
/** The decision about an approval card, without the approval it belongs to. */
|
|
4991
6647
|
this.approvalDecision = output();
|
|
6648
|
+
/** The decision about an approval card together with its approval id. */
|
|
6649
|
+
this.approvalChoice = output();
|
|
4992
6650
|
this.names = computed(() => new Map(this.members().map(member => [member.actorId, member.displayName])), ...(ngDevMode ? [{ debugName: "names" }] : []));
|
|
4993
6651
|
this.virtualThreshold = NH_ASSISTANT_VIRTUAL_SCROLL_THRESHOLD;
|
|
4994
6652
|
this.virtual = computed(() => this.messages().length > NH_ASSISTANT_VIRTUAL_SCROLL_THRESHOLD, ...(ngDevMode ? [{ debugName: "virtual" }] : []));
|
|
@@ -5047,13 +6705,34 @@ class NhAssistantThreadComponent {
|
|
|
5047
6705
|
trackMessage(_index, message) {
|
|
5048
6706
|
return message.id;
|
|
5049
6707
|
}
|
|
6708
|
+
/** True for a user message a trigger wrote into an agent thread. */
|
|
6709
|
+
isTrigger(message) {
|
|
6710
|
+
return message.role === 'user' && message.authorKind === 'trigger';
|
|
6711
|
+
}
|
|
6712
|
+
/** True for an assistant message the agent's rules wrote without a model. */
|
|
6713
|
+
isRule(message) {
|
|
6714
|
+
return message.role === 'assistant' && message.authorKind === 'rule';
|
|
6715
|
+
}
|
|
5050
6716
|
/** True for a user message written by another person in a shared conversation. */
|
|
5051
6717
|
isOthers(message) {
|
|
5052
6718
|
return message.role === 'user'
|
|
6719
|
+
&& !this.isTrigger(message)
|
|
5053
6720
|
&& !!message.authorActorId
|
|
5054
6721
|
&& !!this.currentActorId()
|
|
5055
6722
|
&& message.authorActorId !== this.currentActorId();
|
|
5056
6723
|
}
|
|
6724
|
+
/** Whether and why the current user may decide an approval card. */
|
|
6725
|
+
rightFor(approval) {
|
|
6726
|
+
return this.approvalRights()?.get(approval.approvalId) ?? {
|
|
6727
|
+
canDecide: this.canDecide(),
|
|
6728
|
+
waitingFor: 'turn',
|
|
6729
|
+
decisionOwner: this.decisionOwner()
|
|
6730
|
+
};
|
|
6731
|
+
}
|
|
6732
|
+
choose(approval, decision) {
|
|
6733
|
+
this.approvalDecision.emit(decision);
|
|
6734
|
+
this.approvalChoice.emit({ approvalId: approval.approvalId, decision });
|
|
6735
|
+
}
|
|
5057
6736
|
/** Display name of another person's message, or `null` when the application provides none. */
|
|
5058
6737
|
authorName(message) {
|
|
5059
6738
|
return message.authorActorId ? this.names().get(message.authorActorId) ?? null : null;
|
|
@@ -5074,7 +6753,7 @@ class NhAssistantThreadComponent {
|
|
|
5074
6753
|
}
|
|
5075
6754
|
}
|
|
5076
6755
|
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 }); }
|
|
6756
|
+
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' : isRule(message) ? 'rule' : null\">\n @if (isTrigger(message)) {\n <p class=\"author\">{{ 'nh-assistant.thread.trigger' | translate }}</p>\n } @else if (isRule(message)) {\n <p class=\"author\">{{ 'nh-assistant.thread.rule' | 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
6757
|
}
|
|
5079
6758
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantThreadComponent, decorators: [{
|
|
5080
6759
|
type: Component,
|
|
@@ -5086,8 +6765,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImpo
|
|
|
5086
6765
|
NhAssistantPartGroupsPipe,
|
|
5087
6766
|
NhAssistantToolCallGroupComponent,
|
|
5088
6767
|
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 }] }] } });
|
|
6768
|
+
], 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' : isRule(message) ? 'rule' : null\">\n @if (isTrigger(message)) {\n <p class=\"author\">{{ 'nh-assistant.thread.trigger' | translate }}</p>\n } @else if (isRule(message)) {\n <p class=\"author\">{{ 'nh-assistant.thread.rule' | 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"] }]
|
|
6769
|
+
}], 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
6770
|
|
|
5092
6771
|
let nextId = 0;
|
|
5093
6772
|
/**
|
|
@@ -5300,11 +6979,15 @@ class NhAssistantPanelComponent {
|
|
|
5300
6979
|
decide(decision) {
|
|
5301
6980
|
this.store.decide(decision);
|
|
5302
6981
|
}
|
|
6982
|
+
/** Decides the approval of a card; in an agent thread several proposals can be open. */
|
|
6983
|
+
decideApproval(choice) {
|
|
6984
|
+
this.store.decide(choice.decision, undefined, choice.approvalId);
|
|
6985
|
+
}
|
|
5303
6986
|
cancel() {
|
|
5304
6987
|
void this.store.cancel();
|
|
5305
6988
|
}
|
|
5306
6989
|
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 }); }
|
|
6990
|
+
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
6991
|
}
|
|
5309
6992
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantPanelComponent, decorators: [{
|
|
5310
6993
|
type: Component,
|
|
@@ -5320,22 +7003,96 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImpo
|
|
|
5320
7003
|
NhAssistantPreferencesComponent,
|
|
5321
7004
|
NhAssistantShareComponent,
|
|
5322
7005
|
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"] }]
|
|
7006
|
+
], 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
7007
|
}], ctorParameters: () => [], propDecorators: { drawer: [{ type: i0.ViewChild, args: ['drawer', { isSignal: true }] }] } });
|
|
5325
7008
|
|
|
5326
|
-
/**
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
7009
|
+
/**
|
|
7010
|
+
* A compact count of the agent work on one subject, for example on each row of a project list.
|
|
7011
|
+
* It shows nothing while the subject has no work. Otherwise it shows the number of proposals the
|
|
7012
|
+
* viewer may decide in the accent color, or, when they may decide none, a neutral count of the
|
|
7013
|
+
* open proposals, and a small working indicator while an agent works on the subject. Its tooltip
|
|
7014
|
+
* and accessible name say both counts. Badges on one page share one summary request per tick and
|
|
7015
|
+
* refresh on live agent-work updates of their subject; see `NhAssistantSubjectSummaryService`.
|
|
7016
|
+
*/
|
|
7017
|
+
class NhAssistantSubjectBadgeComponent {
|
|
7018
|
+
constructor() {
|
|
7019
|
+
this.summaries = inject(NhAssistantSubjectSummaryService);
|
|
7020
|
+
this.summary = signal(null, ...(ngDevMode ? [{ debugName: "summary" }] : []));
|
|
7021
|
+
/** The application's kind of subject, for example `project`. */
|
|
7022
|
+
this.subjectType = input.required(...(ngDevMode ? [{ debugName: "subjectType" }] : []));
|
|
7023
|
+
this.subjectId = input.required(...(ngDevMode ? [{ debugName: "subjectId" }] : []));
|
|
7024
|
+
/** The summary while the subject has work to show; `null` otherwise. */
|
|
7025
|
+
this.work = computed(() => {
|
|
7026
|
+
const summary = this.summary();
|
|
7027
|
+
return summary && (summary.openProposals > 0 || summary.runActive) ? summary : null;
|
|
7028
|
+
}, ...(ngDevMode ? [{ debugName: "work" }] : []));
|
|
7029
|
+
this.count = computed(() => {
|
|
7030
|
+
const summary = this.work();
|
|
7031
|
+
if (!summary) {
|
|
7032
|
+
return 0;
|
|
7033
|
+
}
|
|
7034
|
+
return summary.proposalsToDecide > 0 ? summary.proposalsToDecide : summary.openProposals;
|
|
7035
|
+
}, ...(ngDevMode ? [{ debugName: "count" }] : []));
|
|
7036
|
+
this.countLabel = computed(() => this.count() > 99 ? '99+' : String(this.count()), ...(ngDevMode ? [{ debugName: "countLabel" }] : []));
|
|
7037
|
+
this.proposalsKey = computed(() => {
|
|
7038
|
+
const summary = this.work();
|
|
7039
|
+
if (!summary || summary.openProposals === 0) {
|
|
7040
|
+
return null;
|
|
7041
|
+
}
|
|
7042
|
+
return summary.proposalsToDecide > 0 ? 'nh-assistant.subject-badge.to-decide' : 'nh-assistant.subject-badge.open';
|
|
7043
|
+
}, ...(ngDevMode ? [{ debugName: "proposalsKey" }] : []));
|
|
7044
|
+
effect(onCleanup => {
|
|
7045
|
+
const subjectType = this.subjectType();
|
|
7046
|
+
const subjectId = this.subjectId();
|
|
7047
|
+
const subscription = untracked(() => this.summaries.summary(subjectType, subjectId).subscribe(summary => this.summary.set(summary)));
|
|
7048
|
+
onCleanup(() => subscription.unsubscribe());
|
|
7049
|
+
});
|
|
5330
7050
|
}
|
|
5331
|
-
|
|
5332
|
-
}
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
7051
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantSubjectBadgeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
7052
|
+
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: `
|
|
7053
|
+
@if (work(); as summary) {
|
|
7054
|
+
@let proposals = proposalsKey() ? (proposalsKey()! | translate: { toDecide: summary.proposalsToDecide, open: summary.openProposals }) : '';
|
|
7055
|
+
@let working = summary.runActive ? ('nh-assistant.subject-badge.working' | translate) : '';
|
|
7056
|
+
@let label = proposals && working ? proposals + ' ' + working : proposals || working;
|
|
7057
|
+
<span
|
|
7058
|
+
class="subject-badge"
|
|
7059
|
+
[attr.data-state]="summary.proposalsToDecide > 0 ? 'decide' : summary.openProposals > 0 ? 'open' : 'working'"
|
|
7060
|
+
[attr.title]="label">
|
|
7061
|
+
<nh-assistant-icon name="assistant" />
|
|
7062
|
+
@if (count() > 0) {
|
|
7063
|
+
<span class="count" aria-hidden="true">{{ countLabel() }}</span>
|
|
7064
|
+
}
|
|
7065
|
+
@if (summary.runActive) {
|
|
7066
|
+
<span class="working" aria-hidden="true"></span>
|
|
7067
|
+
}
|
|
7068
|
+
<span class="visually-hidden">{{ label }}</span>
|
|
7069
|
+
</span>
|
|
7070
|
+
}
|
|
7071
|
+
`, 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
7072
|
}
|
|
5337
|
-
|
|
5338
|
-
|
|
7073
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.33", ngImport: i0, type: NhAssistantSubjectBadgeComponent, decorators: [{
|
|
7074
|
+
type: Component,
|
|
7075
|
+
args: [{ selector: 'nh-assistant-subject-badge', standalone: true, imports: [TranslatePipe, NhAssistantIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
7076
|
+
@if (work(); as summary) {
|
|
7077
|
+
@let proposals = proposalsKey() ? (proposalsKey()! | translate: { toDecide: summary.proposalsToDecide, open: summary.openProposals }) : '';
|
|
7078
|
+
@let working = summary.runActive ? ('nh-assistant.subject-badge.working' | translate) : '';
|
|
7079
|
+
@let label = proposals && working ? proposals + ' ' + working : proposals || working;
|
|
7080
|
+
<span
|
|
7081
|
+
class="subject-badge"
|
|
7082
|
+
[attr.data-state]="summary.proposalsToDecide > 0 ? 'decide' : summary.openProposals > 0 ? 'open' : 'working'"
|
|
7083
|
+
[attr.title]="label">
|
|
7084
|
+
<nh-assistant-icon name="assistant" />
|
|
7085
|
+
@if (count() > 0) {
|
|
7086
|
+
<span class="count" aria-hidden="true">{{ countLabel() }}</span>
|
|
7087
|
+
}
|
|
7088
|
+
@if (summary.runActive) {
|
|
7089
|
+
<span class="working" aria-hidden="true"></span>
|
|
7090
|
+
}
|
|
7091
|
+
<span class="visually-hidden">{{ label }}</span>
|
|
7092
|
+
</span>
|
|
7093
|
+
}
|
|
7094
|
+
`, 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"] }]
|
|
7095
|
+
}], 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
7096
|
|
|
5340
7097
|
/*
|
|
5341
7098
|
* Public API surface of @newheap/platform-ai-chat.
|
|
@@ -5345,5 +7102,5 @@ const NH_ASSISTANT_DASH_CASE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
|
5345
7102
|
* Generated bundle index. Do not edit.
|
|
5346
7103
|
*/
|
|
5347
7104
|
|
|
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 };
|
|
7105
|
+
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_PROPOSAL_LOOKUP, 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
7106
|
//# sourceMappingURL=newheap-platform-ai-chat.mjs.map
|