@newheap/platform-ai-chat 0.17.0 → 0.18.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 +11 -1
- package/admin/index.d.ts +92 -6
- package/agent-runs/index.d.ts +43 -7
- package/fesm2022/newheap-platform-ai-chat-admin.mjs +837 -155
- package/fesm2022/newheap-platform-ai-chat-admin.mjs.map +1 -1
- package/fesm2022/newheap-platform-ai-chat-agent-runs.mjs +120 -35
- package/fesm2022/newheap-platform-ai-chat-agent-runs.mjs.map +1 -1
- package/fesm2022/newheap-platform-ai-chat-testing.mjs +870 -48
- package/fesm2022/newheap-platform-ai-chat-testing.mjs.map +1 -1
- package/fesm2022/newheap-platform-ai-chat.mjs +1018 -184
- package/fesm2022/newheap-platform-ai-chat.mjs.map +1 -1
- package/i18n/en.json +236 -12
- package/i18n/nl.json +236 -12
- package/index.d.ts +439 -31
- package/package.json +1 -1
- package/testing/index.d.ts +130 -3
|
@@ -2,7 +2,7 @@ import * as i0 from '@angular/core';
|
|
|
2
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
4
|
import { merge, Observable, of, forkJoin, map, Subject, catchError, throwError, switchMap, NEVER, firstValueFrom, Subscription, isObservable, filter, debounceTime, BehaviorSubject } from 'rxjs';
|
|
5
|
-
import { isPlatformBrowser, DOCUMENT, DatePipe,
|
|
5
|
+
import { isPlatformBrowser, DOCUMENT, DatePipe, LocationStrategy, NgTemplateOutlet } from '@angular/common';
|
|
6
6
|
import { BreakpointObserver } from '@angular/cdk/layout';
|
|
7
7
|
import { Overlay } from '@angular/cdk/overlay';
|
|
8
8
|
import { TemplatePortal } from '@angular/cdk/portal';
|
|
@@ -236,7 +236,11 @@ var en = {
|
|
|
236
236
|
trigger: "Event",
|
|
237
237
|
rule: "Automatic rule",
|
|
238
238
|
files: "Attached files",
|
|
239
|
-
"file-meta": "{{type}}, {{size}}"
|
|
239
|
+
"file-meta": "{{type}}, {{size}}",
|
|
240
|
+
application: "Application",
|
|
241
|
+
"application-label": "Message from the application",
|
|
242
|
+
"application-link": "Open",
|
|
243
|
+
"application-trusted": "Instruction for the assistant"
|
|
240
244
|
},
|
|
241
245
|
composer: {
|
|
242
246
|
label: "Message",
|
|
@@ -354,6 +358,7 @@ var en = {
|
|
|
354
358
|
"waiting-for-participant": "Waiting for the participant who started this task to decide.",
|
|
355
359
|
"waiting-for-approver": "Waiting for an approver to decide.",
|
|
356
360
|
"waiting-for-other-approver": "You started this work, so another approver decides.",
|
|
361
|
+
"waiting-for-asker": "Waiting for the person who asked to decide in their own chat.",
|
|
357
362
|
"give-feedback": "What was wrong?",
|
|
358
363
|
"stale-kind": {
|
|
359
364
|
"already-done": "Already done",
|
|
@@ -361,7 +366,20 @@ var en = {
|
|
|
361
366
|
"not-applicable": "No longer needed"
|
|
362
367
|
},
|
|
363
368
|
rationale: "Why: {{rationale}}",
|
|
364
|
-
superseded: "Replaced by a newer proposal"
|
|
369
|
+
superseded: "Replaced by a newer proposal",
|
|
370
|
+
consultation: {
|
|
371
|
+
origin: "{{agent}} proposes this, about {{subject}}",
|
|
372
|
+
answer: "Answer of {{agent}}",
|
|
373
|
+
"own-rights": "Approving runs it right away with your own rights.",
|
|
374
|
+
leave: "Leave for approvers",
|
|
375
|
+
"leave-label": "Leave for approvers: {{summary}}",
|
|
376
|
+
"leave-explain": {
|
|
377
|
+
"subject-agent-rights": "The approvers of {{subject}} decide; it then runs with the agent's own rights.",
|
|
378
|
+
"subject-asker-rights": "The approvers of {{subject}} decide; it then runs with your rights."
|
|
379
|
+
},
|
|
380
|
+
"moved-status": "Left for approvers",
|
|
381
|
+
moved: "It now waits for the approvers of {{subject}}."
|
|
382
|
+
}
|
|
365
383
|
},
|
|
366
384
|
"proposal-feedback": {
|
|
367
385
|
legend: "What was wrong? (optional)",
|
|
@@ -569,6 +587,10 @@ var en = {
|
|
|
569
587
|
"assistant-consult-timeout": "The agent did not answer in time.",
|
|
570
588
|
"assistant-consult-failed": "The agent could not answer.",
|
|
571
589
|
"assistant-consult-read-only": "A consultation cannot change anything.",
|
|
590
|
+
"assistant-consult-proposal-refused": "No route at the subject takes this proposal any more. Approve or reject it here.",
|
|
591
|
+
"assistant-consult-asker-decides": "The person who asked decides this proposal in their own chat.",
|
|
592
|
+
"assistant-consult-route-not-allowed": "None of the agent's consultations allows this route, so it cannot be switched on.",
|
|
593
|
+
"assistant-consult-proposal-moved": "The proposal was left for the subject's approvers.",
|
|
572
594
|
"assistant-moved-to-shadow": "The work went to the agent's shadow thread, where a run in shadow takes it.",
|
|
573
595
|
"assistant-shadow-variant": "A shadow variant always works in shadow.",
|
|
574
596
|
"assistant-service-account-not-used": "This agent does not answer to a technical account.",
|
|
@@ -577,9 +599,9 @@ var en = {
|
|
|
577
599
|
"assistant-trigger-in-code": "This trigger is part of the code and cannot change here.",
|
|
578
600
|
"assistant-trigger-filter-not-found": "The event has no filter with this name. Choose from the filters the event offers.",
|
|
579
601
|
"assistant-skill-in-use": "A trigger still starts this skill. Change or remove the trigger first.",
|
|
580
|
-
"assistant-configuration-limit": "This agent already has as many skills or
|
|
602
|
+
"assistant-configuration-limit": "This agent already has as many skills, triggers or consultations as allowed.",
|
|
581
603
|
"assistant-run-settings-in-code": "This agent is part of the code; its runs are configured there.",
|
|
582
|
-
"assistant-run-settings-in-use": "Skills or
|
|
604
|
+
"assistant-run-settings-in-use": "Skills, triggers or consultations still belong to this agent. Remove them first.",
|
|
583
605
|
"assistant-skill-candidate-not-found": "This skill has no version being evaluated in shadow.",
|
|
584
606
|
"assistant-skill-version-gone": "The skill version this run was for is no longer evaluated.",
|
|
585
607
|
"assistant-account-search-unavailable": "The application offers no account search; enter the account's id.",
|
|
@@ -607,7 +629,10 @@ var en = {
|
|
|
607
629
|
"assistant-file-type-not-accepted": "This type of file cannot be attached.",
|
|
608
630
|
"assistant-file-upload-refused": "The application refused this file.",
|
|
609
631
|
"assistant-file-upload-failed": "The file could not be stored right now. Try again in a moment.",
|
|
610
|
-
"assistant-file-scan-refused": "The file did not pass the scan for harmful content and was not stored."
|
|
632
|
+
"assistant-file-scan-refused": "The file did not pass the scan for harmful content and was not stored.",
|
|
633
|
+
"assistant-consultation-in-code": "The agent allows consultation about this kind of object in code; it cannot change here.",
|
|
634
|
+
"assistant-consultation-not-found": "The consultation was not found.",
|
|
635
|
+
"assistant-consultations-not-allowed": "The application does not let administrators add consultations to this agent."
|
|
611
636
|
},
|
|
612
637
|
notices: {
|
|
613
638
|
"no-answer": "The assistant finished without an answer. Try again or rephrase your question.",
|
|
@@ -765,6 +790,8 @@ var en = {
|
|
|
765
790
|
changed: "Changed by {{user}} on {{date}}.",
|
|
766
791
|
reason: "Note: {{reason}}",
|
|
767
792
|
waiting: "{{count}} events wait for the resume.",
|
|
793
|
+
"stopped-was-off-hint": "It was off when it stopped: releasing the kill switch switches it back off. Resume switches it on.",
|
|
794
|
+
"stopped-was-on-hint": "It was not off when it stopped: releasing the kill switch makes it work again, as Resume does.",
|
|
768
795
|
action: {
|
|
769
796
|
active: "Resume",
|
|
770
797
|
paused: "Pause",
|
|
@@ -852,6 +879,44 @@ var en = {
|
|
|
852
879
|
},
|
|
853
880
|
"input-label-search": "Account"
|
|
854
881
|
},
|
|
882
|
+
"consultation-routes": {
|
|
883
|
+
title: "Consultation routes",
|
|
884
|
+
description: "Switch a route off for all consultations of this agent at once, in code and in the administration. When another assistant consults this agent and the agent proposes a change, a route decides who decides it and whose rights run it.",
|
|
885
|
+
"load-failed": "The consultation routes could not be read.",
|
|
886
|
+
route: {
|
|
887
|
+
"asker-decides": "The person who asked decides",
|
|
888
|
+
"subject-agent-rights": "Approvers decide, agent's rights",
|
|
889
|
+
"subject-asker-rights": "Approvers decide, asker's rights"
|
|
890
|
+
},
|
|
891
|
+
explain: {
|
|
892
|
+
"asker-decides": "The person who asked decides in their own chat; it runs with their own rights.",
|
|
893
|
+
"subject-agent-rights": "The subject's approvers other than the person who asked decide; it runs with the agent's own rights.",
|
|
894
|
+
"subject-asker-rights": "The subject's approvers decide; it runs later with the rights of the person who asked."
|
|
895
|
+
},
|
|
896
|
+
on: "On",
|
|
897
|
+
off: "Off",
|
|
898
|
+
code: "As the consultations allow",
|
|
899
|
+
overridden: "Overridden",
|
|
900
|
+
"not-allowed": "Not allowed",
|
|
901
|
+
"not-allowed-hint": "No consultation of this agent allows this route, so it stays off and cannot be switched on.",
|
|
902
|
+
"subject-types": "For: {{types}}",
|
|
903
|
+
action: {
|
|
904
|
+
on: "Switch on",
|
|
905
|
+
off: "Switch off",
|
|
906
|
+
code: "Back to what the consultations allow"
|
|
907
|
+
},
|
|
908
|
+
"explain-change": {
|
|
909
|
+
on: "Consultations use this route for new proposals again. Proposals already recorded keep their route.",
|
|
910
|
+
off: "Consultations no longer use this route for new proposals; with every route off, the agent's consultations only read. Proposals already recorded keep their route.",
|
|
911
|
+
"code-on": "The consultations decide again: one of them allows this route, so it is on.",
|
|
912
|
+
"code-off": "The consultations decide again: none of them allows this route, so it stays off."
|
|
913
|
+
},
|
|
914
|
+
confirm: {
|
|
915
|
+
on: "Confirm switching on",
|
|
916
|
+
off: "Confirm switching off",
|
|
917
|
+
code: "Confirm"
|
|
918
|
+
}
|
|
919
|
+
},
|
|
855
920
|
"run-config": "Run configuration",
|
|
856
921
|
"run-now": "Run now",
|
|
857
922
|
runs: "Runs",
|
|
@@ -889,7 +954,7 @@ var en = {
|
|
|
889
954
|
instructions: "Instructions",
|
|
890
955
|
"instructions-hint": "What the agent does and how it answers.",
|
|
891
956
|
autonomy: "Autonomy",
|
|
892
|
-
"autonomy-hint": "Only Execute allows changes, and
|
|
957
|
+
"autonomy-hint": "Only Execute allows changes in the agent's own work. In a consultation that may propose, Propose and Simulate propose changes too. Every change still needs approval.",
|
|
893
958
|
"required-policy": "Required policy",
|
|
894
959
|
"required-policy-hint": "Optional. Only users who pass this policy see the agent.",
|
|
895
960
|
profile: "Model profile",
|
|
@@ -1309,7 +1374,8 @@ var en = {
|
|
|
1309
1374
|
agent: "Run settings:",
|
|
1310
1375
|
skill: "Skill {{id}}:",
|
|
1311
1376
|
candidate: "Candidate of skill {{id}}:",
|
|
1312
|
-
trigger: "Trigger {{id}}:"
|
|
1377
|
+
trigger: "Trigger {{id}}:",
|
|
1378
|
+
consultation: "Consultation about {{id}}:"
|
|
1313
1379
|
},
|
|
1314
1380
|
code: {
|
|
1315
1381
|
"unknown-agent": "the agent can no longer run on its own.",
|
|
@@ -1325,7 +1391,18 @@ var en = {
|
|
|
1325
1391
|
"unknown-skill": "the skill no longer exists.",
|
|
1326
1392
|
"invalid-schedule": "the schedule or its time zone is not valid.",
|
|
1327
1393
|
invalid: "a value is not valid.",
|
|
1328
|
-
"not-worked-on": "it concerns a kind of object, or the whole application, that the agent does not work on."
|
|
1394
|
+
"not-worked-on": "it concerns a kind of object, or the whole application, that the agent does not work on.",
|
|
1395
|
+
"unknown-subject-type": "the kind of object no longer exists.",
|
|
1396
|
+
"unknown-agent-policy": "the policy the people who ask need is no longer listed.",
|
|
1397
|
+
"conflicts-with-tool": "another tool already has the name of its tool.",
|
|
1398
|
+
"not-consultable": "the application no longer lets administrators add consultations to this agent.",
|
|
1399
|
+
"route-not-allowed": "a route is no longer allowed, so it is left out.",
|
|
1400
|
+
"agent-answers-to-person": "the route with the agent's account is left out, because the agent answers to the object's responsible person.",
|
|
1401
|
+
"agent-has-no-rights": "the route with the agent's account is left out, because the agent answers for itself and has no rights of its own.",
|
|
1402
|
+
"no-approver": "the route with the agent's account is left out, because the approval policy lets only the person who caused a proposal decide it.",
|
|
1403
|
+
"limit-above-ceiling": "a limit is above what the application allows, so the application's applies.",
|
|
1404
|
+
"requests-not-allowed": "the application no longer allows requests, so it takes none.",
|
|
1405
|
+
"mcp-not-allowed": "the application no longer offers consultations to MCP clients, so they do not see it."
|
|
1329
1406
|
}
|
|
1330
1407
|
},
|
|
1331
1408
|
"field-errors": {
|
|
@@ -1342,7 +1419,9 @@ var en = {
|
|
|
1342
1419
|
"instructions-for-model": "Without fixed rules the model works from these instructions; describe what it should do.",
|
|
1343
1420
|
"application-not-worked-on": "The agent does not work on the whole application. Choose a set of objects, or let the agent work on the whole application first.",
|
|
1344
1421
|
"megabytes-up-to": "Enter a size above 0 and at most {{max}} MB.",
|
|
1345
|
-
"megabytes-positive": "Enter a size above 0 MB."
|
|
1422
|
+
"megabytes-positive": "Enter a size above 0 MB.",
|
|
1423
|
+
"not-allowed": "The application does not allow this.",
|
|
1424
|
+
"routes-required": "Choose at least one route, or let it only answer."
|
|
1346
1425
|
},
|
|
1347
1426
|
settings: {
|
|
1348
1427
|
title: "Run settings",
|
|
@@ -1720,7 +1799,14 @@ var en = {
|
|
|
1720
1799
|
"configured-off": "Run now: not while the application's code switches the agent off.",
|
|
1721
1800
|
"no-settings": "Run now: only once the agent has run settings.",
|
|
1722
1801
|
"no-catalog": "Run now: not until the run catalog can be read."
|
|
1723
|
-
}
|
|
1802
|
+
},
|
|
1803
|
+
consultations: "When another assistant consults it",
|
|
1804
|
+
"no-consultations": "No other assistant can consult it yet.",
|
|
1805
|
+
"consultation-proposes": "Answers and proposes",
|
|
1806
|
+
"consultation-answers": "Only answers",
|
|
1807
|
+
"asked-by-none": "No agent asks it yet",
|
|
1808
|
+
"asked-by-one": "Asked by 1 agent",
|
|
1809
|
+
"asked-by": "Asked by {{count}} agents"
|
|
1724
1810
|
},
|
|
1725
1811
|
skills: {
|
|
1726
1812
|
"code-rules": "Fixed rules in the application's code",
|
|
@@ -1803,7 +1889,124 @@ var en = {
|
|
|
1803
1889
|
"edit-trigger": "Change the trigger",
|
|
1804
1890
|
"edit-skill": "Change the skill",
|
|
1805
1891
|
"edit-trigger-named": "Edit trigger {{trigger}}",
|
|
1806
|
-
"edit-skill-named": "Edit skill {{skill}}"
|
|
1892
|
+
"edit-skill-named": "Edit skill {{skill}}",
|
|
1893
|
+
"edit-consultation": "Change the consultation",
|
|
1894
|
+
"edit-consultation-named": "Edit the consultation about {{type}}"
|
|
1895
|
+
}
|
|
1896
|
+
},
|
|
1897
|
+
consultations: {
|
|
1898
|
+
title: "Consultations",
|
|
1899
|
+
description: "Other assistants may consult this agent about an object. It answers with the rights of the person who asked and, where allowed, proposes changes. A consultation works only once an agent that asks selects its tool.",
|
|
1900
|
+
add: "Add consultation",
|
|
1901
|
+
empty: "Nobody can consult this agent yet.",
|
|
1902
|
+
closed: {
|
|
1903
|
+
application: "The application does not let administrators add consultations; they come from its code.",
|
|
1904
|
+
agent: "This agent is defined in code and does not let administrators add consultations to it.",
|
|
1905
|
+
"needs-settings": "Give the agent run settings before you add consultations.",
|
|
1906
|
+
limit: "This agent has as many consultations as allowed ({{max}}).",
|
|
1907
|
+
"no-types": "Every kind of object this agent works on already has a consultation."
|
|
1908
|
+
},
|
|
1909
|
+
requests: "Takes requests",
|
|
1910
|
+
mcp: "Offered to MCP clients",
|
|
1911
|
+
"no-description": "No description.",
|
|
1912
|
+
askers: "Asked by:",
|
|
1913
|
+
"open-asker": "Open the editor of {{agent}}",
|
|
1914
|
+
"no-askers": "No agent asks it yet. Select its tool in the tools of an agent that should ask it.",
|
|
1915
|
+
"code-hint": "Allowed in code; here only the route switches below change it.",
|
|
1916
|
+
"limits-value": "At most {{calls}} tool calls, {{seconds}} seconds, {{chars}} characters and {{history}} thread messages",
|
|
1917
|
+
"edit-named": "Edit the consultation about {{type}}",
|
|
1918
|
+
"remove-named": "Delete the consultation about {{type}}",
|
|
1919
|
+
"remove-explain": "Its tools disappear; proposals it made can still be decided.",
|
|
1920
|
+
added: "The consultation was added. It answers once an agent that asks selects its tool.",
|
|
1921
|
+
saved: "The consultation was saved.",
|
|
1922
|
+
removed: "The consultation was removed.",
|
|
1923
|
+
roles: {
|
|
1924
|
+
reader: "Reader",
|
|
1925
|
+
participant: "Participant",
|
|
1926
|
+
approver: "Approver"
|
|
1927
|
+
},
|
|
1928
|
+
"roles-lower": {
|
|
1929
|
+
reader: "reader",
|
|
1930
|
+
participant: "participant",
|
|
1931
|
+
approver: "approver"
|
|
1932
|
+
},
|
|
1933
|
+
summary: {
|
|
1934
|
+
ask: "People who are at least {{role}} of the object may ask.",
|
|
1935
|
+
"asker-policy": "They also need {{policy}}.",
|
|
1936
|
+
"asker-policy-agent": "They also need the policy the agent requires, or else access to the assistant.",
|
|
1937
|
+
"read-only": "The agent only answers and proposes nothing.",
|
|
1938
|
+
proposes: "The agent may propose changes when the person who asked is at least {{role}}: {{routes}}.",
|
|
1939
|
+
"proposes-code": "The agent may propose changes: {{routes}}."
|
|
1940
|
+
},
|
|
1941
|
+
routes: {
|
|
1942
|
+
"asker-decides": "The person who asked decides in their chat",
|
|
1943
|
+
"subject-asker-rights": "The object's approvers decide, with the rights of the person who asked",
|
|
1944
|
+
"subject-agent-rights": "The object's approvers decide, with this agent's technical account"
|
|
1945
|
+
},
|
|
1946
|
+
"routes-hint": {
|
|
1947
|
+
"asker-decides": "Approving runs it at once with that person's own rights.",
|
|
1948
|
+
"subject-asker-rights": "It waits at the object and runs later with the rights of the person who asked.",
|
|
1949
|
+
"subject-agent-rights": "It waits at the object for an approver other than the person who asked and runs with the agent's own rights."
|
|
1950
|
+
},
|
|
1951
|
+
"route-blocks": {
|
|
1952
|
+
"not-in-ceiling": "The application does not allow this route.",
|
|
1953
|
+
system: "Not for the whole application.",
|
|
1954
|
+
"agent-answers-to-person": "Not for an agent that answers to the object's responsible person.",
|
|
1955
|
+
"agent-has-no-rights": "Not for an agent that answers for itself: it has no rights of its own to lend."
|
|
1956
|
+
},
|
|
1957
|
+
limits: {
|
|
1958
|
+
"tool-calls": "Tool calls",
|
|
1959
|
+
"timeout-seconds": "Time limit in seconds",
|
|
1960
|
+
"answer-chars": "Longest answer in characters",
|
|
1961
|
+
"history-messages": "Thread messages it reads"
|
|
1962
|
+
},
|
|
1963
|
+
form: {
|
|
1964
|
+
"new-title": "New consultation",
|
|
1965
|
+
"edit-title": "Consultation about {{type}}, version {{version}}",
|
|
1966
|
+
what: "What may other assistants consult it about?",
|
|
1967
|
+
"subject-type": "Kind of object",
|
|
1968
|
+
"subject-type-hint": "Only kinds of objects the agent works on and has no consultation about yet.",
|
|
1969
|
+
description: "When to consult it",
|
|
1970
|
+
"description-hint": "The assistant that asks reads this to decide when to consult this agent.",
|
|
1971
|
+
instructions: "Instructions when consulted",
|
|
1972
|
+
"instructions-hint": "Optional. What the agent keeps in mind when it answers, after the platform's own rules for consultations.",
|
|
1973
|
+
who: "Who may ask?",
|
|
1974
|
+
"minimum-role": "The person who asks is at least",
|
|
1975
|
+
"role-hint": {
|
|
1976
|
+
reader: "Anyone who may read the object.",
|
|
1977
|
+
participant: "People who work on the object.",
|
|
1978
|
+
approver: "People who approve changes to the object."
|
|
1979
|
+
},
|
|
1980
|
+
"asker-policy": "Policy the person needs",
|
|
1981
|
+
"asker-policy-agent": "The policy the agent requires",
|
|
1982
|
+
"asker-policy-hint": "One of the policies the application lists. Without one, the agent's own policy applies, or else access to the assistant.",
|
|
1983
|
+
"do": "What may it do?",
|
|
1984
|
+
answer: "Only answer",
|
|
1985
|
+
"answer-hint": "It answers with the rights of the person who asked and proposes nothing.",
|
|
1986
|
+
propose: "Answer and propose changes",
|
|
1987
|
+
"propose-hint": "It may also propose changes; the routes below decide who decides them.",
|
|
1988
|
+
"answers-only": "The application lets consultations only answer.",
|
|
1989
|
+
routes: "Who decides its proposals?",
|
|
1990
|
+
"routes-hint": "Choose at least one. Of the two routes at the object, choose one at most.",
|
|
1991
|
+
"proposal-role": "Role needed for proposals",
|
|
1992
|
+
"proposal-role-hint": "The person who asked needs at least this role on the object before the agent may propose; with a lower role they get the answer without proposals.",
|
|
1993
|
+
"agent-policy": "The agent's approval policy",
|
|
1994
|
+
"policy-warning": {
|
|
1995
|
+
"four-eyes": "This approval policy uses four eyes, so the person who asked cannot decide their own proposal in their chat; that route does not apply.",
|
|
1996
|
+
"initiator-only": "This approval policy lets only the person who caused a proposal decide it, so the route with the agent's technical account is refused."
|
|
1997
|
+
},
|
|
1998
|
+
advanced: "Advanced: limits, requests and classification",
|
|
1999
|
+
limits: "Limits of one consultation",
|
|
2000
|
+
"limits-hint": "Empty starts at the default within what the application allows.",
|
|
2001
|
+
"limit-range": "From {{min}} to {{max}}.",
|
|
2002
|
+
requests: "Also take requests",
|
|
2003
|
+
"requests-hint": "The assistant that asks may also ask this agent to start a run, with the person as its initiator.",
|
|
2004
|
+
"requests-closed": "The application does not allow requests.",
|
|
2005
|
+
mcp: "Offer to MCP clients",
|
|
2006
|
+
"mcp-hint": "External MCP clients may consult it too, with the rights of their person.",
|
|
2007
|
+
"mcp-closed": "The application does not offer consultations to MCP clients.",
|
|
2008
|
+
"classification-hint": "How sensitive its answers are in the conversation that asked. Without one, the agent's classification applies.",
|
|
2009
|
+
"in-effect": "A consultation is in effect once saved; it answers once an agent that asks selects its tool."
|
|
1807
2010
|
}
|
|
1808
2011
|
}
|
|
1809
2012
|
},
|
|
@@ -1942,6 +2145,7 @@ var en = {
|
|
|
1942
2145
|
inbox: {
|
|
1943
2146
|
title: "Proposals to decide",
|
|
1944
2147
|
description: "Proposals of agents that wait for your decision, the ones that expire first on top.",
|
|
2148
|
+
"description-grouped": "Proposals of agents that wait for your decision, grouped by object; the group with the proposal that expires first is on top.",
|
|
1945
2149
|
empty: "No proposals wait for you.",
|
|
1946
2150
|
select: "Select this proposal",
|
|
1947
2151
|
"select-all": "Select all",
|
|
@@ -1991,7 +2195,22 @@ var en = {
|
|
|
1991
2195
|
shadow: "Shadow"
|
|
1992
2196
|
},
|
|
1993
2197
|
"from-consultation": "From a consultation",
|
|
1994
|
-
"from-consultation-hint": "Another assistant consulted the agent for
|
|
2198
|
+
"from-consultation-hint": "Another assistant consulted the agent for a person, and the agent proposed this.",
|
|
2199
|
+
consultation: {
|
|
2200
|
+
"asked-by": "Asked by {{name}}",
|
|
2201
|
+
"the-asker": "the person who asked",
|
|
2202
|
+
route: {
|
|
2203
|
+
"asker-decides": "The person who asked decides in their own chat; it runs with their own rights.",
|
|
2204
|
+
"subject-agent-rights": "The subject's approvers other than the person who asked decide; it runs with the agent's own rights.",
|
|
2205
|
+
"subject-asker-rights": "The subject's approvers decide; it runs with the rights of the person who asked."
|
|
2206
|
+
},
|
|
2207
|
+
"waiting-for-asker": "Waiting for {{name}} to decide in their own chat.",
|
|
2208
|
+
"four-eyes": "You asked for this, so another approver decides; it runs with the agent's own rights.",
|
|
2209
|
+
"runs-with": {
|
|
2210
|
+
agent: "Runs with the agent's rights",
|
|
2211
|
+
accountable: "Runs with the technical account's rights"
|
|
2212
|
+
}
|
|
2213
|
+
},
|
|
1995
2214
|
"give-feedback": "Give feedback",
|
|
1996
2215
|
"change-feedback": "Change feedback",
|
|
1997
2216
|
"your-feedback": "Your feedback: {{category}}",
|
|
@@ -2070,6 +2289,11 @@ var en = {
|
|
|
2070
2289
|
running: "Running",
|
|
2071
2290
|
completed: "Answered",
|
|
2072
2291
|
failed: "Not answered"
|
|
2292
|
+
},
|
|
2293
|
+
routes: {
|
|
2294
|
+
"asker-decides": "Asker decides: {{count}}",
|
|
2295
|
+
"subject-agent-rights": "Approvers, agent's rights: {{count}}",
|
|
2296
|
+
"subject-asker-rights": "Approvers, asker's rights: {{count}}"
|
|
2073
2297
|
}
|
|
2074
2298
|
},
|
|
2075
2299
|
"object-agents": {
|
|
@@ -2274,7 +2498,11 @@ var nl = {
|
|
|
2274
2498
|
trigger: "Gebeurtenis",
|
|
2275
2499
|
rule: "Automatische regel",
|
|
2276
2500
|
files: "Bijgevoegde bestanden",
|
|
2277
|
-
"file-meta": "{{type}}, {{size}}"
|
|
2501
|
+
"file-meta": "{{type}}, {{size}}",
|
|
2502
|
+
application: "Applicatie",
|
|
2503
|
+
"application-label": "Bericht van de applicatie",
|
|
2504
|
+
"application-link": "Openen",
|
|
2505
|
+
"application-trusted": "Instructie voor de assistent"
|
|
2278
2506
|
},
|
|
2279
2507
|
composer: {
|
|
2280
2508
|
label: "Bericht",
|
|
@@ -2392,6 +2620,7 @@ var nl = {
|
|
|
2392
2620
|
"waiting-for-participant": "Wacht op de beslissing van de deelnemer die deze taak startte.",
|
|
2393
2621
|
"waiting-for-approver": "Wacht tot een beslisser hierover beslist.",
|
|
2394
2622
|
"waiting-for-other-approver": "Jij startte dit werk, dus een andere beslisser beslist hierover.",
|
|
2623
|
+
"waiting-for-asker": "Wacht tot degene die het vroeg, beslist in de eigen chat.",
|
|
2395
2624
|
"give-feedback": "Wat klopte er niet?",
|
|
2396
2625
|
"stale-kind": {
|
|
2397
2626
|
"already-done": "Al gedaan",
|
|
@@ -2399,7 +2628,20 @@ var nl = {
|
|
|
2399
2628
|
"not-applicable": "Niet meer nodig"
|
|
2400
2629
|
},
|
|
2401
2630
|
rationale: "Waarom: {{rationale}}",
|
|
2402
|
-
superseded: "Vervangen door een nieuwer voorstel"
|
|
2631
|
+
superseded: "Vervangen door een nieuwer voorstel",
|
|
2632
|
+
consultation: {
|
|
2633
|
+
origin: "{{agent}} stelt dit voor, over {{subject}}",
|
|
2634
|
+
answer: "Antwoord van {{agent}}",
|
|
2635
|
+
"own-rights": "Goedkeuren voert het meteen uit met je eigen rechten.",
|
|
2636
|
+
leave: "Aan goedkeurders laten",
|
|
2637
|
+
"leave-label": "Aan goedkeurders laten: {{summary}}",
|
|
2638
|
+
"leave-explain": {
|
|
2639
|
+
"subject-agent-rights": "De goedkeurders van {{subject}} beslissen; daarna loopt het met de eigen rechten van de agent.",
|
|
2640
|
+
"subject-asker-rights": "De goedkeurders van {{subject}} beslissen; daarna loopt het met jouw rechten."
|
|
2641
|
+
},
|
|
2642
|
+
"moved-status": "Aan goedkeurders gelaten",
|
|
2643
|
+
moved: "Het wacht nu op de goedkeurders van {{subject}}."
|
|
2644
|
+
}
|
|
2403
2645
|
},
|
|
2404
2646
|
"proposal-feedback": {
|
|
2405
2647
|
legend: "Wat klopte er niet? (optioneel)",
|
|
@@ -2607,6 +2849,10 @@ var nl = {
|
|
|
2607
2849
|
"assistant-consult-timeout": "De agent antwoordde niet op tijd.",
|
|
2608
2850
|
"assistant-consult-failed": "De agent kon niet antwoorden.",
|
|
2609
2851
|
"assistant-consult-read-only": "Een raadpleging kan niets wijzigen.",
|
|
2852
|
+
"assistant-consult-proposal-refused": "Geen route bij het onderwerp neemt dit voorstel nog aan. Keur het hier goed of af.",
|
|
2853
|
+
"assistant-consult-asker-decides": "Degene die het vroeg, beslist over dit voorstel in de eigen chat.",
|
|
2854
|
+
"assistant-consult-route-not-allowed": "Geen van de raadplegingen van de agent staat deze route toe, dus die kan niet aan.",
|
|
2855
|
+
"assistant-consult-proposal-moved": "Het voorstel is aan de goedkeurders van het onderwerp gelaten.",
|
|
2610
2856
|
"assistant-moved-to-shadow": "Het werk is naar de schaduwthread van de agent gegaan, waar een run in schaduw het oppakt.",
|
|
2611
2857
|
"assistant-shadow-variant": "Een schaduwvariant werkt altijd in schaduw.",
|
|
2612
2858
|
"assistant-service-account-not-used": "Deze agent werkt niet onder een technisch account.",
|
|
@@ -2615,9 +2861,9 @@ var nl = {
|
|
|
2615
2861
|
"assistant-trigger-in-code": "Deze trigger staat in de code en kan hier niet veranderen.",
|
|
2616
2862
|
"assistant-trigger-filter-not-found": "De gebeurtenis heeft geen filter met deze naam. Kies uit de filters die de gebeurtenis biedt.",
|
|
2617
2863
|
"assistant-skill-in-use": "Een trigger start deze vaardigheid nog. Pas de trigger eerst aan of verwijder hem.",
|
|
2618
|
-
"assistant-configuration-limit": "Deze agent heeft al zoveel vaardigheden of
|
|
2864
|
+
"assistant-configuration-limit": "Deze agent heeft al zoveel vaardigheden, triggers of raadplegingen als is toegestaan.",
|
|
2619
2865
|
"assistant-run-settings-in-code": "Deze agent staat in de code; daar is ingesteld hoe hij werkt.",
|
|
2620
|
-
"assistant-run-settings-in-use": "Er horen nog vaardigheden of
|
|
2866
|
+
"assistant-run-settings-in-use": "Er horen nog vaardigheden, triggers of raadplegingen bij deze agent. Verwijder die eerst.",
|
|
2621
2867
|
"assistant-skill-candidate-not-found": "Van deze vaardigheid wordt geen versie in schaduw beoordeeld.",
|
|
2622
2868
|
"assistant-skill-version-gone": "De versie van de vaardigheid waarvoor deze run was, wordt niet meer beoordeeld.",
|
|
2623
2869
|
"assistant-account-search-unavailable": "De applicatie biedt geen accountzoeker; vul de id van het account in.",
|
|
@@ -2645,7 +2891,10 @@ var nl = {
|
|
|
2645
2891
|
"assistant-file-type-not-accepted": "Dit type bestand kan niet worden bijgevoegd.",
|
|
2646
2892
|
"assistant-file-upload-refused": "De applicatie heeft dit bestand geweigerd.",
|
|
2647
2893
|
"assistant-file-upload-failed": "Het bestand kon nu niet worden opgeslagen. Probeer het zo opnieuw.",
|
|
2648
|
-
"assistant-file-scan-refused": "Het bestand is niet door de scan op schadelijke inhoud gekomen en is niet opgeslagen."
|
|
2894
|
+
"assistant-file-scan-refused": "Het bestand is niet door de scan op schadelijke inhoud gekomen en is niet opgeslagen.",
|
|
2895
|
+
"assistant-consultation-in-code": "De agent staat raadpleging over deze soort object in code toe; die kan hier niet veranderen.",
|
|
2896
|
+
"assistant-consultation-not-found": "De raadpleging is niet gevonden.",
|
|
2897
|
+
"assistant-consultations-not-allowed": "De applicatie laat beheerders geen raadplegingen aan deze agent toevoegen."
|
|
2649
2898
|
},
|
|
2650
2899
|
notices: {
|
|
2651
2900
|
"no-answer": "De assistent is klaar zonder antwoord. Probeer het opnieuw of stel je vraag anders.",
|
|
@@ -2803,6 +3052,8 @@ var nl = {
|
|
|
2803
3052
|
changed: "Gewijzigd door {{user}} op {{date}}.",
|
|
2804
3053
|
reason: "Notitie: {{reason}}",
|
|
2805
3054
|
waiting: "{{count}} gebeurtenissen wachten op het hervatten.",
|
|
3055
|
+
"stopped-was-off-hint": "Hij stond uit toen hij stopte: de noodstop opheffen zet hem weer uit. Hervatten zet hem aan.",
|
|
3056
|
+
"stopped-was-on-hint": "Hij stond niet uit toen hij stopte: de noodstop opheffen laat hem weer werken, net als Hervatten.",
|
|
2806
3057
|
action: {
|
|
2807
3058
|
active: "Hervatten",
|
|
2808
3059
|
paused: "Pauzeren",
|
|
@@ -2890,6 +3141,44 @@ var nl = {
|
|
|
2890
3141
|
},
|
|
2891
3142
|
"input-label-search": "Account"
|
|
2892
3143
|
},
|
|
3144
|
+
"consultation-routes": {
|
|
3145
|
+
title: "Routes van raadplegingen",
|
|
3146
|
+
description: "Zet een route in één keer uit voor alle raadplegingen van deze agent, in code en in het beheer. Wanneer een andere assistent deze agent raadpleegt en de agent een wijziging voorstelt, bepaalt een route wie erover beslist en met wiens rechten het loopt.",
|
|
3147
|
+
"load-failed": "De routes van raadplegingen konden niet worden gelezen.",
|
|
3148
|
+
route: {
|
|
3149
|
+
"asker-decides": "Degene die het vroeg, beslist",
|
|
3150
|
+
"subject-agent-rights": "Goedkeurders beslissen, rechten van de agent",
|
|
3151
|
+
"subject-asker-rights": "Goedkeurders beslissen, rechten van de vrager"
|
|
3152
|
+
},
|
|
3153
|
+
explain: {
|
|
3154
|
+
"asker-decides": "Degene die het vroeg, beslist in de eigen chat; het loopt met diens eigen rechten.",
|
|
3155
|
+
"subject-agent-rights": "De goedkeurders van het onderwerp, behalve degene die het vroeg, beslissen; het loopt met de eigen rechten van de agent.",
|
|
3156
|
+
"subject-asker-rights": "De goedkeurders van het onderwerp beslissen; het loopt later met de rechten van degene die het vroeg."
|
|
3157
|
+
},
|
|
3158
|
+
on: "Aan",
|
|
3159
|
+
off: "Uit",
|
|
3160
|
+
code: "Zoals de raadplegingen toestaan",
|
|
3161
|
+
overridden: "Overschreven",
|
|
3162
|
+
"not-allowed": "Niet toegestaan",
|
|
3163
|
+
"not-allowed-hint": "Geen raadpleging van deze agent staat deze route toe, dus die blijft uit en kan niet aan.",
|
|
3164
|
+
"subject-types": "Voor: {{types}}",
|
|
3165
|
+
action: {
|
|
3166
|
+
on: "Aanzetten",
|
|
3167
|
+
off: "Uitzetten",
|
|
3168
|
+
code: "Terug naar wat de raadplegingen toestaan"
|
|
3169
|
+
},
|
|
3170
|
+
"explain-change": {
|
|
3171
|
+
on: "Raadplegingen gebruiken deze route weer voor nieuwe voorstellen. Voorstellen die er al zijn, houden hun route.",
|
|
3172
|
+
off: "Raadplegingen gebruiken deze route niet meer voor nieuwe voorstellen; met alle routes uit lezen de raadplegingen van de agent alleen. Voorstellen die er al zijn, houden hun route.",
|
|
3173
|
+
"code-on": "De raadplegingen beslissen weer: een ervan staat deze route toe, dus die staat aan.",
|
|
3174
|
+
"code-off": "De raadplegingen beslissen weer: geen ervan staat deze route toe, dus die blijft uit."
|
|
3175
|
+
},
|
|
3176
|
+
confirm: {
|
|
3177
|
+
on: "Aanzetten bevestigen",
|
|
3178
|
+
off: "Uitzetten bevestigen",
|
|
3179
|
+
code: "Bevestigen"
|
|
3180
|
+
}
|
|
3181
|
+
},
|
|
2893
3182
|
"run-config": "Runconfiguratie",
|
|
2894
3183
|
"run-now": "Nu uitvoeren",
|
|
2895
3184
|
runs: "Runs",
|
|
@@ -2927,7 +3216,7 @@ var nl = {
|
|
|
2927
3216
|
instructions: "Instructies",
|
|
2928
3217
|
"instructions-hint": "Wat de agent doet en hoe hij antwoordt.",
|
|
2929
3218
|
autonomy: "Autonomie",
|
|
2930
|
-
"autonomy-hint": "Alleen Uitvoeren staat wijzigingen toe, en
|
|
3219
|
+
"autonomy-hint": "Alleen Uitvoeren staat wijzigingen toe in het eigen werk van de agent. In een raadpleging die mag voorstellen, stellen Voorstellen en Simuleren ook wijzigingen voor. Elke wijziging vraagt nog steeds om goedkeuring.",
|
|
2931
3220
|
"required-policy": "Vereiste policy",
|
|
2932
3221
|
"required-policy-hint": "Optioneel. Alleen gebruikers die aan deze policy voldoen zien de agent.",
|
|
2933
3222
|
profile: "Modelprofiel",
|
|
@@ -3347,7 +3636,8 @@ var nl = {
|
|
|
3347
3636
|
agent: "Runinstellingen:",
|
|
3348
3637
|
skill: "Vaardigheid {{id}}:",
|
|
3349
3638
|
candidate: "Kandidaat van vaardigheid {{id}}:",
|
|
3350
|
-
trigger: "Trigger {{id}}:"
|
|
3639
|
+
trigger: "Trigger {{id}}:",
|
|
3640
|
+
consultation: "Raadpleging over {{id}}:"
|
|
3351
3641
|
},
|
|
3352
3642
|
code: {
|
|
3353
3643
|
"unknown-agent": "de agent kan niet meer zelfstandig werken.",
|
|
@@ -3363,7 +3653,18 @@ var nl = {
|
|
|
3363
3653
|
"unknown-skill": "de vaardigheid bestaat niet meer.",
|
|
3364
3654
|
"invalid-schedule": "het schema of de tijdzone is niet geldig.",
|
|
3365
3655
|
invalid: "een waarde is niet geldig.",
|
|
3366
|
-
"not-worked-on": "het gaat over een soort object, of de hele applicatie, waaraan de agent niet werkt."
|
|
3656
|
+
"not-worked-on": "het gaat over een soort object, of de hele applicatie, waaraan de agent niet werkt.",
|
|
3657
|
+
"unknown-subject-type": "de soort object bestaat niet meer.",
|
|
3658
|
+
"unknown-agent-policy": "het beleid dat vragers nodig hebben, wordt niet meer aangeboden.",
|
|
3659
|
+
"conflicts-with-tool": "een andere tool heeft al de naam van haar tool.",
|
|
3660
|
+
"not-consultable": "de applicatie laat beheerders geen raadplegingen meer aan deze agent toevoegen.",
|
|
3661
|
+
"route-not-allowed": "een route is niet meer toegestaan en valt weg.",
|
|
3662
|
+
"agent-answers-to-person": "de route met het account van de agent valt weg, omdat de agent verantwoording aflegt aan de verantwoordelijke van het object.",
|
|
3663
|
+
"agent-has-no-rights": "de route met het account van de agent valt weg, omdat de agent voor zichzelf antwoordt en geen eigen rechten heeft.",
|
|
3664
|
+
"no-approver": "de route met het account van de agent valt weg, omdat het goedkeuringsbeleid alleen degene die een voorstel veroorzaakte laat beslissen.",
|
|
3665
|
+
"limit-above-ceiling": "een grens ligt boven wat de applicatie toestaat, dus die van de applicatie geldt.",
|
|
3666
|
+
"requests-not-allowed": "de applicatie staat geen verzoeken meer toe, dus ze neemt er geen aan.",
|
|
3667
|
+
"mcp-not-allowed": "de applicatie biedt raadplegingen niet meer aan MCP-clients aan, dus die zien haar niet."
|
|
3367
3668
|
}
|
|
3368
3669
|
},
|
|
3369
3670
|
"field-errors": {
|
|
@@ -3380,7 +3681,9 @@ var nl = {
|
|
|
3380
3681
|
"instructions-for-model": "Zonder vaste regels werkt het model met deze instructies; beschrijf wat het moet doen.",
|
|
3381
3682
|
"application-not-worked-on": "De agent werkt niet aan de hele applicatie. Kies een verzameling objecten, of laat de agent eerst aan de hele applicatie werken.",
|
|
3382
3683
|
"megabytes-up-to": "Vul een grootte in boven 0 en hooguit {{max}} MB.",
|
|
3383
|
-
"megabytes-positive": "Vul een grootte in boven 0 MB."
|
|
3684
|
+
"megabytes-positive": "Vul een grootte in boven 0 MB.",
|
|
3685
|
+
"not-allowed": "De applicatie staat dit niet toe.",
|
|
3686
|
+
"routes-required": "Kies minstens één route, of laat hem alleen antwoorden."
|
|
3384
3687
|
},
|
|
3385
3688
|
settings: {
|
|
3386
3689
|
title: "Runinstellingen",
|
|
@@ -3758,7 +4061,14 @@ var nl = {
|
|
|
3758
4061
|
"configured-off": "Nu uitvoeren: niet zolang de code van de applicatie de agent uitschakelt.",
|
|
3759
4062
|
"no-settings": "Nu uitvoeren: pas als de agent runinstellingen heeft.",
|
|
3760
4063
|
"no-catalog": "Nu uitvoeren: pas als de runcatalogus gelezen kan worden."
|
|
3761
|
-
}
|
|
4064
|
+
},
|
|
4065
|
+
consultations: "Als een andere assistent hem raadpleegt",
|
|
4066
|
+
"no-consultations": "Nog geen andere assistent kan hem raadplegen.",
|
|
4067
|
+
"consultation-proposes": "Antwoordt en stelt voor",
|
|
4068
|
+
"consultation-answers": "Antwoordt alleen",
|
|
4069
|
+
"asked-by-none": "Nog geen agent vraagt het",
|
|
4070
|
+
"asked-by-one": "Gevraagd door 1 agent",
|
|
4071
|
+
"asked-by": "Gevraagd door {{count}} agents"
|
|
3762
4072
|
},
|
|
3763
4073
|
skills: {
|
|
3764
4074
|
"code-rules": "Vaste regels in de code van de applicatie",
|
|
@@ -3841,7 +4151,124 @@ var nl = {
|
|
|
3841
4151
|
"edit-trigger": "Trigger wijzigen",
|
|
3842
4152
|
"edit-skill": "Vaardigheid wijzigen",
|
|
3843
4153
|
"edit-trigger-named": "Trigger {{trigger}} bewerken",
|
|
3844
|
-
"edit-skill-named": "Vaardigheid {{skill}} bewerken"
|
|
4154
|
+
"edit-skill-named": "Vaardigheid {{skill}} bewerken",
|
|
4155
|
+
"edit-consultation": "Raadpleging wijzigen",
|
|
4156
|
+
"edit-consultation-named": "De raadpleging over {{type}} bewerken"
|
|
4157
|
+
}
|
|
4158
|
+
},
|
|
4159
|
+
consultations: {
|
|
4160
|
+
title: "Raadplegingen",
|
|
4161
|
+
description: "Andere assistenten mogen deze agent raadplegen over een object. Hij antwoordt met de rechten van degene die het vroeg en stelt, waar dat mag, wijzigingen voor. Een raadpleging werkt pas als een agent die vraagt haar tool selecteert.",
|
|
4162
|
+
add: "Raadpleging toevoegen",
|
|
4163
|
+
empty: "Niemand kan deze agent nog raadplegen.",
|
|
4164
|
+
closed: {
|
|
4165
|
+
application: "De applicatie laat beheerders geen raadplegingen toevoegen; die komen uit haar code.",
|
|
4166
|
+
agent: "Deze agent staat in de code en laat beheerders geen raadplegingen toevoegen.",
|
|
4167
|
+
"needs-settings": "Geef de agent runinstellingen voordat je raadplegingen toevoegt.",
|
|
4168
|
+
limit: "Deze agent heeft al zoveel raadplegingen als is toegestaan ({{max}}).",
|
|
4169
|
+
"no-types": "Elke soort object waaraan deze agent werkt, heeft al een raadpleging."
|
|
4170
|
+
},
|
|
4171
|
+
requests: "Neemt verzoeken aan",
|
|
4172
|
+
mcp: "Aangeboden aan MCP-clients",
|
|
4173
|
+
"no-description": "Geen beschrijving.",
|
|
4174
|
+
askers: "Gevraagd door:",
|
|
4175
|
+
"open-asker": "De editor van {{agent}} openen",
|
|
4176
|
+
"no-askers": "Nog geen agent vraagt het. Selecteer de tool bij de tools van een agent die het moet vragen.",
|
|
4177
|
+
"code-hint": "Toegestaan in code; hier veranderen alleen de routeschakelaars hieronder iets.",
|
|
4178
|
+
"limits-value": "Hooguit {{calls}} toolaanroepen, {{seconds}} seconden, {{chars}} tekens en {{history}} berichten uit de thread",
|
|
4179
|
+
"edit-named": "De raadpleging over {{type}} bewerken",
|
|
4180
|
+
"remove-named": "De raadpleging over {{type}} verwijderen",
|
|
4181
|
+
"remove-explain": "Haar tools verdwijnen; voorstellen die ze deed, kunnen nog worden beslist.",
|
|
4182
|
+
added: "De raadpleging is toegevoegd. Ze antwoordt zodra een agent die vraagt haar tool selecteert.",
|
|
4183
|
+
saved: "De raadpleging is opgeslagen.",
|
|
4184
|
+
removed: "De raadpleging is verwijderd.",
|
|
4185
|
+
roles: {
|
|
4186
|
+
reader: "Lezer",
|
|
4187
|
+
participant: "Deelnemer",
|
|
4188
|
+
approver: "Goedkeurder"
|
|
4189
|
+
},
|
|
4190
|
+
"roles-lower": {
|
|
4191
|
+
reader: "lezer",
|
|
4192
|
+
participant: "deelnemer",
|
|
4193
|
+
approver: "goedkeurder"
|
|
4194
|
+
},
|
|
4195
|
+
summary: {
|
|
4196
|
+
ask: "Mensen die minstens {{role}} van het object zijn, mogen het vragen.",
|
|
4197
|
+
"asker-policy": "Ze hebben ook {{policy}} nodig.",
|
|
4198
|
+
"asker-policy-agent": "Ze hebben ook het beleid nodig dat de agent vereist, of anders toegang tot de assistent.",
|
|
4199
|
+
"read-only": "De agent antwoordt alleen en stelt niets voor.",
|
|
4200
|
+
proposes: "De agent mag wijzigingen voorstellen als degene die het vroeg minstens {{role}} is: {{routes}}.",
|
|
4201
|
+
"proposes-code": "De agent mag wijzigingen voorstellen: {{routes}}."
|
|
4202
|
+
},
|
|
4203
|
+
routes: {
|
|
4204
|
+
"asker-decides": "Degene die het vroeg, beslist in de eigen chat",
|
|
4205
|
+
"subject-asker-rights": "De goedkeurders van het object beslissen, met de rechten van degene die het vroeg",
|
|
4206
|
+
"subject-agent-rights": "De goedkeurders van het object beslissen, met het technische account van deze agent"
|
|
4207
|
+
},
|
|
4208
|
+
"routes-hint": {
|
|
4209
|
+
"asker-decides": "Goedkeuren voert het meteen uit met diens eigen rechten.",
|
|
4210
|
+
"subject-asker-rights": "Het wacht bij het object en loopt later met de rechten van degene die het vroeg.",
|
|
4211
|
+
"subject-agent-rights": "Het wacht bij het object op een goedkeurder anders dan degene die het vroeg en loopt met de eigen rechten van de agent."
|
|
4212
|
+
},
|
|
4213
|
+
"route-blocks": {
|
|
4214
|
+
"not-in-ceiling": "De applicatie staat deze route niet toe.",
|
|
4215
|
+
system: "Niet voor de hele applicatie.",
|
|
4216
|
+
"agent-answers-to-person": "Niet voor een agent die verantwoording aflegt aan de verantwoordelijke van het object.",
|
|
4217
|
+
"agent-has-no-rights": "Niet voor een agent die voor zichzelf antwoordt: hij heeft geen eigen rechten om uit te lenen."
|
|
4218
|
+
},
|
|
4219
|
+
limits: {
|
|
4220
|
+
"tool-calls": "Toolaanroepen",
|
|
4221
|
+
"timeout-seconds": "Tijdslimiet in seconden",
|
|
4222
|
+
"answer-chars": "Langste antwoord in tekens",
|
|
4223
|
+
"history-messages": "Berichten uit de thread die hij leest"
|
|
4224
|
+
},
|
|
4225
|
+
form: {
|
|
4226
|
+
"new-title": "Nieuwe raadpleging",
|
|
4227
|
+
"edit-title": "Raadpleging over {{type}}, versie {{version}}",
|
|
4228
|
+
what: "Waarover mogen andere assistenten hem raadplegen?",
|
|
4229
|
+
"subject-type": "Soort object",
|
|
4230
|
+
"subject-type-hint": "Alleen soorten objecten waaraan de agent werkt en waarover hij nog geen raadpleging heeft.",
|
|
4231
|
+
description: "Wanneer je hem raadpleegt",
|
|
4232
|
+
"description-hint": "De assistent die vraagt, leest dit om te bepalen wanneer hij deze agent raadpleegt.",
|
|
4233
|
+
instructions: "Instructies bij een raadpleging",
|
|
4234
|
+
"instructions-hint": "Optioneel. Waar de agent op let als hij antwoordt, na de eigen regels van het platform voor raadplegingen.",
|
|
4235
|
+
who: "Wie mag het vragen?",
|
|
4236
|
+
"minimum-role": "Degene die het vraagt is minstens",
|
|
4237
|
+
"role-hint": {
|
|
4238
|
+
reader: "Iedereen die het object mag lezen.",
|
|
4239
|
+
participant: "Mensen die aan het object werken.",
|
|
4240
|
+
approver: "Mensen die wijzigingen aan het object goedkeuren."
|
|
4241
|
+
},
|
|
4242
|
+
"asker-policy": "Beleid dat de persoon nodig heeft",
|
|
4243
|
+
"asker-policy-agent": "Het beleid dat de agent vereist",
|
|
4244
|
+
"asker-policy-hint": "Een van de beleidsregels die de applicatie aanbiedt. Zonder geldt het eigen beleid van de agent, of anders toegang tot de assistent.",
|
|
4245
|
+
"do": "Wat mag hij doen?",
|
|
4246
|
+
answer: "Alleen antwoorden",
|
|
4247
|
+
"answer-hint": "Hij antwoordt met de rechten van degene die het vroeg en stelt niets voor.",
|
|
4248
|
+
propose: "Antwoorden en wijzigingen voorstellen",
|
|
4249
|
+
"propose-hint": "Hij mag ook wijzigingen voorstellen; de routes hieronder bepalen wie erover beslist.",
|
|
4250
|
+
"answers-only": "De applicatie laat raadplegingen alleen antwoorden.",
|
|
4251
|
+
routes: "Wie beslist over zijn voorstellen?",
|
|
4252
|
+
"routes-hint": "Kies er minstens één. Van de twee routes bij het object kies je er hooguit één.",
|
|
4253
|
+
"proposal-role": "Rol nodig voor voorstellen",
|
|
4254
|
+
"proposal-role-hint": "Degene die het vroeg heeft minstens deze rol op het object nodig voordat de agent iets mag voorstellen; met een lagere rol krijgt die het antwoord zonder voorstellen.",
|
|
4255
|
+
"agent-policy": "Het goedkeuringsbeleid van de agent",
|
|
4256
|
+
"policy-warning": {
|
|
4257
|
+
"four-eyes": "Dit goedkeuringsbeleid vraagt vier ogen, dus degene die het vroeg kan het eigen voorstel niet in de eigen chat beslissen; die route geldt dan niet.",
|
|
4258
|
+
"initiator-only": "Dit goedkeuringsbeleid laat alleen degene die een voorstel veroorzaakte erover beslissen, dus de route met het technische account van de agent wordt geweigerd."
|
|
4259
|
+
},
|
|
4260
|
+
advanced: "Geavanceerd: grenzen, verzoeken en classificatie",
|
|
4261
|
+
limits: "Grenzen van één raadpleging",
|
|
4262
|
+
"limits-hint": "Leeg begint bij de standaard binnen wat de applicatie toestaat.",
|
|
4263
|
+
"limit-range": "Van {{min}} tot en met {{max}}.",
|
|
4264
|
+
requests: "Ook verzoeken aannemen",
|
|
4265
|
+
"requests-hint": "De assistent die vraagt, mag deze agent ook vragen een run te starten, met de persoon als initiatiefnemer.",
|
|
4266
|
+
"requests-closed": "De applicatie staat geen verzoeken toe.",
|
|
4267
|
+
mcp: "Aanbieden aan MCP-clients",
|
|
4268
|
+
"mcp-hint": "Externe MCP-clients mogen hem ook raadplegen, met de rechten van hun persoon.",
|
|
4269
|
+
"mcp-closed": "De applicatie biedt raadplegingen niet aan MCP-clients aan.",
|
|
4270
|
+
"classification-hint": "Hoe gevoelig zijn antwoorden zijn in het gesprek dat vroeg. Zonder geldt de classificatie van de agent.",
|
|
4271
|
+
"in-effect": "Een raadpleging werkt zodra ze is opgeslagen; ze antwoordt zodra een agent die vraagt haar tool selecteert."
|
|
3845
4272
|
}
|
|
3846
4273
|
}
|
|
3847
4274
|
},
|
|
@@ -3980,6 +4407,7 @@ var nl = {
|
|
|
3980
4407
|
inbox: {
|
|
3981
4408
|
title: "Voorstellen om te beslissen",
|
|
3982
4409
|
description: "Voorstellen van agents die op jouw besluit wachten, de eerst verlopende bovenaan.",
|
|
4410
|
+
"description-grouped": "Voorstellen van agents die op jouw besluit wachten, gegroepeerd per object; de groep met het eerst verlopende voorstel staat bovenaan.",
|
|
3983
4411
|
empty: "Er wachten geen voorstellen op jou.",
|
|
3984
4412
|
select: "Dit voorstel selecteren",
|
|
3985
4413
|
"select-all": "Alles selecteren",
|
|
@@ -4029,7 +4457,22 @@ var nl = {
|
|
|
4029
4457
|
shadow: "Schaduw"
|
|
4030
4458
|
},
|
|
4031
4459
|
"from-consultation": "Uit een raadpleging",
|
|
4032
|
-
"from-consultation-hint": "Een andere assistent raadpleegde de agent voor
|
|
4460
|
+
"from-consultation-hint": "Een andere assistent raadpleegde de agent voor iemand, en de agent stelde dit voor.",
|
|
4461
|
+
consultation: {
|
|
4462
|
+
"asked-by": "Gevraagd door {{name}}",
|
|
4463
|
+
"the-asker": "degene die het vroeg",
|
|
4464
|
+
route: {
|
|
4465
|
+
"asker-decides": "Degene die het vroeg, beslist in de eigen chat; het loopt met diens eigen rechten.",
|
|
4466
|
+
"subject-agent-rights": "De goedkeurders van het onderwerp, behalve degene die het vroeg, beslissen; het loopt met de eigen rechten van de agent.",
|
|
4467
|
+
"subject-asker-rights": "De goedkeurders van het onderwerp beslissen; het loopt met de rechten van degene die het vroeg."
|
|
4468
|
+
},
|
|
4469
|
+
"waiting-for-asker": "Wacht tot {{name}} beslist in de eigen chat.",
|
|
4470
|
+
"four-eyes": "Jij vroeg hierom, dus een andere goedkeurder beslist; het loopt met de eigen rechten van de agent.",
|
|
4471
|
+
"runs-with": {
|
|
4472
|
+
agent: "Loopt met de rechten van de agent",
|
|
4473
|
+
accountable: "Loopt met de rechten van het technische account"
|
|
4474
|
+
}
|
|
4475
|
+
},
|
|
4033
4476
|
"give-feedback": "Feedback geven",
|
|
4034
4477
|
"change-feedback": "Feedback wijzigen",
|
|
4035
4478
|
"your-feedback": "Jouw feedback: {{category}}",
|
|
@@ -4108,6 +4551,11 @@ var nl = {
|
|
|
4108
4551
|
running: "Bezig",
|
|
4109
4552
|
completed: "Beantwoord",
|
|
4110
4553
|
failed: "Niet beantwoord"
|
|
4554
|
+
},
|
|
4555
|
+
routes: {
|
|
4556
|
+
"asker-decides": "Vrager beslist: {{count}}",
|
|
4557
|
+
"subject-agent-rights": "Goedkeurders, rechten van de agent: {{count}}",
|
|
4558
|
+
"subject-asker-rights": "Goedkeurders, rechten van de vrager: {{count}}"
|
|
4111
4559
|
}
|
|
4112
4560
|
},
|
|
4113
4561
|
"object-agents": {
|
|
@@ -5003,6 +5451,15 @@ class NhAssistantAdminApiService {
|
|
|
5003
5451
|
setServiceAccount(agentId, request) {
|
|
5004
5452
|
return this.request('POST', `admin/agents/${encodeURIComponent(agentId)}/service-account`, request);
|
|
5005
5453
|
}
|
|
5454
|
+
/**
|
|
5455
|
+
* Switches the routes of the agent's consultations on, off or back to the setting in code; a route
|
|
5456
|
+
* left out keeps its switch. Code is the ceiling: switching on a route that no consultation in code
|
|
5457
|
+
* allows fails with `assistant-consult-route-not-allowed`. Returns the agent's state with its
|
|
5458
|
+
* `consultationRoutes`.
|
|
5459
|
+
*/
|
|
5460
|
+
setConsultationRoutes(agentId, request) {
|
|
5461
|
+
return this.request('POST', `admin/agents/${encodeURIComponent(agentId)}/consultation-routes`, request);
|
|
5462
|
+
}
|
|
5006
5463
|
/**
|
|
5007
5464
|
* Puts an agent, in code or created here, in exactly `groups` of the run catalog's `agentGroups`
|
|
5008
5465
|
* and out of the others. The choice wins over the agent's `InGroup` in code. Fails with a field error
|
|
@@ -5226,6 +5683,27 @@ class NhAssistantAdminApiService {
|
|
|
5226
5683
|
setDataTriggerShadow(agentId, triggerId, request) {
|
|
5227
5684
|
return this.request('POST', `${triggerPath(agentId, triggerId)}/shadow`, request);
|
|
5228
5685
|
}
|
|
5686
|
+
/**
|
|
5687
|
+
* Adds or changes the consultation defined as data of an agent about one subject type, within the
|
|
5688
|
+
* agent's `consultationCeiling`. It is in effect once saved and answers when an asking agent selects
|
|
5689
|
+
* its consult tool. An agent that may get none fails with `assistant-consultations-not-allowed`, an
|
|
5690
|
+
* agent created here without run settings with `assistant-agent-not-configured`, a type the agent
|
|
5691
|
+
* allows consultation about in code with `assistant-consultation-in-code`, more than the ceiling's
|
|
5692
|
+
* `maxConsultations` with `assistant-configuration-limit`, a stale `expectedVersion` with
|
|
5693
|
+
* `assistant-version-conflict` and invalid input with field errors, such as `routes: not-allowed` for
|
|
5694
|
+
* a route outside the ceiling.
|
|
5695
|
+
*/
|
|
5696
|
+
saveDataConsultation(agentId, subjectType, request) {
|
|
5697
|
+
return this.request('PUT', consultationPath(agentId, subjectType), request);
|
|
5698
|
+
}
|
|
5699
|
+
/**
|
|
5700
|
+
* Removes a consultation defined as data: its tools disappear, while its recorded proposals can still
|
|
5701
|
+
* be decided. Fails with `assistant-consultation-not-found` without one and with
|
|
5702
|
+
* `assistant-consultation-in-code` for a consultation in code.
|
|
5703
|
+
*/
|
|
5704
|
+
removeDataConsultation(agentId, subjectType, removal = {}) {
|
|
5705
|
+
return this.request('DELETE', withQuery$1(consultationPath(agentId, subjectType), removal));
|
|
5706
|
+
}
|
|
5229
5707
|
download(path) {
|
|
5230
5708
|
return this.transport.download(path, 'application/x-ndjson', adminStatusCodes);
|
|
5231
5709
|
}
|
|
@@ -5253,6 +5731,9 @@ function skillPath(agentId, skillId) {
|
|
|
5253
5731
|
function triggerPath(agentId, triggerId) {
|
|
5254
5732
|
return `${agentPath(agentId)}/triggers/${encodeURIComponent(triggerId)}`;
|
|
5255
5733
|
}
|
|
5734
|
+
function consultationPath(agentId, subjectType) {
|
|
5735
|
+
return `${agentPath(agentId)}/consultations/${encodeURIComponent(subjectType)}`;
|
|
5736
|
+
}
|
|
5256
5737
|
/** Appends the filter values that are set as query parameters. */
|
|
5257
5738
|
function withQuery$1(path, filter) {
|
|
5258
5739
|
const query = new URLSearchParams();
|
|
@@ -6298,31 +6779,111 @@ function sameKey(current, expected) {
|
|
|
6298
6779
|
/** The longest file name a file reference takes. */
|
|
6299
6780
|
const NH_ASSISTANT_MAX_FILE_NAME_LENGTH = 256;
|
|
6300
6781
|
const unknownMediaType = 'application/octet-stream';
|
|
6301
|
-
|
|
6782
|
+
const zipAliases = ['application/zip', 'application/x-zip-compressed'];
|
|
6783
|
+
/**
|
|
6784
|
+
* The media types the platform names, with the extensions a file picker offers and a short label.
|
|
6785
|
+
* The extensions are the same as the server's table of upload media types.
|
|
6786
|
+
*/
|
|
6302
6787
|
const knownTypes = {
|
|
6303
6788
|
'application/pdf': { extensions: ['.pdf'], label: 'PDF' },
|
|
6304
6789
|
'image/png': { extensions: ['.png'], label: 'PNG' },
|
|
6305
6790
|
'image/jpeg': { extensions: ['.jpg', '.jpeg'], label: 'JPEG' },
|
|
6791
|
+
'image/gif': { extensions: ['.gif'], label: 'GIF' },
|
|
6792
|
+
'image/webp': { extensions: ['.webp'], label: 'WebP' },
|
|
6306
6793
|
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': { extensions: ['.docx'], label: 'Word' },
|
|
6307
|
-
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': { extensions: ['.xlsx'], label: 'Excel' }
|
|
6794
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': { extensions: ['.xlsx'], label: 'Excel' },
|
|
6795
|
+
'application/vnd.openxmlformats-officedocument.presentationml.presentation': { extensions: ['.pptx'], label: 'PowerPoint' },
|
|
6796
|
+
'application/msword': { extensions: ['.doc'], label: 'Word' },
|
|
6797
|
+
'application/vnd.ms-excel': { extensions: ['.xls'], label: 'Excel' },
|
|
6798
|
+
'text/csv': { extensions: ['.csv'], label: 'CSV' },
|
|
6799
|
+
'text/tab-separated-values': { extensions: ['.tsv'], label: 'TSV' },
|
|
6800
|
+
'text/plain': { extensions: ['.txt'], label: 'Text' },
|
|
6801
|
+
'text/markdown': { extensions: ['.md', '.markdown'], label: 'Markdown' },
|
|
6802
|
+
'application/json': { extensions: ['.json'], label: 'JSON' },
|
|
6803
|
+
'application/xml': { extensions: ['.xml'], label: 'XML' }
|
|
6308
6804
|
};
|
|
6805
|
+
/** Types that say nothing about the content, whatever the file's extension. */
|
|
6806
|
+
const genericTypes = new Set([unknownMediaType, 'binary/octet-stream', 'application/unknown']);
|
|
6309
6807
|
/**
|
|
6310
|
-
* The
|
|
6311
|
-
*
|
|
6312
|
-
*
|
|
6808
|
+
* The types browsers commonly give a file of an extension instead of its own type: the program
|
|
6809
|
+
* that opens it, as Windows does with `application/vnd.ms-excel` for `.csv`, an outdated name or
|
|
6810
|
+
* the container format.
|
|
6313
6811
|
*/
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
|
|
6318
|
-
|
|
6319
|
-
|
|
6812
|
+
const aliasesByExtension = {
|
|
6813
|
+
'.csv': [
|
|
6814
|
+
'application/vnd.ms-excel',
|
|
6815
|
+
'application/csv',
|
|
6816
|
+
'application/x-csv',
|
|
6817
|
+
'text/x-csv',
|
|
6818
|
+
'text/comma-separated-values',
|
|
6819
|
+
'text/x-comma-separated-values'
|
|
6820
|
+
],
|
|
6821
|
+
'.md': ['text/x-markdown'],
|
|
6822
|
+
'.markdown': ['text/x-markdown'],
|
|
6823
|
+
'.jpg': ['image/jpg', 'image/pjpeg'],
|
|
6824
|
+
'.jpeg': ['image/jpg', 'image/pjpeg'],
|
|
6825
|
+
'.png': ['image/x-png'],
|
|
6826
|
+
'.docx': zipAliases,
|
|
6827
|
+
'.xlsx': zipAliases,
|
|
6828
|
+
'.pptx': zipAliases,
|
|
6829
|
+
// Browsers send the older name of XML; an application that accepts only it keeps receiving it.
|
|
6830
|
+
'.xml': ['text/xml']
|
|
6831
|
+
};
|
|
6832
|
+
/** The extension of a file name in lowercase, such as `.csv`; empty without one. */
|
|
6833
|
+
function extensionOf(name) {
|
|
6834
|
+
const base = name.slice(Math.max(name.lastIndexOf('/'), name.lastIndexOf('\\')) + 1);
|
|
6835
|
+
const dot = base.lastIndexOf('.');
|
|
6836
|
+
return dot > 0 || (dot === 0 && base.length > 1) ? base.slice(dot).toLowerCase() : '';
|
|
6837
|
+
}
|
|
6838
|
+
/** The application's extensions (`AgentUploads.mappedExtensions`) whose type is accepted, lowercase. */
|
|
6839
|
+
function acceptedMappings(acceptedMediaTypes, mappedExtensions) {
|
|
6840
|
+
return (mappedExtensions ?? [])
|
|
6841
|
+
.map(mapping => ({ extension: mapping.extension.toLowerCase(), mediaType: mapping.mediaType.toLowerCase() }))
|
|
6842
|
+
.filter(mapping => acceptedMediaTypes.includes(mapping.mediaType));
|
|
6843
|
+
}
|
|
6844
|
+
/** The platform's media type of an extension, or `null`. */
|
|
6845
|
+
function knownMediaTypeOf(extension) {
|
|
6320
6846
|
for (const [mediaType, known] of Object.entries(knownTypes)) {
|
|
6321
|
-
if (known.extensions.
|
|
6847
|
+
if (known.extensions.includes(extension)) {
|
|
6322
6848
|
return mediaType;
|
|
6323
6849
|
}
|
|
6324
6850
|
}
|
|
6325
|
-
return
|
|
6851
|
+
return null;
|
|
6852
|
+
}
|
|
6853
|
+
/**
|
|
6854
|
+
* The accepted type an extension names: the application's mapping, otherwise the platform's table;
|
|
6855
|
+
* `null` when neither names an accepted type.
|
|
6856
|
+
*/
|
|
6857
|
+
function mediaTypeOfExtension(extension, acceptedMediaTypes, mappings) {
|
|
6858
|
+
if (extension.length === 0) {
|
|
6859
|
+
return null;
|
|
6860
|
+
}
|
|
6861
|
+
const mapped = mappings.find(mapping => mapping.extension === extension);
|
|
6862
|
+
if (mapped) {
|
|
6863
|
+
return mapped.mediaType;
|
|
6864
|
+
}
|
|
6865
|
+
const known = knownMediaTypeOf(extension);
|
|
6866
|
+
return known && acceptedMediaTypes.includes(known) ? known : null;
|
|
6867
|
+
}
|
|
6868
|
+
/**
|
|
6869
|
+
* The media type a chosen file uploads as, lowercase and without parameters, by the same rule as the
|
|
6870
|
+
* server. A browser gives no type, or a generic one, for a file its system does not know, and some
|
|
6871
|
+
* systems name an extension after the program that opens it, such as `application/vnd.ms-excel` for
|
|
6872
|
+
* a `.csv` file; the file name's extension then names the type, but only a type of
|
|
6873
|
+
* `acceptedMediaTypes`: the type the application mapped the extension to (`mappedExtensions`),
|
|
6874
|
+
* otherwise the platform's. A specific type stays as it is, so a type that is not accepted is refused
|
|
6875
|
+
* whatever the extension. Without a type and an accepted extension, `application/octet-stream`.
|
|
6876
|
+
*/
|
|
6877
|
+
function nhAssistantMediaTypeOf(file, acceptedMediaTypes, mappedExtensions) {
|
|
6878
|
+
const declared = file.type.split(';')[0].trim().toLowerCase();
|
|
6879
|
+
const extension = extensionOf(file.name);
|
|
6880
|
+
if (declared.length === 0 || genericTypes.has(declared) || (aliasesByExtension[extension] ?? []).includes(declared)) {
|
|
6881
|
+
const derived = mediaTypeOfExtension(extension, acceptedMediaTypes, acceptedMappings(acceptedMediaTypes, mappedExtensions));
|
|
6882
|
+
if (derived) {
|
|
6883
|
+
return derived;
|
|
6884
|
+
}
|
|
6885
|
+
}
|
|
6886
|
+
return declared.length > 0 ? declared : unknownMediaType;
|
|
6326
6887
|
}
|
|
6327
6888
|
/**
|
|
6328
6889
|
* The failure code of a file before it is uploaded, or `null` when it may be: the most files of one
|
|
@@ -6344,11 +6905,19 @@ function nhAssistantFileProblem(file, mediaType, uploads, attached) {
|
|
|
6344
6905
|
}
|
|
6345
6906
|
return null;
|
|
6346
6907
|
}
|
|
6347
|
-
/**
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
|
|
6351
|
-
|
|
6908
|
+
/**
|
|
6909
|
+
* The `accept` attribute of a file input for these accepted media types: each type and the extensions
|
|
6910
|
+
* that name it, those the application mapped to it (`mappedExtensions`) and those of the platform's
|
|
6911
|
+
* table that the application did not map to another accepted type.
|
|
6912
|
+
*/
|
|
6913
|
+
function nhAssistantAcceptAttribute(mediaTypes, mappedExtensions) {
|
|
6914
|
+
const mappings = acceptedMappings(mediaTypes, mappedExtensions);
|
|
6915
|
+
const values = mediaTypes.flatMap(mediaType => [
|
|
6916
|
+
mediaType,
|
|
6917
|
+
...mappings.filter(mapping => mapping.mediaType === mediaType).map(mapping => mapping.extension),
|
|
6918
|
+
...(knownTypes[mediaType]?.extensions ?? []).filter(extension => !mappings.some(mapping => mapping.extension === extension))
|
|
6919
|
+
]);
|
|
6920
|
+
return [...new Set(values)].join(',');
|
|
6352
6921
|
}
|
|
6353
6922
|
/** A short name of a media type, such as `PDF` or `Word`; otherwise its subtype in capitals. */
|
|
6354
6923
|
function nhAssistantFileTypeLabel(mediaType) {
|
|
@@ -6473,7 +7042,11 @@ function applyNhAssistantEvent(conversation, event, clientMessageId) {
|
|
|
6473
7042
|
};
|
|
6474
7043
|
}
|
|
6475
7044
|
}
|
|
6476
|
-
/**
|
|
7045
|
+
/**
|
|
7046
|
+
* Records the user's approval decision locally until the server state is reloaded. A consulted
|
|
7047
|
+
* agent's proposal that the user left for the subject's approvers (`leave`) closes here as the server
|
|
7048
|
+
* closes it, rejected in this conversation, and can no longer be left; the waiting tool call goes on.
|
|
7049
|
+
*/
|
|
6477
7050
|
function applyNhAssistantApprovalDecision(conversation, approvalId, decision) {
|
|
6478
7051
|
const approval = conversation.messages
|
|
6479
7052
|
.flatMap(message => message.parts)
|
|
@@ -6483,7 +7056,7 @@ function applyNhAssistantApprovalDecision(conversation, approvalId, decision) {
|
|
|
6483
7056
|
const messages = conversation.messages.map(message => ({
|
|
6484
7057
|
...message,
|
|
6485
7058
|
parts: message.parts.map(part => part.type === 'approval' && part.approvalId === approvalId
|
|
6486
|
-
?
|
|
7059
|
+
? decidedApproval(part, decision)
|
|
6487
7060
|
: part)
|
|
6488
7061
|
}));
|
|
6489
7062
|
let updated = {
|
|
@@ -6497,15 +7070,24 @@ function applyNhAssistantApprovalDecision(conversation, approvalId, decision) {
|
|
|
6497
7070
|
if (awaiting) {
|
|
6498
7071
|
updated = updateToolCall(updated, awaiting.invocationId, part => ({
|
|
6499
7072
|
...part,
|
|
6500
|
-
status: decision === '
|
|
7073
|
+
status: decision === 'reject' ? 'rejected' : 'running'
|
|
6501
7074
|
}));
|
|
6502
7075
|
}
|
|
6503
7076
|
}
|
|
6504
7077
|
return updated;
|
|
6505
7078
|
}
|
|
7079
|
+
/** An approval part after the user's decision. */
|
|
7080
|
+
function decidedApproval(part, decision) {
|
|
7081
|
+
const decided = { ...part, status: decision === 'approve' ? 'approved' : 'rejected' };
|
|
7082
|
+
if (decision === 'leave' && part.consultation) {
|
|
7083
|
+
decided.consultation = { ...part.consultation, leaveRoute: null };
|
|
7084
|
+
}
|
|
7085
|
+
return decided;
|
|
7086
|
+
}
|
|
6506
7087
|
/**
|
|
6507
|
-
* Applies a live update of another session: `message.created` adds a participant's
|
|
6508
|
-
*
|
|
7088
|
+
* Applies a live update of another session: `message.created` adds a participant's or the
|
|
7089
|
+
* application's message once, where its sequence number puts it; the turn events behave exactly
|
|
7090
|
+
* like the starting request's stream. Unknown types are ignored.
|
|
6509
7091
|
*/
|
|
6510
7092
|
function applyNhAssistantLiveEvent(conversation, event) {
|
|
6511
7093
|
if (event.type === 'message.created') {
|
|
@@ -6513,7 +7095,7 @@ function applyNhAssistantLiveEvent(conversation, event) {
|
|
|
6513
7095
|
if (!message?.id || conversation.messages.some(existing => existing.id === message.id)) {
|
|
6514
7096
|
return conversation;
|
|
6515
7097
|
}
|
|
6516
|
-
return { ...conversation, messages:
|
|
7098
|
+
return { ...conversation, messages: insertBySequence(conversation.messages, message) };
|
|
6517
7099
|
}
|
|
6518
7100
|
if (!NH_ASSISTANT_SSE_EVENT_TYPES.includes(event.type)) {
|
|
6519
7101
|
return conversation;
|
|
@@ -6560,6 +7142,26 @@ function conversationStatusFor(status) {
|
|
|
6560
7142
|
return 'failed';
|
|
6561
7143
|
}
|
|
6562
7144
|
}
|
|
7145
|
+
/**
|
|
7146
|
+
* Places a stored message before the messages at the end that have a higher sequence number. A
|
|
7147
|
+
* message without a number, such as an optimistic message or the answer a running turn is still
|
|
7148
|
+
* writing, counts as earlier: the server numbers a turn's messages when the turn starts, so a message
|
|
7149
|
+
* stored during the turn follows its answer. A message without a number of its own goes last.
|
|
7150
|
+
*/
|
|
7151
|
+
function insertBySequence(messages, message) {
|
|
7152
|
+
const sequence = message.sequence;
|
|
7153
|
+
let index = messages.length;
|
|
7154
|
+
if (typeof sequence === 'number' && sequence > 0) {
|
|
7155
|
+
while (index > 0) {
|
|
7156
|
+
const previous = messages[index - 1].sequence;
|
|
7157
|
+
if (typeof previous !== 'number' || previous <= sequence) {
|
|
7158
|
+
break;
|
|
7159
|
+
}
|
|
7160
|
+
index--;
|
|
7161
|
+
}
|
|
7162
|
+
}
|
|
7163
|
+
return [...messages.slice(0, index), message, ...messages.slice(index)];
|
|
7164
|
+
}
|
|
6563
7165
|
function createAssistantMessage(id) {
|
|
6564
7166
|
return { id, role: 'assistant', createdAt: new Date().toISOString(), parts: [] };
|
|
6565
7167
|
}
|
|
@@ -6815,6 +7417,8 @@ const NH_ASSISTANT_NO_ANSWER_NOTICE_CODE = 'assistant-no-answer';
|
|
|
6815
7417
|
*/
|
|
6816
7418
|
const NH_ASSISTANT_MESSAGE_QUEUED_NOTICE_CODE = 'assistant-message-queued';
|
|
6817
7419
|
const fourEyesRequiredCode = 'assistant-four-eyes-required';
|
|
7420
|
+
/** A consultation's proposal in the consulted thread that the person who asked decides in their own chat. */
|
|
7421
|
+
const askerDecidesCode = 'assistant-consult-asker-decides';
|
|
6818
7422
|
/** True when a conversation has messages the caller has not read. A running turn is not unread yet. */
|
|
6819
7423
|
function nhAssistantIsUnread(summary) {
|
|
6820
7424
|
if (summary.status === 'running' || summary.lastReadSequence === undefined) {
|
|
@@ -6928,6 +7532,12 @@ class NhAssistantStore {
|
|
|
6928
7532
|
/** Non-blocking remark about the latest completed turn; shown only while no error is shown. */
|
|
6929
7533
|
this.notice = computed(() => this.activeSession()?.notice ?? null, ...(ngDevMode ? [{ debugName: "notice" }] : []));
|
|
6930
7534
|
this.lastUsage = computed(() => this.activeSession()?.lastUsage ?? null, ...(ngDevMode ? [{ debugName: "lastUsage" }] : []));
|
|
7535
|
+
/**
|
|
7536
|
+
* The answer the running turn of the open conversation writes, while this tab streams the turn or
|
|
7537
|
+
* follows it live; `null` otherwise. Pass it to `nh-assistant-thread` as `writingMessageId`, so
|
|
7538
|
+
* the working indicator stays under that answer when a message arrives after it during the turn.
|
|
7539
|
+
*/
|
|
7540
|
+
this.writingMessageId = computed(() => this.activeSession()?.writingMessageId ?? null, ...(ngDevMode ? [{ debugName: "writingMessageId" }] : []));
|
|
6931
7541
|
/** Text of a message the server did not accept, so the composer can offer it again. */
|
|
6932
7542
|
this.restoredDraft = this.restoredDraftState.asReadonly();
|
|
6933
7543
|
/** Page context that the next message would send, for the transparency chip. */
|
|
@@ -7012,7 +7622,7 @@ class NhAssistantStore {
|
|
|
7012
7622
|
const decision = decisions.get(approval.approvalId);
|
|
7013
7623
|
rights.set(approval.approvalId, {
|
|
7014
7624
|
canDecide: decision?.canDecide === true,
|
|
7015
|
-
waitingFor: decision?.reasonCode
|
|
7625
|
+
waitingFor: waitingForOf(decision?.reasonCode),
|
|
7016
7626
|
decisionOwner: null
|
|
7017
7627
|
});
|
|
7018
7628
|
}
|
|
@@ -7515,8 +8125,11 @@ class NhAssistantStore {
|
|
|
7515
8125
|
/**
|
|
7516
8126
|
* Approves or rejects an approval of the open conversation: `approvalId`, or the pending approval
|
|
7517
8127
|
* when it is left out. A proposal of an agent's run is decided with its proposal hash through the
|
|
7518
|
-
* agent-run endpoint; other approvals resume their turn as a stream.
|
|
7519
|
-
*
|
|
8128
|
+
* agent-run endpoint; other approvals resume their turn as a stream. `leave` leaves a consulted
|
|
8129
|
+
* agent's proposal that has a `consultation.leaveRoute` for the subject's approvers; it resumes the
|
|
8130
|
+
* turn like a decision and reads the conversation again, which then names the proposal that waits
|
|
8131
|
+
* at the subject. Ignores calls the current user may not make, `leave` for other approvals and
|
|
8132
|
+
* repeated calls while a decision is in flight.
|
|
7520
8133
|
*/
|
|
7521
8134
|
decide(decision, reason, approvalId) {
|
|
7522
8135
|
const session = this.activeSession();
|
|
@@ -7530,9 +8143,15 @@ class NhAssistantStore {
|
|
|
7530
8143
|
if (!approval || this.approvalRights().get(approval.approvalId)?.canDecide !== true) {
|
|
7531
8144
|
return;
|
|
7532
8145
|
}
|
|
8146
|
+
if (decision === 'leave' && !approval.consultation?.leaveRoute) {
|
|
8147
|
+
// Only a consulted agent's proposal with a route at the subject can be left for its approvers.
|
|
8148
|
+
return;
|
|
8149
|
+
}
|
|
7533
8150
|
const proposal = conversation.thread?.decisions.find(item => item.approvalId === approval.approvalId);
|
|
7534
8151
|
if (proposal?.runId) {
|
|
7535
|
-
|
|
8152
|
+
if (decision !== 'leave') {
|
|
8153
|
+
void this.decideProposal(conversation.id, approval, decision, reason);
|
|
8154
|
+
}
|
|
7536
8155
|
return;
|
|
7537
8156
|
}
|
|
7538
8157
|
if (conversation.pendingApproval?.approvalId !== approval.approvalId) {
|
|
@@ -7564,7 +8183,8 @@ class NhAssistantStore {
|
|
|
7564
8183
|
},
|
|
7565
8184
|
complete: () => {
|
|
7566
8185
|
this.patchSession(conversationId, current => ({ ...current, deciding: false }));
|
|
7567
|
-
|
|
8186
|
+
// A proposal left for the subject's approvers names the one that waits there only once read again.
|
|
8187
|
+
this.afterTurn(conversationId, failed || decision === 'leave');
|
|
7568
8188
|
}
|
|
7569
8189
|
});
|
|
7570
8190
|
}
|
|
@@ -7638,7 +8258,7 @@ class NhAssistantStore {
|
|
|
7638
8258
|
if (this.sessionsState().get(conversationId)?.streaming) {
|
|
7639
8259
|
this.streams.get(conversationId)?.unsubscribe();
|
|
7640
8260
|
this.streams.delete(conversationId);
|
|
7641
|
-
this.patchSession(conversationId, current => ({ ...current, streaming: false, deciding: false }));
|
|
8261
|
+
this.patchSession(conversationId, current => ({ ...current, streaming: false, deciding: false, writingMessageId: null }));
|
|
7642
8262
|
void this.reloadSession(conversationId);
|
|
7643
8263
|
}
|
|
7644
8264
|
}, cancelGracePeriodMs));
|
|
@@ -7811,7 +8431,7 @@ class NhAssistantStore {
|
|
|
7811
8431
|
const added = [];
|
|
7812
8432
|
const pending = [];
|
|
7813
8433
|
for (const file of files) {
|
|
7814
|
-
const mediaType = nhAssistantMediaTypeOf(file);
|
|
8434
|
+
const mediaType = nhAssistantMediaTypeOf(file, uploads.acceptedMediaTypes, uploads.mappedExtensions);
|
|
7815
8435
|
const problem = nhAssistantFileProblem(file, mediaType, uploads, attached);
|
|
7816
8436
|
const attachment = {
|
|
7817
8437
|
id: createClientMessageId(),
|
|
@@ -8122,12 +8742,12 @@ class NhAssistantStore {
|
|
|
8122
8742
|
}
|
|
8123
8743
|
runStream(conversationId, stream, handlers) {
|
|
8124
8744
|
this.streams.get(conversationId)?.unsubscribe();
|
|
8125
|
-
this.patchSession(conversationId, session => ({ ...session, streaming: true }));
|
|
8745
|
+
this.patchSession(conversationId, session => ({ ...session, streaming: true, writingMessageId: null }));
|
|
8126
8746
|
const finish = () => {
|
|
8127
8747
|
clearTimeout(this.cancelTimers.get(conversationId));
|
|
8128
8748
|
this.cancelTimers.delete(conversationId);
|
|
8129
8749
|
this.streams.delete(conversationId);
|
|
8130
|
-
this.patchSession(conversationId, session => ({ ...session, streaming: false }));
|
|
8750
|
+
this.patchSession(conversationId, session => ({ ...session, streaming: false, writingMessageId: null }));
|
|
8131
8751
|
handlers.complete();
|
|
8132
8752
|
};
|
|
8133
8753
|
const subscription = stream.subscribe({
|
|
@@ -8172,7 +8792,7 @@ class NhAssistantStore {
|
|
|
8172
8792
|
notice = completionNotice(conversation, event.data.errorCode);
|
|
8173
8793
|
}
|
|
8174
8794
|
}
|
|
8175
|
-
return { ...session, conversation, error, notice, lastUsage };
|
|
8795
|
+
return { ...session, conversation, error, notice, lastUsage, writingMessageId: writingAnswerAfter(session.writingMessageId, event) };
|
|
8176
8796
|
});
|
|
8177
8797
|
}
|
|
8178
8798
|
afterTurn(conversationId, reload) {
|
|
@@ -8260,7 +8880,8 @@ class NhAssistantStore {
|
|
|
8260
8880
|
const wasRunning = session.conversation.status === 'running';
|
|
8261
8881
|
this.patchSession(change.conversationId, current => ({
|
|
8262
8882
|
...current,
|
|
8263
|
-
conversation: applyNhAssistantConversationChange(current.conversation, change)
|
|
8883
|
+
conversation: applyNhAssistantConversationChange(current.conversation, change),
|
|
8884
|
+
writingMessageId: change.status === 'running' ? current.writingMessageId : null
|
|
8264
8885
|
}));
|
|
8265
8886
|
if (wasRunning && change.status !== 'running') {
|
|
8266
8887
|
// Another session ended its turn; the snapshot holds the exact answer and approvals.
|
|
@@ -8283,18 +8904,27 @@ class NhAssistantStore {
|
|
|
8283
8904
|
this.draftErrorState.set(clientError(NhAssistantClientErrorCodes.conversationRemoved));
|
|
8284
8905
|
}
|
|
8285
8906
|
}
|
|
8907
|
+
/**
|
|
8908
|
+
* Applies a live turn event or message to a conversation this tab shows. While the tab streams a
|
|
8909
|
+
* turn of the conversation, its stream brings the turn's events and its own message, so it applies
|
|
8910
|
+
* only messages that others stored meanwhile: the application's and other people's.
|
|
8911
|
+
*/
|
|
8286
8912
|
onLiveEvent(event) {
|
|
8287
8913
|
const session = this.sessionsState().get(event.conversationId);
|
|
8288
8914
|
if (event.type === 'turn.started') {
|
|
8289
8915
|
this.patchSummary(event.conversationId, summary => ({ ...summary, status: 'running', activeActorId: event.actorId }));
|
|
8290
8916
|
}
|
|
8291
|
-
if (!session
|
|
8292
|
-
|
|
8917
|
+
if (!session) {
|
|
8918
|
+
return;
|
|
8919
|
+
}
|
|
8920
|
+
if (session.streaming && !isMessageOfOthers(session.conversation, event)) {
|
|
8293
8921
|
return;
|
|
8294
8922
|
}
|
|
8295
8923
|
this.patchSession(event.conversationId, current => ({
|
|
8296
8924
|
...current,
|
|
8297
|
-
conversation: applyNhAssistantLiveEvent(current.conversation, event)
|
|
8925
|
+
conversation: applyNhAssistantLiveEvent(current.conversation, event),
|
|
8926
|
+
// A turn this tab follows live names its answer; while the tab streams, its stream does.
|
|
8927
|
+
writingMessageId: current.streaming ? current.writingMessageId : writingAnswerAfter(current.writingMessageId, event)
|
|
8298
8928
|
}));
|
|
8299
8929
|
}
|
|
8300
8930
|
/**
|
|
@@ -8346,8 +8976,8 @@ class NhAssistantStore {
|
|
|
8346
8976
|
const next = new Map(sessions);
|
|
8347
8977
|
const existing = next.get(conversation.id);
|
|
8348
8978
|
next.set(conversation.id, existing
|
|
8349
|
-
? { ...existing, conversation }
|
|
8350
|
-
: { conversation, streaming: false, deciding: false, error: null, notice: null, lastUsage: null });
|
|
8979
|
+
? { ...existing, conversation, writingMessageId: conversation.status === 'running' ? existing.writingMessageId : null }
|
|
8980
|
+
: { conversation, streaming: false, deciding: false, error: null, notice: null, lastUsage: null, writingMessageId: null });
|
|
8351
8981
|
if (next.size > maxSessions) {
|
|
8352
8982
|
const activeId = this.activeIdState();
|
|
8353
8983
|
for (const [id, session] of next) {
|
|
@@ -8476,16 +9106,59 @@ function messageQueuedNotice(fileCount) {
|
|
|
8476
9106
|
messageKey: fileCount > 0 ? 'nh-assistant.notices.message-queued-with-files' : 'nh-assistant.notices.message-queued'
|
|
8477
9107
|
};
|
|
8478
9108
|
}
|
|
8479
|
-
/**
|
|
9109
|
+
/**
|
|
9110
|
+
* The answer a turn writes after `event`: `turn.started` names it, `turn.completed` and `error` end
|
|
9111
|
+
* the turn, and every other event leaves it as it is.
|
|
9112
|
+
*/
|
|
9113
|
+
function writingAnswerAfter(current, event) {
|
|
9114
|
+
switch (event.type) {
|
|
9115
|
+
case 'turn.started':
|
|
9116
|
+
return event.data?.assistantMessageId ?? null;
|
|
9117
|
+
case 'turn.completed':
|
|
9118
|
+
case 'error':
|
|
9119
|
+
return null;
|
|
9120
|
+
default:
|
|
9121
|
+
return current;
|
|
9122
|
+
}
|
|
9123
|
+
}
|
|
9124
|
+
/**
|
|
9125
|
+
* A completed turn with a server code, or without any answer text, explains itself with a notice.
|
|
9126
|
+
* Another person's message that arrived while the turn ran does not end this tab's turn.
|
|
9127
|
+
*/
|
|
8480
9128
|
function completionNotice(conversation, errorCode) {
|
|
8481
9129
|
if (errorCode) {
|
|
8482
9130
|
return clientError(errorCode);
|
|
8483
9131
|
}
|
|
8484
|
-
|
|
9132
|
+
const ownTurn = { ...conversation, messages: conversation.messages.filter(message => !isOtherPersonsMessage(conversation, message)) };
|
|
9133
|
+
if (!nhAssistantLatestTurnHasText(ownTurn)) {
|
|
8485
9134
|
return { code: NH_ASSISTANT_NO_ANSWER_NOTICE_CODE, messageKey: 'nh-assistant.notices.no-answer' };
|
|
8486
9135
|
}
|
|
8487
9136
|
return null;
|
|
8488
9137
|
}
|
|
9138
|
+
/**
|
|
9139
|
+
* True for a live `message.created` of a message that this tab did not write: the application's, or
|
|
9140
|
+
* a user message of another writer. A user message is this tab's own when the current user wrote it,
|
|
9141
|
+
* and when the tab does not know the current user, so a turn's own message never shows twice.
|
|
9142
|
+
*/
|
|
9143
|
+
function isMessageOfOthers(conversation, event) {
|
|
9144
|
+
if (event.type !== 'message.created') {
|
|
9145
|
+
return false;
|
|
9146
|
+
}
|
|
9147
|
+
const message = event.data;
|
|
9148
|
+
if (!message?.id) {
|
|
9149
|
+
return false;
|
|
9150
|
+
}
|
|
9151
|
+
if (message.role === 'application') {
|
|
9152
|
+
return true;
|
|
9153
|
+
}
|
|
9154
|
+
return message.role === 'user' && isOtherPersonsMessage(conversation, message, event.actorId);
|
|
9155
|
+
}
|
|
9156
|
+
/** True for a user message that a known writer other than the current user wrote. */
|
|
9157
|
+
function isOtherPersonsMessage(conversation, message, fallbackAuthor) {
|
|
9158
|
+
const author = message.authorActorId ?? fallbackAuthor;
|
|
9159
|
+
const currentActorId = conversation.currentActorId;
|
|
9160
|
+
return message.role === 'user' && !!author && !!currentActorId && author !== currentActorId;
|
|
9161
|
+
}
|
|
8489
9162
|
/** The approvals of a conversation that still wait for a decision, each once. */
|
|
8490
9163
|
/** Every approval of the conversation once, by approval id; later parts win. */
|
|
8491
9164
|
function approvalParts(conversation) {
|
|
@@ -8513,6 +9186,20 @@ function pendingApprovalParts(conversation) {
|
|
|
8513
9186
|
}
|
|
8514
9187
|
return [...approvals.values()];
|
|
8515
9188
|
}
|
|
9189
|
+
/**
|
|
9190
|
+
* Who decides an open proposal of an agent thread the caller may not decide, by the server's reason:
|
|
9191
|
+
* the person who consulted the agent, another approver for four-eyes, otherwise an approver.
|
|
9192
|
+
*/
|
|
9193
|
+
function waitingForOf(reasonCode) {
|
|
9194
|
+
switch (reasonCode) {
|
|
9195
|
+
case askerDecidesCode:
|
|
9196
|
+
return 'asker';
|
|
9197
|
+
case fourEyesRequiredCode:
|
|
9198
|
+
return 'other-approver';
|
|
9199
|
+
default:
|
|
9200
|
+
return 'approver';
|
|
9201
|
+
}
|
|
9202
|
+
}
|
|
8516
9203
|
/**
|
|
8517
9204
|
* Shows the decision of an agent's proposal until the reloaded snapshot arrives: the approval's new
|
|
8518
9205
|
* status, no open decision for it and, for the approval the conversation waited for, no pending one.
|
|
@@ -9246,6 +9933,14 @@ const NH_ASSISTANT_SERVER_ERROR_CODES = [
|
|
|
9246
9933
|
'assistant-consult-timeout',
|
|
9247
9934
|
'assistant-consult-failed',
|
|
9248
9935
|
'assistant-consult-read-only',
|
|
9936
|
+
'assistant-consult-proposal-refused',
|
|
9937
|
+
'assistant-consult-asker-decides',
|
|
9938
|
+
'assistant-consult-route-not-allowed',
|
|
9939
|
+
'assistant-consult-proposal-moved',
|
|
9940
|
+
// Consultations defined as data in the administration
|
|
9941
|
+
'assistant-consultation-in-code',
|
|
9942
|
+
'assistant-consultation-not-found',
|
|
9943
|
+
'assistant-consultations-not-allowed',
|
|
9249
9944
|
// Preferences and administration
|
|
9250
9945
|
'assistant-validation',
|
|
9251
9946
|
'assistant-instructions-too-long',
|
|
@@ -9722,8 +10417,11 @@ class NhAssistantComposerComponent {
|
|
|
9722
10417
|
/** True while a file of the next message still uploads. */
|
|
9723
10418
|
this.uploading = computed(() => this.attachments().some(attachment => attachment.state === 'uploading'), ...(ngDevMode ? [{ debugName: "uploading" }] : []));
|
|
9724
10419
|
this.canSend = computed(() => !this.disabled() && !this.sendDisabled() && this.length() > 0 && !this.tooLong() && !this.uploading(), ...(ngDevMode ? [{ debugName: "canSend" }] : []));
|
|
9725
|
-
/** The `accept` attribute of the file input. */
|
|
9726
|
-
this.accept = computed(() =>
|
|
10420
|
+
/** The `accept` attribute of the file input, with the extensions the application mapped. */
|
|
10421
|
+
this.accept = computed(() => {
|
|
10422
|
+
const uploads = this.uploads();
|
|
10423
|
+
return nhAssistantAcceptAttribute(uploads?.acceptedMediaTypes ?? [], uploads?.mappedExtensions);
|
|
10424
|
+
}, ...(ngDevMode ? [{ debugName: "accept" }] : []));
|
|
9727
10425
|
/** The values the attachment texts name: the accepted types, the largest size and the most files. */
|
|
9728
10426
|
this.uploadParams = computed(() => {
|
|
9729
10427
|
const uploads = this.uploads();
|
|
@@ -10342,6 +11040,159 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImpo
|
|
|
10342
11040
|
args: [{ name: 'nhAssistantMarkdown', standalone: true }]
|
|
10343
11041
|
}] });
|
|
10344
11042
|
|
|
11043
|
+
/**
|
|
11044
|
+
* The name of a subject with the name of its type, linked to where the application shows the
|
|
11045
|
+
* subject (internal). The host's `subjectLinks` builder wins over the server's link; a link within
|
|
11046
|
+
* the application navigates through the router, an `https` link leaves it, and anything else shows
|
|
11047
|
+
* no link. Ctrl-, Cmd-, Shift- and middle-clicks keep the browser's behavior, such as a new tab.
|
|
11048
|
+
* A name that already says the type, as "The whole application" does for the application as a
|
|
11049
|
+
* whole, is shown once.
|
|
11050
|
+
*/
|
|
11051
|
+
class NhAssistantSubjectLinkComponent {
|
|
11052
|
+
constructor() {
|
|
11053
|
+
this.config = inject(NH_ASSISTANT_CONFIG, { optional: true });
|
|
11054
|
+
this.router = inject(Router, { optional: true });
|
|
11055
|
+
this.locationStrategy = inject(LocationStrategy, { optional: true });
|
|
11056
|
+
this.injector = inject(EnvironmentInjector);
|
|
11057
|
+
this.subjectType = input.required(...(ngDevMode ? [{ debugName: "subjectType" }] : []));
|
|
11058
|
+
this.subjectId = input.required(...(ngDevMode ? [{ debugName: "subjectId" }] : []));
|
|
11059
|
+
/** The text people read, usually the subject's display name. */
|
|
11060
|
+
this.label = input.required(...(ngDevMode ? [{ debugName: "label" }] : []));
|
|
11061
|
+
/** The name of the subject type, such as "Project"; nothing is shown without one. */
|
|
11062
|
+
this.typeName = input(null, ...(ngDevMode ? [{ debugName: "typeName" }] : []));
|
|
11063
|
+
/** The server's link to the subject. */
|
|
11064
|
+
this.link = input(null, ...(ngDevMode ? [{ debugName: "link" }] : []));
|
|
11065
|
+
/** The type's name, unless the subject's name is the type's name itself, as for the whole application. */
|
|
11066
|
+
this.shownTypeName = computed(() => {
|
|
11067
|
+
const type = this.typeName()?.trim();
|
|
11068
|
+
if (!type) {
|
|
11069
|
+
return null;
|
|
11070
|
+
}
|
|
11071
|
+
return type.toLocaleLowerCase() === this.label().trim().toLocaleLowerCase() ? null : type;
|
|
11072
|
+
}, ...(ngDevMode ? [{ debugName: "shownTypeName" }] : []));
|
|
11073
|
+
this.target = computed(() => {
|
|
11074
|
+
const builder = this.config?.subjectLinks?.[this.subjectType()];
|
|
11075
|
+
const subject = { type: this.subjectType(), id: this.subjectId() };
|
|
11076
|
+
const custom = builder ? runInInjectionContext(this.injector, () => builder(subject)) : undefined;
|
|
11077
|
+
if (custom === null) {
|
|
11078
|
+
return null;
|
|
11079
|
+
}
|
|
11080
|
+
if (Array.isArray(custom)) {
|
|
11081
|
+
return this.routeTarget(this.router ? this.router.serializeUrl(this.router.createUrlTree([...custom])) : null);
|
|
11082
|
+
}
|
|
11083
|
+
return this.urlTarget(typeof custom === 'string' ? custom : this.link() ?? null);
|
|
11084
|
+
}, ...(ngDevMode ? [{ debugName: "target" }] : []));
|
|
11085
|
+
}
|
|
11086
|
+
open(event, target) {
|
|
11087
|
+
nhAssistantOpenLink(event, target, this.router);
|
|
11088
|
+
}
|
|
11089
|
+
urlTarget(url) {
|
|
11090
|
+
return nhAssistantLinkTarget(url, this.router, this.locationStrategy);
|
|
11091
|
+
}
|
|
11092
|
+
routeTarget(url) {
|
|
11093
|
+
return nhAssistantRouteTarget(url, this.router, this.locationStrategy);
|
|
11094
|
+
}
|
|
11095
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantSubjectLinkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
11096
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.34", type: NhAssistantSubjectLinkComponent, isStandalone: true, selector: "nh-assistant-subject-link", inputs: { subjectType: { classPropertyName: "subjectType", publicName: "subjectType", isSignal: true, isRequired: true, transformFunction: null }, subjectId: { classPropertyName: "subjectId", publicName: "subjectId", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, typeName: { classPropertyName: "typeName", publicName: "typeName", isSignal: true, isRequired: false, transformFunction: null }, link: { classPropertyName: "link", publicName: "link", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
11097
|
+
@if (shownTypeName(); as type) {
|
|
11098
|
+
<!-- The space keeps the type and the name apart for screen readers; the flex gap shows it. -->
|
|
11099
|
+
<span class="nh-assistant-subject-type">{{ type }}</span>{{ ' ' }}
|
|
11100
|
+
}
|
|
11101
|
+
@if (target(); as link) {
|
|
11102
|
+
<a class="nh-assistant-subject-name" [href]="link.href" (click)="open($event, link)">{{ label() }}</a>
|
|
11103
|
+
} @else {
|
|
11104
|
+
<span class="nh-assistant-subject-name">{{ label() }}</span>
|
|
11105
|
+
}
|
|
11106
|
+
`, 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;flex-wrap:wrap;align-items:baseline;gap:.35em;min-width:0;overflow-wrap:anywhere}.nh-assistant-subject-type{color:var(--nha-text-muted);font-size:.85em;font-weight:500}a.nh-assistant-subject-name{border-radius:2px;color:inherit;text-decoration:underline;text-underline-offset:.15em}a.nh-assistant-subject-name:hover{color:var(--nha-accent)}a.nh-assistant-subject-name:focus-visible{outline:2px solid var(--nha-focus);outline-offset:2px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
11107
|
+
}
|
|
11108
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantSubjectLinkComponent, decorators: [{
|
|
11109
|
+
type: Component,
|
|
11110
|
+
args: [{ selector: 'nh-assistant-subject-link', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
11111
|
+
@if (shownTypeName(); as type) {
|
|
11112
|
+
<!-- The space keeps the type and the name apart for screen readers; the flex gap shows it. -->
|
|
11113
|
+
<span class="nh-assistant-subject-type">{{ type }}</span>{{ ' ' }}
|
|
11114
|
+
}
|
|
11115
|
+
@if (target(); as link) {
|
|
11116
|
+
<a class="nh-assistant-subject-name" [href]="link.href" (click)="open($event, link)">{{ label() }}</a>
|
|
11117
|
+
} @else {
|
|
11118
|
+
<span class="nh-assistant-subject-name">{{ label() }}</span>
|
|
11119
|
+
}
|
|
11120
|
+
`, 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;flex-wrap:wrap;align-items:baseline;gap:.35em;min-width:0;overflow-wrap:anywhere}.nh-assistant-subject-type{color:var(--nha-text-muted);font-size:.85em;font-weight:500}a.nh-assistant-subject-name{border-radius:2px;color:inherit;text-decoration:underline;text-underline-offset:.15em}a.nh-assistant-subject-name:hover{color:var(--nha-accent)}a.nh-assistant-subject-name:focus-visible{outline:2px solid var(--nha-focus);outline-offset:2px}\n"] }]
|
|
11121
|
+
}], propDecorators: { subjectType: [{ type: i0.Input, args: [{ isSignal: true, alias: "subjectType", required: true }] }], subjectId: [{ type: i0.Input, args: [{ isSignal: true, alias: "subjectId", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], typeName: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeName", required: false }] }], link: [{ type: i0.Input, args: [{ isSignal: true, alias: "link", required: false }] }] } });
|
|
11122
|
+
/**
|
|
11123
|
+
* Where a link the server sent leads (internal): a path within the application, which the router
|
|
11124
|
+
* opens, an `https` address elsewhere, or `null` for anything else, so no other scheme reaches an
|
|
11125
|
+
* anchor. Subject links and the links of application messages follow this rule.
|
|
11126
|
+
*/
|
|
11127
|
+
function nhAssistantLinkTarget(url, router, locationStrategy) {
|
|
11128
|
+
if (!url) {
|
|
11129
|
+
return null;
|
|
11130
|
+
}
|
|
11131
|
+
if (url.startsWith('/') && !url.startsWith('//') && !url.startsWith('/\\')) {
|
|
11132
|
+
return nhAssistantRouteTarget(url, router, locationStrategy);
|
|
11133
|
+
}
|
|
11134
|
+
return /^https:\/\/[^/\\\s]+/i.test(url) ? { href: url, route: null } : null;
|
|
11135
|
+
}
|
|
11136
|
+
/** A route of the application as the browser shows it and as the router opens it (internal). */
|
|
11137
|
+
function nhAssistantRouteTarget(url, router, locationStrategy) {
|
|
11138
|
+
if (!url) {
|
|
11139
|
+
return null;
|
|
11140
|
+
}
|
|
11141
|
+
return {
|
|
11142
|
+
href: locationStrategy ? locationStrategy.prepareExternalUrl(url) : url,
|
|
11143
|
+
route: router ? url : null
|
|
11144
|
+
};
|
|
11145
|
+
}
|
|
11146
|
+
/**
|
|
11147
|
+
* Opens a route of the application through the router on a plain click (internal). Ctrl-, Cmd-,
|
|
11148
|
+
* Shift-, Alt- and middle-clicks, and addresses outside the application, keep the browser's behavior.
|
|
11149
|
+
*/
|
|
11150
|
+
function nhAssistantOpenLink(event, target, router) {
|
|
11151
|
+
if (!target.route || !router || event.button !== 0 || event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) {
|
|
11152
|
+
return;
|
|
11153
|
+
}
|
|
11154
|
+
event.preventDefault();
|
|
11155
|
+
void router.navigateByUrl(target.route);
|
|
11156
|
+
}
|
|
11157
|
+
|
|
11158
|
+
/**
|
|
11159
|
+
* The link of a message the application posted (internal): a path within the application opens
|
|
11160
|
+
* through the router, an `https` address leaves it, and anything else shows nothing, by the same rule
|
|
11161
|
+
* as subject links.
|
|
11162
|
+
*/
|
|
11163
|
+
class NhAssistantMessageLinkComponent {
|
|
11164
|
+
constructor() {
|
|
11165
|
+
this.router = inject(Router, { optional: true });
|
|
11166
|
+
this.locationStrategy = inject(LocationStrategy, { optional: true });
|
|
11167
|
+
/** A path within the application or an `https` address. */
|
|
11168
|
+
this.url = input.required(...(ngDevMode ? [{ debugName: "url" }] : []));
|
|
11169
|
+
/** The text people read. */
|
|
11170
|
+
this.label = input.required(...(ngDevMode ? [{ debugName: "label" }] : []));
|
|
11171
|
+
this.target = computed(() => nhAssistantLinkTarget(this.url(), this.router, this.locationStrategy), ...(ngDevMode ? [{ debugName: "target" }] : []));
|
|
11172
|
+
}
|
|
11173
|
+
open(event, target) {
|
|
11174
|
+
nhAssistantOpenLink(event, target, this.router);
|
|
11175
|
+
}
|
|
11176
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantMessageLinkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
11177
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.34", type: NhAssistantMessageLinkComponent, isStandalone: true, selector: "nh-assistant-message-link", inputs: { url: { classPropertyName: "url", publicName: "url", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
|
|
11178
|
+
@if (target(); as link) {
|
|
11179
|
+
<a class="nh-assistant-message-link" [href]="link.href" (click)="open($event, link)">
|
|
11180
|
+
<nh-assistant-icon name="link" />{{ label() }}
|
|
11181
|
+
</a>
|
|
11182
|
+
}
|
|
11183
|
+
`, isInline: true, styles: [":host{display:block}.nh-assistant-message-link{display:inline-flex;align-items:center;gap:6px;color:var(--nha-accent);font-size:13px;font-weight:600;text-decoration:none}.nh-assistant-message-link:hover{text-decoration:underline}.nh-assistant-message-link:focus-visible{outline:2px solid var(--nha-focus);outline-offset:2px;border-radius:4px}\n"], dependencies: [{ kind: "component", type: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
11184
|
+
}
|
|
11185
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantMessageLinkComponent, decorators: [{
|
|
11186
|
+
type: Component,
|
|
11187
|
+
args: [{ selector: 'nh-assistant-message-link', standalone: true, imports: [NhAssistantIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
11188
|
+
@if (target(); as link) {
|
|
11189
|
+
<a class="nh-assistant-message-link" [href]="link.href" (click)="open($event, link)">
|
|
11190
|
+
<nh-assistant-icon name="link" />{{ label() }}
|
|
11191
|
+
</a>
|
|
11192
|
+
}
|
|
11193
|
+
`, styles: [":host{display:block}.nh-assistant-message-link{display:inline-flex;align-items:center;gap:6px;color:var(--nha-accent);font-size:13px;font-weight:600;text-decoration:none}.nh-assistant-message-link:hover{text-decoration:underline}.nh-assistant-message-link:focus-visible{outline:2px solid var(--nha-focus);outline-offset:2px;border-radius:4px}\n"] }]
|
|
11194
|
+
}], propDecorators: { url: [{ type: i0.Input, args: [{ isSignal: true, alias: "url", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }] } });
|
|
11195
|
+
|
|
10345
11196
|
/**
|
|
10346
11197
|
* Groups consecutive tool calls of a message, and consecutive files. Text and approvals break a
|
|
10347
11198
|
* group, so an approval always stays visible. A tool group's key is its first invocation id, so it
|
|
@@ -10526,10 +11377,16 @@ function formatNhAssistantCountdown(milliseconds) {
|
|
|
10526
11377
|
* proposed arguments and a countdown to expiry. A decision is emitted at most once until
|
|
10527
11378
|
* the host reports that it is no longer deciding. After the user rejected an agent's proposal
|
|
10528
11379
|
* (`canGiveFeedback`), a low-key action asks what was wrong. A proposal that closed because it no
|
|
10529
|
-
* longer fits shows why, such as "Already done", with the application's explanation.
|
|
11380
|
+
* longer fits shows why, such as "Already done", with the application's explanation. A proposal of
|
|
11381
|
+
* another agent the assistant consulted (`consultation`) names that agent and the subject, offers the
|
|
11382
|
+
* agent's answer and says that approving runs it with the user's own rights; with a
|
|
11383
|
+
* `consultation.leaveRoute` a third action leaves it for the subject's approvers (`leave`), after which
|
|
11384
|
+
* the card says that it waits for them.
|
|
10530
11385
|
*/
|
|
10531
11386
|
class NhAssistantApprovalCardComponent {
|
|
10532
11387
|
constructor() {
|
|
11388
|
+
/** Names the consulted agent as the status does; the card also works outside `provideNhAssistant`. */
|
|
11389
|
+
this.store = inject(NhAssistantStore, { optional: true });
|
|
10533
11390
|
this.approval = input.required(...(ngDevMode ? [{ debugName: "approval" }] : []));
|
|
10534
11391
|
/** True while the host submits a decision; disables both buttons. */
|
|
10535
11392
|
this.deciding = input(false, ...(ngDevMode ? [{ debugName: "deciding" }] : []));
|
|
@@ -10554,6 +11411,8 @@ class NhAssistantApprovalCardComponent {
|
|
|
10554
11411
|
this.decide = output();
|
|
10555
11412
|
this.now = signal(Date.now(), ...(ngDevMode ? [{ debugName: "now" }] : []));
|
|
10556
11413
|
this.submitted = signal(false, ...(ngDevMode ? [{ debugName: "submitted" }] : []));
|
|
11414
|
+
/** The decision this card sent last, so a proposal left for the approvers reads as such at once. */
|
|
11415
|
+
this.chosen = signal(null, ...(ngDevMode ? [{ debugName: "chosen" }] : []));
|
|
10557
11416
|
/** The feedback question is expanded. */
|
|
10558
11417
|
this.feedbackOpen = signal(false, ...(ngDevMode ? [{ debugName: "feedbackOpen" }] : []));
|
|
10559
11418
|
/** The feedback was stored; the question thanks the person and the action disappears. */
|
|
@@ -10563,11 +11422,45 @@ class NhAssistantApprovalCardComponent {
|
|
|
10563
11422
|
this.expired = computed(() => this.approval().status === 'expired' ||
|
|
10564
11423
|
(this.approval().status === 'pending' && this.remainingMs() <= 0), ...(ngDevMode ? [{ debugName: "expired" }] : []));
|
|
10565
11424
|
this.effectiveStatus = computed(() => this.expired() ? 'expired' : this.approval().status, ...(ngDevMode ? [{ debugName: "effectiveStatus" }] : []));
|
|
11425
|
+
/** Where a consulted agent's proposal comes from; `null` for the conversation's own proposals. */
|
|
11426
|
+
this.consultation = computed(() => this.approval().consultation ?? null, ...(ngDevMode ? [{ debugName: "consultation" }] : []));
|
|
11427
|
+
/**
|
|
11428
|
+
* The translation keys of the consulted agent's name: the name the assistant's status gives it,
|
|
11429
|
+
* then the convention `nh-assistant.agents.{id}.name`; the id when neither translates.
|
|
11430
|
+
*/
|
|
11431
|
+
this.consultedAgentKeys = computed(() => {
|
|
11432
|
+
const consultation = this.consultation();
|
|
11433
|
+
if (!consultation) {
|
|
11434
|
+
return [];
|
|
11435
|
+
}
|
|
11436
|
+
const convention = `nh-assistant.agents.${consultation.agentId}.name`;
|
|
11437
|
+
const known = this.store?.agents().find(agent => agent.id === consultation.agentId)?.displayNameKey;
|
|
11438
|
+
return known && known !== convention ? [known, convention] : [convention];
|
|
11439
|
+
}, ...(ngDevMode ? [{ debugName: "consultedAgentKeys" }] : []));
|
|
11440
|
+
/** The subject the consulted agent was asked about, by name or else by id. */
|
|
11441
|
+
this.consultedSubject = computed(() => {
|
|
11442
|
+
const consultation = this.consultation();
|
|
11443
|
+
return consultation ? consultation.subjectDisplayName ?? consultation.subjectId : null;
|
|
11444
|
+
}, ...(ngDevMode ? [{ debugName: "consultedSubject" }] : []));
|
|
11445
|
+
/**
|
|
11446
|
+
* The user left the consulted agent's proposal for the subject's approvers: the server names the
|
|
11447
|
+
* proposal that waits there, or this card just sent the decision and it was accepted.
|
|
11448
|
+
*/
|
|
11449
|
+
this.leftForApprovers = computed(() => {
|
|
11450
|
+
const consultation = this.consultation();
|
|
11451
|
+
return consultation !== null
|
|
11452
|
+
&& (!!consultation.movedToApprovalId || (this.chosen() === 'leave' && this.approval().status !== 'pending'));
|
|
11453
|
+
}, ...(ngDevMode ? [{ debugName: "leftForApprovers" }] : []));
|
|
11454
|
+
/** The status the card shows: `moved` for a proposal left for the subject's approvers. */
|
|
11455
|
+
this.shownStatus = computed(() => this.leftForApprovers() ? 'moved' : this.effectiveStatus(), ...(ngDevMode ? [{ debugName: "shownStatus" }] : []));
|
|
10566
11456
|
/**
|
|
10567
11457
|
* Translation key of the status: the kind of a stale proposal, "replaced" for a superseded one,
|
|
10568
|
-
* otherwise the status itself.
|
|
11458
|
+
* "left for approvers" for a consulted agent's proposal the user left, otherwise the status itself.
|
|
10569
11459
|
*/
|
|
10570
11460
|
this.statusKey = computed(() => {
|
|
11461
|
+
if (this.leftForApprovers()) {
|
|
11462
|
+
return 'nh-assistant.approval.consultation.moved-status';
|
|
11463
|
+
}
|
|
10571
11464
|
const kind = this.approval().staleKind;
|
|
10572
11465
|
if (this.effectiveStatus() === 'stale' && kind) {
|
|
10573
11466
|
return `nh-assistant.approval.stale-kind.${kind}`;
|
|
@@ -10583,6 +11476,9 @@ class NhAssistantApprovalCardComponent {
|
|
|
10583
11476
|
this.actionable = computed(() => this.effectiveStatus() === 'pending', ...(ngDevMode ? [{ debugName: "actionable" }] : []));
|
|
10584
11477
|
this.succeeded = computed(() => ['approved', 'executing', 'executed', 'resolved'].includes(this.effectiveStatus()), ...(ngDevMode ? [{ debugName: "succeeded" }] : []));
|
|
10585
11478
|
this.disabled = computed(() => !this.actionable() || !this.canDecide() || this.deciding() || this.submitted(), ...(ngDevMode ? [{ debugName: "disabled" }] : []));
|
|
11479
|
+
/** The route at the subject the pending proposal may be left for; `null` when it can only be approved or rejected. */
|
|
11480
|
+
this.leaveRoute = computed(() => this.actionable() ? this.consultation()?.leaveRoute ?? null : null, ...(ngDevMode ? [{ debugName: "leaveRoute" }] : []));
|
|
11481
|
+
this.leaveHintId = `nh-assistant-approval-leave-${nextId$5++}`;
|
|
10586
11482
|
this.countdown = computed(() => formatNhAssistantCountdown(this.remainingMs()), ...(ngDevMode ? [{ debugName: "countdown" }] : []));
|
|
10587
11483
|
this.presentedSummary = computed(() => this.approval().presentation?.summary ?? null, ...(ngDevMode ? [{ debugName: "presentedSummary" }] : []));
|
|
10588
11484
|
this.feedbackAvailable = computed(() => this.canGiveFeedback() && this.effectiveStatus() === 'rejected', ...(ngDevMode ? [{ debugName: "feedbackAvailable" }] : []));
|
|
@@ -10595,6 +11491,8 @@ class NhAssistantApprovalCardComponent {
|
|
|
10595
11491
|
return 'nh-assistant.approval.waiting-for-approver';
|
|
10596
11492
|
case 'other-approver':
|
|
10597
11493
|
return 'nh-assistant.approval.waiting-for-other-approver';
|
|
11494
|
+
case 'asker':
|
|
11495
|
+
return 'nh-assistant.approval.waiting-for-asker';
|
|
10598
11496
|
default:
|
|
10599
11497
|
return this.decisionOwner() ? 'nh-assistant.approval.waiting-for' : 'nh-assistant.approval.waiting-for-participant';
|
|
10600
11498
|
}
|
|
@@ -10620,11 +11518,13 @@ class NhAssistantApprovalCardComponent {
|
|
|
10620
11518
|
}
|
|
10621
11519
|
});
|
|
10622
11520
|
}
|
|
11521
|
+
/** Emits a decision; `leave` only for a consulted agent's proposal with a route at the subject. */
|
|
10623
11522
|
choose(decision) {
|
|
10624
|
-
if (this.disabled()) {
|
|
11523
|
+
if (this.disabled() || (decision === 'leave' && this.leaveRoute() === null)) {
|
|
10625
11524
|
return;
|
|
10626
11525
|
}
|
|
10627
11526
|
this.submitted.set(true);
|
|
11527
|
+
this.chosen.set(decision);
|
|
10628
11528
|
this.decide.emit(decision);
|
|
10629
11529
|
}
|
|
10630
11530
|
toggleFeedback() {
|
|
@@ -10639,13 +11539,13 @@ class NhAssistantApprovalCardComponent {
|
|
|
10639
11539
|
this.feedbackToggle()?.nativeElement.focus();
|
|
10640
11540
|
}
|
|
10641
11541
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantApprovalCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
10642
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.34", 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 }); }
|
|
11542
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.34", 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": "shownStatus()" } }, 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]=\"leftForApprovers() ? 'users' : 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 <!-- Another agent the assistant consulted proposes this; the person who asked decides it here. -->\n @if (consultation(); as origin) {\n <p class=\"consultation-origin\" data-consultation-origin>\n <nh-assistant-icon name=\"users\" />\n <span>{{ 'nh-assistant.approval.consultation.origin' | translate: { agent: (consultedAgentKeys() | nhAssistantTranslate: origin.agentId), subject: consultedSubject() } }}</span>\n </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 @if (consultation(); as origin) {\n @if (origin.answer) {\n <details class=\"consultation-answer\" data-consultation-answer>\n <summary>{{ 'nh-assistant.approval.consultation.answer' | translate: { agent: (consultedAgentKeys() | nhAssistantTranslate: origin.agentId) } }}</summary>\n <p>{{ origin.answer }}</p>\n </details>\n }\n @if (leftForApprovers()) {\n <p class=\"consultation-moved\" role=\"status\" data-consultation-moved>\n <nh-assistant-icon name=\"users\" />\n <span>{{ 'nh-assistant.approval.consultation.moved' | translate: { subject: consultedSubject() } }}</span>\n </p>\n }\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 @if (consultation()) {\n <p class=\"consultation-rights\" data-consultation-rights>{{ 'nh-assistant.approval.consultation.own-rights' | translate }}</p>\n }\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 <!-- The person may leave a consulted agent's proposal for the subject's approvers instead. -->\n @if (leaveRoute(); as route) {\n <div class=\"leave-for-approvers\">\n <p class=\"leave-explanation\" [id]=\"leaveHintId\">{{ 'nh-assistant.approval.consultation.leave-explain.' + route | translate: { subject: consultedSubject() } }}</p>\n <button\n class=\"leave\"\n type=\"button\"\n data-leave\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'nh-assistant.approval.consultation.leave-label' | translate: { summary: presentedSummary() ?? ('nh-assistant.approval.fallback-summary' | translate) }\"\n [attr.aria-describedby]=\"leaveHintId\"\n (click)=\"choose('leave')\">\n {{ 'nh-assistant.approval.consultation.leave' | translate }}\n </button>\n </div>\n }\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}.consultation-origin,.consultation-moved{display:flex;align-items:flex-start;gap:6px;margin:8px 0 0;color:var(--nha-text-muted);font-size:13px;overflow-wrap:anywhere}.consultation-moved{border-radius:8px;padding:8px 10px;color:var(--nha-text);background:var(--nha-surface-strong)}.consultation-answer{margin-top:8px;font-size:13px}.consultation-answer summary{cursor:pointer;color:var(--nha-text-muted);font-size:12px;font-weight:700}.consultation-answer summary:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.consultation-answer p{max-height:200px;margin:6px 0 0;overflow:auto;border-radius:8px;padding:8px;background:var(--nha-surface);white-space:pre-wrap;overflow-wrap:anywhere}.consultation-rights{margin:10px 0 0;color:var(--nha-text-muted);font-size:12px}.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}.leave-for-approvers{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:8px;margin-top:10px}.leave-explanation{flex:1 1 200px;margin:0;color:var(--nha-text-muted);font-size:12px}.reject,.leave{color:var(--nha-text);background:transparent}.reject:hover:not(:disabled),.leave: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" }, { kind: "pipe", type: NhAssistantTranslatePipe, name: "nhAssistantTranslate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
10643
11543
|
}
|
|
10644
11544
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantApprovalCardComponent, decorators: [{
|
|
10645
11545
|
type: Component,
|
|
10646
|
-
args: [{ selector: 'nh-assistant-approval-card', standalone: true, imports: [TranslatePipe, NhAssistantIconComponent, NhAssistantProposalFeedbackComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
10647
|
-
'[attr.data-status]': '
|
|
10648
|
-
}, 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"] }]
|
|
11546
|
+
args: [{ selector: 'nh-assistant-approval-card', standalone: true, imports: [TranslatePipe, NhAssistantTranslatePipe, NhAssistantIconComponent, NhAssistantProposalFeedbackComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
|
|
11547
|
+
'[attr.data-status]': 'shownStatus()'
|
|
11548
|
+
}, template: "<section class=\"card\" [attr.aria-label]=\"'nh-assistant.approval.title' | translate\">\n <header class=\"header\">\n <nh-assistant-icon [name]=\"leftForApprovers() ? 'users' : 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 <!-- Another agent the assistant consulted proposes this; the person who asked decides it here. -->\n @if (consultation(); as origin) {\n <p class=\"consultation-origin\" data-consultation-origin>\n <nh-assistant-icon name=\"users\" />\n <span>{{ 'nh-assistant.approval.consultation.origin' | translate: { agent: (consultedAgentKeys() | nhAssistantTranslate: origin.agentId), subject: consultedSubject() } }}</span>\n </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 @if (consultation(); as origin) {\n @if (origin.answer) {\n <details class=\"consultation-answer\" data-consultation-answer>\n <summary>{{ 'nh-assistant.approval.consultation.answer' | translate: { agent: (consultedAgentKeys() | nhAssistantTranslate: origin.agentId) } }}</summary>\n <p>{{ origin.answer }}</p>\n </details>\n }\n @if (leftForApprovers()) {\n <p class=\"consultation-moved\" role=\"status\" data-consultation-moved>\n <nh-assistant-icon name=\"users\" />\n <span>{{ 'nh-assistant.approval.consultation.moved' | translate: { subject: consultedSubject() } }}</span>\n </p>\n }\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 @if (consultation()) {\n <p class=\"consultation-rights\" data-consultation-rights>{{ 'nh-assistant.approval.consultation.own-rights' | translate }}</p>\n }\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 <!-- The person may leave a consulted agent's proposal for the subject's approvers instead. -->\n @if (leaveRoute(); as route) {\n <div class=\"leave-for-approvers\">\n <p class=\"leave-explanation\" [id]=\"leaveHintId\">{{ 'nh-assistant.approval.consultation.leave-explain.' + route | translate: { subject: consultedSubject() } }}</p>\n <button\n class=\"leave\"\n type=\"button\"\n data-leave\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'nh-assistant.approval.consultation.leave-label' | translate: { summary: presentedSummary() ?? ('nh-assistant.approval.fallback-summary' | translate) }\"\n [attr.aria-describedby]=\"leaveHintId\"\n (click)=\"choose('leave')\">\n {{ 'nh-assistant.approval.consultation.leave' | translate }}\n </button>\n </div>\n }\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}.consultation-origin,.consultation-moved{display:flex;align-items:flex-start;gap:6px;margin:8px 0 0;color:var(--nha-text-muted);font-size:13px;overflow-wrap:anywhere}.consultation-moved{border-radius:8px;padding:8px 10px;color:var(--nha-text);background:var(--nha-surface-strong)}.consultation-answer{margin-top:8px;font-size:13px}.consultation-answer summary{cursor:pointer;color:var(--nha-text-muted);font-size:12px;font-weight:700}.consultation-answer summary:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.consultation-answer p{max-height:200px;margin:6px 0 0;overflow:auto;border-radius:8px;padding:8px;background:var(--nha-surface);white-space:pre-wrap;overflow-wrap:anywhere}.consultation-rights{margin:10px 0 0;color:var(--nha-text-muted);font-size:12px}.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}.leave-for-approvers{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:8px;margin-top:10px}.leave-explanation{flex:1 1 200px;margin:0;color:var(--nha-text-muted);font-size:12px}.reject,.leave{color:var(--nha-text);background:transparent}.reject:hover:not(:disabled),.leave: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"] }]
|
|
10649
11549
|
}], 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 }] }] } });
|
|
10650
11550
|
|
|
10651
11551
|
let nextId$4 = 0;
|
|
@@ -10827,9 +11727,12 @@ const stickToBottomDistance = 120;
|
|
|
10827
11727
|
* The messages of a conversation as a live log. Assistant text renders as sanitized
|
|
10828
11728
|
* Markdown, consecutive tool calls as one collapsible group that counts along, and
|
|
10829
11729
|
* approvals as cards. The files a person attached show by name, type and size. Messages a
|
|
10830
|
-
* trigger wrote into an agent thread read as events, not as a person
|
|
10831
|
-
*
|
|
10832
|
-
*
|
|
11730
|
+
* trigger wrote into an agent thread read as events, not as a person, and a message the application
|
|
11731
|
+
* posted reads as the application's, with its sender, title, Markdown text and link, and a label when
|
|
11732
|
+
* the assistant follows it as an instruction. While a turn runs without visible progress, a working
|
|
11733
|
+
* indicator shows the elapsed time: under the answer `writingMessageId` names, also when the
|
|
11734
|
+
* application's or another person's message arrived after it, and at the end without it. The thread
|
|
11735
|
+
* follows new content while the user is at the bottom.
|
|
10833
11736
|
*/
|
|
10834
11737
|
class NhAssistantThreadComponent {
|
|
10835
11738
|
/** The type and size of an attached file, in the current language. */
|
|
@@ -10877,6 +11780,13 @@ class NhAssistantThreadComponent {
|
|
|
10877
11780
|
* card of a rejected proposal ask what was wrong.
|
|
10878
11781
|
*/
|
|
10879
11782
|
this.approvalRights = input(null, ...(ngDevMode ? [{ debugName: "approvalRights" }] : []));
|
|
11783
|
+
/**
|
|
11784
|
+
* The answer the running turn writes, for example `NhAssistantStore.writingMessageId`. The working
|
|
11785
|
+
* indicator stays under that answer, also when the application's or a colleague's message arrives
|
|
11786
|
+
* after it during the turn. Without it, or when it names no message of `messages`, the indicator
|
|
11787
|
+
* shows at the end of the thread.
|
|
11788
|
+
*/
|
|
11789
|
+
this.writingMessageId = input(null, ...(ngDevMode ? [{ debugName: "writingMessageId" }] : []));
|
|
10880
11790
|
/** The decision about an approval card, without the approval it belongs to. */
|
|
10881
11791
|
this.approvalDecision = output();
|
|
10882
11792
|
/** The decision about an approval card together with its approval id. */
|
|
@@ -10892,14 +11802,16 @@ class NhAssistantThreadComponent {
|
|
|
10892
11802
|
/**
|
|
10893
11803
|
* True while the turn runs and nothing else shows progress: before the first part and
|
|
10894
11804
|
* between tool calls while the model decides what to do next. Streaming text and a
|
|
10895
|
-
* running tool group show their own progress; an approval waits for the user.
|
|
11805
|
+
* running tool group show their own progress; an approval waits for the user. With
|
|
11806
|
+
* `writingMessageId`, the answer it names decides, as if the messages after it were not there.
|
|
10896
11807
|
*/
|
|
10897
11808
|
this.working = computed(() => {
|
|
10898
11809
|
if (!this.streaming()) {
|
|
10899
11810
|
return false;
|
|
10900
11811
|
}
|
|
10901
|
-
const
|
|
10902
|
-
|
|
11812
|
+
const messages = this.messages();
|
|
11813
|
+
const last = this.answerBeingWritten() ?? messages[messages.length - 1];
|
|
11814
|
+
if (!last || last.role === 'user' || last.role === 'application' || last.parts.length === 0) {
|
|
10903
11815
|
return true;
|
|
10904
11816
|
}
|
|
10905
11817
|
const lastPart = last.parts[last.parts.length - 1];
|
|
@@ -10907,6 +11819,19 @@ class NhAssistantThreadComponent {
|
|
|
10907
11819
|
&& lastPart.status !== 'running'
|
|
10908
11820
|
&& lastPart.status !== 'awaiting-approval';
|
|
10909
11821
|
}, ...(ngDevMode ? [{ debugName: "working" }] : []));
|
|
11822
|
+
/**
|
|
11823
|
+
* The message in whose row the working indicator shows: the answer `writingMessageId` names.
|
|
11824
|
+
* `null` puts the indicator at the end of the thread.
|
|
11825
|
+
*/
|
|
11826
|
+
this.workingAfterId = computed(() => this.answerBeingWritten()?.id ?? null, ...(ngDevMode ? [{ debugName: "workingAfterId" }] : []));
|
|
11827
|
+
/** While a turn runs: the answer `writingMessageId` names, when `messages` holds it. */
|
|
11828
|
+
this.answerBeingWritten = computed(() => {
|
|
11829
|
+
const writingMessageId = this.writingMessageId();
|
|
11830
|
+
if (!this.streaming() || !writingMessageId) {
|
|
11831
|
+
return null;
|
|
11832
|
+
}
|
|
11833
|
+
return this.messages().find(message => message.id === writingMessageId) ?? null;
|
|
11834
|
+
}, ...(ngDevMode ? [{ debugName: "answerBeingWritten" }] : []));
|
|
10910
11835
|
/** Whole seconds since the running turn started. */
|
|
10911
11836
|
this.elapsedSeconds = computed(() => {
|
|
10912
11837
|
const startedAt = this.turnStartedAt();
|
|
@@ -10948,6 +11873,10 @@ class NhAssistantThreadComponent {
|
|
|
10948
11873
|
isTrigger(message) {
|
|
10949
11874
|
return message.role === 'user' && message.authorKind === 'trigger';
|
|
10950
11875
|
}
|
|
11876
|
+
/** True for a message the application posted into the conversation. */
|
|
11877
|
+
isApplication(message) {
|
|
11878
|
+
return message.role === 'application';
|
|
11879
|
+
}
|
|
10951
11880
|
/** True for an assistant message the agent's rules wrote without a model. */
|
|
10952
11881
|
isRule(message) {
|
|
10953
11882
|
return message.role === 'assistant' && message.authorKind === 'rule';
|
|
@@ -10992,7 +11921,7 @@ class NhAssistantThreadComponent {
|
|
|
10992
11921
|
}
|
|
10993
11922
|
}
|
|
10994
11923
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantThreadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
10995
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.34", 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", retryMessage: "retryMessage" }, 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 ('files') {\n <ul class=\"files\" [attr.aria-label]=\"'nh-assistant.thread.files' | translate\">\n @for (file of group.files; track $index) {\n <li class=\"file\">\n <nh-assistant-icon class=\"file-icon\" name=\"file\" />\n <span class=\"file-name\" [attr.title]=\"file.name\">{{ file.name }}</span>\n <span class=\"file-meta\">{{ 'nh-assistant.thread.file-meta' | translate: fileMeta(file) }}</span>\n </li>\n }\n </ul>\n }\n @case ('connection') {\n <nh-assistant-connection-required\n class=\"part\"\n [part]=\"group.part\"\n [canRetry]=\"!streaming()\"\n (retry)=\"retryAfterConnect(message)\" />\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%}.files{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:6px;max-width:88%;min-width:0;margin:0;padding:0;list-style:none}.message:not([data-role=user]) .files,.message[data-others=true] .files{justify-content:flex-start}.file{display:flex;align-items:center;gap:6px;min-width:0;max-width:100%;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:4px 10px;color:var(--nha-text);background:var(--nha-surface);font-size:12px}.file-icon{flex-shrink:0;color:var(--nha-text-muted)}.file-name{min-width:0;overflow:hidden;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.file-meta{flex-shrink:0;color:var(--nha-text-muted);white-space:nowrap}: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 hr{margin:12px 0;border:0;border-top:1px solid var(--nha-border)}: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: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }, { 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: "component", type: NhAssistantConnectionRequiredComponent, selector: "nh-assistant-connection-required", inputs: ["part", "canRetry"], outputs: ["retry"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: NhAssistantMarkdownPipe, name: "nhAssistantMarkdown" }, { kind: "pipe", type: NhAssistantPartGroupsPipe, name: "nhAssistantPartGroups" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
11924
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.34", 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 }, writingMessageId: { classPropertyName: "writingMessageId", publicName: "writingMessageId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { approvalDecision: "approvalDecision", approvalChoice: "approvalChoice", retryMessage: "retryMessage" }, 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 @if (working() && message.id === workingAfterId()) {\n <ng-container [ngTemplateOutlet]=\"workingTemplate\" />\n }\n </div>\n @if (working() && workingAfterId() === null) {\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 <!-- The answer a turn still writes (writingMessageId) keeps the indicator under it, also when messages arrived after it. -->\n @if (working() && message.id === workingAfterId()) {\n <ng-container [ngTemplateOutlet]=\"workingTemplate\" />\n }\n </div>\n }\n @if (working() && workingAfterId() === null) {\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' : isApplication(message) ? 'application' : null\"\n [attr.aria-label]=\"isApplication(message) ? ('nh-assistant.thread.application-label' | translate) : 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 (isApplication(message)) {\n <!-- The application's message reads as the application's, also when it names who in the application posted it. -->\n <p class=\"author\">\n <nh-assistant-icon class=\"author-icon\" name=\"bell\" />\n @if (message.application?.senderName; as sender) {\n <!-- The space keeps the sender and the tag apart for screen readers; the flex gap shows it. -->\n <span>{{ sender }}</span>{{ ' ' }}<span class=\"tag\">{{ 'nh-assistant.thread.application' | translate }}</span>\n } @else {\n <span>{{ 'nh-assistant.thread.application' | translate }}</span>\n }\n @if (message.application?.trusted) {\n <!-- People see which of the application's messages the assistant follows as an instruction. -->\n {{ ' ' }}<span class=\"tag trusted\">{{ 'nh-assistant.thread.application-trusted' | translate }}</span>\n }\n </p>\n @if (message.application?.title; as title) {\n <p class=\"application-title\">{{ title }}</p>\n }\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' || isApplication(message)) && 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 ('files') {\n <ul class=\"files\" [attr.aria-label]=\"'nh-assistant.thread.files' | translate\">\n @for (file of group.files; track $index) {\n <li class=\"file\">\n <nh-assistant-icon class=\"file-icon\" name=\"file\" />\n <span class=\"file-name\" [attr.title]=\"file.name\">{{ file.name }}</span>\n <span class=\"file-meta\">{{ 'nh-assistant.thread.file-meta' | translate: fileMeta(file) }}</span>\n </li>\n }\n </ul>\n }\n @case ('connection') {\n <nh-assistant-connection-required\n class=\"part\"\n [part]=\"group.part\"\n [canRetry]=\"!streaming()\"\n (retry)=\"retryAfterConnect(message)\" />\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 @if (isApplication(message) && message.application?.link; as link) {\n <nh-assistant-message-link\n class=\"application-link\"\n [url]=\"link.url\"\n [label]=\"link.label || ('nh-assistant.thread.application-link' | translate)\" />\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%}.files{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:6px;max-width:88%;min-width:0;margin:0;padding:0;list-style:none}.message:not([data-role=user]) .files,.message[data-others=true] .files{justify-content:flex-start}.file{display:flex;align-items:center;gap:6px;min-width:0;max-width:100%;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:4px 10px;color:var(--nha-text);background:var(--nha-surface);font-size:12px}.file-icon{flex-shrink:0;color:var(--nha-text-muted)}.file-name{min-width:0;overflow:hidden;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.file-meta{flex-shrink:0;color:var(--nha-text-muted);white-space:nowrap}: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 hr{margin:12px 0;border:0;border-top:1px solid var(--nha-border)}: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}}.message[data-author=application]{align-items:stretch;border-left:3px solid var(--nha-accent);border-radius:var(--nha-radius-control);padding:8px 12px;background:var(--nha-surface-strong)}.message[data-author=application] .author{display:flex;align-items:center;gap:6px}.message[data-author=application] .author-icon{color:var(--nha-accent)}.message[data-author=application] .tag{border:1px solid var(--nha-border);border-radius:999px;padding:0 6px;color:var(--nha-text-muted);font-weight:500}.message[data-author=application] .application-title{margin:0;color:var(--nha-text);font-size:14px;font-weight:600}.message[data-author=application] .bubble{max-width:none;padding:0;color:var(--nha-text);background:transparent}\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: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }, { kind: "component", type: NhAssistantMessageLinkComponent, selector: "nh-assistant-message-link", inputs: ["url", "label"] }, { 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: "component", type: NhAssistantConnectionRequiredComponent, selector: "nh-assistant-connection-required", inputs: ["part", "canRetry"], outputs: ["retry"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: NhAssistantMarkdownPipe, name: "nhAssistantMarkdown" }, { kind: "pipe", type: NhAssistantPartGroupsPipe, name: "nhAssistantPartGroups" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
10996
11925
|
}
|
|
10997
11926
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantThreadComponent, decorators: [{
|
|
10998
11927
|
type: Component,
|
|
@@ -11002,12 +11931,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImpo
|
|
|
11002
11931
|
TranslatePipe,
|
|
11003
11932
|
NhAssistantIconComponent,
|
|
11004
11933
|
NhAssistantMarkdownPipe,
|
|
11934
|
+
NhAssistantMessageLinkComponent,
|
|
11005
11935
|
NhAssistantPartGroupsPipe,
|
|
11006
11936
|
NhAssistantToolCallGroupComponent,
|
|
11007
11937
|
NhAssistantApprovalCardComponent,
|
|
11008
11938
|
NhAssistantConnectionRequiredComponent
|
|
11009
|
-
], 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 ('files') {\n <ul class=\"files\" [attr.aria-label]=\"'nh-assistant.thread.files' | translate\">\n @for (file of group.files; track $index) {\n <li class=\"file\">\n <nh-assistant-icon class=\"file-icon\" name=\"file\" />\n <span class=\"file-name\" [attr.title]=\"file.name\">{{ file.name }}</span>\n <span class=\"file-meta\">{{ 'nh-assistant.thread.file-meta' | translate: fileMeta(file) }}</span>\n </li>\n }\n </ul>\n }\n @case ('connection') {\n <nh-assistant-connection-required\n class=\"part\"\n [part]=\"group.part\"\n [canRetry]=\"!streaming()\"\n (retry)=\"retryAfterConnect(message)\" />\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%}.files{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:6px;max-width:88%;min-width:0;margin:0;padding:0;list-style:none}.message:not([data-role=user]) .files,.message[data-others=true] .files{justify-content:flex-start}.file{display:flex;align-items:center;gap:6px;min-width:0;max-width:100%;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:4px 10px;color:var(--nha-text);background:var(--nha-surface);font-size:12px}.file-icon{flex-shrink:0;color:var(--nha-text-muted)}.file-name{min-width:0;overflow:hidden;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.file-meta{flex-shrink:0;color:var(--nha-text-muted);white-space:nowrap}: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 hr{margin:12px 0;border:0;border-top:1px solid var(--nha-border)}: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"] }]
|
|
11010
|
-
}], 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"] }], retryMessage: [{ type: i0.Output, args: ["retryMessage"] }], scroller: [{ type: i0.ViewChild, args: ['scroller', { isSignal: true }] }], viewport: [{ type: i0.ViewChild, args: [i0.forwardRef(() => CdkVirtualScrollViewport), { isSignal: true }] }] } });
|
|
11939
|
+
], 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 @if (working() && message.id === workingAfterId()) {\n <ng-container [ngTemplateOutlet]=\"workingTemplate\" />\n }\n </div>\n @if (working() && workingAfterId() === null) {\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 <!-- The answer a turn still writes (writingMessageId) keeps the indicator under it, also when messages arrived after it. -->\n @if (working() && message.id === workingAfterId()) {\n <ng-container [ngTemplateOutlet]=\"workingTemplate\" />\n }\n </div>\n }\n @if (working() && workingAfterId() === null) {\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' : isApplication(message) ? 'application' : null\"\n [attr.aria-label]=\"isApplication(message) ? ('nh-assistant.thread.application-label' | translate) : 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 (isApplication(message)) {\n <!-- The application's message reads as the application's, also when it names who in the application posted it. -->\n <p class=\"author\">\n <nh-assistant-icon class=\"author-icon\" name=\"bell\" />\n @if (message.application?.senderName; as sender) {\n <!-- The space keeps the sender and the tag apart for screen readers; the flex gap shows it. -->\n <span>{{ sender }}</span>{{ ' ' }}<span class=\"tag\">{{ 'nh-assistant.thread.application' | translate }}</span>\n } @else {\n <span>{{ 'nh-assistant.thread.application' | translate }}</span>\n }\n @if (message.application?.trusted) {\n <!-- People see which of the application's messages the assistant follows as an instruction. -->\n {{ ' ' }}<span class=\"tag trusted\">{{ 'nh-assistant.thread.application-trusted' | translate }}</span>\n }\n </p>\n @if (message.application?.title; as title) {\n <p class=\"application-title\">{{ title }}</p>\n }\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' || isApplication(message)) && 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 ('files') {\n <ul class=\"files\" [attr.aria-label]=\"'nh-assistant.thread.files' | translate\">\n @for (file of group.files; track $index) {\n <li class=\"file\">\n <nh-assistant-icon class=\"file-icon\" name=\"file\" />\n <span class=\"file-name\" [attr.title]=\"file.name\">{{ file.name }}</span>\n <span class=\"file-meta\">{{ 'nh-assistant.thread.file-meta' | translate: fileMeta(file) }}</span>\n </li>\n }\n </ul>\n }\n @case ('connection') {\n <nh-assistant-connection-required\n class=\"part\"\n [part]=\"group.part\"\n [canRetry]=\"!streaming()\"\n (retry)=\"retryAfterConnect(message)\" />\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 @if (isApplication(message) && message.application?.link; as link) {\n <nh-assistant-message-link\n class=\"application-link\"\n [url]=\"link.url\"\n [label]=\"link.label || ('nh-assistant.thread.application-link' | translate)\" />\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%}.files{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:6px;max-width:88%;min-width:0;margin:0;padding:0;list-style:none}.message:not([data-role=user]) .files,.message[data-others=true] .files{justify-content:flex-start}.file{display:flex;align-items:center;gap:6px;min-width:0;max-width:100%;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:4px 10px;color:var(--nha-text);background:var(--nha-surface);font-size:12px}.file-icon{flex-shrink:0;color:var(--nha-text-muted)}.file-name{min-width:0;overflow:hidden;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.file-meta{flex-shrink:0;color:var(--nha-text-muted);white-space:nowrap}: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 hr{margin:12px 0;border:0;border-top:1px solid var(--nha-border)}: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}}.message[data-author=application]{align-items:stretch;border-left:3px solid var(--nha-accent);border-radius:var(--nha-radius-control);padding:8px 12px;background:var(--nha-surface-strong)}.message[data-author=application] .author{display:flex;align-items:center;gap:6px}.message[data-author=application] .author-icon{color:var(--nha-accent)}.message[data-author=application] .tag{border:1px solid var(--nha-border);border-radius:999px;padding:0 6px;color:var(--nha-text-muted);font-weight:500}.message[data-author=application] .application-title{margin:0;color:var(--nha-text);font-size:14px;font-weight:600}.message[data-author=application] .bubble{max-width:none;padding:0;color:var(--nha-text);background:transparent}\n"] }]
|
|
11940
|
+
}], 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 }] }], writingMessageId: [{ type: i0.Input, args: [{ isSignal: true, alias: "writingMessageId", required: false }] }], approvalDecision: [{ type: i0.Output, args: ["approvalDecision"] }], approvalChoice: [{ type: i0.Output, args: ["approvalChoice"] }], retryMessage: [{ type: i0.Output, args: ["retryMessage"] }], scroller: [{ type: i0.ViewChild, args: ['scroller', { isSignal: true }] }], viewport: [{ type: i0.ViewChild, args: [i0.forwardRef(() => CdkVirtualScrollViewport), { isSignal: true }] }] } });
|
|
11011
11941
|
|
|
11012
11942
|
let nextId$1 = 0;
|
|
11013
11943
|
/**
|
|
@@ -11232,7 +12162,7 @@ class NhAssistantPanelComponent {
|
|
|
11232
12162
|
void this.store.cancel();
|
|
11233
12163
|
}
|
|
11234
12164
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
11235
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.34", 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 (retryMessage)=\"store.send($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 } @else if (lastTurnFailed()) {\n <p class=\"notice\">{{ 'nh-assistant.panel.last-turn-failed' | 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.files?.length) {\n <span class=\"queue-state queue-files\" data-queue-files>{{ 'nh-assistant.queue.files' | translate: { count: item.files!.length, names: fileNames(item.files!) } }}</span>\n }\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 [uploads]=\"store.uploads()\"\n [attachments]=\"store.attachments()\"\n (restored)=\"store.consumeRestoredDraft()\"\n (send)=\"send($event)\"\n (attach)=\"attach($event)\"\n (removeAttachment)=\"store.removeAttachment($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-files{display:block;overflow-wrap:anywhere}.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", "uploads", "attachments"], outputs: ["send", "cancel", "restored", "attach", "removeAttachment"] }, { 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", "retryMessage"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: NhAssistantTranslatePipe, name: "nhAssistantTranslate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
12165
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.34", 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 [writingMessageId]=\"store.writingMessageId()\"\n (approvalChoice)=\"decideApproval($event)\"\n (retryMessage)=\"store.send($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 } @else if (lastTurnFailed()) {\n <p class=\"notice\">{{ 'nh-assistant.panel.last-turn-failed' | 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.files?.length) {\n <span class=\"queue-state queue-files\" data-queue-files>{{ 'nh-assistant.queue.files' | translate: { count: item.files!.length, names: fileNames(item.files!) } }}</span>\n }\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 [uploads]=\"store.uploads()\"\n [attachments]=\"store.attachments()\"\n (restored)=\"store.consumeRestoredDraft()\"\n (send)=\"send($event)\"\n (attach)=\"attach($event)\"\n (removeAttachment)=\"store.removeAttachment($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-files{display:block;overflow-wrap:anywhere}.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", "uploads", "attachments"], outputs: ["send", "cancel", "restored", "attach", "removeAttachment"] }, { 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", "writingMessageId"], outputs: ["approvalDecision", "approvalChoice", "retryMessage"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: NhAssistantTranslatePipe, name: "nhAssistantTranslate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
11236
12166
|
}
|
|
11237
12167
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantPanelComponent, decorators: [{
|
|
11238
12168
|
type: Component,
|
|
@@ -11248,7 +12178,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImpo
|
|
|
11248
12178
|
NhAssistantPreferencesComponent,
|
|
11249
12179
|
NhAssistantShareComponent,
|
|
11250
12180
|
NhAssistantThreadComponent
|
|
11251
|
-
], 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 (retryMessage)=\"store.send($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 } @else if (lastTurnFailed()) {\n <p class=\"notice\">{{ 'nh-assistant.panel.last-turn-failed' | 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.files?.length) {\n <span class=\"queue-state queue-files\" data-queue-files>{{ 'nh-assistant.queue.files' | translate: { count: item.files!.length, names: fileNames(item.files!) } }}</span>\n }\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 [uploads]=\"store.uploads()\"\n [attachments]=\"store.attachments()\"\n (restored)=\"store.consumeRestoredDraft()\"\n (send)=\"send($event)\"\n (attach)=\"attach($event)\"\n (removeAttachment)=\"store.removeAttachment($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-files{display:block;overflow-wrap:anywhere}.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"] }]
|
|
12181
|
+
], 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 [writingMessageId]=\"store.writingMessageId()\"\n (approvalChoice)=\"decideApproval($event)\"\n (retryMessage)=\"store.send($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 } @else if (lastTurnFailed()) {\n <p class=\"notice\">{{ 'nh-assistant.panel.last-turn-failed' | 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.files?.length) {\n <span class=\"queue-state queue-files\" data-queue-files>{{ 'nh-assistant.queue.files' | translate: { count: item.files!.length, names: fileNames(item.files!) } }}</span>\n }\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 [uploads]=\"store.uploads()\"\n [attachments]=\"store.attachments()\"\n (restored)=\"store.consumeRestoredDraft()\"\n (send)=\"send($event)\"\n (attach)=\"attach($event)\"\n (removeAttachment)=\"store.removeAttachment($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-files{display:block;overflow-wrap:anywhere}.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"] }]
|
|
11252
12182
|
}], ctorParameters: () => [], propDecorators: { drawer: [{ type: i0.ViewChild, args: ['drawer', { isSignal: true }] }] } });
|
|
11253
12183
|
|
|
11254
12184
|
/**
|
|
@@ -11573,102 +12503,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImpo
|
|
|
11573
12503
|
args: [{ selector: 'nh-assistant-picker', standalone: true, imports: [TranslatePipe, NhAssistantTranslatePipe, NhAssistantIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"picker\" [class.open]=\"open()\">\n <div class=\"control\">\n <input\n #field\n type=\"text\"\n role=\"combobox\"\n autocomplete=\"off\"\n aria-autocomplete=\"list\"\n [id]=\"inputId()\"\n [attr.aria-expanded]=\"open()\"\n [attr.aria-controls]=\"listId\"\n [attr.aria-activedescendant]=\"activeId()\"\n [attr.aria-invalid]=\"invalid() ? true : null\"\n [attr.aria-describedby]=\"fieldDescribedBy()\"\n [attr.placeholder]=\"placeholder()\"\n [disabled]=\"disabled()\"\n [value]=\"inputText()\"\n (input)=\"onInput($event)\"\n (click)=\"openList()\"\n (keydown)=\"onKeydown($event)\"\n (blur)=\"onBlur()\" />\n <button\n type=\"button\"\n class=\"toggle\"\n tabindex=\"-1\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'nh-assistant.picker.toggle' | translate\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"toggle()\">\n <nh-assistant-icon name=\"chevron-down\" />\n </button>\n </div>\n @if (searchError(); as failure) {\n <!-- A failed search says why and offers to search again; the list never claims nothing was found. -->\n <p class=\"field-error search-error\" role=\"alert\" data-search-error [id]=\"searchErrorId\">\n <nh-assistant-icon name=\"warning\" />\n <span>{{ searchErrorKeys() | nhAssistantTranslate: failure.code }}</span>\n <button\n type=\"button\"\n class=\"link\"\n data-search-retry\n [disabled]=\"disabled()\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"retrySearch()\">{{ 'nh-assistant.picker.search-again' | translate }}</button>\n </p>\n }\n @if (open()) {\n <ul class=\"listbox\" role=\"listbox\" [id]=\"listId\">\n @for (option of visible(); track option.value; let index = $index) {\n <li\n role=\"option\"\n [id]=\"optionId(index)\"\n [class.active]=\"index === active()\"\n [attr.aria-selected]=\"option.value === (value() ?? '')\"\n (mousedown)=\"$event.preventDefault()\"\n (mousemove)=\"active.set(index)\"\n (click)=\"choose(option)\">\n <span class=\"label\">{{ option.label }}</span>\n @if (option.description) {\n <span class=\"description\">{{ option.description }}</span>\n }\n </li>\n } @empty {\n <li class=\"state\" role=\"presentation\">{{ emptyKey() | translate }}</li>\n }\n </ul>\n } @else if (selected()?.description; as description) {\n <p class=\"chosen\">{{ description }}</p>\n }\n</div>\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)}: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 input[type=email],:host input[type=number],:host input[type=date],:host input[type=time],:host input[type=datetime-local],:host select,:host textarea{box-sizing:border-box;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 input[type=email]:focus-visible,:host input[type=number]:focus-visible,:host input[type=date]:focus-visible,:host input[type=time]:focus-visible,:host input[type=datetime-local]: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 input[type=email]:disabled,:host input[type=number]:disabled,:host input[type=date]:disabled,:host input[type=time]:disabled,:host input[type=datetime-local]: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 input[type=email][aria-invalid=true],:host input[type=number][aria-invalid=true],:host input[type=date][aria-invalid=true],:host input[type=time][aria-invalid=true],:host input[type=datetime-local][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 input[type=number]{font-variant-numeric:tabular-nums}: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 .field-error{display:flex;align-items:flex-start;gap:6px;margin:4px 2px 0;color:var(--nha-danger);font-size:12px;font-weight:600;overflow-wrap:anywhere}:host .field-error nh-assistant-icon{flex:none;margin-top:1px}:host .form-error{box-sizing:border-box;display:flex;align-items:flex-start;gap:8px;margin:0;border:1px solid var(--nha-danger);border-radius:var(--nha-radius-control);padding:8px 12px;color:var(--nha-text);background:var(--nha-danger-soft);font-size:13px;overflow-wrap:anywhere}:host .form-error ul{margin:4px 0 0;padding-left:18px}:host .message{box-sizing:border-box;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;overflow-wrap:anywhere}: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.info{border-color:var(--nha-border);color:var(--nha-text);background:var(--nha-surface)}:host .message nh-assistant-icon{flex:none}:host button.link{min-height:0;border:0;padding:0;color:var(--nha-accent, currentColor);background:none;font:inherit;text-decoration:underline;cursor:pointer}:host button.link:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}:host button.link:disabled{cursor:not-allowed;opacity:.55}:host .primary,:host .secondary,:host .danger{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;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}.picker{position:relative;min-width:0}.control{position:relative}.control input{padding-right:36px;text-overflow:ellipsis}.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);background:transparent;cursor:pointer;position:absolute;top:50%;right:4px;width:30px;height:30px;transform:translateY(-50%);color:var(--nha-text-muted)}.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}.open .toggle{transform:translateY(-50%) rotate(180deg)}.listbox{position:absolute;z-index:30;top:calc(100% + 4px);right:0;left:0;max-height:280px;margin:0;overflow-y:auto;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:4px;background:var(--nha-background);box-shadow:0 12px 28px #0000002e;list-style:none}[role=option]{display:grid;gap:2px;border-radius:8px;padding:7px 10px;cursor:pointer;overflow-wrap:anywhere}[role=option].active{background:var(--nha-surface-strong)}[role=option][aria-selected=true] .label{font-weight:700}.label{font-size:13px}.description,.chosen{color:var(--nha-text-muted);font-size:12px;line-height:1.4}.chosen{margin:4px 2px 0;overflow-wrap:anywhere}.state{padding:8px 10px;color:var(--nha-text-muted);font-size:12px}.search-error{flex-wrap:wrap;align-items:baseline}.search-error span{flex:1 1 auto;min-width:0}\n"] }]
|
|
11574
12504
|
}], ctorParameters: () => [], propDecorators: { field: [{ type: i0.ViewChild, args: ['field', { isSignal: true }] }], inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: true }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], selectedLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedLabel", required: false }] }], noneLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "noneLabel", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], search: [{ type: i0.Input, args: [{ isSignal: true, alias: "search", required: false }] }], minQueryLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "minQueryLength", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], describedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "describedBy", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], optionChange: [{ type: i0.Output, args: ["optionChange"] }] } });
|
|
11575
12505
|
|
|
11576
|
-
/**
|
|
11577
|
-
* The name of a subject with the name of its type, linked to where the application shows the
|
|
11578
|
-
* subject (internal). The host's `subjectLinks` builder wins over the server's link; a link within
|
|
11579
|
-
* the application navigates through the router, an `https` link leaves it, and anything else shows
|
|
11580
|
-
* no link. Ctrl-, Cmd-, Shift- and middle-clicks keep the browser's behavior, such as a new tab.
|
|
11581
|
-
* A name that already says the type, as "The whole application" does for the application as a
|
|
11582
|
-
* whole, is shown once.
|
|
11583
|
-
*/
|
|
11584
|
-
class NhAssistantSubjectLinkComponent {
|
|
11585
|
-
constructor() {
|
|
11586
|
-
this.config = inject(NH_ASSISTANT_CONFIG, { optional: true });
|
|
11587
|
-
this.router = inject(Router, { optional: true });
|
|
11588
|
-
this.locationStrategy = inject(LocationStrategy, { optional: true });
|
|
11589
|
-
this.injector = inject(EnvironmentInjector);
|
|
11590
|
-
this.subjectType = input.required(...(ngDevMode ? [{ debugName: "subjectType" }] : []));
|
|
11591
|
-
this.subjectId = input.required(...(ngDevMode ? [{ debugName: "subjectId" }] : []));
|
|
11592
|
-
/** The text people read, usually the subject's display name. */
|
|
11593
|
-
this.label = input.required(...(ngDevMode ? [{ debugName: "label" }] : []));
|
|
11594
|
-
/** The name of the subject type, such as "Project"; nothing is shown without one. */
|
|
11595
|
-
this.typeName = input(null, ...(ngDevMode ? [{ debugName: "typeName" }] : []));
|
|
11596
|
-
/** The server's link to the subject. */
|
|
11597
|
-
this.link = input(null, ...(ngDevMode ? [{ debugName: "link" }] : []));
|
|
11598
|
-
/** The type's name, unless the subject's name is the type's name itself, as for the whole application. */
|
|
11599
|
-
this.shownTypeName = computed(() => {
|
|
11600
|
-
const type = this.typeName()?.trim();
|
|
11601
|
-
if (!type) {
|
|
11602
|
-
return null;
|
|
11603
|
-
}
|
|
11604
|
-
return type.toLocaleLowerCase() === this.label().trim().toLocaleLowerCase() ? null : type;
|
|
11605
|
-
}, ...(ngDevMode ? [{ debugName: "shownTypeName" }] : []));
|
|
11606
|
-
this.target = computed(() => {
|
|
11607
|
-
const builder = this.config?.subjectLinks?.[this.subjectType()];
|
|
11608
|
-
const subject = { type: this.subjectType(), id: this.subjectId() };
|
|
11609
|
-
const custom = builder ? runInInjectionContext(this.injector, () => builder(subject)) : undefined;
|
|
11610
|
-
if (custom === null) {
|
|
11611
|
-
return null;
|
|
11612
|
-
}
|
|
11613
|
-
if (Array.isArray(custom)) {
|
|
11614
|
-
return this.routeTarget(this.router ? this.router.serializeUrl(this.router.createUrlTree([...custom])) : null);
|
|
11615
|
-
}
|
|
11616
|
-
return this.urlTarget(typeof custom === 'string' ? custom : this.link() ?? null);
|
|
11617
|
-
}, ...(ngDevMode ? [{ debugName: "target" }] : []));
|
|
11618
|
-
}
|
|
11619
|
-
open(event, target) {
|
|
11620
|
-
if (!target.route || !this.router || event.button !== 0 || event.ctrlKey || event.metaKey || event.shiftKey || event.altKey) {
|
|
11621
|
-
return;
|
|
11622
|
-
}
|
|
11623
|
-
event.preventDefault();
|
|
11624
|
-
void this.router.navigateByUrl(target.route);
|
|
11625
|
-
}
|
|
11626
|
-
urlTarget(url) {
|
|
11627
|
-
if (!url) {
|
|
11628
|
-
return null;
|
|
11629
|
-
}
|
|
11630
|
-
if (url.startsWith('/') && !url.startsWith('//') && !url.startsWith('/\\')) {
|
|
11631
|
-
return this.routeTarget(url);
|
|
11632
|
-
}
|
|
11633
|
-
return /^https:\/\/[^/\\\s]+/i.test(url) ? { href: url, route: null } : null;
|
|
11634
|
-
}
|
|
11635
|
-
routeTarget(url) {
|
|
11636
|
-
if (!url) {
|
|
11637
|
-
return null;
|
|
11638
|
-
}
|
|
11639
|
-
return {
|
|
11640
|
-
href: this.locationStrategy ? this.locationStrategy.prepareExternalUrl(url) : url,
|
|
11641
|
-
route: this.router ? url : null
|
|
11642
|
-
};
|
|
11643
|
-
}
|
|
11644
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantSubjectLinkComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
11645
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.34", type: NhAssistantSubjectLinkComponent, isStandalone: true, selector: "nh-assistant-subject-link", inputs: { subjectType: { classPropertyName: "subjectType", publicName: "subjectType", isSignal: true, isRequired: true, transformFunction: null }, subjectId: { classPropertyName: "subjectId", publicName: "subjectId", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, typeName: { classPropertyName: "typeName", publicName: "typeName", isSignal: true, isRequired: false, transformFunction: null }, link: { classPropertyName: "link", publicName: "link", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
11646
|
-
@if (shownTypeName(); as type) {
|
|
11647
|
-
<!-- The space keeps the type and the name apart for screen readers; the flex gap shows it. -->
|
|
11648
|
-
<span class="nh-assistant-subject-type">{{ type }}</span>{{ ' ' }}
|
|
11649
|
-
}
|
|
11650
|
-
@if (target(); as link) {
|
|
11651
|
-
<a class="nh-assistant-subject-name" [href]="link.href" (click)="open($event, link)">{{ label() }}</a>
|
|
11652
|
-
} @else {
|
|
11653
|
-
<span class="nh-assistant-subject-name">{{ label() }}</span>
|
|
11654
|
-
}
|
|
11655
|
-
`, 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;flex-wrap:wrap;align-items:baseline;gap:.35em;min-width:0;overflow-wrap:anywhere}.nh-assistant-subject-type{color:var(--nha-text-muted);font-size:.85em;font-weight:500}a.nh-assistant-subject-name{border-radius:2px;color:inherit;text-decoration:underline;text-underline-offset:.15em}a.nh-assistant-subject-name:hover{color:var(--nha-accent)}a.nh-assistant-subject-name:focus-visible{outline:2px solid var(--nha-focus);outline-offset:2px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
11656
|
-
}
|
|
11657
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantSubjectLinkComponent, decorators: [{
|
|
11658
|
-
type: Component,
|
|
11659
|
-
args: [{ selector: 'nh-assistant-subject-link', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
11660
|
-
@if (shownTypeName(); as type) {
|
|
11661
|
-
<!-- The space keeps the type and the name apart for screen readers; the flex gap shows it. -->
|
|
11662
|
-
<span class="nh-assistant-subject-type">{{ type }}</span>{{ ' ' }}
|
|
11663
|
-
}
|
|
11664
|
-
@if (target(); as link) {
|
|
11665
|
-
<a class="nh-assistant-subject-name" [href]="link.href" (click)="open($event, link)">{{ label() }}</a>
|
|
11666
|
-
} @else {
|
|
11667
|
-
<span class="nh-assistant-subject-name">{{ label() }}</span>
|
|
11668
|
-
}
|
|
11669
|
-
`, 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;flex-wrap:wrap;align-items:baseline;gap:.35em;min-width:0;overflow-wrap:anywhere}.nh-assistant-subject-type{color:var(--nha-text-muted);font-size:.85em;font-weight:500}a.nh-assistant-subject-name{border-radius:2px;color:inherit;text-decoration:underline;text-underline-offset:.15em}a.nh-assistant-subject-name:hover{color:var(--nha-accent)}a.nh-assistant-subject-name:focus-visible{outline:2px solid var(--nha-focus);outline-offset:2px}\n"] }]
|
|
11670
|
-
}], propDecorators: { subjectType: [{ type: i0.Input, args: [{ isSignal: true, alias: "subjectType", required: true }] }], subjectId: [{ type: i0.Input, args: [{ isSignal: true, alias: "subjectId", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], typeName: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeName", required: false }] }], link: [{ type: i0.Input, args: [{ isSignal: true, alias: "link", required: false }] }] } });
|
|
11671
|
-
|
|
11672
12506
|
/*
|
|
11673
12507
|
* Public API surface of @newheap/platform-ai-chat.
|
|
11674
12508
|
*/
|
|
@@ -11677,5 +12511,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImpo
|
|
|
11677
12511
|
* Generated bundle index. Do not edit.
|
|
11678
12512
|
*/
|
|
11679
12513
|
|
|
11680
|
-
export { NH_ASSISTANT_ACCESS_POLICY, NH_ASSISTANT_CONFIG, NH_ASSISTANT_CONVERSATION_FRAGMENT, NH_ASSISTANT_ERROR_CODES, 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_MCP_AUTHORIZATION_MESSAGE, NH_ASSISTANT_MCP_AUTHORIZATION_WINDOW, NH_ASSISTANT_MESSAGE_QUEUED_NOTICE_CODE, NH_ASSISTANT_NO_ANSWER_NOTICE_CODE, NH_ASSISTANT_PAGE_CONTEXT_LIMITS, NH_ASSISTANT_PANEL_MOBILE_QUERY, NH_ASSISTANT_PANEL_WIDTH, NH_ASSISTANT_RESULT_PREVIEW_LENGTH, NH_ASSISTANT_RUN_HISTORY_MAX_PAGE, NH_ASSISTANT_RUN_RESULT_MAX_LENGTH, NH_ASSISTANT_SERVER_ERROR_CODES, NH_ASSISTANT_SSE_EVENT_TYPES, NH_ASSISTANT_SUBJECT_SEARCH, NH_ASSISTANT_SYSTEM_GLOBAL_ID, NH_ASSISTANT_SYSTEM_SUBJECT_TYPE, NH_ASSISTANT_TRANSLATIONS, NH_ASSISTANT_UPLOAD, NH_ASSISTANT_UPLOAD_HEADER, NH_ASSISTANT_VIRTUAL_SCROLL_THRESHOLD, NhAssistantActivityComponent, NhAssistantAdminApiService, NhAssistantAgentPickerComponent, NhAssistantAgentRunApiService, NhAssistantAllowAllAccessPolicy, NhAssistantApiError, NhAssistantApiService, NhAssistantApprovalCardComponent, NhAssistantClientErrorCodes, NhAssistantComposerComponent, NhAssistantConnectionRequiredComponent, NhAssistantConversationListComponent, NhAssistantLauncherComponent, NhAssistantLiveService, NhAssistantMcpConnectService, NhAssistantMcpConnectionsComponent, NhAssistantMcpPopupAuthorizationWindow, NhAssistantPanelComponent, NhAssistantPanelService, NhAssistantPreferencesComponent, NhAssistantProposalFeedbackComponent, NhAssistantPushService, NhAssistantShareComponent, NhAssistantSseParser, NhAssistantStore, NhAssistantSubjectBadgeComponent, NhAssistantSubjectSummaryService, NhAssistantThreadComponent, NhAssistantToolCallCardComponent, NhAssistantToolCallGroupComponent, NhAssistantTranslationMerger, applyNhAssistantApprovalDecision, applyNhAssistantConversationChange, applyNhAssistantEvent, applyNhAssistantLiveEvent, describeNhAssistantClientContext, formatNhAssistantCountdown, nhAssistantCodeForStatus, nhAssistantConnectMcp, nhAssistantErrorMessageKey, nhAssistantIsUnread, nhAssistantLatestTurnHasText, nhAssistantMcpAuthorizationFromFragment, nhAssistantShareLink, nhAssistantXhrUpload, normalizeNhAssistantClientContext, provideNhAssistant, toNhAssistantError, NH_ASSISTANT_DASH_CASE as ɵNH_ASSISTANT_DASH_CASE, NhAssistantIconComponent as ɵNhAssistantIconComponent, NhAssistantMarkdownPipe as ɵNhAssistantMarkdownPipe, NhAssistantPickerComponent as ɵNhAssistantPickerComponent, NhAssistantSubjectLinkComponent as ɵNhAssistantSubjectLinkComponent, NhAssistantTranslatePipe as ɵNhAssistantTranslatePipe, nhAssistantCodeKeys as ɵnhAssistantCodeKeys, nhAssistantErrorKeys as ɵnhAssistantErrorKeys, nhAssistantErrorOf as ɵnhAssistantErrorOf, nhAssistantReloadOnAgentWork as ɵnhAssistantReloadOnAgentWork, nhAssistantWatchAgentWork as ɵnhAssistantWatchAgentWork, toNhAssistantError as ɵtoNhAssistantError };
|
|
12514
|
+
export { NH_ASSISTANT_ACCESS_POLICY, NH_ASSISTANT_CONFIG, NH_ASSISTANT_CONVERSATION_FRAGMENT, NH_ASSISTANT_ERROR_CODES, 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_MCP_AUTHORIZATION_MESSAGE, NH_ASSISTANT_MCP_AUTHORIZATION_WINDOW, NH_ASSISTANT_MESSAGE_QUEUED_NOTICE_CODE, NH_ASSISTANT_NO_ANSWER_NOTICE_CODE, NH_ASSISTANT_PAGE_CONTEXT_LIMITS, NH_ASSISTANT_PANEL_MOBILE_QUERY, NH_ASSISTANT_PANEL_WIDTH, NH_ASSISTANT_RESULT_PREVIEW_LENGTH, NH_ASSISTANT_RUN_HISTORY_MAX_PAGE, NH_ASSISTANT_RUN_RESULT_MAX_LENGTH, NH_ASSISTANT_SERVER_ERROR_CODES, NH_ASSISTANT_SSE_EVENT_TYPES, NH_ASSISTANT_SUBJECT_SEARCH, NH_ASSISTANT_SYSTEM_GLOBAL_ID, NH_ASSISTANT_SYSTEM_SUBJECT_TYPE, NH_ASSISTANT_TRANSLATIONS, NH_ASSISTANT_UPLOAD, NH_ASSISTANT_UPLOAD_HEADER, NH_ASSISTANT_VIRTUAL_SCROLL_THRESHOLD, NhAssistantActivityComponent, NhAssistantAdminApiService, NhAssistantAgentPickerComponent, NhAssistantAgentRunApiService, NhAssistantAllowAllAccessPolicy, NhAssistantApiError, NhAssistantApiService, NhAssistantApprovalCardComponent, NhAssistantClientErrorCodes, NhAssistantComposerComponent, NhAssistantConnectionRequiredComponent, NhAssistantConversationListComponent, NhAssistantLauncherComponent, NhAssistantLiveService, NhAssistantMcpConnectService, NhAssistantMcpConnectionsComponent, NhAssistantMcpPopupAuthorizationWindow, NhAssistantPanelComponent, NhAssistantPanelService, NhAssistantPreferencesComponent, NhAssistantProposalFeedbackComponent, NhAssistantPushService, NhAssistantShareComponent, NhAssistantSseParser, NhAssistantStore, NhAssistantSubjectBadgeComponent, NhAssistantSubjectSummaryService, NhAssistantThreadComponent, NhAssistantToolCallCardComponent, NhAssistantToolCallGroupComponent, NhAssistantTranslationMerger, applyNhAssistantApprovalDecision, applyNhAssistantConversationChange, applyNhAssistantEvent, applyNhAssistantLiveEvent, describeNhAssistantClientContext, formatNhAssistantCountdown, nhAssistantCodeForStatus, nhAssistantConnectMcp, nhAssistantErrorMessageKey, nhAssistantIsUnread, nhAssistantLatestTurnHasText, nhAssistantMcpAuthorizationFromFragment, nhAssistantShareLink, nhAssistantXhrUpload, normalizeNhAssistantClientContext, provideNhAssistant, toNhAssistantError, NH_ASSISTANT_DASH_CASE as ɵNH_ASSISTANT_DASH_CASE, NhAssistantIconComponent as ɵNhAssistantIconComponent, NhAssistantMarkdownPipe as ɵNhAssistantMarkdownPipe, NhAssistantPickerComponent as ɵNhAssistantPickerComponent, NhAssistantSubjectLinkComponent as ɵNhAssistantSubjectLinkComponent, NhAssistantTranslatePipe as ɵNhAssistantTranslatePipe, nhAssistantCodeKeys as ɵnhAssistantCodeKeys, nhAssistantErrorKeys as ɵnhAssistantErrorKeys, nhAssistantErrorOf as ɵnhAssistantErrorOf, nhAssistantMediaTypeOf as ɵnhAssistantMediaTypeOf, nhAssistantReloadOnAgentWork as ɵnhAssistantReloadOnAgentWork, nhAssistantWatchAgentWork as ɵnhAssistantWatchAgentWork, toNhAssistantError as ɵtoNhAssistantError };
|
|
11681
12515
|
//# sourceMappingURL=newheap-platform-ai-chat.mjs.map
|