@newheap/platform-ai-chat 0.15.0 → 0.16.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/admin/index.d.ts +106 -13
- package/fesm2022/newheap-platform-ai-chat-admin.mjs +446 -36
- package/fesm2022/newheap-platform-ai-chat-admin.mjs.map +1 -1
- package/fesm2022/newheap-platform-ai-chat-testing.mjs +522 -43
- package/fesm2022/newheap-platform-ai-chat-testing.mjs.map +1 -1
- package/fesm2022/newheap-platform-ai-chat.mjs +1054 -63
- package/fesm2022/newheap-platform-ai-chat.mjs.map +1 -1
- package/i18n/en.json +127 -8
- package/i18n/nl.json +127 -8
- package/index.d.ts +424 -38
- package/package.json +1 -1
- package/testing/index.d.ts +64 -7
|
@@ -106,6 +106,53 @@ const NH_ASSISTANT_FETCH = new InjectionToken('NH_ASSISTANT_FETCH', {
|
|
|
106
106
|
providedIn: 'root',
|
|
107
107
|
factory: () => (input, init) => globalThis.fetch(input, init)
|
|
108
108
|
});
|
|
109
|
+
/**
|
|
110
|
+
* Sends the uploads of the assistant client. The default uses `XMLHttpRequest`, because `fetch` does
|
|
111
|
+
* not report upload progress. The mock API from `@newheap/platform-ai-chat/testing` replaces it.
|
|
112
|
+
*/
|
|
113
|
+
const NH_ASSISTANT_UPLOAD = new InjectionToken('NH_ASSISTANT_UPLOAD', {
|
|
114
|
+
providedIn: 'root',
|
|
115
|
+
factory: () => nhAssistantXhrUpload
|
|
116
|
+
});
|
|
117
|
+
/** The default upload: `XMLHttpRequest` with upload progress, answered as a `Response`. */
|
|
118
|
+
function nhAssistantXhrUpload(request) {
|
|
119
|
+
return new Promise((resolve, reject) => {
|
|
120
|
+
if (request.signal.aborted) {
|
|
121
|
+
reject(new DOMException('The upload was aborted.', 'AbortError'));
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const xhr = new XMLHttpRequest();
|
|
125
|
+
const abort = () => xhr.abort();
|
|
126
|
+
request.signal.addEventListener('abort', abort, { once: true });
|
|
127
|
+
const finish = () => request.signal.removeEventListener('abort', abort);
|
|
128
|
+
xhr.open('POST', request.url);
|
|
129
|
+
for (const [name, value] of Object.entries(request.headers)) {
|
|
130
|
+
xhr.setRequestHeader(name, value);
|
|
131
|
+
}
|
|
132
|
+
xhr.upload.onprogress = event => {
|
|
133
|
+
if (event.lengthComputable) {
|
|
134
|
+
request.onProgress(event.loaded, event.total);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
xhr.onload = () => {
|
|
138
|
+
finish();
|
|
139
|
+
const contentType = xhr.getResponseHeader('content-type');
|
|
140
|
+
resolve(new Response(xhr.status === 204 ? null : xhr.responseText, {
|
|
141
|
+
status: xhr.status,
|
|
142
|
+
headers: contentType ? { 'content-type': contentType } : {}
|
|
143
|
+
}));
|
|
144
|
+
};
|
|
145
|
+
xhr.onerror = () => {
|
|
146
|
+
finish();
|
|
147
|
+
reject(new TypeError('The upload did not reach the server.'));
|
|
148
|
+
};
|
|
149
|
+
xhr.onabort = () => {
|
|
150
|
+
finish();
|
|
151
|
+
reject(new DOMException('The upload was aborted.', 'AbortError'));
|
|
152
|
+
};
|
|
153
|
+
xhr.send(request.body);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
109
156
|
/** Default policy: every user who can reach the host may use the assistant. */
|
|
110
157
|
class NhAssistantAllowAllAccessPolicy {
|
|
111
158
|
canUse() {
|
|
@@ -187,7 +234,9 @@ var en = {
|
|
|
187
234
|
elapsed: "{{seconds}}s",
|
|
188
235
|
participant: "Participant",
|
|
189
236
|
trigger: "Event",
|
|
190
|
-
rule: "Automatic rule"
|
|
237
|
+
rule: "Automatic rule",
|
|
238
|
+
files: "Attached files",
|
|
239
|
+
"file-meta": "{{type}}, {{size}}"
|
|
191
240
|
},
|
|
192
241
|
composer: {
|
|
193
242
|
label: "Message",
|
|
@@ -202,6 +251,18 @@ var en = {
|
|
|
202
251
|
"waiting-for-approval": "Waiting for approval",
|
|
203
252
|
"running-by": "{{name}} is working with the assistant…",
|
|
204
253
|
"waiting-for-approval-by": "Waiting for {{name}} to decide…"
|
|
254
|
+
},
|
|
255
|
+
attach: "Attach files",
|
|
256
|
+
"attach-hint": "{{types}}, up to {{max}} each, at most {{count}} files per message",
|
|
257
|
+
attachments: "Attached files",
|
|
258
|
+
"remove-attachment": "Remove {{name}}",
|
|
259
|
+
uploading: "Uploading {{name}}",
|
|
260
|
+
"uploading-hint": "The message can be sent once the files are uploaded",
|
|
261
|
+
"attachment-errors": {
|
|
262
|
+
"assistant-file-type-not-accepted": "This type of file cannot be attached. Attach {{types}}.",
|
|
263
|
+
"assistant-file-too-large": "The file is larger than {{max}}.",
|
|
264
|
+
"assistant-file-count-limit": "A message can carry at most {{count}} files.",
|
|
265
|
+
"assistant-file-upload-invalid": "The file is empty or its name cannot be used."
|
|
205
266
|
}
|
|
206
267
|
},
|
|
207
268
|
queue: {
|
|
@@ -219,7 +280,8 @@ var en = {
|
|
|
219
280
|
preparing: "Preparing…",
|
|
220
281
|
sending: "Sending…",
|
|
221
282
|
failed: "Not sent; resume to retry"
|
|
222
|
-
}
|
|
283
|
+
},
|
|
284
|
+
files: "Files ({{count}}): {{names}}"
|
|
223
285
|
},
|
|
224
286
|
"tool-call": {
|
|
225
287
|
label: "Tool call: {{name}}",
|
|
@@ -355,7 +417,8 @@ var en = {
|
|
|
355
417
|
"assistant-proposal-already-open": "Not proposed again: the same proposal already waits.",
|
|
356
418
|
"assistant-proposal-recently-rejected": "Not proposed again: people rejected it recently.",
|
|
357
419
|
"assistant-proposal-placed": "Proposed at its object; waits for that object's approvers",
|
|
358
|
-
"assistant-proposal-not-placed": "Not proposed: the object it concerns does not accept it."
|
|
420
|
+
"assistant-proposal-not-placed": "Not proposed: the object it concerns does not accept it.",
|
|
421
|
+
"assistant-proposal-not-needed": "Not proposed: the work is already done or no longer applies."
|
|
359
422
|
},
|
|
360
423
|
errors: {
|
|
361
424
|
generic: "Something went wrong. Please try again.",
|
|
@@ -409,6 +472,19 @@ var en = {
|
|
|
409
472
|
"assistant-message-invalid": "The message is empty or not valid. Write a question and send it again.",
|
|
410
473
|
"assistant-message-too-long": "The message is too long. Shorten it and send it again.",
|
|
411
474
|
"assistant-model-unavailable": "The language model is temporarily unavailable. Try again in a moment.",
|
|
475
|
+
"assistant-classification-not-permitted": "The model this agent uses may not receive data of the agent's classification. Ask an administrator to choose a model profile that permits it.",
|
|
476
|
+
"assistant-files-unavailable": "This application gives the assistant no files.",
|
|
477
|
+
"assistant-files-not-accepted": "This agent does not accept files. An administrator can switch files on for it.",
|
|
478
|
+
"assistant-file-reference-invalid": "A file could not be read because its reference is not valid.",
|
|
479
|
+
"assistant-file-not-found": "The file was not found. It may have been removed.",
|
|
480
|
+
"assistant-file-forbidden": "You may not read this file, so the assistant did not read it either.",
|
|
481
|
+
"assistant-file-unavailable": "The file could not be opened right now. Try again in a moment.",
|
|
482
|
+
"assistant-file-reader-failed": "The reading tool for this type of file could not turn it into text.",
|
|
483
|
+
"assistant-file-too-large": "The file is larger than the assistant may read. Share a smaller file.",
|
|
484
|
+
"assistant-file-too-many-pages": "The file has more pages than the assistant may read. Share a shorter document.",
|
|
485
|
+
"assistant-file-count-limit": "The assistant may read no more files in this answer. Ask about fewer files at a time.",
|
|
486
|
+
"assistant-file-token-limit": "The files are too long for one answer. Ask about fewer or shorter files at a time.",
|
|
487
|
+
"assistant-file-type-not-supported": "The model of this agent cannot read files of this type.",
|
|
412
488
|
"assistant-proposal-hash-mismatch": "The proposed change was modified in the meantime, so nothing was changed. Reload the conversation and decide again.",
|
|
413
489
|
"assistant-title-invalid": "The conversation title is not valid. Use a shorter title.",
|
|
414
490
|
"assistant-tool-call-limit-reached": "The assistant reached the maximum number of steps for one answer. Ask a narrower question.",
|
|
@@ -439,6 +515,8 @@ var en = {
|
|
|
439
515
|
"assistant-run-token-limit": "This run stopped because it reached its token budget. What it did so far stays; an approver can resume it.",
|
|
440
516
|
"assistant-skill-stopped": "This skill of the agent has been stopped.",
|
|
441
517
|
"assistant-skill-paused": "This skill of the agent is paused; its events wait until it resumes.",
|
|
518
|
+
"assistant-agent-off": "This agent is switched off; it takes no work from other agents until it is switched on.",
|
|
519
|
+
"assistant-skill-off": "This skill of the agent is switched off.",
|
|
442
520
|
"assistant-skill-not-found": "The agent has no such skill.",
|
|
443
521
|
"assistant-skill-not-available": "That skill is not available now.",
|
|
444
522
|
"assistant-skill-switch-limit": "The run already switched skills as often as allowed.",
|
|
@@ -522,11 +600,19 @@ var en = {
|
|
|
522
600
|
"assistant-mcp-authorization-cancelled": "The authorization window was closed before it finished.",
|
|
523
601
|
"assistant-mcp-popup-blocked": "The browser blocked the authorization window. Allow pop-ups for this site and try again.",
|
|
524
602
|
"assistant-proposal-placed": "The proposal waits at the object it concerns, for that object's approvers.",
|
|
525
|
-
"assistant-proposal-not-placed": "The proposal was not placed at the object it concerns."
|
|
603
|
+
"assistant-proposal-not-placed": "The proposal was not placed at the object it concerns.",
|
|
604
|
+
"assistant-proposal-not-needed": "Not proposed: the work is already done or no longer applies.",
|
|
605
|
+
"assistant-file-upload-invalid": "The file could not be uploaded. Choose one file and try again.",
|
|
606
|
+
"assistant-file-type-not-accepted": "This type of file cannot be attached.",
|
|
607
|
+
"assistant-file-upload-refused": "The application refused this file.",
|
|
608
|
+
"assistant-file-upload-failed": "The file could not be stored right now. Try again in a moment.",
|
|
609
|
+
"assistant-file-scan-refused": "The file did not pass the scan for harmful content and was not stored."
|
|
526
610
|
},
|
|
527
611
|
notices: {
|
|
528
612
|
"no-answer": "The assistant finished without an answer. Try again or rephrase your question.",
|
|
529
|
-
"conversation-removed": "This conversation is no longer available to you."
|
|
613
|
+
"conversation-removed": "This conversation is no longer available to you.",
|
|
614
|
+
"message-queued": "The agent is working in this conversation. Your message waits for its next run, which adds it here.",
|
|
615
|
+
"message-queued-with-files": "The agent is working in this conversation. Your message and its files wait for its next run, which adds them here."
|
|
530
616
|
},
|
|
531
617
|
preferences: {
|
|
532
618
|
title: "Your preferences",
|
|
@@ -672,22 +758,31 @@ var en = {
|
|
|
672
758
|
stopped: "Stopped",
|
|
673
759
|
"configured-off": "Off in configuration",
|
|
674
760
|
"configured-off-hint": "Disabled in the application's code until the next deployment; it cannot be resumed here.",
|
|
761
|
+
off: "Off",
|
|
762
|
+
"off-hint": "Events, schedules and other triggers are ignored until it is switched on, and nothing waits for it. People can still chat with it and start it by hand.",
|
|
763
|
+
"starts-off": "It started off, as its code says.",
|
|
675
764
|
changed: "Changed by {{user}} on {{date}}.",
|
|
676
765
|
reason: "Note: {{reason}}",
|
|
677
766
|
waiting: "{{count}} events wait for the resume.",
|
|
678
767
|
action: {
|
|
679
768
|
active: "Resume",
|
|
680
769
|
paused: "Pause",
|
|
770
|
+
off: "Switch off",
|
|
771
|
+
"switch-on": "Switch on",
|
|
681
772
|
stopped: "Stop"
|
|
682
773
|
},
|
|
683
774
|
explain: {
|
|
684
775
|
active: "The agent works again. Every subject with waiting events gets one run.",
|
|
685
776
|
paused: "No new runs start. Events wait and run after the resume; running work finishes and approved proposals execute.",
|
|
777
|
+
off: "Events, schedules, webhooks, hand-offs and follow-ups are ignored and nothing waits; switching on later starts nothing for them. Running work finishes, open proposals can still be decided and people can still chat with the agent and start it by hand.",
|
|
778
|
+
"switch-on": "The agent takes its events, schedules and other triggers again. Nothing that happened while it was off runs.",
|
|
686
779
|
stopped: "Active runs are cancelled, their open proposals withdrawn and waiting events dropped. Approved proposals wait until the agent works again."
|
|
687
780
|
},
|
|
688
781
|
confirm: {
|
|
689
782
|
active: "Confirm resume",
|
|
690
783
|
paused: "Confirm pause",
|
|
784
|
+
off: "Confirm switch off",
|
|
785
|
+
"switch-on": "Confirm switch on",
|
|
691
786
|
stopped: "Confirm stop"
|
|
692
787
|
},
|
|
693
788
|
"note-label": "Note for other administrators (optional)",
|
|
@@ -700,6 +795,8 @@ var en = {
|
|
|
700
795
|
explain: {
|
|
701
796
|
active: "The skill {{skill}} works again. Every subject with events waiting for it gets one run.",
|
|
702
797
|
paused: "The skill {{skill}} starts no new runs: its events wait, triage no longer chooses it and running work finishes. The other skills keep working.",
|
|
798
|
+
off: "Triggers of the skill {{skill}} are ignored and rules and triage no longer route work to it; nothing waits for it. Running work finishes. The other skills keep working.",
|
|
799
|
+
"switch-on": "The skill {{skill}} takes work again. Nothing that happened while it was off runs.",
|
|
703
800
|
stopped: "The runs of the skill {{skill}} are cancelled and its waiting events dropped; its triggers are refused. The other skills keep working."
|
|
704
801
|
}
|
|
705
802
|
},
|
|
@@ -776,7 +873,10 @@ var en = {
|
|
|
776
873
|
"view-policy": "Results for the whole application seen by: {{policy}}."
|
|
777
874
|
},
|
|
778
875
|
groups: "Groups: {{groups}}",
|
|
779
|
-
"catalog-failed": "The building blocks of agent runs could not be read. Until they are, agents cannot be run now here and what an agent works on and its groups stay as they are."
|
|
876
|
+
"catalog-failed": "The building blocks of agent runs could not be read. Until they are, agents cannot be run now here and what an agent works on and its groups stay as they are.",
|
|
877
|
+
warnings: {
|
|
878
|
+
"classification-not-permitted": "Neither the agent's model profile nor the profile's fallbacks permit its data classification. The agent is saved, but its model calls are refused until a profile permits the classification."
|
|
879
|
+
}
|
|
780
880
|
},
|
|
781
881
|
"agent-editor": {
|
|
782
882
|
"new-title": "New agent",
|
|
@@ -791,7 +891,14 @@ var en = {
|
|
|
791
891
|
"autonomy-hint": "Only Execute allows changes, and every change still needs approval.",
|
|
792
892
|
"required-policy": "Required policy",
|
|
793
893
|
"required-policy-hint": "Optional. Only users who pass this policy see the agent.",
|
|
894
|
+
profile: "Model profile",
|
|
895
|
+
"profile-default": "The application's chat profile",
|
|
896
|
+
"profile-hint": "The model the agent works with, from the profiles the application offers for agents created here.",
|
|
897
|
+
classification: "Data classification",
|
|
898
|
+
"classification-hint": "How sensitive the data is that the agent's model calls carry, in chats with it and in its runs. Its model profile or one of the profile's fallbacks must permit it; a skill may set its own.",
|
|
794
899
|
"state-hint": "Pause, stop and resume the agent from the agent list. Saving never changes its state.",
|
|
900
|
+
"switch-on-now": "Switch on now",
|
|
901
|
+
"switch-on-now-hint": "Otherwise the agent starts off: its events, schedules and other triggers are ignored until you switch it on in the agent list. People can still chat with it and start it by hand.",
|
|
795
902
|
"tool-selectors": "Tool selectors",
|
|
796
903
|
"tool-selectors-hint": "Choose tools from the catalog or add a pattern such as sample-api.project.*. MCP tools come from the assigned servers.",
|
|
797
904
|
"add-selector": "Add pattern",
|
|
@@ -1006,7 +1113,33 @@ var en = {
|
|
|
1006
1113
|
"client-secret-reference": "Client secret reference",
|
|
1007
1114
|
"organization-account-label": "Connected account",
|
|
1008
1115
|
"organization-account-label-hint": "Which account the organization connects, for example \"Planning service account\". Everyone using the server sees it.",
|
|
1009
|
-
"organization-account-label-required": "Name the account that will be connected."
|
|
1116
|
+
"organization-account-label-required": "Name the account that will be connected.",
|
|
1117
|
+
reader: {
|
|
1118
|
+
title: "Reading documents",
|
|
1119
|
+
hint: "Models that cannot read a file type themselves can get the file as text from a reader of this server. The assistant calls the reader's tool with the file name, media type and base64 content of the file, after the application's own readers.",
|
|
1120
|
+
empty: "This server reads no files.",
|
|
1121
|
+
item: "Reader {{number}}",
|
|
1122
|
+
add: "Add reader",
|
|
1123
|
+
"add-hint": "Each reader is one of the server's synced tools; a server has at most {{max}}.",
|
|
1124
|
+
limit: "This server has the most readers it can have ({{max}}).",
|
|
1125
|
+
remove: "Remove",
|
|
1126
|
+
"remove-label": "Remove reader {{number}}",
|
|
1127
|
+
"media-types": "Media types",
|
|
1128
|
+
"media-types-hint": "Comma-separated, at most 16, such as application/vnd.openxmlformats-officedocument.wordprocessingml.document.",
|
|
1129
|
+
"media-types-invalid": "Enter 1 to 16 media types of the form type/subtype, separated by commas.",
|
|
1130
|
+
tool: "Reading tool",
|
|
1131
|
+
"tool-choose": "Choose a tool",
|
|
1132
|
+
"tool-hint": "A tool synced from this server that turns a file into text. It does not have to be enabled for agents.",
|
|
1133
|
+
"tool-duplicate": "Another reader of this server already uses this tool.",
|
|
1134
|
+
"tool-sync": "Sync the server's tools first, then add a reader.",
|
|
1135
|
+
priority: "Priority",
|
|
1136
|
+
"priority-hint": "Readers with a higher priority are asked first, after the application's own readers; with the same priority, by server and tool name.",
|
|
1137
|
+
"priority-invalid": "Enter a whole number from {{min}} to {{max}}.",
|
|
1138
|
+
"max-classification": "Highest classification",
|
|
1139
|
+
"max-classification-choose": "Choose a classification",
|
|
1140
|
+
"max-classification-hint": "A file that is more sensitive is never sent to this server.",
|
|
1141
|
+
"leaves-application": "A file sent to this server leaves the application. Choose a classification the server may receive; for personal connections the person's consent to background use still applies."
|
|
1142
|
+
}
|
|
1010
1143
|
},
|
|
1011
1144
|
tools: {
|
|
1012
1145
|
title: "Tools of {{name}}",
|
|
@@ -1154,6 +1287,19 @@ var en = {
|
|
|
1154
1287
|
"replay-progress": "{{replayed}} of at most {{max}} runs replayed, {{skipped}} skipped"
|
|
1155
1288
|
},
|
|
1156
1289
|
"run-config": {
|
|
1290
|
+
files: {
|
|
1291
|
+
label: "Accepts files",
|
|
1292
|
+
"agent-hint": "People's messages and the agent's tools may give it documents, such as PDFs, within the application's limits. A new agent accepts none until you switch this on.",
|
|
1293
|
+
"skill-hint": "Runs in this skill may read documents when the agent accepts them too. A new skill accepts none until you switch this on.",
|
|
1294
|
+
"limits-title": "Lower file limits",
|
|
1295
|
+
"agent-limits-hint": "Leave a field empty to keep the application's limit. A value can only lower it.",
|
|
1296
|
+
"skill-limits-hint": "Leave a field empty to keep the agent's limit. A value can only lower the application's.",
|
|
1297
|
+
"max-file-megabytes": "Largest file (MB)",
|
|
1298
|
+
"max-file-pages": "Most pages per file",
|
|
1299
|
+
"max-files-per-turn": "Most files per turn",
|
|
1300
|
+
"max-file-tokens-per-turn": "Most file tokens per turn",
|
|
1301
|
+
"application-limit": "The application allows at most {{value}}."
|
|
1302
|
+
},
|
|
1157
1303
|
back: "Back to agents",
|
|
1158
1304
|
title: "Run configuration of {{name}}",
|
|
1159
1305
|
description: "Skills and triggers that the administration adds without a deploy, made of the building blocks the application registers. New skills and triggers start in shadow, where nobody is notified and nothing executes.",
|
|
@@ -1192,7 +1338,9 @@ var en = {
|
|
|
1192
1338
|
taken: "This id is already in use.",
|
|
1193
1339
|
"name-letters": "Use at least one letter in the name.",
|
|
1194
1340
|
"instructions-for-model": "Without fixed rules the model works from these instructions; describe what it should do.",
|
|
1195
|
-
"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."
|
|
1341
|
+
"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.",
|
|
1342
|
+
"megabytes-up-to": "Enter a size above 0 and at most {{max}} MB.",
|
|
1343
|
+
"megabytes-positive": "Enter a size above 0 MB."
|
|
1196
1344
|
},
|
|
1197
1345
|
settings: {
|
|
1198
1346
|
title: "Run settings",
|
|
@@ -1293,6 +1441,8 @@ var en = {
|
|
|
1293
1441
|
"apply-shadow": "Evaluate in shadow next to the live version first",
|
|
1294
1442
|
"apply-live": "Put live at once",
|
|
1295
1443
|
"starts-in-shadow": "A new skill starts in shadow.",
|
|
1444
|
+
"switch-on-now": "Switch on now",
|
|
1445
|
+
"switch-on-now-hint": "Otherwise the skill starts off: triggers that name it are ignored and rules and triage do not route to it until you switch it on in the agent list.",
|
|
1296
1446
|
versions: "Versions",
|
|
1297
1447
|
"remove-explain": "Its versions stay as history. A trigger that starts it must go first.",
|
|
1298
1448
|
what: {
|
|
@@ -1304,6 +1454,22 @@ var en = {
|
|
|
1304
1454
|
"rules-hint": "The rules decide without a model; a doubtful case goes to the model with the instructions below.",
|
|
1305
1455
|
"model-hint": "The model decides with the instructions below and the chosen tools.",
|
|
1306
1456
|
"profile-hint": "The model profile it uses; by default the agent's.",
|
|
1457
|
+
classification: {
|
|
1458
|
+
label: "Data classification",
|
|
1459
|
+
agent: "The agent's classification",
|
|
1460
|
+
hint: "How sensitive the data is that the skill sends to its model. Its model profile must permit it; otherwise its model calls are refused.",
|
|
1461
|
+
"public": "Public",
|
|
1462
|
+
"public-hint": "Data anyone may see.",
|
|
1463
|
+
internal: "Internal",
|
|
1464
|
+
"internal-hint": "Data of the organization; the default.",
|
|
1465
|
+
confidential: "Confidential",
|
|
1466
|
+
"confidential-hint": "Data only some people may see, such as personal or financial data.",
|
|
1467
|
+
restricted: "Restricted",
|
|
1468
|
+
"restricted-hint": "The most sensitive data, for which only specially approved models may be used."
|
|
1469
|
+
},
|
|
1470
|
+
warnings: {
|
|
1471
|
+
"classification-not-permitted": "Neither the skill's model profile nor the profile's fallbacks permit its data classification. The skill is saved, but its model calls are refused until a profile permits the classification."
|
|
1472
|
+
},
|
|
1307
1473
|
"apply-shadow-hint": "The new version runs next to the live one; compare them, then promote or discard it.",
|
|
1308
1474
|
"apply-live-hint": "For a small correction: the new version replaces the live one at once.",
|
|
1309
1475
|
"tools-pattern": "All tools that match: {{count}} now.",
|
|
@@ -2086,7 +2252,9 @@ var nl = {
|
|
|
2086
2252
|
elapsed: "{{seconds}} s",
|
|
2087
2253
|
participant: "Deelnemer",
|
|
2088
2254
|
trigger: "Gebeurtenis",
|
|
2089
|
-
rule: "Automatische regel"
|
|
2255
|
+
rule: "Automatische regel",
|
|
2256
|
+
files: "Bijgevoegde bestanden",
|
|
2257
|
+
"file-meta": "{{type}}, {{size}}"
|
|
2090
2258
|
},
|
|
2091
2259
|
composer: {
|
|
2092
2260
|
label: "Bericht",
|
|
@@ -2101,6 +2269,18 @@ var nl = {
|
|
|
2101
2269
|
"waiting-for-approval": "Wacht op goedkeuring",
|
|
2102
2270
|
"running-by": "{{name}} werkt met de assistent…",
|
|
2103
2271
|
"waiting-for-approval-by": "Wacht op de beslissing van {{name}}…"
|
|
2272
|
+
},
|
|
2273
|
+
attach: "Bestanden bijvoegen",
|
|
2274
|
+
"attach-hint": "{{types}}, elk tot {{max}}, hoogstens {{count}} bestanden per bericht",
|
|
2275
|
+
attachments: "Bijgevoegde bestanden",
|
|
2276
|
+
"remove-attachment": "{{name}} verwijderen",
|
|
2277
|
+
uploading: "{{name}} wordt geüpload",
|
|
2278
|
+
"uploading-hint": "Het bericht kan worden verstuurd zodra de bestanden zijn geüpload",
|
|
2279
|
+
"attachment-errors": {
|
|
2280
|
+
"assistant-file-type-not-accepted": "Dit type bestand kan niet worden bijgevoegd. Voeg {{types}} bij.",
|
|
2281
|
+
"assistant-file-too-large": "Het bestand is groter dan {{max}}.",
|
|
2282
|
+
"assistant-file-count-limit": "Een bericht kan hoogstens {{count}} bestanden bevatten.",
|
|
2283
|
+
"assistant-file-upload-invalid": "Het bestand is leeg of de naam is niet bruikbaar."
|
|
2104
2284
|
}
|
|
2105
2285
|
},
|
|
2106
2286
|
queue: {
|
|
@@ -2118,7 +2298,8 @@ var nl = {
|
|
|
2118
2298
|
preparing: "Klaarzetten…",
|
|
2119
2299
|
sending: "Versturen…",
|
|
2120
2300
|
failed: "Niet verstuurd; hervat om opnieuw te proberen"
|
|
2121
|
-
}
|
|
2301
|
+
},
|
|
2302
|
+
files: "Bestanden ({{count}}): {{names}}"
|
|
2122
2303
|
},
|
|
2123
2304
|
"tool-call": {
|
|
2124
2305
|
label: "Toolaanroep: {{name}}",
|
|
@@ -2254,7 +2435,8 @@ var nl = {
|
|
|
2254
2435
|
"assistant-proposal-already-open": "Niet opnieuw voorgesteld: hetzelfde voorstel wacht al.",
|
|
2255
2436
|
"assistant-proposal-recently-rejected": "Niet opnieuw voorgesteld: het is kort geleden afgewezen.",
|
|
2256
2437
|
"assistant-proposal-placed": "Voorgesteld bij het object; wacht op de goedkeurders daarvan",
|
|
2257
|
-
"assistant-proposal-not-placed": "Niet voorgesteld: het object waar het om gaat accepteert het niet."
|
|
2438
|
+
"assistant-proposal-not-placed": "Niet voorgesteld: het object waar het om gaat accepteert het niet.",
|
|
2439
|
+
"assistant-proposal-not-needed": "Niet voorgesteld: het werk is al gedaan of is niet meer van toepassing."
|
|
2258
2440
|
},
|
|
2259
2441
|
errors: {
|
|
2260
2442
|
generic: "Er ging iets mis. Probeer het opnieuw.",
|
|
@@ -2308,6 +2490,19 @@ var nl = {
|
|
|
2308
2490
|
"assistant-message-invalid": "Het bericht is leeg of ongeldig. Schrijf een vraag en verstuur hem opnieuw.",
|
|
2309
2491
|
"assistant-message-too-long": "Het bericht is te lang. Maak het korter en verstuur het opnieuw.",
|
|
2310
2492
|
"assistant-model-unavailable": "Het taalmodel is tijdelijk niet beschikbaar. Probeer het zo opnieuw.",
|
|
2493
|
+
"assistant-classification-not-permitted": "Het model dat deze agent gebruikt mag geen gegevens met de classificatie van de agent ontvangen. Vraag een beheerder een modelprofiel te kiezen dat dit toestaat.",
|
|
2494
|
+
"assistant-files-unavailable": "Deze applicatie geeft de assistent geen bestanden.",
|
|
2495
|
+
"assistant-files-not-accepted": "Deze agent neemt geen bestanden aan. Een beheerder kan bestanden voor hem aanzetten.",
|
|
2496
|
+
"assistant-file-reference-invalid": "Een bestand kon niet worden gelezen omdat de verwijzing ongeldig is.",
|
|
2497
|
+
"assistant-file-not-found": "Het bestand is niet gevonden. Misschien is het verwijderd.",
|
|
2498
|
+
"assistant-file-forbidden": "Je mag dit bestand niet lezen, dus de assistent heeft het ook niet gelezen.",
|
|
2499
|
+
"assistant-file-unavailable": "Het bestand kon nu niet worden geopend. Probeer het zo opnieuw.",
|
|
2500
|
+
"assistant-file-reader-failed": "Het leesprogramma voor dit type bestand kon het niet omzetten in tekst.",
|
|
2501
|
+
"assistant-file-too-large": "Het bestand is groter dan de assistent mag lezen. Deel een kleiner bestand.",
|
|
2502
|
+
"assistant-file-too-many-pages": "Het bestand heeft meer pagina's dan de assistent mag lezen. Deel een korter document.",
|
|
2503
|
+
"assistant-file-count-limit": "De assistent mag in dit antwoord geen bestanden meer lezen. Vraag naar minder bestanden tegelijk.",
|
|
2504
|
+
"assistant-file-token-limit": "De bestanden zijn te lang voor één antwoord. Vraag naar minder of kortere bestanden tegelijk.",
|
|
2505
|
+
"assistant-file-type-not-supported": "Het model van deze agent kan bestanden van dit type niet lezen.",
|
|
2311
2506
|
"assistant-proposal-hash-mismatch": "De voorgestelde wijziging is intussen veranderd, er is niets gewijzigd. Laad het gesprek opnieuw en besluit opnieuw.",
|
|
2312
2507
|
"assistant-title-invalid": "De titel van het gesprek is ongeldig. Gebruik een kortere titel.",
|
|
2313
2508
|
"assistant-tool-call-limit-reached": "De assistent heeft het maximale aantal stappen voor één antwoord bereikt. Stel een gerichtere vraag.",
|
|
@@ -2338,6 +2533,8 @@ var nl = {
|
|
|
2338
2533
|
"assistant-run-token-limit": "Deze run is gestopt omdat hij zijn tokenbudget heeft bereikt. Wat hij al deed blijft staan; een goedkeurder kan hem hervatten.",
|
|
2339
2534
|
"assistant-skill-stopped": "Deze vaardigheid van de agent is gestopt.",
|
|
2340
2535
|
"assistant-skill-paused": "Deze vaardigheid van de agent is gepauzeerd; haar gebeurtenissen wachten tot ze hervat wordt.",
|
|
2536
|
+
"assistant-agent-off": "Deze agent staat uit; hij neemt geen werk van andere agents aan tot hij aangezet wordt.",
|
|
2537
|
+
"assistant-skill-off": "Deze vaardigheid van de agent staat uit.",
|
|
2341
2538
|
"assistant-skill-not-found": "De agent heeft deze vaardigheid niet.",
|
|
2342
2539
|
"assistant-skill-not-available": "Die vaardigheid is nu niet beschikbaar.",
|
|
2343
2540
|
"assistant-skill-switch-limit": "De run is al zo vaak van vaardigheid gewisseld als mag.",
|
|
@@ -2421,11 +2618,19 @@ var nl = {
|
|
|
2421
2618
|
"assistant-mcp-authorization-cancelled": "Het autorisatievenster is gesloten voordat het klaar was.",
|
|
2422
2619
|
"assistant-mcp-popup-blocked": "De browser heeft het autorisatievenster geblokkeerd. Sta pop-ups toe voor deze site en probeer het opnieuw.",
|
|
2423
2620
|
"assistant-proposal-placed": "Het voorstel wacht bij het object waar het om gaat, op de goedkeurders daarvan.",
|
|
2424
|
-
"assistant-proposal-not-placed": "Het voorstel is niet bij het object geplaatst waar het om gaat."
|
|
2621
|
+
"assistant-proposal-not-placed": "Het voorstel is niet bij het object geplaatst waar het om gaat.",
|
|
2622
|
+
"assistant-proposal-not-needed": "Niet voorgesteld: het werk is al gedaan of is niet meer van toepassing.",
|
|
2623
|
+
"assistant-file-upload-invalid": "Het bestand kon niet worden geüpload. Kies één bestand en probeer het opnieuw.",
|
|
2624
|
+
"assistant-file-type-not-accepted": "Dit type bestand kan niet worden bijgevoegd.",
|
|
2625
|
+
"assistant-file-upload-refused": "De applicatie heeft dit bestand geweigerd.",
|
|
2626
|
+
"assistant-file-upload-failed": "Het bestand kon nu niet worden opgeslagen. Probeer het zo opnieuw.",
|
|
2627
|
+
"assistant-file-scan-refused": "Het bestand is niet door de scan op schadelijke inhoud gekomen en is niet opgeslagen."
|
|
2425
2628
|
},
|
|
2426
2629
|
notices: {
|
|
2427
2630
|
"no-answer": "De assistent is klaar zonder antwoord. Probeer het opnieuw of stel je vraag anders.",
|
|
2428
|
-
"conversation-removed": "Dit gesprek is niet meer voor je beschikbaar."
|
|
2631
|
+
"conversation-removed": "Dit gesprek is niet meer voor je beschikbaar.",
|
|
2632
|
+
"message-queued": "De agent werkt in dit gesprek. Je bericht wacht op zijn volgende run, die het hier toevoegt.",
|
|
2633
|
+
"message-queued-with-files": "De agent werkt in dit gesprek. Je bericht en de bestanden wachten op zijn volgende run, die ze hier toevoegt."
|
|
2429
2634
|
},
|
|
2430
2635
|
preferences: {
|
|
2431
2636
|
title: "Je voorkeuren",
|
|
@@ -2571,22 +2776,31 @@ var nl = {
|
|
|
2571
2776
|
stopped: "Gestopt",
|
|
2572
2777
|
"configured-off": "Uit in configuratie",
|
|
2573
2778
|
"configured-off-hint": "Uitgeschakeld in de code van de applicatie tot de volgende deployment; hier kun je de agent niet hervatten.",
|
|
2779
|
+
off: "Uit",
|
|
2780
|
+
"off-hint": "Gebeurtenissen, schema's en andere triggers worden genegeerd tot de agent aangezet wordt, en er wacht niets op hem. Mensen kunnen nog met de agent chatten en hem handmatig starten.",
|
|
2781
|
+
"starts-off": "De agent begon uit, zoals zijn code zegt.",
|
|
2574
2782
|
changed: "Gewijzigd door {{user}} op {{date}}.",
|
|
2575
2783
|
reason: "Notitie: {{reason}}",
|
|
2576
2784
|
waiting: "{{count}} gebeurtenissen wachten op het hervatten.",
|
|
2577
2785
|
action: {
|
|
2578
2786
|
active: "Hervatten",
|
|
2579
2787
|
paused: "Pauzeren",
|
|
2788
|
+
off: "Uitzetten",
|
|
2789
|
+
"switch-on": "Aanzetten",
|
|
2580
2790
|
stopped: "Stoppen"
|
|
2581
2791
|
},
|
|
2582
2792
|
explain: {
|
|
2583
2793
|
active: "De agent werkt weer. Elk onderwerp met wachtende gebeurtenissen krijgt één run.",
|
|
2584
2794
|
paused: "Er starten geen nieuwe runs. Gebeurtenissen wachten en lopen na het hervatten; lopend werk wordt afgemaakt en goedgekeurde voorstellen worden uitgevoerd.",
|
|
2795
|
+
off: "Gebeurtenissen, schema's, webhooks, overdrachten en vervolgruns worden genegeerd en er wacht niets; later aanzetten start er niets voor. Lopend werk wordt afgemaakt, open voorstellen kunnen nog beoordeeld worden en mensen kunnen nog met de agent chatten en hem handmatig starten.",
|
|
2796
|
+
"switch-on": "De agent neemt zijn gebeurtenissen, schema's en andere triggers weer aan. Wat er gebeurde terwijl hij uit stond, loopt niet alsnog.",
|
|
2585
2797
|
stopped: "Actieve runs worden geannuleerd, hun open voorstellen ingetrokken en wachtende gebeurtenissen vervallen. Goedgekeurde voorstellen wachten tot de agent weer werkt."
|
|
2586
2798
|
},
|
|
2587
2799
|
confirm: {
|
|
2588
2800
|
active: "Hervatten bevestigen",
|
|
2589
2801
|
paused: "Pauze bevestigen",
|
|
2802
|
+
off: "Uitzetten bevestigen",
|
|
2803
|
+
"switch-on": "Aanzetten bevestigen",
|
|
2590
2804
|
stopped: "Stop bevestigen"
|
|
2591
2805
|
},
|
|
2592
2806
|
"note-label": "Notitie voor andere beheerders (optioneel)",
|
|
@@ -2599,6 +2813,8 @@ var nl = {
|
|
|
2599
2813
|
explain: {
|
|
2600
2814
|
active: "De vaardigheid {{skill}} werkt weer. Elk onderwerp met gebeurtenissen die erop wachten krijgt één run.",
|
|
2601
2815
|
paused: "De vaardigheid {{skill}} start geen nieuwe runs: haar gebeurtenissen wachten, triage kiest haar niet meer en lopend werk wordt afgemaakt. De andere vaardigheden werken door.",
|
|
2816
|
+
off: "Triggers van de vaardigheid {{skill}} worden genegeerd en regels en triage sturen er geen werk meer heen; er wacht niets op haar. Lopend werk wordt afgemaakt. De andere vaardigheden werken door.",
|
|
2817
|
+
"switch-on": "De vaardigheid {{skill}} neemt weer werk aan. Wat er gebeurde terwijl ze uit stond, loopt niet alsnog.",
|
|
2602
2818
|
stopped: "De runs van de vaardigheid {{skill}} worden geannuleerd en haar wachtende gebeurtenissen vervallen; haar triggers worden geweigerd. De andere vaardigheden werken door."
|
|
2603
2819
|
}
|
|
2604
2820
|
},
|
|
@@ -2675,7 +2891,10 @@ var nl = {
|
|
|
2675
2891
|
"view-policy": "Resultaten voor de hele applicatie zichtbaar voor: {{policy}}."
|
|
2676
2892
|
},
|
|
2677
2893
|
groups: "Groepen: {{groups}}",
|
|
2678
|
-
"catalog-failed": "De bouwstenen van agentruns konden niet worden gelezen. Tot dat lukt, kunnen agents hier niet nu draaien en blijven waar een agent aan werkt en zijn groepen zoals ze zijn."
|
|
2894
|
+
"catalog-failed": "De bouwstenen van agentruns konden niet worden gelezen. Tot dat lukt, kunnen agents hier niet nu draaien en blijven waar een agent aan werkt en zijn groepen zoals ze zijn.",
|
|
2895
|
+
warnings: {
|
|
2896
|
+
"classification-not-permitted": "Het modelprofiel van de agent en de terugvalprofielen daarvan staan zijn dataclassificatie niet toe. De agent is opgeslagen, maar zijn modelaanroepen worden geweigerd tot een profiel de classificatie toestaat."
|
|
2897
|
+
}
|
|
2679
2898
|
},
|
|
2680
2899
|
"agent-editor": {
|
|
2681
2900
|
"new-title": "Nieuwe agent",
|
|
@@ -2690,7 +2909,14 @@ var nl = {
|
|
|
2690
2909
|
"autonomy-hint": "Alleen Uitvoeren staat wijzigingen toe, en elke wijziging vraagt nog steeds om goedkeuring.",
|
|
2691
2910
|
"required-policy": "Vereiste policy",
|
|
2692
2911
|
"required-policy-hint": "Optioneel. Alleen gebruikers die aan deze policy voldoen zien de agent.",
|
|
2912
|
+
profile: "Modelprofiel",
|
|
2913
|
+
"profile-default": "Het chatprofiel van de applicatie",
|
|
2914
|
+
"profile-hint": "Het model waarmee de agent werkt, uit de profielen die de applicatie aanbiedt voor agents die hier gemaakt worden.",
|
|
2915
|
+
classification: "Dataclassificatie",
|
|
2916
|
+
"classification-hint": "Hoe gevoelig de gegevens zijn die de modelaanroepen van de agent bevatten, in chats met hem en in zijn runs. Zijn modelprofiel of een van de terugvalprofielen daarvan moet dit toestaan; een vaardigheid kan een eigen classificatie hebben.",
|
|
2693
2917
|
"state-hint": "Pauzeer, stop en hervat de agent vanuit de lijst met agents. Opslaan verandert de stand nooit.",
|
|
2918
|
+
"switch-on-now": "Nu aanzetten",
|
|
2919
|
+
"switch-on-now-hint": "Anders begint de agent uit: zijn gebeurtenissen, schema's en andere triggers worden genegeerd tot je hem aanzet in de lijst met agents. Mensen kunnen nog met de agent chatten en hem handmatig starten.",
|
|
2694
2920
|
"tool-selectors": "Toolselectors",
|
|
2695
2921
|
"tool-selectors-hint": "Kies tools uit de catalogus of voeg een patroon toe, zoals sample-api.project.*. MCP-tools komen van de toegewezen servers.",
|
|
2696
2922
|
"add-selector": "Patroon toevoegen",
|
|
@@ -2905,7 +3131,33 @@ var nl = {
|
|
|
2905
3131
|
"client-secret-reference": "Verwijzing naar het clientgeheim",
|
|
2906
3132
|
"organization-account-label": "Gekoppeld account",
|
|
2907
3133
|
"organization-account-label-hint": "Welk account de organisatie koppelt, bijvoorbeeld \"Planning-serviceaccount\". Iedereen die de server gebruikt ziet het.",
|
|
2908
|
-
"organization-account-label-required": "Noem het account dat gekoppeld wordt."
|
|
3134
|
+
"organization-account-label-required": "Noem het account dat gekoppeld wordt.",
|
|
3135
|
+
reader: {
|
|
3136
|
+
title: "Documenten lezen",
|
|
3137
|
+
hint: "Modellen die een bestandstype niet zelf kunnen lezen, kunnen het bestand als tekst van een leesprogramma van deze server krijgen. De assistent roept de tool van het leesprogramma aan met de bestandsnaam, het mediatype en de base64-inhoud van het bestand, na de eigen leesprogramma's van de applicatie.",
|
|
3138
|
+
empty: "Deze server leest geen bestanden.",
|
|
3139
|
+
item: "Leesprogramma {{number}}",
|
|
3140
|
+
add: "Leesprogramma toevoegen",
|
|
3141
|
+
"add-hint": "Elk leesprogramma is een van de gesynchroniseerde tools van de server; een server heeft er hoogstens {{max}}.",
|
|
3142
|
+
limit: "Deze server heeft het grootste aantal leesprogramma's dat kan ({{max}}).",
|
|
3143
|
+
remove: "Verwijderen",
|
|
3144
|
+
"remove-label": "Leesprogramma {{number}} verwijderen",
|
|
3145
|
+
"media-types": "Mediatypes",
|
|
3146
|
+
"media-types-hint": "Gescheiden door komma's, hoogstens 16, zoals application/vnd.openxmlformats-officedocument.wordprocessingml.document.",
|
|
3147
|
+
"media-types-invalid": "Vul 1 tot 16 mediatypes in van de vorm type/subtype, gescheiden door komma's.",
|
|
3148
|
+
tool: "Leesprogramma",
|
|
3149
|
+
"tool-choose": "Kies een tool",
|
|
3150
|
+
"tool-hint": "Een tool die van deze server is gesynchroniseerd en een bestand omzet in tekst. Hij hoeft niet voor agents aan te staan.",
|
|
3151
|
+
"tool-duplicate": "Een ander leesprogramma van deze server gebruikt deze tool al.",
|
|
3152
|
+
"tool-sync": "Synchroniseer eerst de tools van de server en voeg dan een leesprogramma toe.",
|
|
3153
|
+
priority: "Prioriteit",
|
|
3154
|
+
"priority-hint": "Leesprogramma's met een hogere prioriteit worden eerst gevraagd, na de eigen leesprogramma's van de applicatie; bij dezelfde prioriteit op server en toolnaam.",
|
|
3155
|
+
"priority-invalid": "Vul een geheel getal in van {{min}} tot {{max}}.",
|
|
3156
|
+
"max-classification": "Hoogste classificatie",
|
|
3157
|
+
"max-classification-choose": "Kies een classificatie",
|
|
3158
|
+
"max-classification-hint": "Een gevoeliger bestand wordt nooit naar deze server gestuurd.",
|
|
3159
|
+
"leaves-application": "Een bestand dat naar deze server gaat, verlaat de applicatie. Kies een classificatie die de server mag ontvangen; voor persoonlijke verbindingen blijft de toestemming van de persoon voor gebruik op de achtergrond gelden."
|
|
3160
|
+
}
|
|
2909
3161
|
},
|
|
2910
3162
|
tools: {
|
|
2911
3163
|
title: "Tools van {{name}}",
|
|
@@ -3053,6 +3305,19 @@ var nl = {
|
|
|
3053
3305
|
"replay-progress": "{{replayed}} van hoogstens {{max}} runs herhaald, {{skipped}} overgeslagen"
|
|
3054
3306
|
},
|
|
3055
3307
|
"run-config": {
|
|
3308
|
+
files: {
|
|
3309
|
+
label: "Neemt bestanden aan",
|
|
3310
|
+
"agent-hint": "Berichten van mensen en de tools van de agent mogen hem documenten geven, zoals pdf's, binnen de limieten van de applicatie. Een nieuwe agent neemt er geen aan totdat je dit aanzet.",
|
|
3311
|
+
"skill-hint": "Runs in deze vaardigheid mogen documenten lezen als de agent ze ook aanneemt. Een nieuwe vaardigheid neemt er geen aan totdat je dit aanzet.",
|
|
3312
|
+
"limits-title": "Lagere bestandslimieten",
|
|
3313
|
+
"agent-limits-hint": "Laat een veld leeg om de limiet van de applicatie te houden. Een waarde kan die alleen verlagen.",
|
|
3314
|
+
"skill-limits-hint": "Laat een veld leeg om de limiet van de agent te houden. Een waarde kan die van de applicatie alleen verlagen.",
|
|
3315
|
+
"max-file-megabytes": "Grootste bestand (MB)",
|
|
3316
|
+
"max-file-pages": "Meeste pagina's per bestand",
|
|
3317
|
+
"max-files-per-turn": "Meeste bestanden per beurt",
|
|
3318
|
+
"max-file-tokens-per-turn": "Meeste bestandstokens per beurt",
|
|
3319
|
+
"application-limit": "De applicatie staat hooguit {{value}} toe."
|
|
3320
|
+
},
|
|
3056
3321
|
back: "Terug naar agents",
|
|
3057
3322
|
title: "Runconfiguratie van {{name}}",
|
|
3058
3323
|
description: "Vaardigheden en triggers die het beheer zonder deploy toevoegt, gemaakt van de bouwstenen die de applicatie registreert. Nieuwe vaardigheden en triggers starten in schaduw, waar niemand een melding krijgt en niets wordt uitgevoerd.",
|
|
@@ -3091,7 +3356,9 @@ var nl = {
|
|
|
3091
3356
|
taken: "Deze id is al in gebruik.",
|
|
3092
3357
|
"name-letters": "Gebruik minstens één letter in de naam.",
|
|
3093
3358
|
"instructions-for-model": "Zonder vaste regels werkt het model met deze instructies; beschrijf wat het moet doen.",
|
|
3094
|
-
"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."
|
|
3359
|
+
"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.",
|
|
3360
|
+
"megabytes-up-to": "Vul een grootte in boven 0 en hooguit {{max}} MB.",
|
|
3361
|
+
"megabytes-positive": "Vul een grootte in boven 0 MB."
|
|
3095
3362
|
},
|
|
3096
3363
|
settings: {
|
|
3097
3364
|
title: "Runinstellingen",
|
|
@@ -3192,6 +3459,8 @@ var nl = {
|
|
|
3192
3459
|
"apply-shadow": "Eerst in schaduw beoordelen naast de live versie",
|
|
3193
3460
|
"apply-live": "Direct live zetten",
|
|
3194
3461
|
"starts-in-shadow": "Een nieuwe vaardigheid start in schaduw.",
|
|
3462
|
+
"switch-on-now": "Nu aanzetten",
|
|
3463
|
+
"switch-on-now-hint": "Anders begint de vaardigheid uit: triggers die haar noemen worden genegeerd en regels en triage sturen er niets heen tot je haar aanzet in de lijst met agents.",
|
|
3195
3464
|
versions: "Versies",
|
|
3196
3465
|
"remove-explain": "Haar versies blijven bewaard. Een trigger die haar start moet eerst weg.",
|
|
3197
3466
|
what: {
|
|
@@ -3203,6 +3472,22 @@ var nl = {
|
|
|
3203
3472
|
"rules-hint": "De regels beslissen zonder model; een twijfelgeval gaat naar het model met de instructies hieronder.",
|
|
3204
3473
|
"model-hint": "Het model beslist met de instructies hieronder en de gekozen tools.",
|
|
3205
3474
|
"profile-hint": "Het modelprofiel dat hij gebruikt; standaard dat van de agent.",
|
|
3475
|
+
classification: {
|
|
3476
|
+
label: "Dataclassificatie",
|
|
3477
|
+
agent: "De classificatie van de agent",
|
|
3478
|
+
hint: "Hoe gevoelig de gegevens zijn die de vaardigheid naar haar model stuurt. Haar modelprofiel moet dit toestaan; anders worden haar modelaanroepen geweigerd.",
|
|
3479
|
+
"public": "Openbaar",
|
|
3480
|
+
"public-hint": "Gegevens die iedereen mag zien.",
|
|
3481
|
+
internal: "Intern",
|
|
3482
|
+
"internal-hint": "Gegevens van de organisatie; de standaard.",
|
|
3483
|
+
confidential: "Vertrouwelijk",
|
|
3484
|
+
"confidential-hint": "Gegevens die alleen sommige mensen mogen zien, zoals persoonlijke of financiële gegevens.",
|
|
3485
|
+
restricted: "Strikt vertrouwelijk",
|
|
3486
|
+
"restricted-hint": "De gevoeligste gegevens, waarvoor alleen speciaal goedgekeurde modellen gebruikt mogen worden."
|
|
3487
|
+
},
|
|
3488
|
+
warnings: {
|
|
3489
|
+
"classification-not-permitted": "Het modelprofiel van de vaardigheid en de terugvalprofielen daarvan staan haar dataclassificatie niet toe. De vaardigheid is opgeslagen, maar haar modelaanroepen worden geweigerd tot een profiel de classificatie toestaat."
|
|
3490
|
+
},
|
|
3206
3491
|
"apply-shadow-hint": "De nieuwe versie draait naast de live versie; vergelijk ze en promoveer of verwerp hem daarna.",
|
|
3207
3492
|
"apply-live-hint": "Voor een kleine correctie: de nieuwe versie vervangt de live versie meteen.",
|
|
3208
3493
|
"tools-pattern": "Alle tools die passen: nu {{count}}.",
|
|
@@ -4080,6 +4365,11 @@ class NhAssistantSseParser {
|
|
|
4080
4365
|
}
|
|
4081
4366
|
}
|
|
4082
4367
|
|
|
4368
|
+
/**
|
|
4369
|
+
* The request header every upload carries. A browser cannot send it with a cross-site form, so the
|
|
4370
|
+
* upload endpoint keeps the posture of the JSON endpoints.
|
|
4371
|
+
*/
|
|
4372
|
+
const NH_ASSISTANT_UPLOAD_HEADER = 'X-NH-Assistant-Upload';
|
|
4083
4373
|
/** A failed JSON request of the assistant API. Carries a stable code, never response text. */
|
|
4084
4374
|
class NhAssistantApiError extends Error {
|
|
4085
4375
|
constructor(status, code, messageKey,
|
|
@@ -4126,6 +4416,8 @@ function nhAssistantCodeForStatus(status) {
|
|
|
4126
4416
|
}
|
|
4127
4417
|
const safeCode = /^[a-z0-9][a-z0-9.-]{0,79}$/;
|
|
4128
4418
|
const safeField = /^[a-zA-Z][a-zA-Z0-9.]{0,63}$/;
|
|
4419
|
+
/** A field of one item of a list, such as `readers[0].toolName`. */
|
|
4420
|
+
const safeItemField = /^[a-zA-Z][a-zA-Z0-9]{0,31}\[\d{1,4}\](\.[a-zA-Z][a-zA-Z0-9]{0,31})?$/;
|
|
4129
4421
|
/**
|
|
4130
4422
|
* Whether a value the server or a callback page sent is a code the library may show: lowercase
|
|
4131
4423
|
* letters, digits, dots and dashes, at most 80 characters. Every reader of untrusted codes uses this
|
|
@@ -4142,6 +4434,7 @@ class NhAssistantTransport {
|
|
|
4142
4434
|
constructor() {
|
|
4143
4435
|
this.config = inject(NH_ASSISTANT_CONFIG);
|
|
4144
4436
|
this.fetchFn = inject(NH_ASSISTANT_FETCH);
|
|
4437
|
+
this.uploadFn = inject(NH_ASSISTANT_UPLOAD);
|
|
4145
4438
|
this.translate = inject(TranslateService, { optional: true });
|
|
4146
4439
|
this.injector = inject(EnvironmentInjector);
|
|
4147
4440
|
}
|
|
@@ -4157,6 +4450,36 @@ class NhAssistantTransport {
|
|
|
4157
4450
|
const init = await this.createInit(method, accept, body, signal);
|
|
4158
4451
|
return this.fetchFn(this.url(path), init);
|
|
4159
4452
|
}
|
|
4453
|
+
/**
|
|
4454
|
+
* Uploads `form` with progress and resolves with the parsed JSON answer. Rejects with
|
|
4455
|
+
* `NhAssistantApiError`, never with raw text: the code of a refused upload's error body, the
|
|
4456
|
+
* generic status mapping without one and `assistant-network` when it did not reach the server.
|
|
4457
|
+
* An aborted upload rejects with an `AbortError`.
|
|
4458
|
+
*/
|
|
4459
|
+
async upload(path, form, onProgress, signal) {
|
|
4460
|
+
const headers = await this.headers('application/json');
|
|
4461
|
+
headers[NH_ASSISTANT_UPLOAD_HEADER] = '1';
|
|
4462
|
+
let response;
|
|
4463
|
+
try {
|
|
4464
|
+
response = await this.uploadFn({ url: this.url(path), headers, body: form, signal, onProgress });
|
|
4465
|
+
}
|
|
4466
|
+
catch (error) {
|
|
4467
|
+
if (signal.aborted) {
|
|
4468
|
+
throw error;
|
|
4469
|
+
}
|
|
4470
|
+
throw createApiError(0, nhAssistantCodeForStatus(0));
|
|
4471
|
+
}
|
|
4472
|
+
if (!response.ok) {
|
|
4473
|
+
const failure = await this.failure(response, {});
|
|
4474
|
+
throw createApiError(response.status, failure.code, failure.fieldErrors);
|
|
4475
|
+
}
|
|
4476
|
+
try {
|
|
4477
|
+
return JSON.parse(await response.text());
|
|
4478
|
+
}
|
|
4479
|
+
catch {
|
|
4480
|
+
throw createApiError(response.status, NhAssistantClientErrorCodes.invalidResponse);
|
|
4481
|
+
}
|
|
4482
|
+
}
|
|
4160
4483
|
/**
|
|
4161
4484
|
* Downloads a file of the assistant API. Rejects with `NhAssistantApiError`, never with raw text:
|
|
4162
4485
|
* the code and field errors of a refused request's error body (whatever its content type), the
|
|
@@ -4203,7 +4526,7 @@ class NhAssistantTransport {
|
|
|
4203
4526
|
if (errors && typeof errors === 'object' && !Array.isArray(errors)) {
|
|
4204
4527
|
for (const [field, values] of Object.entries(errors)) {
|
|
4205
4528
|
const safe = Array.isArray(values) ? values.filter(nhAssistantIsSafeCode) : [];
|
|
4206
|
-
if (safeField.test(field) && safe.length > 0) {
|
|
4529
|
+
if ((safeField.test(field) || safeItemField.test(field)) && safe.length > 0) {
|
|
4207
4530
|
fieldErrors[field] = safe;
|
|
4208
4531
|
}
|
|
4209
4532
|
}
|
|
@@ -4246,16 +4569,7 @@ class NhAssistantTransport {
|
|
|
4246
4569
|
}
|
|
4247
4570
|
}
|
|
4248
4571
|
async createInit(method, accept, body, signal) {
|
|
4249
|
-
const headers = await
|
|
4250
|
-
headers['Accept'] = accept;
|
|
4251
|
-
const token = await runInInjectionContext(this.injector, () => this.config.getAccessToken());
|
|
4252
|
-
if (token) {
|
|
4253
|
-
headers['Authorization'] = `Bearer ${token}`;
|
|
4254
|
-
}
|
|
4255
|
-
const language = this.translate?.getCurrentLang();
|
|
4256
|
-
if (language) {
|
|
4257
|
-
headers['Accept-Language'] = language;
|
|
4258
|
-
}
|
|
4572
|
+
const headers = await this.headers(accept);
|
|
4259
4573
|
if (body !== undefined) {
|
|
4260
4574
|
headers['Content-Type'] = 'application/json';
|
|
4261
4575
|
}
|
|
@@ -4267,6 +4581,20 @@ class NhAssistantTransport {
|
|
|
4267
4581
|
cache: 'no-store'
|
|
4268
4582
|
};
|
|
4269
4583
|
}
|
|
4584
|
+
/** The host's extra headers, `Accept`, the bearer token and the current language. */
|
|
4585
|
+
async headers(accept) {
|
|
4586
|
+
const headers = await nhAssistantRequestHeaders(this.config, this.injector);
|
|
4587
|
+
headers['Accept'] = accept;
|
|
4588
|
+
const token = await runInInjectionContext(this.injector, () => this.config.getAccessToken());
|
|
4589
|
+
if (token) {
|
|
4590
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
4591
|
+
}
|
|
4592
|
+
const language = this.translate?.getCurrentLang();
|
|
4593
|
+
if (language) {
|
|
4594
|
+
headers['Accept-Language'] = language;
|
|
4595
|
+
}
|
|
4596
|
+
return headers;
|
|
4597
|
+
}
|
|
4270
4598
|
url(path) {
|
|
4271
4599
|
const base = this.config.apiBaseUrl.replace(/\/+$/, '');
|
|
4272
4600
|
return `${base}/${path}`;
|
|
@@ -4300,6 +4628,8 @@ async function nhAssistantRequestHeaders(config, injector) {
|
|
|
4300
4628
|
}
|
|
4301
4629
|
|
|
4302
4630
|
const knownEventTypes = new Set(NH_ASSISTANT_SSE_EVENT_TYPES);
|
|
4631
|
+
/** The outcomes of a trigger receipt with which the next run takes a waiting message. */
|
|
4632
|
+
const queuedOutcomes = new Set(['started', 'queued', 'coalesced', 'superseded', 'waiting', 'deferred']);
|
|
4303
4633
|
/**
|
|
4304
4634
|
* Client for the assistant HTTP API. JSON endpoints return cold observables that abort
|
|
4305
4635
|
* their request on unsubscribe. Streaming endpoints emit every contract event and complete
|
|
@@ -4330,11 +4660,33 @@ class NhAssistantApiService {
|
|
|
4330
4660
|
return this.transport.json('DELETE', `conversations/${encodeURIComponent(conversationId)}`);
|
|
4331
4661
|
}
|
|
4332
4662
|
sendMessage(conversationId, request) {
|
|
4333
|
-
return this.requestStream(`conversations/${encodeURIComponent(conversationId)}/messages`, request);
|
|
4663
|
+
return this.requestStream(`conversations/${encodeURIComponent(conversationId)}/messages`, request, true);
|
|
4664
|
+
}
|
|
4665
|
+
/**
|
|
4666
|
+
* Uploads one file to a conversation as `multipart/form-data` (`POST conversations/{id}/files`):
|
|
4667
|
+
* emits the progress while the file is sent and then the stored file, whose reference a message
|
|
4668
|
+
* carries in `files`. Fails with `NhAssistantApiError`; unsubscribing aborts the upload. The
|
|
4669
|
+
* media type of `file` is the one the upload declares.
|
|
4670
|
+
*/
|
|
4671
|
+
uploadFile(conversationId, file, name) {
|
|
4672
|
+
return new Observable(subscriber => {
|
|
4673
|
+
const abort = new AbortController();
|
|
4674
|
+
const form = new FormData();
|
|
4675
|
+
form.append('file', file, name);
|
|
4676
|
+
this.transport.upload(`conversations/${encodeURIComponent(conversationId)}/files`, form, (loaded, total) => subscriber.next({ type: 'progress', loaded, total }), abort.signal).then(reference => {
|
|
4677
|
+
subscriber.next({ type: 'uploaded', file: reference });
|
|
4678
|
+
subscriber.complete();
|
|
4679
|
+
}, error => {
|
|
4680
|
+
if (!abort.signal.aborted) {
|
|
4681
|
+
subscriber.error(error);
|
|
4682
|
+
}
|
|
4683
|
+
});
|
|
4684
|
+
return () => abort.abort();
|
|
4685
|
+
});
|
|
4334
4686
|
}
|
|
4335
4687
|
decideApproval(conversationId, approvalId, request) {
|
|
4336
4688
|
const path = `conversations/${encodeURIComponent(conversationId)}/approvals/${encodeURIComponent(approvalId)}/decide`;
|
|
4337
|
-
return this.requestStream(path, request);
|
|
4689
|
+
return this.requestStream(path, request, false);
|
|
4338
4690
|
}
|
|
4339
4691
|
cancel(conversationId) {
|
|
4340
4692
|
return this.transport.json('POST', `conversations/${encodeURIComponent(conversationId)}/cancel`);
|
|
@@ -4416,14 +4768,14 @@ class NhAssistantApiService {
|
|
|
4416
4768
|
completeMcpAuthorization(request) {
|
|
4417
4769
|
return this.transport.json('POST', 'mcp-connections/complete', request);
|
|
4418
4770
|
}
|
|
4419
|
-
requestStream(path, body) {
|
|
4771
|
+
requestStream(path, body, queuedReceipt) {
|
|
4420
4772
|
return new Observable(subscriber => {
|
|
4421
4773
|
const abort = new AbortController();
|
|
4422
|
-
void this.executeStream(path, body, abort.signal, subscriber);
|
|
4774
|
+
void this.executeStream(path, body, queuedReceipt, abort.signal, subscriber);
|
|
4423
4775
|
return () => abort.abort();
|
|
4424
4776
|
});
|
|
4425
4777
|
}
|
|
4426
|
-
async executeStream(path, body, signal, subscriber) {
|
|
4778
|
+
async executeStream(path, body, queuedReceipt, signal, subscriber) {
|
|
4427
4779
|
let response;
|
|
4428
4780
|
try {
|
|
4429
4781
|
response = await this.transport.fetch('POST', path, 'text/event-stream', body, signal);
|
|
@@ -4436,6 +4788,10 @@ class NhAssistantApiService {
|
|
|
4436
4788
|
this.emitClientError(subscriber, signal, await this.transport.failureCode(response));
|
|
4437
4789
|
return;
|
|
4438
4790
|
}
|
|
4791
|
+
if (queuedReceipt && response.status === 202) {
|
|
4792
|
+
await this.readQueuedReceipt(response, signal, subscriber);
|
|
4793
|
+
return;
|
|
4794
|
+
}
|
|
4439
4795
|
const contentType = response.headers.get('content-type') ?? '';
|
|
4440
4796
|
if (!response.body || !contentType.toLowerCase().startsWith('text/event-stream')) {
|
|
4441
4797
|
this.emitClientError(subscriber, signal, NhAssistantClientErrorCodes.invalidResponse);
|
|
@@ -4443,6 +4799,33 @@ class NhAssistantApiService {
|
|
|
4443
4799
|
}
|
|
4444
4800
|
await this.readStream(response.body, signal, subscriber);
|
|
4445
4801
|
}
|
|
4802
|
+
/**
|
|
4803
|
+
* A message to an agent's thread whose run works waits for the next run: the server answers `202`
|
|
4804
|
+
* with the trigger receipt instead of a stream. A receipt the run takes the message with becomes
|
|
4805
|
+
* `message.queued`; one that refused it becomes an `error` with its code.
|
|
4806
|
+
*/
|
|
4807
|
+
async readQueuedReceipt(response, signal, subscriber) {
|
|
4808
|
+
let receipt;
|
|
4809
|
+
try {
|
|
4810
|
+
receipt = JSON.parse(await response.text());
|
|
4811
|
+
}
|
|
4812
|
+
catch {
|
|
4813
|
+
receipt = null;
|
|
4814
|
+
}
|
|
4815
|
+
if (signal.aborted || subscriber.closed) {
|
|
4816
|
+
return;
|
|
4817
|
+
}
|
|
4818
|
+
if (!receipt || typeof receipt.outcome !== 'string') {
|
|
4819
|
+
this.emitClientError(subscriber, signal, NhAssistantClientErrorCodes.invalidResponse);
|
|
4820
|
+
return;
|
|
4821
|
+
}
|
|
4822
|
+
if (!queuedOutcomes.has(receipt.outcome)) {
|
|
4823
|
+
this.emitClientError(subscriber, signal, nhAssistantIsSafeCode(receipt.reasonCode) ? receipt.reasonCode : NhAssistantClientErrorCodes.conversationBusy);
|
|
4824
|
+
return;
|
|
4825
|
+
}
|
|
4826
|
+
subscriber.next({ type: 'message.queued', data: receipt });
|
|
4827
|
+
subscriber.complete();
|
|
4828
|
+
}
|
|
4446
4829
|
async readStream(body, signal, subscriber) {
|
|
4447
4830
|
const reader = body.getReader();
|
|
4448
4831
|
const decoder = new TextDecoder();
|
|
@@ -5860,6 +6243,89 @@ function sameKey(current, expected) {
|
|
|
5860
6243
|
return bytes.length === expected.length && bytes.every((byte, index) => byte === expected[index]);
|
|
5861
6244
|
}
|
|
5862
6245
|
|
|
6246
|
+
/** The longest file name a file reference takes. */
|
|
6247
|
+
const NH_ASSISTANT_MAX_FILE_NAME_LENGTH = 256;
|
|
6248
|
+
const unknownMediaType = 'application/octet-stream';
|
|
6249
|
+
/** The media types the platform names, with the extensions a file picker offers and a short label. */
|
|
6250
|
+
const knownTypes = {
|
|
6251
|
+
'application/pdf': { extensions: ['.pdf'], label: 'PDF' },
|
|
6252
|
+
'image/png': { extensions: ['.png'], label: 'PNG' },
|
|
6253
|
+
'image/jpeg': { extensions: ['.jpg', '.jpeg'], label: 'JPEG' },
|
|
6254
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': { extensions: ['.docx'], label: 'Word' },
|
|
6255
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': { extensions: ['.xlsx'], label: 'Excel' }
|
|
6256
|
+
};
|
|
6257
|
+
/**
|
|
6258
|
+
* The media type of a chosen file, lowercase and without parameters. A browser that does not know
|
|
6259
|
+
* the type, as happens for Office documents, gives none; the extension of a known type then names
|
|
6260
|
+
* it. Otherwise `application/octet-stream`.
|
|
6261
|
+
*/
|
|
6262
|
+
function nhAssistantMediaTypeOf(file) {
|
|
6263
|
+
const declared = file.type.split(';')[0].trim().toLowerCase();
|
|
6264
|
+
if (declared.length > 0 && declared !== unknownMediaType) {
|
|
6265
|
+
return declared;
|
|
6266
|
+
}
|
|
6267
|
+
const name = file.name.toLowerCase();
|
|
6268
|
+
for (const [mediaType, known] of Object.entries(knownTypes)) {
|
|
6269
|
+
if (known.extensions.some(extension => name.endsWith(extension))) {
|
|
6270
|
+
return mediaType;
|
|
6271
|
+
}
|
|
6272
|
+
}
|
|
6273
|
+
return unknownMediaType;
|
|
6274
|
+
}
|
|
6275
|
+
/**
|
|
6276
|
+
* The failure code of a file before it is uploaded, or `null` when it may be: the most files of one
|
|
6277
|
+
* message, an accepted media type, content and a usable name, and the largest size. `attached` counts
|
|
6278
|
+
* the files the message already carries.
|
|
6279
|
+
*/
|
|
6280
|
+
function nhAssistantFileProblem(file, mediaType, uploads, attached) {
|
|
6281
|
+
if (attached >= uploads.maxFiles) {
|
|
6282
|
+
return 'assistant-file-count-limit';
|
|
6283
|
+
}
|
|
6284
|
+
if (!uploads.acceptedMediaTypes.includes(mediaType)) {
|
|
6285
|
+
return 'assistant-file-type-not-accepted';
|
|
6286
|
+
}
|
|
6287
|
+
if (file.size === 0 || file.name.trim().length === 0 || file.name.length > NH_ASSISTANT_MAX_FILE_NAME_LENGTH) {
|
|
6288
|
+
return 'assistant-file-upload-invalid';
|
|
6289
|
+
}
|
|
6290
|
+
if (file.size > uploads.maxFileBytes) {
|
|
6291
|
+
return 'assistant-file-too-large';
|
|
6292
|
+
}
|
|
6293
|
+
return null;
|
|
6294
|
+
}
|
|
6295
|
+
/** The `accept` attribute of a file input for these media types: each type and its known extensions. */
|
|
6296
|
+
function nhAssistantAcceptAttribute(mediaTypes) {
|
|
6297
|
+
return mediaTypes
|
|
6298
|
+
.flatMap(mediaType => [mediaType, ...(knownTypes[mediaType]?.extensions ?? [])])
|
|
6299
|
+
.join(',');
|
|
6300
|
+
}
|
|
6301
|
+
/** A short name of a media type, such as `PDF` or `Word`; otherwise its subtype in capitals. */
|
|
6302
|
+
function nhAssistantFileTypeLabel(mediaType) {
|
|
6303
|
+
const known = knownTypes[mediaType];
|
|
6304
|
+
if (known) {
|
|
6305
|
+
return known.label;
|
|
6306
|
+
}
|
|
6307
|
+
const subtype = mediaType.split('/')[1] ?? mediaType;
|
|
6308
|
+
return subtype.split(/[.+]/).pop().toUpperCase();
|
|
6309
|
+
}
|
|
6310
|
+
/** A size in bytes as people read it, such as `820 KB` or `1.4 MB`, in the given language. */
|
|
6311
|
+
function nhAssistantFormatFileSize(bytes, language) {
|
|
6312
|
+
const units = ['B', 'KB', 'MB', 'GB'];
|
|
6313
|
+
let value = Math.max(0, bytes);
|
|
6314
|
+
let unit = 0;
|
|
6315
|
+
while (value >= 1024 && unit < units.length - 1) {
|
|
6316
|
+
value /= 1024;
|
|
6317
|
+
unit++;
|
|
6318
|
+
}
|
|
6319
|
+
let format;
|
|
6320
|
+
try {
|
|
6321
|
+
format = new Intl.NumberFormat(language || undefined, { maximumFractionDigits: unit === 0 || value >= 10 ? 0 : 1 });
|
|
6322
|
+
}
|
|
6323
|
+
catch {
|
|
6324
|
+
format = new Intl.NumberFormat(undefined, { maximumFractionDigits: unit === 0 || value >= 10 ? 0 : 1 });
|
|
6325
|
+
}
|
|
6326
|
+
return `${format.format(value)} ${units[unit]}`;
|
|
6327
|
+
}
|
|
6328
|
+
|
|
5863
6329
|
/**
|
|
5864
6330
|
* Applies one assistant event to a conversation and returns the new conversation.
|
|
5865
6331
|
* The input is never mutated, so the result can be stored in a signal directly.
|
|
@@ -6291,6 +6757,11 @@ function validId(value) {
|
|
|
6291
6757
|
|
|
6292
6758
|
/** Notice code for a completed turn that produced no answer text and no server code. */
|
|
6293
6759
|
const NH_ASSISTANT_NO_ANSWER_NOTICE_CODE = 'assistant-no-answer';
|
|
6760
|
+
/**
|
|
6761
|
+
* Notice code for a message that waits for the next run of the agent working in its thread: the
|
|
6762
|
+
* server answered `202` with the trigger receipt, and the run adds the message, with its files.
|
|
6763
|
+
*/
|
|
6764
|
+
const NH_ASSISTANT_MESSAGE_QUEUED_NOTICE_CODE = 'assistant-message-queued';
|
|
6294
6765
|
const fourEyesRequiredCode = 'assistant-four-eyes-required';
|
|
6295
6766
|
/** True when a conversation has messages the caller has not read. A running turn is not unread yet. */
|
|
6296
6767
|
function nhAssistantIsUnread(summary) {
|
|
@@ -6355,7 +6826,12 @@ class NhAssistantStore {
|
|
|
6355
6826
|
this.submittingState = signal(false, ...(ngDevMode ? [{ debugName: "submittingState" }] : []));
|
|
6356
6827
|
/** Agent proposals the current user rejected in this session and may give feedback on. */
|
|
6357
6828
|
this.feedbackApprovalsState = signal(new Set(), ...(ngDevMode ? [{ debugName: "feedbackApprovalsState" }] : []));
|
|
6829
|
+
/** The attachments of the next message per conversation; `''` for a new conversation. */
|
|
6830
|
+
this.attachmentsState = signal(new Map(), ...(ngDevMode ? [{ debugName: "attachmentsState" }] : []));
|
|
6358
6831
|
this.destroyed = false;
|
|
6832
|
+
this.uploadSubscriptions = new Map();
|
|
6833
|
+
/** The conversation an attachment of a new conversation creates, once for every file chosen meanwhile. */
|
|
6834
|
+
this.attachmentConversation = null;
|
|
6359
6835
|
this.streams = new Map();
|
|
6360
6836
|
this.cancelTimers = new Map();
|
|
6361
6837
|
/** Pending reloads of agent threads after live agent-work updates, per conversation. */
|
|
@@ -6434,6 +6910,31 @@ class NhAssistantStore {
|
|
|
6434
6910
|
&& ['running', 'waiting-for-approval'].includes(conversation?.status ?? '')
|
|
6435
6911
|
&& (this.isOwner() || ownsTurn);
|
|
6436
6912
|
}, ...(ngDevMode ? [{ debugName: "canSteer" }] : []));
|
|
6913
|
+
/**
|
|
6914
|
+
* What the person may attach to the next message: the uploads of the open conversation's agent, or
|
|
6915
|
+
* of the selected agent for a new conversation. `null` when nothing can be attached: the server
|
|
6916
|
+
* offers no uploads for the agent, the conversation is archived, or the person only reads the
|
|
6917
|
+
* agent's thread.
|
|
6918
|
+
*/
|
|
6919
|
+
this.uploads = computed(() => {
|
|
6920
|
+
const conversation = this.activeConversation();
|
|
6921
|
+
const thread = conversation?.thread;
|
|
6922
|
+
if (!this.enabled() || conversation?.status === 'archived'
|
|
6923
|
+
|| (thread && thread.role !== 'participant' && thread.role !== 'approver')) {
|
|
6924
|
+
return null;
|
|
6925
|
+
}
|
|
6926
|
+
const agentId = conversation?.agentId ?? this.selectedAgentIdState();
|
|
6927
|
+
const uploads = this.agents().find(agent => agent.id === agentId)?.uploads ?? null;
|
|
6928
|
+
return uploads && uploads.acceptedMediaTypes.length > 0 && uploads.maxFiles > 0 ? uploads : null;
|
|
6929
|
+
}, ...(ngDevMode ? [{ debugName: "uploads" }] : []));
|
|
6930
|
+
/** The attachments of the next message of the open conversation, in the order they were chosen. */
|
|
6931
|
+
this.attachments = computed(() => this.attachmentsState().get(this.activeIdState() ?? newConversationKey) ?? [], ...(ngDevMode ? [{ debugName: "attachments" }] : []));
|
|
6932
|
+
/** The stored files of the open conversation's next message, for `submit`. */
|
|
6933
|
+
this.attachedFiles = computed(() => this.attachments()
|
|
6934
|
+
.filter(attachment => attachment.state === 'uploaded' && attachment.file !== null)
|
|
6935
|
+
.map(attachment => attachment.file), ...(ngDevMode ? [{ debugName: "attachedFiles" }] : []));
|
|
6936
|
+
/** True while a file of the next message still uploads; the message waits for it. */
|
|
6937
|
+
this.uploading = computed(() => this.attachments().some(attachment => attachment.state === 'uploading'), ...(ngDevMode ? [{ debugName: "uploading" }] : []));
|
|
6437
6938
|
/** The owner and participants of the open conversation; empty while it is not shared. */
|
|
6438
6939
|
this.members = computed(() => this.activeConversation()?.members ?? [], ...(ngDevMode ? [{ debugName: "members" }] : []));
|
|
6439
6940
|
/** True when the current user owns the open conversation (or it is new). */
|
|
@@ -6534,6 +7035,9 @@ class NhAssistantStore {
|
|
|
6534
7035
|
this.liveSubscription.unsubscribe();
|
|
6535
7036
|
this.stopAllTurns();
|
|
6536
7037
|
this.clearAgentWorkTimers();
|
|
7038
|
+
for (const upload of this.uploadSubscriptions.values()) {
|
|
7039
|
+
upload.unsubscribe();
|
|
7040
|
+
}
|
|
6537
7041
|
clearTimeout(this.refreshTimer);
|
|
6538
7042
|
});
|
|
6539
7043
|
effect(() => {
|
|
@@ -6722,26 +7226,28 @@ class NhAssistantStore {
|
|
|
6722
7226
|
/**
|
|
6723
7227
|
* Sends a user message. Creates a conversation with the selected agent when none is open,
|
|
6724
7228
|
* shows the message optimistically and applies the streamed events. Other conversations keep
|
|
6725
|
-
* their own turns. Resolves `false` when the message was not accepted.
|
|
7229
|
+
* their own turns. Resolves `false` when the message was not accepted. `files` are uploaded
|
|
7230
|
+
* attachments, such as `attachedFiles()`; once the message is accepted, the attachments of the
|
|
7231
|
+
* conversation leave the composer.
|
|
6726
7232
|
*/
|
|
6727
|
-
async send(text) {
|
|
7233
|
+
async send(text, files = []) {
|
|
6728
7234
|
// Browsers only ask for notification permission during a user action such as this one.
|
|
6729
7235
|
void this.push.onUserGesture().catch(() => undefined);
|
|
6730
|
-
return this.sendMessage(text);
|
|
7236
|
+
return this.sendMessage(text, undefined, files);
|
|
6731
7237
|
}
|
|
6732
7238
|
/**
|
|
6733
7239
|
* Sends immediately when the conversation accepts a message, otherwise appends to the
|
|
6734
7240
|
* conversation's FIFO queue. A shown error alone does not hold a message back while nothing is
|
|
6735
7241
|
* queued: the person asks again, for example after a failed turn. Queued messages still wait
|
|
6736
|
-
* for an explicit `resumeQueue` after Stop or a failure.
|
|
7242
|
+
* for an explicit `resumeQueue` after Stop or a failure. A queued message keeps its `files`.
|
|
6737
7243
|
*/
|
|
6738
|
-
async submit(text) {
|
|
7244
|
+
async submit(text, files = []) {
|
|
6739
7245
|
const stopped = this.pausedQueuesState().has(this.activeIdState() ?? newConversationKey);
|
|
6740
7246
|
if (this.canSend() && this.queuedMessages().length === 0 && !stopped && !this.submittingState()) {
|
|
6741
7247
|
const revision = this.accountRevision;
|
|
6742
7248
|
this.submittingState.set(true);
|
|
6743
7249
|
try {
|
|
6744
|
-
return await this.send(text);
|
|
7250
|
+
return await this.send(text, files);
|
|
6745
7251
|
}
|
|
6746
7252
|
finally {
|
|
6747
7253
|
if (revision === this.accountRevision && !this.destroyed) {
|
|
@@ -6756,8 +7262,15 @@ class NhAssistantStore {
|
|
|
6756
7262
|
}
|
|
6757
7263
|
void this.push.onUserGesture().catch(() => undefined);
|
|
6758
7264
|
const revision = this.accountRevision;
|
|
6759
|
-
const item = {
|
|
7265
|
+
const item = {
|
|
7266
|
+
id: createClientMessageId(),
|
|
7267
|
+
conversationId,
|
|
7268
|
+
text: trimmed,
|
|
7269
|
+
state: 'preparing',
|
|
7270
|
+
...(files.length > 0 ? { files: [...files] } : {})
|
|
7271
|
+
};
|
|
6760
7272
|
this.queuedState.update(items => [...items, item]);
|
|
7273
|
+
this.releaseAttachments(conversationId, files);
|
|
6761
7274
|
const clientContext = await this.contextForMessage();
|
|
6762
7275
|
if (revision !== this.accountRevision || this.destroyed) {
|
|
6763
7276
|
return false;
|
|
@@ -6820,7 +7333,7 @@ class NhAssistantStore {
|
|
|
6820
7333
|
const revision = this.accountRevision;
|
|
6821
7334
|
this.queueDispatchState.set(item.id);
|
|
6822
7335
|
this.queuedState.update(items => items.map(current => current.id === item.id ? { ...current, state: 'sending' } : current));
|
|
6823
|
-
const accepted = await this.sendMessage(item.text, item);
|
|
7336
|
+
const accepted = await this.sendMessage(item.text, item, item.files ?? []);
|
|
6824
7337
|
if (revision !== this.accountRevision || this.destroyed) {
|
|
6825
7338
|
return;
|
|
6826
7339
|
}
|
|
@@ -6831,7 +7344,7 @@ class NhAssistantStore {
|
|
|
6831
7344
|
}
|
|
6832
7345
|
this.queueDispatchState.set(null);
|
|
6833
7346
|
}
|
|
6834
|
-
async sendMessage(text, queued) {
|
|
7347
|
+
async sendMessage(text, queued, files) {
|
|
6835
7348
|
const trimmed = text.trim();
|
|
6836
7349
|
const accountRevision = this.accountRevision;
|
|
6837
7350
|
await this.initialize();
|
|
@@ -6871,6 +7384,10 @@ class NhAssistantStore {
|
|
|
6871
7384
|
const conversationId = conversation.id;
|
|
6872
7385
|
const clientMessageId = createClientMessageId();
|
|
6873
7386
|
const previousStatus = conversation.status;
|
|
7387
|
+
const parts = [
|
|
7388
|
+
{ type: 'text', text: trimmed },
|
|
7389
|
+
...files.map(file => ({ type: 'file', name: file.name, mediaType: file.mediaType, size: file.size }))
|
|
7390
|
+
];
|
|
6874
7391
|
this.patchSession(conversationId, session => ({
|
|
6875
7392
|
...session,
|
|
6876
7393
|
error: null,
|
|
@@ -6884,7 +7401,7 @@ class NhAssistantStore {
|
|
|
6884
7401
|
id: clientMessageId,
|
|
6885
7402
|
role: 'user',
|
|
6886
7403
|
createdAt: new Date().toISOString(),
|
|
6887
|
-
parts
|
|
7404
|
+
parts,
|
|
6888
7405
|
authorActorId: session.conversation.currentActorId ?? null
|
|
6889
7406
|
}
|
|
6890
7407
|
]
|
|
@@ -6895,12 +7412,24 @@ class NhAssistantStore {
|
|
|
6895
7412
|
this.pendingSendResolves.set(conversationId, resolve);
|
|
6896
7413
|
let started = false;
|
|
6897
7414
|
let failedAfterStart = false;
|
|
7415
|
+
let waitsForRun = false;
|
|
6898
7416
|
this.runStream(conversationId, this.api.sendMessage(conversationId, {
|
|
6899
7417
|
text: trimmed,
|
|
6900
7418
|
clientMessageId,
|
|
6901
|
-
...(clientContext === undefined ? {} : { clientContext })
|
|
7419
|
+
...(clientContext === undefined ? {} : { clientContext }),
|
|
7420
|
+
...(files.length > 0 ? { files: [...files] } : {})
|
|
6902
7421
|
}), {
|
|
6903
7422
|
next: event => {
|
|
7423
|
+
if (event.type === 'message.queued') {
|
|
7424
|
+
// An agent's run works in the thread: the message, with its files, waits for the next run,
|
|
7425
|
+
// which adds it to the thread. Until then the notice says so.
|
|
7426
|
+
waitsForRun = true;
|
|
7427
|
+
this.removeMessage(conversationId, clientMessageId, previousStatus);
|
|
7428
|
+
this.releaseAttachments(conversationId, files);
|
|
7429
|
+
this.patchSession(conversationId, session => ({ ...session, notice: messageQueuedNotice(files.length) }));
|
|
7430
|
+
resolve(true);
|
|
7431
|
+
return;
|
|
7432
|
+
}
|
|
6904
7433
|
if (event.type === 'error' && !started) {
|
|
6905
7434
|
this.removeMessage(conversationId, clientMessageId, previousStatus);
|
|
6906
7435
|
if (!queued && this.activeIdState() === conversationId) {
|
|
@@ -6912,6 +7441,8 @@ class NhAssistantStore {
|
|
|
6912
7441
|
}
|
|
6913
7442
|
if (event.type === 'turn.started') {
|
|
6914
7443
|
started = true;
|
|
7444
|
+
// The message carries the files now; they leave the composer.
|
|
7445
|
+
this.releaseAttachments(conversationId, files);
|
|
6915
7446
|
resolve(true);
|
|
6916
7447
|
}
|
|
6917
7448
|
if (event.type === 'error') {
|
|
@@ -6921,8 +7452,9 @@ class NhAssistantStore {
|
|
|
6921
7452
|
},
|
|
6922
7453
|
complete: () => {
|
|
6923
7454
|
this.pendingSendResolves.delete(conversationId);
|
|
6924
|
-
resolve(started);
|
|
6925
|
-
|
|
7455
|
+
resolve(started || waitsForRun);
|
|
7456
|
+
// A waiting message started no turn here: the thread's own state comes from the server.
|
|
7457
|
+
this.afterTurn(conversationId, failedAfterStart || waitsForRun);
|
|
6926
7458
|
}
|
|
6927
7459
|
});
|
|
6928
7460
|
this.sendingState.set(null);
|
|
@@ -7210,6 +7742,159 @@ class NhAssistantStore {
|
|
|
7210
7742
|
this.restoredDraftState.set(null);
|
|
7211
7743
|
return draft;
|
|
7212
7744
|
}
|
|
7745
|
+
/**
|
|
7746
|
+
* Attaches files to the next message of the open conversation and uploads them at once. Each
|
|
7747
|
+
* file is checked first against `uploads()`: the most files of one message, the accepted media
|
|
7748
|
+
* types and the largest size; a file that fails stays as a failed attachment with its code and
|
|
7749
|
+
* is never uploaded. A new conversation is created first, because an upload belongs to one.
|
|
7750
|
+
*/
|
|
7751
|
+
async attach(files) {
|
|
7752
|
+
const uploads = this.uploads();
|
|
7753
|
+
if (!uploads || files.length === 0) {
|
|
7754
|
+
return;
|
|
7755
|
+
}
|
|
7756
|
+
const revision = this.accountRevision;
|
|
7757
|
+
const key = this.activeIdState() ?? newConversationKey;
|
|
7758
|
+
let attached = this.attachmentsOf(key).filter(attachment => attachment.state !== 'failed').length;
|
|
7759
|
+
const added = [];
|
|
7760
|
+
const pending = [];
|
|
7761
|
+
for (const file of files) {
|
|
7762
|
+
const mediaType = nhAssistantMediaTypeOf(file);
|
|
7763
|
+
const problem = nhAssistantFileProblem(file, mediaType, uploads, attached);
|
|
7764
|
+
const attachment = {
|
|
7765
|
+
id: createClientMessageId(),
|
|
7766
|
+
name: file.name,
|
|
7767
|
+
mediaType,
|
|
7768
|
+
size: file.size,
|
|
7769
|
+
state: problem ? 'failed' : 'uploading',
|
|
7770
|
+
progress: 0,
|
|
7771
|
+
file: null,
|
|
7772
|
+
error: problem ? clientError(problem) : null
|
|
7773
|
+
};
|
|
7774
|
+
added.push(attachment);
|
|
7775
|
+
if (!problem) {
|
|
7776
|
+
attached++;
|
|
7777
|
+
// The upload declares the media type the check accepted, also when the browser gave none.
|
|
7778
|
+
pending.push({ attachment, content: file.slice(0, file.size, mediaType) });
|
|
7779
|
+
}
|
|
7780
|
+
}
|
|
7781
|
+
this.patchAttachments(key, attachments => [...attachments, ...added]);
|
|
7782
|
+
if (pending.length === 0) {
|
|
7783
|
+
return;
|
|
7784
|
+
}
|
|
7785
|
+
let conversationId = this.activeIdState();
|
|
7786
|
+
if (conversationId === null) {
|
|
7787
|
+
this.attachmentConversation ??= this.createConversation().finally(() => {
|
|
7788
|
+
this.attachmentConversation = null;
|
|
7789
|
+
});
|
|
7790
|
+
const conversation = await this.attachmentConversation;
|
|
7791
|
+
if (revision !== this.accountRevision || this.destroyed) {
|
|
7792
|
+
return;
|
|
7793
|
+
}
|
|
7794
|
+
if (!conversation) {
|
|
7795
|
+
const error = this.draftErrorState() ?? clientError(NhAssistantClientErrorCodes.server);
|
|
7796
|
+
this.patchAttachments(key, attachments => attachments.map(attachment => pending.some(item => item.attachment.id === attachment.id)
|
|
7797
|
+
? { ...attachment, state: 'failed', error }
|
|
7798
|
+
: attachment));
|
|
7799
|
+
return;
|
|
7800
|
+
}
|
|
7801
|
+
conversationId = conversation.id;
|
|
7802
|
+
this.moveAttachments(key, conversationId);
|
|
7803
|
+
}
|
|
7804
|
+
for (const { attachment, content } of pending) {
|
|
7805
|
+
this.startUpload(conversationId, attachment, content);
|
|
7806
|
+
}
|
|
7807
|
+
}
|
|
7808
|
+
/** Removes an attachment of the next message; an upload in progress stops. */
|
|
7809
|
+
removeAttachment(attachmentId) {
|
|
7810
|
+
this.uploadSubscriptions.get(attachmentId)?.unsubscribe();
|
|
7811
|
+
this.uploadSubscriptions.delete(attachmentId);
|
|
7812
|
+
this.attachmentsState.update(all => {
|
|
7813
|
+
const next = new Map();
|
|
7814
|
+
for (const [key, attachments] of all) {
|
|
7815
|
+
const kept = attachments.filter(attachment => attachment.id !== attachmentId);
|
|
7816
|
+
if (kept.length > 0) {
|
|
7817
|
+
next.set(key, kept);
|
|
7818
|
+
}
|
|
7819
|
+
}
|
|
7820
|
+
return next;
|
|
7821
|
+
});
|
|
7822
|
+
}
|
|
7823
|
+
startUpload(conversationId, attachment, content) {
|
|
7824
|
+
const revision = this.accountRevision;
|
|
7825
|
+
const update = (change) => {
|
|
7826
|
+
if (revision === this.accountRevision) {
|
|
7827
|
+
this.patchAttachments(conversationId, attachments => attachments.map(item => item.id === attachment.id ? { ...item, ...change } : item));
|
|
7828
|
+
}
|
|
7829
|
+
};
|
|
7830
|
+
const subscription = this.api.uploadFile(conversationId, content, attachment.name).subscribe({
|
|
7831
|
+
next: event => {
|
|
7832
|
+
if (event.type === 'progress') {
|
|
7833
|
+
update({ progress: event.total > 0 ? Math.min(1, event.loaded / event.total) : 0 });
|
|
7834
|
+
return;
|
|
7835
|
+
}
|
|
7836
|
+
update({ state: 'uploaded', progress: 1, file: event.file });
|
|
7837
|
+
},
|
|
7838
|
+
error: error => {
|
|
7839
|
+
this.uploadSubscriptions.delete(attachment.id);
|
|
7840
|
+
update({ state: 'failed', error: error instanceof NhAssistantApiError
|
|
7841
|
+
? { code: error.code, messageKey: error.messageKey }
|
|
7842
|
+
: clientError(NhAssistantClientErrorCodes.server) });
|
|
7843
|
+
},
|
|
7844
|
+
complete: () => this.uploadSubscriptions.delete(attachment.id)
|
|
7845
|
+
});
|
|
7846
|
+
if (!subscription.closed) {
|
|
7847
|
+
this.uploadSubscriptions.set(attachment.id, subscription);
|
|
7848
|
+
}
|
|
7849
|
+
}
|
|
7850
|
+
attachmentsOf(key) {
|
|
7851
|
+
return this.attachmentsState().get(key) ?? [];
|
|
7852
|
+
}
|
|
7853
|
+
patchAttachments(key, update) {
|
|
7854
|
+
this.attachmentsState.update(all => {
|
|
7855
|
+
const next = new Map(all);
|
|
7856
|
+
const attachments = update(all.get(key) ?? []);
|
|
7857
|
+
if (attachments.length > 0) {
|
|
7858
|
+
next.set(key, attachments);
|
|
7859
|
+
}
|
|
7860
|
+
else {
|
|
7861
|
+
next.delete(key);
|
|
7862
|
+
}
|
|
7863
|
+
return next;
|
|
7864
|
+
});
|
|
7865
|
+
}
|
|
7866
|
+
/** Moves the attachments of a new conversation to the conversation that was created for them. */
|
|
7867
|
+
moveAttachments(from, to) {
|
|
7868
|
+
const moving = this.attachmentsOf(from);
|
|
7869
|
+
this.patchAttachments(from, () => []);
|
|
7870
|
+
this.patchAttachments(to, attachments => [...attachments, ...moving]);
|
|
7871
|
+
}
|
|
7872
|
+
/**
|
|
7873
|
+
* The attachments a message took leave the composer, and with them the failed ones; files that
|
|
7874
|
+
* still upload stay for the next message.
|
|
7875
|
+
*/
|
|
7876
|
+
releaseAttachments(conversationId, files) {
|
|
7877
|
+
const sent = new Set(files.map(file => file.id));
|
|
7878
|
+
this.patchAttachments(conversationId, attachments => attachments.filter(attachment => attachment.state === 'uploading' || (attachment.state === 'uploaded' && !sent.has(attachment.file?.id ?? ''))));
|
|
7879
|
+
}
|
|
7880
|
+
/** Stops the uploads of a conversation, or of every conversation, and forgets their attachments. */
|
|
7881
|
+
dropAttachments(conversationId) {
|
|
7882
|
+
for (const [key, attachments] of this.attachmentsState()) {
|
|
7883
|
+
if (conversationId !== undefined && key !== conversationId) {
|
|
7884
|
+
continue;
|
|
7885
|
+
}
|
|
7886
|
+
for (const attachment of attachments) {
|
|
7887
|
+
this.uploadSubscriptions.get(attachment.id)?.unsubscribe();
|
|
7888
|
+
this.uploadSubscriptions.delete(attachment.id);
|
|
7889
|
+
}
|
|
7890
|
+
}
|
|
7891
|
+
if (conversationId === undefined) {
|
|
7892
|
+
this.attachmentsState.set(new Map());
|
|
7893
|
+
}
|
|
7894
|
+
else {
|
|
7895
|
+
this.patchAttachments(conversationId, () => []);
|
|
7896
|
+
}
|
|
7897
|
+
}
|
|
7213
7898
|
async loadConversation(conversationId, restoring) {
|
|
7214
7899
|
if (this.sessionsState().has(conversationId)) {
|
|
7215
7900
|
const session = this.sessionsState().get(conversationId);
|
|
@@ -7353,6 +8038,8 @@ class NhAssistantStore {
|
|
|
7353
8038
|
this.steeringState.set(null);
|
|
7354
8039
|
this.submittingState.set(false);
|
|
7355
8040
|
this.feedbackApprovalsState.set(new Set());
|
|
8041
|
+
this.dropAttachments();
|
|
8042
|
+
this.attachmentConversation = null;
|
|
7356
8043
|
this.clearAgentWorkTimers();
|
|
7357
8044
|
void this.live.stop();
|
|
7358
8045
|
void this.push.deactivate().catch(() => undefined);
|
|
@@ -7667,6 +8354,7 @@ class NhAssistantStore {
|
|
|
7667
8354
|
}
|
|
7668
8355
|
/** Removes a deleted, left or inaccessible conversation everywhere. */
|
|
7669
8356
|
forget(conversationId) {
|
|
8357
|
+
this.dropAttachments(conversationId);
|
|
7670
8358
|
this.queuedState.update(items => items.filter(item => item.conversationId !== conversationId));
|
|
7671
8359
|
this.pausedQueuesState.update(paused => new Set([...paused].filter(id => id !== conversationId)));
|
|
7672
8360
|
this.streams.get(conversationId)?.unsubscribe();
|
|
@@ -7726,6 +8414,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImpo
|
|
|
7726
8414
|
function clientError(code) {
|
|
7727
8415
|
return { code, messageKey: nhAssistantErrorMessageKey(code) };
|
|
7728
8416
|
}
|
|
8417
|
+
/**
|
|
8418
|
+
* The notice of a message that waits for the next run of the agent working in the thread, with its
|
|
8419
|
+
* files when it carries any; the run adds the message to the thread when it takes it.
|
|
8420
|
+
*/
|
|
8421
|
+
function messageQueuedNotice(fileCount) {
|
|
8422
|
+
return {
|
|
8423
|
+
code: NH_ASSISTANT_MESSAGE_QUEUED_NOTICE_CODE,
|
|
8424
|
+
messageKey: fileCount > 0 ? 'nh-assistant.notices.message-queued-with-files' : 'nh-assistant.notices.message-queued'
|
|
8425
|
+
};
|
|
8426
|
+
}
|
|
7729
8427
|
/** A completed turn with a server code, or without any answer text, explains itself with a notice. */
|
|
7730
8428
|
function completionNotice(conversation, errorCode) {
|
|
7731
8429
|
if (errorCode) {
|
|
@@ -8348,6 +9046,203 @@ function provideNhAssistant(config) {
|
|
|
8348
9046
|
]);
|
|
8349
9047
|
}
|
|
8350
9048
|
|
|
9049
|
+
/**
|
|
9050
|
+
* Failure codes the assistant back-end sends in error bodies, SSE `error` events and tool
|
|
9051
|
+
* result codes (`NhAssistantErrorCodes`, `NhAssistantAdminErrorCodes` and the runtime codes
|
|
9052
|
+
* of `NewHeap.Platform.AI.Chat`). Every code needs a text under `nh-assistant.errors.<code>`
|
|
9053
|
+
* in each bundled language; the translation tests enforce that.
|
|
9054
|
+
*/
|
|
9055
|
+
const NH_ASSISTANT_SERVER_ERROR_CODES = [
|
|
9056
|
+
// Chat endpoints and turns
|
|
9057
|
+
'assistant-disabled',
|
|
9058
|
+
'assistant-conversation-not-found',
|
|
9059
|
+
'assistant-conversation-busy',
|
|
9060
|
+
'assistant-agent-not-found',
|
|
9061
|
+
'assistant-agent-forbidden',
|
|
9062
|
+
'assistant-message-invalid',
|
|
9063
|
+
'assistant-message-too-long',
|
|
9064
|
+
'assistant-message-duplicate',
|
|
9065
|
+
'assistant-title-invalid',
|
|
9066
|
+
'assistant-approval-not-found',
|
|
9067
|
+
'assistant-approval-not-pending',
|
|
9068
|
+
'assistant-approval-decision-invalid',
|
|
9069
|
+
'assistant-proposal-hash-mismatch',
|
|
9070
|
+
'assistant-approval-expired',
|
|
9071
|
+
'assistant-approval-invalid',
|
|
9072
|
+
'assistant-approval-rejected',
|
|
9073
|
+
'assistant-budget-exhausted',
|
|
9074
|
+
'assistant-tool-call-limit-reached',
|
|
9075
|
+
'assistant-tools-disabled',
|
|
9076
|
+
'assistant-turn-timeout',
|
|
9077
|
+
'assistant-model-unavailable',
|
|
9078
|
+
'assistant-classification-not-permitted',
|
|
9079
|
+
'assistant-files-unavailable',
|
|
9080
|
+
'assistant-files-not-accepted',
|
|
9081
|
+
'assistant-file-reference-invalid',
|
|
9082
|
+
'assistant-file-not-found',
|
|
9083
|
+
'assistant-file-forbidden',
|
|
9084
|
+
'assistant-file-unavailable',
|
|
9085
|
+
'assistant-file-reader-failed',
|
|
9086
|
+
'assistant-file-too-large',
|
|
9087
|
+
'assistant-file-too-many-pages',
|
|
9088
|
+
'assistant-file-count-limit',
|
|
9089
|
+
'assistant-file-token-limit',
|
|
9090
|
+
'assistant-file-type-not-supported',
|
|
9091
|
+
'assistant-file-upload-invalid',
|
|
9092
|
+
'assistant-file-type-not-accepted',
|
|
9093
|
+
'assistant-file-upload-refused',
|
|
9094
|
+
'assistant-file-upload-failed',
|
|
9095
|
+
'assistant-file-scan-refused',
|
|
9096
|
+
'assistant-turn-failed',
|
|
9097
|
+
'assistant-context-unavailable',
|
|
9098
|
+
'assistant-actor-mismatch',
|
|
9099
|
+
// Sharing and notifications
|
|
9100
|
+
'assistant-owner-required',
|
|
9101
|
+
'assistant-approval-forbidden',
|
|
9102
|
+
'assistant-turn-forbidden',
|
|
9103
|
+
'assistant-share-link-invalid',
|
|
9104
|
+
'assistant-participant-not-found',
|
|
9105
|
+
'assistant-participant-limit-reached',
|
|
9106
|
+
'assistant-directory-unavailable',
|
|
9107
|
+
'assistant-push-unavailable',
|
|
9108
|
+
'assistant-push-subscription-invalid',
|
|
9109
|
+
// Agent runs
|
|
9110
|
+
'assistant-subject-not-found',
|
|
9111
|
+
'assistant-subject-invalid',
|
|
9112
|
+
'assistant-subject-type-unknown',
|
|
9113
|
+
'assistant-subject-search-unavailable',
|
|
9114
|
+
'assistant-agent-does-not-work-on',
|
|
9115
|
+
'assistant-subject-access-denied',
|
|
9116
|
+
'assistant-no-accountable-owner',
|
|
9117
|
+
'assistant-agent-disabled',
|
|
9118
|
+
'assistant-agent-stopped',
|
|
9119
|
+
'assistant-agent-paused',
|
|
9120
|
+
'assistant-agent-configured-off',
|
|
9121
|
+
'assistant-run-token-limit',
|
|
9122
|
+
'assistant-skill-stopped',
|
|
9123
|
+
'assistant-skill-paused',
|
|
9124
|
+
'assistant-agent-off',
|
|
9125
|
+
'assistant-skill-off',
|
|
9126
|
+
'assistant-skill-not-found',
|
|
9127
|
+
'assistant-skill-not-available',
|
|
9128
|
+
'assistant-skill-switch-limit',
|
|
9129
|
+
'assistant-moved-to-shadow',
|
|
9130
|
+
'assistant-shadow-variant',
|
|
9131
|
+
'assistant-service-account-not-used',
|
|
9132
|
+
'assistant-service-account-not-found',
|
|
9133
|
+
'assistant-skill-in-code',
|
|
9134
|
+
'assistant-trigger-in-code',
|
|
9135
|
+
'assistant-skill-in-use',
|
|
9136
|
+
'assistant-configuration-limit',
|
|
9137
|
+
'assistant-run-settings-in-code',
|
|
9138
|
+
'assistant-run-settings-in-use',
|
|
9139
|
+
'assistant-skill-candidate-not-found',
|
|
9140
|
+
'assistant-skill-version-gone',
|
|
9141
|
+
'assistant-account-search-unavailable',
|
|
9142
|
+
'assistant-handoff-unavailable',
|
|
9143
|
+
'assistant-handoff-not-accepted',
|
|
9144
|
+
'assistant-handoff-subject-unavailable',
|
|
9145
|
+
'assistant-handoff-shadow-to-live',
|
|
9146
|
+
'assistant-handoff-chain-limit',
|
|
9147
|
+
'assistant-handoff-limit-reached',
|
|
9148
|
+
'assistant-replay-not-shadow',
|
|
9149
|
+
'assistant-replay-active',
|
|
9150
|
+
'assistant-evaluation-not-variant',
|
|
9151
|
+
'assistant-agent-not-configured',
|
|
9152
|
+
'assistant-run-not-found',
|
|
9153
|
+
'assistant-run-not-retryable',
|
|
9154
|
+
'assistant-run-filter-invalid',
|
|
9155
|
+
'assistant-run-operation-cancelled',
|
|
9156
|
+
'assistant-run-operation-stalled',
|
|
9157
|
+
'assistant-run-operation-ended',
|
|
9158
|
+
'assistant-board-not-found',
|
|
9159
|
+
'assistant-board-scope-invalid',
|
|
9160
|
+
'assistant-approval-policy-denied',
|
|
9161
|
+
'assistant-four-eyes-required',
|
|
9162
|
+
'assistant-execution-not-uncertain',
|
|
9163
|
+
'assistant-proposal-stale',
|
|
9164
|
+
'assistant-proposal-check-failed',
|
|
9165
|
+
'assistant-proposal-already-open',
|
|
9166
|
+
'assistant-proposal-recently-rejected',
|
|
9167
|
+
'assistant-proposal-superseded',
|
|
9168
|
+
'assistant-proposal-refused',
|
|
9169
|
+
'assistant-proposal-placed',
|
|
9170
|
+
'assistant-proposal-not-placed',
|
|
9171
|
+
'assistant-proposal-not-needed',
|
|
9172
|
+
'assistant-trigger-not-found',
|
|
9173
|
+
'assistant-trigger-rate-limited',
|
|
9174
|
+
'assistant-trigger-rolled-back',
|
|
9175
|
+
'assistant-webhook-signature-invalid',
|
|
9176
|
+
'assistant-webhook-payload-invalid',
|
|
9177
|
+
'assistant-webhook-payload-too-large',
|
|
9178
|
+
'assistant-memory-not-configured',
|
|
9179
|
+
'assistant-memory-invalid',
|
|
9180
|
+
'assistant-memory-conflict',
|
|
9181
|
+
'assistant-artifact-invalid',
|
|
9182
|
+
'assistant-artifact-outside-turn',
|
|
9183
|
+
'assistant-artifact-not-found',
|
|
9184
|
+
// Feedback on proposals
|
|
9185
|
+
'assistant-feedback-invalid',
|
|
9186
|
+
'assistant-feedback-not-accepted',
|
|
9187
|
+
// Consultations of other agents
|
|
9188
|
+
'assistant-consult-subject-unavailable',
|
|
9189
|
+
'assistant-consult-agent-unavailable',
|
|
9190
|
+
'assistant-consult-depth-exceeded',
|
|
9191
|
+
'assistant-consult-budget-exhausted',
|
|
9192
|
+
'assistant-consult-limit-reached',
|
|
9193
|
+
'assistant-consult-timeout',
|
|
9194
|
+
'assistant-consult-failed',
|
|
9195
|
+
'assistant-consult-read-only',
|
|
9196
|
+
// Preferences and administration
|
|
9197
|
+
'assistant-validation',
|
|
9198
|
+
'assistant-instructions-too-long',
|
|
9199
|
+
'assistant-forbidden',
|
|
9200
|
+
'assistant-not-found',
|
|
9201
|
+
'assistant-context-not-found',
|
|
9202
|
+
'assistant-version-conflict',
|
|
9203
|
+
'assistant-agent-exists',
|
|
9204
|
+
'assistant-code-agent-not-deletable',
|
|
9205
|
+
'assistant-agent-not-code',
|
|
9206
|
+
'assistant-mcp-server-not-found',
|
|
9207
|
+
'assistant-mcp-server-exists',
|
|
9208
|
+
'assistant-mcp-tool-not-found',
|
|
9209
|
+
'assistant-mcp-unreachable',
|
|
9210
|
+
'assistant-mcp-unauthorized',
|
|
9211
|
+
'assistant-mcp-host-blocked',
|
|
9212
|
+
'assistant-mcp-schema-changed',
|
|
9213
|
+
'assistant-mcp-tool-disabled',
|
|
9214
|
+
'assistant-mcp-insufficient-scope',
|
|
9215
|
+
'assistant-mcp-headless-not-allowed',
|
|
9216
|
+
'assistant-mcp-credential-mismatch',
|
|
9217
|
+
'assistant-mcp-consent-required',
|
|
9218
|
+
'assistant-mcp-initiator-only',
|
|
9219
|
+
'assistant-mcp-delegated-token-unavailable',
|
|
9220
|
+
'assistant-mcp-secret-reference-invalid',
|
|
9221
|
+
'assistant-mcp-certificate-reference-invalid',
|
|
9222
|
+
'assistant-mcp-secret-unreadable',
|
|
9223
|
+
'assistant-mcp-secret-unavailable',
|
|
9224
|
+
// MCP OAuth connections
|
|
9225
|
+
'assistant-mcp-connection-required',
|
|
9226
|
+
'assistant-mcp-reauthorization-required',
|
|
9227
|
+
'assistant-mcp-scope-not-allowed',
|
|
9228
|
+
'assistant-mcp-authorization-failed',
|
|
9229
|
+
'assistant-mcp-authorization-expired',
|
|
9230
|
+
'assistant-mcp-authorization-invalid',
|
|
9231
|
+
'assistant-mcp-discovery-failed',
|
|
9232
|
+
'assistant-mcp-pkce-unsupported',
|
|
9233
|
+
'assistant-mcp-client-registration-required',
|
|
9234
|
+
'assistant-mcp-token-request-failed',
|
|
9235
|
+
'assistant-mcp-issuer-changed',
|
|
9236
|
+
'assistant-mcp-authorization-not-confirmed',
|
|
9237
|
+
'assistant-mcp-oauth-not-configured',
|
|
9238
|
+
'assistant-mcp-too-many-authorizations',
|
|
9239
|
+
'assistant-mcp-connection-not-supported'
|
|
9240
|
+
];
|
|
9241
|
+
/** Every code the assistant UI can show: server codes and client-side transport codes. */
|
|
9242
|
+
const NH_ASSISTANT_ERROR_CODES = [
|
|
9243
|
+
...new Set([...NH_ASSISTANT_SERVER_ERROR_CODES, ...Object.values(NhAssistantClientErrorCodes)])
|
|
9244
|
+
];
|
|
9245
|
+
|
|
8351
9246
|
/** Fragment the panel reads to join a shared conversation: `#nh-assistant-join=<id>.<token>`. */
|
|
8352
9247
|
const NH_ASSISTANT_JOIN_FRAGMENT = 'nh-assistant-join';
|
|
8353
9248
|
/** Fragment a clicked notification opens: `#nh-assistant-conversation=<id>`. */
|
|
@@ -8387,7 +9282,9 @@ const builtInPaths = {
|
|
|
8387
9282
|
users: 'M9 11a3.5 3.5 0 1 0 0-7 3.5 3.5 0 1 0 0 7zM3 20c0-3.3 2.7-6 6-6s6 2.7 6 6M16 4.5a3.5 3.5 0 0 1 0 6.5M18 14.5c1.8.9 3 2.8 3 5',
|
|
8388
9283
|
copy: 'M9 9h11v11H9zM5 15H4V4h11v1',
|
|
8389
9284
|
bell: 'M6 16V11a6 6 0 0 1 12 0v5l2 2H4zM10 20a2 2 0 0 0 4 0',
|
|
8390
|
-
leave: 'M10 4H5v16h5M14 8l4 4-4 4M18 12H9'
|
|
9285
|
+
leave: 'M10 4H5v16h5M14 8l4 4-4 4M18 12H9',
|
|
9286
|
+
attach: 'M19 11.5l-7.6 7.6a4.6 4.6 0 0 1-6.5-6.5l8.3-8.3a3 3 0 0 1 4.3 4.3l-8.2 8.2a1.5 1.5 0 0 1-2.1-2.1l7.4-7.4',
|
|
9287
|
+
file: 'M6 3h8l4 4v14H6zM14 3v4h4'
|
|
8391
9288
|
};
|
|
8392
9289
|
/** Decorative icon. The surrounding control provides the accessible name. */
|
|
8393
9290
|
class NhAssistantIconComponent {
|
|
@@ -8711,7 +9608,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImpo
|
|
|
8711
9608
|
let nextId$a = 0;
|
|
8712
9609
|
/**
|
|
8713
9610
|
* Message input. Enter sends when sending is available; Shift+Enter inserts a new line.
|
|
8714
|
-
* Drafting remains available while the assistant runs or waits for approval.
|
|
9611
|
+
* Drafting remains available while the assistant runs or waits for approval. With `uploads`, an
|
|
9612
|
+
* attach button chooses files for the next message; the host uploads them and passes them back as
|
|
9613
|
+
* `attachments`, which show their progress, or why they cannot be attached, at the control. Sending
|
|
9614
|
+
* waits while a file uploads.
|
|
8715
9615
|
*/
|
|
8716
9616
|
class NhAssistantComposerComponent {
|
|
8717
9617
|
constructor() {
|
|
@@ -8732,15 +9632,30 @@ class NhAssistantComposerComponent {
|
|
|
8732
9632
|
this.maxLength = input(null, ...(ngDevMode ? [{ debugName: "maxLength" }] : []));
|
|
8733
9633
|
/** Text to put back into the input, for example a message the server did not accept. */
|
|
8734
9634
|
this.restore = input(null, ...(ngDevMode ? [{ debugName: "restore" }] : []));
|
|
9635
|
+
/**
|
|
9636
|
+
* What the person may attach, for example `NhAssistantStore.uploads()`; `null` hides the attach
|
|
9637
|
+
* button.
|
|
9638
|
+
*/
|
|
9639
|
+
this.uploads = input(null, ...(ngDevMode ? [{ debugName: "uploads" }] : []));
|
|
9640
|
+
/** The attachments of the next message, for example `NhAssistantStore.attachments()`. */
|
|
9641
|
+
this.attachments = input([], ...(ngDevMode ? [{ debugName: "attachments" }] : []));
|
|
8735
9642
|
this.send = output();
|
|
8736
9643
|
this.cancel = output();
|
|
8737
9644
|
this.restored = output();
|
|
9645
|
+
/** Files the person chose to attach, for example for `NhAssistantStore.attach`. */
|
|
9646
|
+
this.attach = output();
|
|
9647
|
+
/** The id of an attachment the person removed, for example for `NhAssistantStore.removeAttachment`. */
|
|
9648
|
+
this.removeAttachment = output();
|
|
8738
9649
|
this.host = inject(ElementRef);
|
|
9650
|
+
this.translate = inject(TranslateService, { optional: true });
|
|
8739
9651
|
this.textarea = viewChild.required('textarea');
|
|
9652
|
+
this.attachButton = viewChild('attachButton', ...(ngDevMode ? [{ debugName: "attachButton" }] : []));
|
|
8740
9653
|
this.ownedFocus = false;
|
|
8741
9654
|
this.previousBusy = false;
|
|
8742
9655
|
this.inputId = `nh-assistant-composer-${nextId$a++}`;
|
|
8743
9656
|
this.hintId = `${this.inputId}-hint`;
|
|
9657
|
+
this.attachHintId = `${this.inputId}-attach-hint`;
|
|
9658
|
+
this.attachmentsId = `${this.inputId}-attachments`;
|
|
8744
9659
|
this.text = signal('', ...(ngDevMode ? [{ debugName: "text" }] : []));
|
|
8745
9660
|
this.length = computed(() => this.text().trim().length, ...(ngDevMode ? [{ debugName: "length" }] : []));
|
|
8746
9661
|
this.tooLong = computed(() => {
|
|
@@ -8751,7 +9666,20 @@ class NhAssistantComposerComponent {
|
|
|
8751
9666
|
const max = this.maxLength();
|
|
8752
9667
|
return max !== null && this.length() >= max * 0.8;
|
|
8753
9668
|
}, ...(ngDevMode ? [{ debugName: "showCounter" }] : []));
|
|
8754
|
-
|
|
9669
|
+
/** True while a file of the next message still uploads. */
|
|
9670
|
+
this.uploading = computed(() => this.attachments().some(attachment => attachment.state === 'uploading'), ...(ngDevMode ? [{ debugName: "uploading" }] : []));
|
|
9671
|
+
this.canSend = computed(() => !this.disabled() && !this.sendDisabled() && this.length() > 0 && !this.tooLong() && !this.uploading(), ...(ngDevMode ? [{ debugName: "canSend" }] : []));
|
|
9672
|
+
/** The `accept` attribute of the file input. */
|
|
9673
|
+
this.accept = computed(() => nhAssistantAcceptAttribute(this.uploads()?.acceptedMediaTypes ?? []), ...(ngDevMode ? [{ debugName: "accept" }] : []));
|
|
9674
|
+
/** The values the attachment texts name: the accepted types, the largest size and the most files. */
|
|
9675
|
+
this.uploadParams = computed(() => {
|
|
9676
|
+
const uploads = this.uploads();
|
|
9677
|
+
return {
|
|
9678
|
+
types: (uploads?.acceptedMediaTypes ?? []).map(nhAssistantFileTypeLabel).join(', '),
|
|
9679
|
+
max: uploads ? this.size(uploads.maxFileBytes) : '',
|
|
9680
|
+
count: uploads?.maxFiles ?? 0
|
|
9681
|
+
};
|
|
9682
|
+
}, ...(ngDevMode ? [{ debugName: "uploadParams" }] : []));
|
|
8755
9683
|
effect(() => {
|
|
8756
9684
|
const draft = this.restore();
|
|
8757
9685
|
if (draft !== null) {
|
|
@@ -8801,6 +9729,40 @@ class NhAssistantComposerComponent {
|
|
|
8801
9729
|
this.text.set('');
|
|
8802
9730
|
this.send.emit(text);
|
|
8803
9731
|
}
|
|
9732
|
+
/** Hands the chosen files to the host and empties the input, so the same file can be chosen again. */
|
|
9733
|
+
onFilesChosen(event) {
|
|
9734
|
+
const input = event.target;
|
|
9735
|
+
const files = Array.from(input.files ?? []);
|
|
9736
|
+
input.value = '';
|
|
9737
|
+
if (files.length > 0) {
|
|
9738
|
+
this.attach.emit(files);
|
|
9739
|
+
}
|
|
9740
|
+
}
|
|
9741
|
+
/** Removes an attachment and keeps the keyboard in the composer, on the attach button. */
|
|
9742
|
+
remove(attachment) {
|
|
9743
|
+
this.removeAttachment.emit(attachment.id);
|
|
9744
|
+
queueMicrotask(() => (this.attachButton()?.nativeElement ?? this.textarea().nativeElement).focus({ preventScroll: true }));
|
|
9745
|
+
}
|
|
9746
|
+
/** The translation keys of why an attachment failed, from the composer's own text to the generic one. */
|
|
9747
|
+
errorKeys(attachment) {
|
|
9748
|
+
const error = attachment.error;
|
|
9749
|
+
if (!error) {
|
|
9750
|
+
return [];
|
|
9751
|
+
}
|
|
9752
|
+
return [
|
|
9753
|
+
`nh-assistant.composer.attachment-errors.${error.code}`,
|
|
9754
|
+
error.messageKey,
|
|
9755
|
+
`nh-assistant.errors.${error.code}`,
|
|
9756
|
+
'nh-assistant.errors.generic'
|
|
9757
|
+
];
|
|
9758
|
+
}
|
|
9759
|
+
percent(attachment) {
|
|
9760
|
+
return Math.round(attachment.progress * 100);
|
|
9761
|
+
}
|
|
9762
|
+
/** A size in bytes in the current language, such as `1.4 MB`. */
|
|
9763
|
+
size(bytes) {
|
|
9764
|
+
return nhAssistantFormatFileSize(bytes, this.translate?.getCurrentLang());
|
|
9765
|
+
}
|
|
8804
9766
|
restoreOwnedFocus() {
|
|
8805
9767
|
if (!this.ownedFocus || matchMedia('(pointer: coarse)').matches) {
|
|
8806
9768
|
return;
|
|
@@ -8811,12 +9773,12 @@ class NhAssistantComposerComponent {
|
|
|
8811
9773
|
}
|
|
8812
9774
|
}
|
|
8813
9775
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantComposerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
8814
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.34", type: NhAssistantComposerComponent, isStandalone: true, selector: "nh-assistant-composer", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, sendDisabled: { classPropertyName: "sendDisabled", publicName: "sendDisabled", isSignal: true, isRequired: false, transformFunction: null }, busy: { classPropertyName: "busy", publicName: "busy", isSignal: true, isRequired: false, transformFunction: null }, queueWhileBusy: { classPropertyName: "queueWhileBusy", publicName: "queueWhileBusy", isSignal: true, isRequired: false, transformFunction: null }, status: { classPropertyName: "status", publicName: "status", isSignal: true, isRequired: false, transformFunction: null }, statusActor: { classPropertyName: "statusActor", publicName: "statusActor", isSignal: true, isRequired: false, transformFunction: null }, canStop: { classPropertyName: "canStop", publicName: "canStop", isSignal: true, isRequired: false, transformFunction: null }, maxLength: { classPropertyName: "maxLength", publicName: "maxLength", isSignal: true, isRequired: false, transformFunction: null }, restore: { classPropertyName: "restore", publicName: "restore", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { send: "send", cancel: "cancel", restored: "restored" }, host: { listeners: { "focusin": "onFocusIn()", "focusout": "onFocusOut($event)" } }, viewQueries: [{ propertyName: "textarea", first: true, predicate: ["textarea"], descendants: true, isSignal: true }], ngImport: i0, template: "<form class=\"composer\" (submit)=\"onSubmit($event)\">\n <label class=\"visually-hidden\" [for]=\"inputId\">{{ 'nh-assistant.composer.label' | translate }}</label>\n <textarea\n #textarea\n data-nh-assistant-autofocus\n rows=\"2\"\n [id]=\"inputId\"\n [value]=\"text()\"\n [disabled]=\"disabled()\"\n [attr.placeholder]=\"'nh-assistant.composer.placeholder' | translate\"\n [attr.aria-describedby]=\"hintId\"\n [attr.aria-invalid]=\"tooLong() ? true : null\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"></textarea>\n\n <div class=\"prompt-bar\">\n <div class=\"prompt-controls\">\n <ng-content select=\"[nhAssistantComposerControls]\" />\n </div>\n <div class=\"actions\">\n @if (busy() && canStop()) {\n <button\n class=\"stop\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.composer.stop' | translate\"\n [attr.title]=\"'nh-assistant.composer.stop' | translate\"\n (click)=\"cancel.emit()\">\n <nh-assistant-icon name=\"stop\" />\n </button>\n }\n @if (!busy() || !canStop() || queueWhileBusy()) {\n <button\n class=\"send\"\n type=\"submit\"\n [disabled]=\"!canSend()\"\n [attr.aria-label]=\"(busy() && queueWhileBusy() ? 'nh-assistant.queue.add' : 'nh-assistant.composer.send') | translate\"\n [attr.title]=\"(busy() && queueWhileBusy() ? 'nh-assistant.queue.add' : 'nh-assistant.composer.send') | translate\">\n <nh-assistant-icon name=\"send\" />\n </button>\n }\n </div>\n </div>\n</form>\n\n@if (status() === 'waiting-for-approval') {\n <p class=\"composer-status\" role=\"status\" aria-live=\"polite\">\n @if (statusActor(); as name) {\n {{ 'nh-assistant.composer.status.waiting-for-approval-by' | translate: { name } }}\n } @else {\n {{ 'nh-assistant.composer.status.waiting-for-approval' | translate }}\n }\n </p>\n}\n\n<p class=\"hint\" [id]=\"hintId\">\n @if (tooLong()) {\n <span class=\"error\">{{ 'nh-assistant.composer.too-long' | translate }}</span>\n } @else {\n <span>{{ (busy() && queueWhileBusy() ? 'nh-assistant.queue.hint' : 'nh-assistant.composer.hint') | translate }}</span>\n }\n @if (showCounter()) {\n <span class=\"counter\" [class.error]=\"tooLong()\">\n {{ 'nh-assistant.composer.characters' | translate: { count: length(), max: maxLength() } }}\n </span>\n }\n</p>\n", styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:block}.composer{display:flex;flex-direction:column;gap:8px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:8px 8px 8px 14px;background:var(--nha-surface);box-shadow:0 2px 6px color-mix(in srgb,var(--nha-text) 4%,transparent)}.composer:focus-within{border-color:var(--nha-focus);box-shadow:0 0 0 2px color-mix(in srgb,var(--nha-focus) 16%,transparent)}textarea{width:100%;min-width:0;min-height:44px;max-height:180px;border:0;padding:6px 0;color:var(--nha-text);background:transparent;font:inherit;font-size:14px;line-height:1.45;resize:vertical;outline:none}textarea::placeholder{color:var(--nha-text-muted);opacity:1}textarea:disabled{cursor:not-allowed}.prompt-bar{display:flex;align-items:center;gap:8px;min-width:0}.prompt-controls{flex:1;min-width:0}.actions{display:flex;flex-shrink:0;gap:6px}.send,.stop{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border:1px solid transparent;border-radius:var(--nha-radius-control);color:var(--nha-text-muted);background:transparent;cursor:pointer;width:40px;height:40px}.send:focus-visible,.stop:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.send:hover:not(:disabled),.stop:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}.send:disabled,.stop:disabled{cursor:not-allowed;opacity:.5}.send:not(:disabled){color:var(--nha-accent-contrast);background:var(--nha-accent)}.send:not(:disabled):hover{color:var(--nha-accent-contrast);background:var(--nha-accent);filter:brightness(1.12)}.stop{color:var(--nha-danger);border-color:var(--nha-danger)}.hint,.composer-status{display:flex;justify-content:space-between;gap:8px;margin:6px 2px 0;color:var(--nha-text-muted);font-size:11px}.composer-status{margin-bottom:0}.counter{flex:none;font-variant-numeric:tabular-nums}.error{color:var(--nha-danger)}.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}\n"], dependencies: [{ kind: "component", type: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
9776
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.34", type: NhAssistantComposerComponent, isStandalone: true, selector: "nh-assistant-composer", inputs: { disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, sendDisabled: { classPropertyName: "sendDisabled", publicName: "sendDisabled", isSignal: true, isRequired: false, transformFunction: null }, busy: { classPropertyName: "busy", publicName: "busy", isSignal: true, isRequired: false, transformFunction: null }, queueWhileBusy: { classPropertyName: "queueWhileBusy", publicName: "queueWhileBusy", isSignal: true, isRequired: false, transformFunction: null }, status: { classPropertyName: "status", publicName: "status", isSignal: true, isRequired: false, transformFunction: null }, statusActor: { classPropertyName: "statusActor", publicName: "statusActor", isSignal: true, isRequired: false, transformFunction: null }, canStop: { classPropertyName: "canStop", publicName: "canStop", isSignal: true, isRequired: false, transformFunction: null }, maxLength: { classPropertyName: "maxLength", publicName: "maxLength", isSignal: true, isRequired: false, transformFunction: null }, restore: { classPropertyName: "restore", publicName: "restore", isSignal: true, isRequired: false, transformFunction: null }, uploads: { classPropertyName: "uploads", publicName: "uploads", isSignal: true, isRequired: false, transformFunction: null }, attachments: { classPropertyName: "attachments", publicName: "attachments", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { send: "send", cancel: "cancel", restored: "restored", attach: "attach", removeAttachment: "removeAttachment" }, host: { listeners: { "focusin": "onFocusIn()", "focusout": "onFocusOut($event)" } }, viewQueries: [{ propertyName: "textarea", first: true, predicate: ["textarea"], descendants: true, isSignal: true }, { propertyName: "attachButton", first: true, predicate: ["attachButton"], descendants: true, isSignal: true }], ngImport: i0, template: "<form class=\"composer\" (submit)=\"onSubmit($event)\">\n <label class=\"visually-hidden\" [for]=\"inputId\">{{ 'nh-assistant.composer.label' | translate }}</label>\n <textarea\n #textarea\n data-nh-assistant-autofocus\n rows=\"2\"\n [id]=\"inputId\"\n [value]=\"text()\"\n [disabled]=\"disabled()\"\n [attr.placeholder]=\"'nh-assistant.composer.placeholder' | translate\"\n [attr.aria-describedby]=\"hintId\"\n [attr.aria-invalid]=\"tooLong() ? true : null\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"></textarea>\n\n @if (attachments().length > 0) {\n <ul class=\"attachments\" [id]=\"attachmentsId\" [attr.aria-label]=\"'nh-assistant.composer.attachments' | translate\">\n @for (attachment of attachments(); track attachment.id) {\n <li class=\"attachment\" [attr.data-state]=\"attachment.state\">\n <nh-assistant-icon class=\"attachment-icon\" [name]=\"attachment.state === 'failed' ? 'warning' : 'file'\" />\n <div class=\"attachment-body\">\n <span class=\"attachment-name\" [attr.title]=\"attachment.name\">{{ attachment.name }}</span>\n @switch (attachment.state) {\n @case ('uploading') {\n <progress\n class=\"attachment-progress\"\n max=\"100\"\n [value]=\"percent(attachment)\"\n [attr.aria-label]=\"'nh-assistant.composer.uploading' | translate: { name: attachment.name }\"></progress>\n }\n @case ('failed') {\n <span class=\"attachment-error\" role=\"alert\">\n {{ errorKeys(attachment) | nhAssistantTranslate: (attachment.error?.code ?? '') : uploadParams() }}\n </span>\n }\n @default {\n <span class=\"attachment-meta\">{{ size(attachment.size) }}</span>\n }\n }\n </div>\n <button\n class=\"attachment-remove\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.composer.remove-attachment' | translate: { name: attachment.name }\"\n [attr.title]=\"'nh-assistant.composer.remove-attachment' | translate: { name: attachment.name }\"\n (click)=\"remove(attachment)\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </li>\n }\n </ul>\n }\n\n <div class=\"prompt-bar\">\n @if (uploads()) {\n <button\n #attachButton\n class=\"attach\"\n type=\"button\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'nh-assistant.composer.attach' | translate\"\n [attr.title]=\"'nh-assistant.composer.attach-hint' | translate: uploadParams()\"\n [attr.aria-describedby]=\"attachHintId\"\n (click)=\"fileInput.click()\">\n <nh-assistant-icon name=\"attach\" />\n </button>\n <span class=\"visually-hidden\" [id]=\"attachHintId\">{{ 'nh-assistant.composer.attach-hint' | translate: uploadParams() }}</span>\n <input\n #fileInput\n class=\"visually-hidden\"\n type=\"file\"\n multiple\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [accept]=\"accept()\"\n (change)=\"onFilesChosen($event)\" />\n }\n <div class=\"prompt-controls\">\n <ng-content select=\"[nhAssistantComposerControls]\" />\n </div>\n <div class=\"actions\">\n @if (busy() && canStop()) {\n <button\n class=\"stop\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.composer.stop' | translate\"\n [attr.title]=\"'nh-assistant.composer.stop' | translate\"\n (click)=\"cancel.emit()\">\n <nh-assistant-icon name=\"stop\" />\n </button>\n }\n @if (!busy() || !canStop() || queueWhileBusy()) {\n <button\n class=\"send\"\n type=\"submit\"\n [disabled]=\"!canSend()\"\n [attr.aria-label]=\"(busy() && queueWhileBusy() ? 'nh-assistant.queue.add' : 'nh-assistant.composer.send') | translate\"\n [attr.title]=\"(busy() && queueWhileBusy() ? 'nh-assistant.queue.add' : 'nh-assistant.composer.send') | translate\">\n <nh-assistant-icon name=\"send\" />\n </button>\n }\n </div>\n </div>\n</form>\n\n@if (status() === 'waiting-for-approval') {\n <p class=\"composer-status\" role=\"status\" aria-live=\"polite\">\n @if (statusActor(); as name) {\n {{ 'nh-assistant.composer.status.waiting-for-approval-by' | translate: { name } }}\n } @else {\n {{ 'nh-assistant.composer.status.waiting-for-approval' | translate }}\n }\n </p>\n}\n\n<p class=\"hint\" [id]=\"hintId\">\n @if (tooLong()) {\n <span class=\"error\">{{ 'nh-assistant.composer.too-long' | translate }}</span>\n } @else if (uploading()) {\n <span>{{ 'nh-assistant.composer.uploading-hint' | translate }}</span>\n } @else {\n <span>{{ (busy() && queueWhileBusy() ? 'nh-assistant.queue.hint' : 'nh-assistant.composer.hint') | translate }}</span>\n }\n @if (showCounter()) {\n <span class=\"counter\" [class.error]=\"tooLong()\">\n {{ 'nh-assistant.composer.characters' | translate: { count: length(), max: maxLength() } }}\n </span>\n }\n</p>\n", styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:block}.composer{display:flex;flex-direction:column;gap:8px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:8px 8px 8px 14px;background:var(--nha-surface);box-shadow:0 2px 6px color-mix(in srgb,var(--nha-text) 4%,transparent)}.composer:focus-within{border-color:var(--nha-focus);box-shadow:0 0 0 2px color-mix(in srgb,var(--nha-focus) 16%,transparent)}textarea{width:100%;min-width:0;min-height:44px;max-height:180px;border:0;padding:6px 0;color:var(--nha-text);background:transparent;font:inherit;font-size:14px;line-height:1.45;resize:vertical;outline:none}textarea::placeholder{color:var(--nha-text-muted);opacity:1}textarea:disabled{cursor:not-allowed}.prompt-bar{display:flex;align-items:center;gap:8px;min-width:0}.prompt-controls{flex:1;min-width:0}.actions{display:flex;flex-shrink:0;gap:6px}.send,.stop{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border:1px solid transparent;border-radius:var(--nha-radius-control);color:var(--nha-text-muted);background:transparent;cursor:pointer;width:40px;height:40px}.send:focus-visible,.stop:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.send:hover:not(:disabled),.stop:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}.send:disabled,.stop:disabled{cursor:not-allowed;opacity:.5}.send:not(:disabled){color:var(--nha-accent-contrast);background:var(--nha-accent)}.send:not(:disabled):hover{color:var(--nha-accent-contrast);background:var(--nha-accent);filter:brightness(1.12)}.stop{color:var(--nha-danger);border-color:var(--nha-danger)}.attach{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;flex-shrink:0;width:40px;height:40px}.attach:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.attach:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}.attach:disabled{cursor:not-allowed;opacity:.5}.attachments{display:flex;flex-wrap:wrap;gap:6px;min-width:0;margin:0;padding:0;list-style:none}.attachment{display:flex;align-items:center;gap:8px;min-width:0;max-width:100%;flex:0 1 240px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:4px 4px 4px 10px;background:var(--nha-background)}.attachment[data-state=failed]{flex-basis:100%;border-color:var(--nha-danger);background:var(--nha-danger-soft)}.attachment-icon{flex-shrink:0;color:var(--nha-text-muted)}[data-state=failed] .attachment-icon{color:var(--nha-danger)}.attachment-body{display:flex;flex:1;flex-direction:column;gap:2px;min-width:0}.attachment-name{overflow:hidden;font-size:12px;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.attachment-meta,.attachment-error{font-size:11px;color:var(--nha-text-muted);overflow-wrap:anywhere}.attachment-error{color:var(--nha-danger)}.attachment-progress{width:100%;height:4px;accent-color:var(--nha-accent)}.attachment-remove{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;flex-shrink:0;width:32px;height:32px}.attachment-remove:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.attachment-remove:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}.attachment-remove:disabled{cursor:not-allowed;opacity:.5}.hint,.composer-status{display:flex;justify-content:space-between;gap:8px;margin:6px 2px 0;color:var(--nha-text-muted);font-size:11px}.composer-status{margin-bottom:0}.counter{flex:none;font-variant-numeric:tabular-nums}.error{color:var(--nha-danger)}.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}\n"], dependencies: [{ kind: "component", type: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: NhAssistantTranslatePipe, name: "nhAssistantTranslate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
8815
9777
|
}
|
|
8816
9778
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantComposerComponent, decorators: [{
|
|
8817
9779
|
type: Component,
|
|
8818
|
-
args: [{ selector: 'nh-assistant-composer', standalone: true, imports: [TranslatePipe, NhAssistantIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<form class=\"composer\" (submit)=\"onSubmit($event)\">\n <label class=\"visually-hidden\" [for]=\"inputId\">{{ 'nh-assistant.composer.label' | translate }}</label>\n <textarea\n #textarea\n data-nh-assistant-autofocus\n rows=\"2\"\n [id]=\"inputId\"\n [value]=\"text()\"\n [disabled]=\"disabled()\"\n [attr.placeholder]=\"'nh-assistant.composer.placeholder' | translate\"\n [attr.aria-describedby]=\"hintId\"\n [attr.aria-invalid]=\"tooLong() ? true : null\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"></textarea>\n\n <div class=\"prompt-bar\">\n <div class=\"prompt-controls\">\n <ng-content select=\"[nhAssistantComposerControls]\" />\n </div>\n <div class=\"actions\">\n @if (busy() && canStop()) {\n <button\n class=\"stop\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.composer.stop' | translate\"\n [attr.title]=\"'nh-assistant.composer.stop' | translate\"\n (click)=\"cancel.emit()\">\n <nh-assistant-icon name=\"stop\" />\n </button>\n }\n @if (!busy() || !canStop() || queueWhileBusy()) {\n <button\n class=\"send\"\n type=\"submit\"\n [disabled]=\"!canSend()\"\n [attr.aria-label]=\"(busy() && queueWhileBusy() ? 'nh-assistant.queue.add' : 'nh-assistant.composer.send') | translate\"\n [attr.title]=\"(busy() && queueWhileBusy() ? 'nh-assistant.queue.add' : 'nh-assistant.composer.send') | translate\">\n <nh-assistant-icon name=\"send\" />\n </button>\n }\n </div>\n </div>\n</form>\n\n@if (status() === 'waiting-for-approval') {\n <p class=\"composer-status\" role=\"status\" aria-live=\"polite\">\n @if (statusActor(); as name) {\n {{ 'nh-assistant.composer.status.waiting-for-approval-by' | translate: { name } }}\n } @else {\n {{ 'nh-assistant.composer.status.waiting-for-approval' | translate }}\n }\n </p>\n}\n\n<p class=\"hint\" [id]=\"hintId\">\n @if (tooLong()) {\n <span class=\"error\">{{ 'nh-assistant.composer.too-long' | translate }}</span>\n } @else {\n <span>{{ (busy() && queueWhileBusy() ? 'nh-assistant.queue.hint' : 'nh-assistant.composer.hint') | translate }}</span>\n }\n @if (showCounter()) {\n <span class=\"counter\" [class.error]=\"tooLong()\">\n {{ 'nh-assistant.composer.characters' | translate: { count: length(), max: maxLength() } }}\n </span>\n }\n</p>\n", styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:block}.composer{display:flex;flex-direction:column;gap:8px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:8px 8px 8px 14px;background:var(--nha-surface);box-shadow:0 2px 6px color-mix(in srgb,var(--nha-text) 4%,transparent)}.composer:focus-within{border-color:var(--nha-focus);box-shadow:0 0 0 2px color-mix(in srgb,var(--nha-focus) 16%,transparent)}textarea{width:100%;min-width:0;min-height:44px;max-height:180px;border:0;padding:6px 0;color:var(--nha-text);background:transparent;font:inherit;font-size:14px;line-height:1.45;resize:vertical;outline:none}textarea::placeholder{color:var(--nha-text-muted);opacity:1}textarea:disabled{cursor:not-allowed}.prompt-bar{display:flex;align-items:center;gap:8px;min-width:0}.prompt-controls{flex:1;min-width:0}.actions{display:flex;flex-shrink:0;gap:6px}.send,.stop{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border:1px solid transparent;border-radius:var(--nha-radius-control);color:var(--nha-text-muted);background:transparent;cursor:pointer;width:40px;height:40px}.send:focus-visible,.stop:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.send:hover:not(:disabled),.stop:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}.send:disabled,.stop:disabled{cursor:not-allowed;opacity:.5}.send:not(:disabled){color:var(--nha-accent-contrast);background:var(--nha-accent)}.send:not(:disabled):hover{color:var(--nha-accent-contrast);background:var(--nha-accent);filter:brightness(1.12)}.stop{color:var(--nha-danger);border-color:var(--nha-danger)}.hint,.composer-status{display:flex;justify-content:space-between;gap:8px;margin:6px 2px 0;color:var(--nha-text-muted);font-size:11px}.composer-status{margin-bottom:0}.counter{flex:none;font-variant-numeric:tabular-nums}.error{color:var(--nha-danger)}.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}\n"] }]
|
|
8819
|
-
}], ctorParameters: () => [], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], sendDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "sendDisabled", required: false }] }], busy: [{ type: i0.Input, args: [{ isSignal: true, alias: "busy", required: false }] }], queueWhileBusy: [{ type: i0.Input, args: [{ isSignal: true, alias: "queueWhileBusy", required: false }] }], status: [{ type: i0.Input, args: [{ isSignal: true, alias: "status", required: false }] }], statusActor: [{ type: i0.Input, args: [{ isSignal: true, alias: "statusActor", required: false }] }], canStop: [{ type: i0.Input, args: [{ isSignal: true, alias: "canStop", required: false }] }], maxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLength", required: false }] }], restore: [{ type: i0.Input, args: [{ isSignal: true, alias: "restore", required: false }] }], send: [{ type: i0.Output, args: ["send"] }], cancel: [{ type: i0.Output, args: ["cancel"] }], restored: [{ type: i0.Output, args: ["restored"] }], textarea: [{ type: i0.ViewChild, args: ['textarea', { isSignal: true }] }], onFocusIn: [{
|
|
9780
|
+
args: [{ selector: 'nh-assistant-composer', standalone: true, imports: [TranslatePipe, NhAssistantIconComponent, NhAssistantTranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, template: "<form class=\"composer\" (submit)=\"onSubmit($event)\">\n <label class=\"visually-hidden\" [for]=\"inputId\">{{ 'nh-assistant.composer.label' | translate }}</label>\n <textarea\n #textarea\n data-nh-assistant-autofocus\n rows=\"2\"\n [id]=\"inputId\"\n [value]=\"text()\"\n [disabled]=\"disabled()\"\n [attr.placeholder]=\"'nh-assistant.composer.placeholder' | translate\"\n [attr.aria-describedby]=\"hintId\"\n [attr.aria-invalid]=\"tooLong() ? true : null\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"></textarea>\n\n @if (attachments().length > 0) {\n <ul class=\"attachments\" [id]=\"attachmentsId\" [attr.aria-label]=\"'nh-assistant.composer.attachments' | translate\">\n @for (attachment of attachments(); track attachment.id) {\n <li class=\"attachment\" [attr.data-state]=\"attachment.state\">\n <nh-assistant-icon class=\"attachment-icon\" [name]=\"attachment.state === 'failed' ? 'warning' : 'file'\" />\n <div class=\"attachment-body\">\n <span class=\"attachment-name\" [attr.title]=\"attachment.name\">{{ attachment.name }}</span>\n @switch (attachment.state) {\n @case ('uploading') {\n <progress\n class=\"attachment-progress\"\n max=\"100\"\n [value]=\"percent(attachment)\"\n [attr.aria-label]=\"'nh-assistant.composer.uploading' | translate: { name: attachment.name }\"></progress>\n }\n @case ('failed') {\n <span class=\"attachment-error\" role=\"alert\">\n {{ errorKeys(attachment) | nhAssistantTranslate: (attachment.error?.code ?? '') : uploadParams() }}\n </span>\n }\n @default {\n <span class=\"attachment-meta\">{{ size(attachment.size) }}</span>\n }\n }\n </div>\n <button\n class=\"attachment-remove\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.composer.remove-attachment' | translate: { name: attachment.name }\"\n [attr.title]=\"'nh-assistant.composer.remove-attachment' | translate: { name: attachment.name }\"\n (click)=\"remove(attachment)\">\n <nh-assistant-icon name=\"close\" />\n </button>\n </li>\n }\n </ul>\n }\n\n <div class=\"prompt-bar\">\n @if (uploads()) {\n <button\n #attachButton\n class=\"attach\"\n type=\"button\"\n [disabled]=\"disabled()\"\n [attr.aria-label]=\"'nh-assistant.composer.attach' | translate\"\n [attr.title]=\"'nh-assistant.composer.attach-hint' | translate: uploadParams()\"\n [attr.aria-describedby]=\"attachHintId\"\n (click)=\"fileInput.click()\">\n <nh-assistant-icon name=\"attach\" />\n </button>\n <span class=\"visually-hidden\" [id]=\"attachHintId\">{{ 'nh-assistant.composer.attach-hint' | translate: uploadParams() }}</span>\n <input\n #fileInput\n class=\"visually-hidden\"\n type=\"file\"\n multiple\n tabindex=\"-1\"\n aria-hidden=\"true\"\n [accept]=\"accept()\"\n (change)=\"onFilesChosen($event)\" />\n }\n <div class=\"prompt-controls\">\n <ng-content select=\"[nhAssistantComposerControls]\" />\n </div>\n <div class=\"actions\">\n @if (busy() && canStop()) {\n <button\n class=\"stop\"\n type=\"button\"\n [attr.aria-label]=\"'nh-assistant.composer.stop' | translate\"\n [attr.title]=\"'nh-assistant.composer.stop' | translate\"\n (click)=\"cancel.emit()\">\n <nh-assistant-icon name=\"stop\" />\n </button>\n }\n @if (!busy() || !canStop() || queueWhileBusy()) {\n <button\n class=\"send\"\n type=\"submit\"\n [disabled]=\"!canSend()\"\n [attr.aria-label]=\"(busy() && queueWhileBusy() ? 'nh-assistant.queue.add' : 'nh-assistant.composer.send') | translate\"\n [attr.title]=\"(busy() && queueWhileBusy() ? 'nh-assistant.queue.add' : 'nh-assistant.composer.send') | translate\">\n <nh-assistant-icon name=\"send\" />\n </button>\n }\n </div>\n </div>\n</form>\n\n@if (status() === 'waiting-for-approval') {\n <p class=\"composer-status\" role=\"status\" aria-live=\"polite\">\n @if (statusActor(); as name) {\n {{ 'nh-assistant.composer.status.waiting-for-approval-by' | translate: { name } }}\n } @else {\n {{ 'nh-assistant.composer.status.waiting-for-approval' | translate }}\n }\n </p>\n}\n\n<p class=\"hint\" [id]=\"hintId\">\n @if (tooLong()) {\n <span class=\"error\">{{ 'nh-assistant.composer.too-long' | translate }}</span>\n } @else if (uploading()) {\n <span>{{ 'nh-assistant.composer.uploading-hint' | translate }}</span>\n } @else {\n <span>{{ (busy() && queueWhileBusy() ? 'nh-assistant.queue.hint' : 'nh-assistant.composer.hint') | translate }}</span>\n }\n @if (showCounter()) {\n <span class=\"counter\" [class.error]=\"tooLong()\">\n {{ 'nh-assistant.composer.characters' | translate: { count: length(), max: maxLength() } }}\n </span>\n }\n</p>\n", styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:block}.composer{display:flex;flex-direction:column;gap:8px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:8px 8px 8px 14px;background:var(--nha-surface);box-shadow:0 2px 6px color-mix(in srgb,var(--nha-text) 4%,transparent)}.composer:focus-within{border-color:var(--nha-focus);box-shadow:0 0 0 2px color-mix(in srgb,var(--nha-focus) 16%,transparent)}textarea{width:100%;min-width:0;min-height:44px;max-height:180px;border:0;padding:6px 0;color:var(--nha-text);background:transparent;font:inherit;font-size:14px;line-height:1.45;resize:vertical;outline:none}textarea::placeholder{color:var(--nha-text-muted);opacity:1}textarea:disabled{cursor:not-allowed}.prompt-bar{display:flex;align-items:center;gap:8px;min-width:0}.prompt-controls{flex:1;min-width:0}.actions{display:flex;flex-shrink:0;gap:6px}.send,.stop{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border:1px solid transparent;border-radius:var(--nha-radius-control);color:var(--nha-text-muted);background:transparent;cursor:pointer;width:40px;height:40px}.send:focus-visible,.stop:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.send:hover:not(:disabled),.stop:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}.send:disabled,.stop:disabled{cursor:not-allowed;opacity:.5}.send:not(:disabled){color:var(--nha-accent-contrast);background:var(--nha-accent)}.send:not(:disabled):hover{color:var(--nha-accent-contrast);background:var(--nha-accent);filter:brightness(1.12)}.stop{color:var(--nha-danger);border-color:var(--nha-danger)}.attach{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;flex-shrink:0;width:40px;height:40px}.attach:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.attach:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}.attach:disabled{cursor:not-allowed;opacity:.5}.attachments{display:flex;flex-wrap:wrap;gap:6px;min-width:0;margin:0;padding:0;list-style:none}.attachment{display:flex;align-items:center;gap:8px;min-width:0;max-width:100%;flex:0 1 240px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);padding:4px 4px 4px 10px;background:var(--nha-background)}.attachment[data-state=failed]{flex-basis:100%;border-color:var(--nha-danger);background:var(--nha-danger-soft)}.attachment-icon{flex-shrink:0;color:var(--nha-text-muted)}[data-state=failed] .attachment-icon{color:var(--nha-danger)}.attachment-body{display:flex;flex:1;flex-direction:column;gap:2px;min-width:0}.attachment-name{overflow:hidden;font-size:12px;font-weight:600;text-overflow:ellipsis;white-space:nowrap}.attachment-meta,.attachment-error{font-size:11px;color:var(--nha-text-muted);overflow-wrap:anywhere}.attachment-error{color:var(--nha-danger)}.attachment-progress{width:100%;height:4px;accent-color:var(--nha-accent)}.attachment-remove{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;flex-shrink:0;width:32px;height:32px}.attachment-remove:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.attachment-remove:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}.attachment-remove:disabled{cursor:not-allowed;opacity:.5}.hint,.composer-status{display:flex;justify-content:space-between;gap:8px;margin:6px 2px 0;color:var(--nha-text-muted);font-size:11px}.composer-status{margin-bottom:0}.counter{flex:none;font-variant-numeric:tabular-nums}.error{color:var(--nha-danger)}.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}\n"] }]
|
|
9781
|
+
}], ctorParameters: () => [], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], sendDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "sendDisabled", required: false }] }], busy: [{ type: i0.Input, args: [{ isSignal: true, alias: "busy", required: false }] }], queueWhileBusy: [{ type: i0.Input, args: [{ isSignal: true, alias: "queueWhileBusy", required: false }] }], status: [{ type: i0.Input, args: [{ isSignal: true, alias: "status", required: false }] }], statusActor: [{ type: i0.Input, args: [{ isSignal: true, alias: "statusActor", required: false }] }], canStop: [{ type: i0.Input, args: [{ isSignal: true, alias: "canStop", required: false }] }], maxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLength", required: false }] }], restore: [{ type: i0.Input, args: [{ isSignal: true, alias: "restore", required: false }] }], uploads: [{ type: i0.Input, args: [{ isSignal: true, alias: "uploads", required: false }] }], attachments: [{ type: i0.Input, args: [{ isSignal: true, alias: "attachments", required: false }] }], send: [{ type: i0.Output, args: ["send"] }], cancel: [{ type: i0.Output, args: ["cancel"] }], restored: [{ type: i0.Output, args: ["restored"] }], attach: [{ type: i0.Output, args: ["attach"] }], removeAttachment: [{ type: i0.Output, args: ["removeAttachment"] }], textarea: [{ type: i0.ViewChild, args: ['textarea', { isSignal: true }] }], attachButton: [{ type: i0.ViewChild, args: ['attachButton', { isSignal: true }] }], onFocusIn: [{
|
|
8820
9782
|
type: HostListener,
|
|
8821
9783
|
args: ['focusin']
|
|
8822
9784
|
}], onFocusOut: [{
|
|
@@ -9328,15 +10290,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImpo
|
|
|
9328
10290
|
}] });
|
|
9329
10291
|
|
|
9330
10292
|
/**
|
|
9331
|
-
* Groups consecutive tool calls of a message. Text and approvals break a
|
|
9332
|
-
* approval always stays visible. A group's key is its first invocation id, so it
|
|
9333
|
-
* its identity while later calls of the same run are appended.
|
|
10293
|
+
* Groups consecutive tool calls of a message, and consecutive files. Text and approvals break a
|
|
10294
|
+
* group, so an approval always stays visible. A tool group's key is its first invocation id, so it
|
|
10295
|
+
* keeps its identity while later calls of the same run are appended.
|
|
9334
10296
|
*/
|
|
9335
10297
|
function groupNhAssistantParts(parts) {
|
|
9336
10298
|
const groups = [];
|
|
9337
10299
|
let tools = null;
|
|
10300
|
+
let files = null;
|
|
9338
10301
|
parts.forEach((part, index) => {
|
|
9339
10302
|
if (part.type === 'tool-call') {
|
|
10303
|
+
files = null;
|
|
9340
10304
|
if (!tools) {
|
|
9341
10305
|
tools = { kind: 'tools', key: `tools:${part.invocationId}`, calls: [] };
|
|
9342
10306
|
groups.push(tools);
|
|
@@ -9345,6 +10309,15 @@ function groupNhAssistantParts(parts) {
|
|
|
9345
10309
|
return;
|
|
9346
10310
|
}
|
|
9347
10311
|
tools = null;
|
|
10312
|
+
if (part.type === 'file') {
|
|
10313
|
+
if (!files) {
|
|
10314
|
+
files = { kind: 'files', key: `files:${index}`, files: [] };
|
|
10315
|
+
groups.push(files);
|
|
10316
|
+
}
|
|
10317
|
+
files.files.push(part);
|
|
10318
|
+
return;
|
|
10319
|
+
}
|
|
10320
|
+
files = null;
|
|
9348
10321
|
if (part.type === 'approval') {
|
|
9349
10322
|
groups.push({ kind: 'approval', key: `approval:${part.approvalId}`, part });
|
|
9350
10323
|
}
|
|
@@ -9800,11 +10773,19 @@ const stickToBottomDistance = 120;
|
|
|
9800
10773
|
/**
|
|
9801
10774
|
* The messages of a conversation as a live log. Assistant text renders as sanitized
|
|
9802
10775
|
* Markdown, consecutive tool calls as one collapsible group that counts along, and
|
|
9803
|
-
* approvals as cards.
|
|
9804
|
-
*
|
|
9805
|
-
*
|
|
10776
|
+
* approvals as cards. The files a person attached show by name, type and size. Messages a
|
|
10777
|
+
* trigger wrote into an agent thread read as events, not as a person. While a turn runs
|
|
10778
|
+
* without visible progress, a working indicator shows the elapsed time. The thread follows
|
|
10779
|
+
* new content while the user is at the bottom.
|
|
9806
10780
|
*/
|
|
9807
10781
|
class NhAssistantThreadComponent {
|
|
10782
|
+
/** The type and size of an attached file, in the current language. */
|
|
10783
|
+
fileMeta(file) {
|
|
10784
|
+
return {
|
|
10785
|
+
type: nhAssistantFileTypeLabel(file.mediaType),
|
|
10786
|
+
size: nhAssistantFormatFileSize(file.size, this.translate?.getCurrentLang())
|
|
10787
|
+
};
|
|
10788
|
+
}
|
|
9808
10789
|
/** Asks the question again that the assistant message with a connection card answered. */
|
|
9809
10790
|
retryAfterConnect(message) {
|
|
9810
10791
|
const messages = this.messages();
|
|
@@ -9823,6 +10804,7 @@ class NhAssistantThreadComponent {
|
|
|
9823
10804
|
}
|
|
9824
10805
|
constructor() {
|
|
9825
10806
|
this.injector = inject(Injector);
|
|
10807
|
+
this.translate = inject(TranslateService, { optional: true });
|
|
9826
10808
|
this.messages = input.required(...(ngDevMode ? [{ debugName: "messages" }] : []));
|
|
9827
10809
|
this.streaming = input(false, ...(ngDevMode ? [{ debugName: "streaming" }] : []));
|
|
9828
10810
|
this.deciding = input(false, ...(ngDevMode ? [{ debugName: "deciding" }] : []));
|
|
@@ -9957,7 +10939,7 @@ class NhAssistantThreadComponent {
|
|
|
9957
10939
|
}
|
|
9958
10940
|
}
|
|
9959
10941
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantThreadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
9960
|
-
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 ('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%}: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: 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 }); }
|
|
10942
|
+
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 }); }
|
|
9961
10943
|
}
|
|
9962
10944
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantThreadComponent, decorators: [{
|
|
9963
10945
|
type: Component,
|
|
@@ -9965,12 +10947,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImpo
|
|
|
9965
10947
|
NgTemplateOutlet,
|
|
9966
10948
|
ScrollingModule,
|
|
9967
10949
|
TranslatePipe,
|
|
10950
|
+
NhAssistantIconComponent,
|
|
9968
10951
|
NhAssistantMarkdownPipe,
|
|
9969
10952
|
NhAssistantPartGroupsPipe,
|
|
9970
10953
|
NhAssistantToolCallGroupComponent,
|
|
9971
10954
|
NhAssistantApprovalCardComponent,
|
|
9972
10955
|
NhAssistantConnectionRequiredComponent
|
|
9973
|
-
], 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 ('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%}: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"] }]
|
|
10956
|
+
], 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"] }]
|
|
9974
10957
|
}], 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 }] }] } });
|
|
9975
10958
|
|
|
9976
10959
|
let nextId$1 = 0;
|
|
@@ -10128,7 +11111,15 @@ class NhAssistantPanelComponent {
|
|
|
10128
11111
|
this.showConversations.set(false);
|
|
10129
11112
|
this.showPreferences.set(false);
|
|
10130
11113
|
this.showShare.set(false);
|
|
10131
|
-
void this.store.submit(text);
|
|
11114
|
+
void this.store.submit(text, this.store.attachedFiles());
|
|
11115
|
+
}
|
|
11116
|
+
/** Uploads the files the person attached to the next message. */
|
|
11117
|
+
attach(files) {
|
|
11118
|
+
void this.store.attach(files);
|
|
11119
|
+
}
|
|
11120
|
+
/** The names of the files a queued message carries, as the queue lists them. */
|
|
11121
|
+
fileNames(files) {
|
|
11122
|
+
return files.map(file => file.name).join(', ');
|
|
10132
11123
|
}
|
|
10133
11124
|
editQueuedMessage(id, text) {
|
|
10134
11125
|
this.resumeAfterEditing = !this.store.queuePaused();
|
|
@@ -10188,7 +11179,7 @@ class NhAssistantPanelComponent {
|
|
|
10188
11179
|
void this.store.cancel();
|
|
10189
11180
|
}
|
|
10190
11181
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
10191
|
-
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.state !== 'queued') {\n <span class=\"queue-state\">{{ 'nh-assistant.queue.state.' + item.state | translate }}</span>\n }\n </div>\n <div class=\"queue-buttons\">\n @if (store.canSteer() && item.state === 'queued') {\n <button type=\"button\" class=\"queue-steer\" [disabled]=\"editingQueuedId() !== null\"\n [attr.title]=\"'nh-assistant.queue.steer-hint' | translate\"\n (click)=\"store.steerQueuedMessage(item.id)\">{{ 'nh-assistant.queue.steer' | translate }}</button>\n }\n <button type=\"button\" class=\"icon-button\" [disabled]=\"item.state === 'sending'\"\n [attr.aria-label]=\"'nh-assistant.queue.edit' | translate\" [attr.title]=\"'nh-assistant.queue.edit' | translate\"\n (click)=\"editQueuedMessage(item.id, item.text)\"><nh-assistant-icon name=\"edit\" /></button>\n <button type=\"button\" class=\"icon-button\" [disabled]=\"item.state === 'sending'\"\n [attr.aria-label]=\"'nh-assistant.queue.remove' | translate\" [attr.title]=\"'nh-assistant.queue.remove' | translate\"\n (click)=\"store.removeQueuedMessage(item.id)\"><nh-assistant-icon name=\"close\" /></button>\n </div>\n }\n </li>\n }\n </ol>\n </section>\n }\n <nh-assistant-composer\n [disabled]=\"closed()\"\n [sendDisabled]=\"composerDisabled()\"\n [busy]=\"busy()\"\n [queueWhileBusy]=\"true\"\n [status]=\"composerStatus()\"\n [statusActor]=\"activeMemberName()\"\n [canStop]=\"canStop()\"\n [maxLength]=\"store.limits()?.maxMessageChars ?? null\"\n [restore]=\"store.restoredDraft()\"\n (restored)=\"store.consumeRestoredDraft()\"\n (send)=\"send($event)\"\n (cancel)=\"cancel()\">\n @if (store.agents().length > 1) {\n <nh-assistant-agent-picker\n nhAssistantComposerControls\n [compact]=\"true\"\n [agents]=\"store.agents()\"\n [selectedAgentId]=\"store.selectedAgentId()\"\n (agentChange)=\"store.selectAgent($event)\" />\n }\n </nh-assistant-composer>\n </footer>\n }\n </section>\n</ng-template>\n", styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}.drawer{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){.drawer{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:contents}.drawer{position:relative;display:flex;flex-direction:column;box-sizing:border-box;width:100%;height:100%;border-left:1px solid var(--nha-border);color:var(--nha-text);background:var(--nha-background);box-shadow:var(--nha-shadow);font-family:var(--nha-font);font-size:14px}.drawer *,.drawer *:before,.drawer *:after{box-sizing:border-box}.header{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:6px 12px;border-bottom:1px solid var(--nha-border);padding:12px 12px 12px 16px}.heading{display:contents}h2{grid-row:1;min-width:0;margin:0;overflow:hidden;font-size:16px;font-weight:750;letter-spacing:-.01em;text-overflow:ellipsis;white-space:nowrap}.header-actions{display:flex;flex:none;grid-column:2;grid-row:1;gap:2px;min-width:0}.header-actions .icon-button{width:32px;height:32px}.icon-button{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border:1px solid transparent;border-radius:var(--nha-radius-control);color:var(--nha-text-muted);background:transparent;cursor:pointer;text-decoration:none}.icon-button:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.icon-button:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}.icon-button:disabled{cursor:not-allowed;opacity:.5}.icon-button.active{color:var(--nha-highlight);background:var(--nha-surface-strong)}.body{position:relative;display:flex;flex:1;flex-direction:column;min-height:0;background:var(--nha-background)}.thread{flex:1;min-height:0}.overlay-view{position:absolute;z-index:1;inset:0;border-bottom:1px solid var(--nha-border);background:var(--nha-background)}.state{display:flex;flex:1;flex-direction:column;align-items:center;justify-content:center;gap:4px;padding:24px;color:var(--nha-text-muted);text-align:center}.state p{max-width:32ch;margin:0;font-size:13px}.state-icon{margin-bottom:8px;color:var(--nha-highlight);font-size:28px}.state .state-title{color:var(--nha-text);font-size:15px;font-weight:700}.error{display:flex;align-items:center;gap:8px;margin:0 12px 8px;border:1px solid var(--nha-danger);border-radius:var(--nha-radius-control);padding:4px 4px 4px 12px;color:var(--nha-danger);background:var(--nha-danger-soft)}.error p{flex:1;margin:0;color:var(--nha-text);font-size:13px}.turn-notice{display:flex;align-items:center;gap:8px;margin:0 12px 8px;border:1px solid var(--nha-warning);border-radius:var(--nha-radius-control);padding:4px 4px 4px 12px;color:var(--nha-warning);background:var(--nha-warning-soft)}.turn-notice p{flex:1;margin:0;color:var(--nha-text);font-size:13px}.footer{padding:12px 16px 14px}.conversation-actions{display:flex;align-items:center;gap:4px;margin:0 -4px 6px}.page-context-button{margin-left:auto}.message-queue{margin-bottom:10px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);background:var(--nha-background);overflow:hidden;font-size:12px}.queue-heading{display:flex;justify-content:space-between;align-items:center;gap:8px;padding:8px 10px;color:var(--nha-text-muted)}.queue-items{max-height:180px;overflow-y:auto;margin:0;padding:0;list-style:none}.queue-item{display:flex;align-items:center;gap:8px;border-top:1px solid var(--nha-border);padding:6px 8px 6px 10px}.queue-item.failed{border-left:2px solid var(--nha-danger)}.queue-message{flex:1;min-width:0}.queue-message p{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere;margin:0}.queue-state{color:var(--nha-text-muted);font-size:11px}.queue-buttons{display:flex;flex-shrink:0;align-items:center;gap:2px}.queue-buttons .icon-button{width:28px;height:30px}.queue-resume,.queue-steer{border:0;border-radius:6px;padding:6px;color:var(--nha-highlight);background:transparent;font:inherit;cursor:pointer}.queue-resume:hover,.queue-steer:hover{background:var(--nha-surface)}.queue-resume:focus-visible,.queue-steer:focus-visible{outline:2px solid var(--nha-focus)}.queue-resume:disabled,.queue-steer:disabled{cursor:not-allowed;opacity:.5}.queue-edit{width:100%}.queue-edit textarea{box-sizing:border-box;width:100%;border:1px solid var(--nha-border);border-radius:6px;padding:8px;color:var(--nha-text);background:var(--nha-surface);font:inherit;resize:vertical}.queue-edit .queue-buttons{justify-content:flex-end}.notification-button{position:relative}.notification-button.off:after{content:\"\";position:absolute;width:18px;height:2px;border-radius:1px;background:currentColor;transform:rotate(-45deg)}.notification-button.blocked{color:var(--nha-warning)}@media(max-width:359px){.header{gap:6px;padding-inline:12px}.header-actions .icon-button{width:28px;height:32px}}.notice{margin:0 0 8px;color:var(--nha-text-muted);font-size:12px}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: NhAssistantIconComponent, selector: "nh-assistant-icon", inputs: ["name"] }, { kind: "component", type: NhAssistantActivityComponent, selector: "nh-assistant-activity", inputs: ["items"], outputs: ["conversationSelect"] }, { kind: "component", type: NhAssistantAgentPickerComponent, selector: "nh-assistant-agent-picker", inputs: ["agents", "selectedAgentId", "disabled", "compact"], outputs: ["agentChange"] }, { kind: "component", type: NhAssistantComposerComponent, selector: "nh-assistant-composer", inputs: ["disabled", "sendDisabled", "busy", "queueWhileBusy", "status", "statusActor", "canStop", "maxLength", "restore"], outputs: ["send", "cancel", "restored"] }, { kind: "component", type: NhAssistantConversationListComponent, selector: "nh-assistant-conversation-list", inputs: ["conversations", "total", "activeConversationId", "loading", "disabled"], outputs: ["conversationSelect", "conversationDelete", "conversationCreate"] }, { kind: "component", type: NhAssistantPreferencesComponent, selector: "nh-assistant-preferences", outputs: ["closed", "saved"] }, { kind: "component", type: NhAssistantShareComponent, selector: "nh-assistant-share", inputs: ["section"], outputs: ["closed"] }, { kind: "component", type: NhAssistantThreadComponent, selector: "nh-assistant-thread", inputs: ["messages", "streaming", "deciding", "markdown", "members", "currentActorId", "canDecide", "decisionOwner", "approvalRights"], outputs: ["approvalDecision", "approvalChoice", "retryMessage"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "pipe", type: NhAssistantTranslatePipe, name: "nhAssistantTranslate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
11182
|
+
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 }); }
|
|
10192
11183
|
}
|
|
10193
11184
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImport: i0, type: NhAssistantPanelComponent, decorators: [{
|
|
10194
11185
|
type: Component,
|
|
@@ -10204,7 +11195,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImpo
|
|
|
10204
11195
|
NhAssistantPreferencesComponent,
|
|
10205
11196
|
NhAssistantShareComponent,
|
|
10206
11197
|
NhAssistantThreadComponent
|
|
10207
|
-
], 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.state !== 'queued') {\n <span class=\"queue-state\">{{ 'nh-assistant.queue.state.' + item.state | translate }}</span>\n }\n </div>\n <div class=\"queue-buttons\">\n @if (store.canSteer() && item.state === 'queued') {\n <button type=\"button\" class=\"queue-steer\" [disabled]=\"editingQueuedId() !== null\"\n [attr.title]=\"'nh-assistant.queue.steer-hint' | translate\"\n (click)=\"store.steerQueuedMessage(item.id)\">{{ 'nh-assistant.queue.steer' | translate }}</button>\n }\n <button type=\"button\" class=\"icon-button\" [disabled]=\"item.state === 'sending'\"\n [attr.aria-label]=\"'nh-assistant.queue.edit' | translate\" [attr.title]=\"'nh-assistant.queue.edit' | translate\"\n (click)=\"editQueuedMessage(item.id, item.text)\"><nh-assistant-icon name=\"edit\" /></button>\n <button type=\"button\" class=\"icon-button\" [disabled]=\"item.state === 'sending'\"\n [attr.aria-label]=\"'nh-assistant.queue.remove' | translate\" [attr.title]=\"'nh-assistant.queue.remove' | translate\"\n (click)=\"store.removeQueuedMessage(item.id)\"><nh-assistant-icon name=\"close\" /></button>\n </div>\n }\n </li>\n }\n </ol>\n </section>\n }\n <nh-assistant-composer\n [disabled]=\"closed()\"\n [sendDisabled]=\"composerDisabled()\"\n [busy]=\"busy()\"\n [queueWhileBusy]=\"true\"\n [status]=\"composerStatus()\"\n [statusActor]=\"activeMemberName()\"\n [canStop]=\"canStop()\"\n [maxLength]=\"store.limits()?.maxMessageChars ?? null\"\n [restore]=\"store.restoredDraft()\"\n (restored)=\"store.consumeRestoredDraft()\"\n (send)=\"send($event)\"\n (cancel)=\"cancel()\">\n @if (store.agents().length > 1) {\n <nh-assistant-agent-picker\n nhAssistantComposerControls\n [compact]=\"true\"\n [agents]=\"store.agents()\"\n [selectedAgentId]=\"store.selectedAgentId()\"\n (agentChange)=\"store.selectAgent($event)\" />\n }\n </nh-assistant-composer>\n </footer>\n }\n </section>\n</ng-template>\n", styles: [":host{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){:host{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}.drawer{--nha-font: var(--nh-assistant-font, inherit);--nha-background: var(--nh-assistant-background, #ffffff);--nha-surface: var(--nh-assistant-surface, #f5f7fb);--nha-surface-strong: var(--nh-assistant-surface-strong, #eceff6);--nha-text: var(--nh-assistant-text, #080c1d);--nha-text-muted: var(--nh-assistant-text-muted, #5b6482);--nha-border: var(--nh-assistant-border, #dce0ec);--nha-accent: var(--nh-assistant-accent, #022962);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #c84f00);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #247d4e);--nha-success-soft: var(--nh-assistant-success-soft, #e6f5ec);--nha-warning: var(--nh-assistant-warning, #8a5a00);--nha-warning-soft: var(--nh-assistant-warning-soft, #fff3d9);--nha-danger: var(--nh-assistant-danger, #b92e52);--nha-danger-soft: var(--nh-assistant-danger-soft, #ffe9ef);--nha-focus: var(--nh-assistant-focus, #ff6700);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(2 41 98 / .16));--nha-radius: var(--nh-assistant-radius, 16px);--nha-radius-control: var(--nh-assistant-radius-control, 12px);--nha-mono: var(--nh-assistant-font-mono, ui-monospace, \"Cascadia Code\", Consolas, monospace)}@media(prefers-color-scheme:dark){.drawer{--nha-background: var(--nh-assistant-background, #0b0e22);--nha-surface: var(--nh-assistant-surface, #11152d);--nha-surface-strong: var(--nh-assistant-surface-strong, #181d3b);--nha-text: var(--nh-assistant-text, #ffffff);--nha-text-muted: var(--nh-assistant-text-muted, #b8bed8);--nha-border: var(--nh-assistant-border, #2b3155);--nha-accent: var(--nh-assistant-accent, #0f4087);--nha-accent-contrast: var(--nh-assistant-accent-contrast, #ffffff);--nha-highlight: var(--nh-assistant-highlight, #ff6700);--nha-user-bubble: var(--nh-assistant-user-bubble, #022962);--nha-user-text: var(--nh-assistant-user-text, #ffffff);--nha-success: var(--nh-assistant-success, #67c58f);--nha-success-soft: var(--nh-assistant-success-soft, #102d24);--nha-warning: var(--nh-assistant-warning, #ffbf5b);--nha-warning-soft: var(--nh-assistant-warning-soft, #342818);--nha-danger: var(--nh-assistant-danger, #ff6d8a);--nha-danger-soft: var(--nh-assistant-danger-soft, #351b2a);--nha-shadow: var(--nh-assistant-shadow, 0 16px 36px rgb(0 0 0 / .32))}}:host{display:contents}.drawer{position:relative;display:flex;flex-direction:column;box-sizing:border-box;width:100%;height:100%;border-left:1px solid var(--nha-border);color:var(--nha-text);background:var(--nha-background);box-shadow:var(--nha-shadow);font-family:var(--nha-font);font-size:14px}.drawer *,.drawer *:before,.drawer *:after{box-sizing:border-box}.header{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:6px 12px;border-bottom:1px solid var(--nha-border);padding:12px 12px 12px 16px}.heading{display:contents}h2{grid-row:1;min-width:0;margin:0;overflow:hidden;font-size:16px;font-weight:750;letter-spacing:-.01em;text-overflow:ellipsis;white-space:nowrap}.header-actions{display:flex;flex:none;grid-column:2;grid-row:1;gap:2px;min-width:0}.header-actions .icon-button{width:32px;height:32px}.icon-button{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border:1px solid transparent;border-radius:var(--nha-radius-control);color:var(--nha-text-muted);background:transparent;cursor:pointer;text-decoration:none}.icon-button:focus-visible{outline:3px solid color-mix(in srgb,var(--nha-focus) 80%,transparent);outline-offset:2px}.icon-button:hover:not(:disabled){color:var(--nha-text);background:var(--nha-surface-strong)}.icon-button:disabled{cursor:not-allowed;opacity:.5}.icon-button.active{color:var(--nha-highlight);background:var(--nha-surface-strong)}.body{position:relative;display:flex;flex:1;flex-direction:column;min-height:0;background:var(--nha-background)}.thread{flex:1;min-height:0}.overlay-view{position:absolute;z-index:1;inset:0;border-bottom:1px solid var(--nha-border);background:var(--nha-background)}.state{display:flex;flex:1;flex-direction:column;align-items:center;justify-content:center;gap:4px;padding:24px;color:var(--nha-text-muted);text-align:center}.state p{max-width:32ch;margin:0;font-size:13px}.state-icon{margin-bottom:8px;color:var(--nha-highlight);font-size:28px}.state .state-title{color:var(--nha-text);font-size:15px;font-weight:700}.error{display:flex;align-items:center;gap:8px;margin:0 12px 8px;border:1px solid var(--nha-danger);border-radius:var(--nha-radius-control);padding:4px 4px 4px 12px;color:var(--nha-danger);background:var(--nha-danger-soft)}.error p{flex:1;margin:0;color:var(--nha-text);font-size:13px}.turn-notice{display:flex;align-items:center;gap:8px;margin:0 12px 8px;border:1px solid var(--nha-warning);border-radius:var(--nha-radius-control);padding:4px 4px 4px 12px;color:var(--nha-warning);background:var(--nha-warning-soft)}.turn-notice p{flex:1;margin:0;color:var(--nha-text);font-size:13px}.footer{padding:12px 16px 14px}.conversation-actions{display:flex;align-items:center;gap:4px;margin:0 -4px 6px}.page-context-button{margin-left:auto}.message-queue{margin-bottom:10px;border:1px solid var(--nha-border);border-radius:var(--nha-radius-control);background:var(--nha-background);overflow:hidden;font-size:12px}.queue-heading{display:flex;justify-content:space-between;align-items:center;gap:8px;padding:8px 10px;color:var(--nha-text-muted)}.queue-items{max-height:180px;overflow-y:auto;margin:0;padding:0;list-style:none}.queue-item{display:flex;align-items:center;gap:8px;border-top:1px solid var(--nha-border);padding:6px 8px 6px 10px}.queue-item.failed{border-left:2px solid var(--nha-danger)}.queue-message{flex:1;min-width:0}.queue-message p{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere;margin:0}.queue-state{color:var(--nha-text-muted);font-size:11px}.queue-buttons{display:flex;flex-shrink:0;align-items:center;gap:2px}.queue-buttons .icon-button{width:28px;height:30px}.queue-resume,.queue-steer{border:0;border-radius:6px;padding:6px;color:var(--nha-highlight);background:transparent;font:inherit;cursor:pointer}.queue-resume:hover,.queue-steer:hover{background:var(--nha-surface)}.queue-resume:focus-visible,.queue-steer:focus-visible{outline:2px solid var(--nha-focus)}.queue-resume:disabled,.queue-steer:disabled{cursor:not-allowed;opacity:.5}.queue-edit{width:100%}.queue-edit textarea{box-sizing:border-box;width:100%;border:1px solid var(--nha-border);border-radius:6px;padding:8px;color:var(--nha-text);background:var(--nha-surface);font:inherit;resize:vertical}.queue-edit .queue-buttons{justify-content:flex-end}.notification-button{position:relative}.notification-button.off:after{content:\"\";position:absolute;width:18px;height:2px;border-radius:1px;background:currentColor;transform:rotate(-45deg)}.notification-button.blocked{color:var(--nha-warning)}@media(max-width:359px){.header{gap:6px;padding-inline:12px}.header-actions .icon-button{width:28px;height:32px}}.notice{margin:0 0 8px;color:var(--nha-text-muted);font-size:12px}\n"] }]
|
|
11198
|
+
], 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"] }]
|
|
10208
11199
|
}], ctorParameters: () => [], propDecorators: { drawer: [{ type: i0.ViewChild, args: ['drawer', { isSignal: true }] }] } });
|
|
10209
11200
|
|
|
10210
11201
|
/**
|
|
@@ -10633,5 +11624,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.34", ngImpo
|
|
|
10633
11624
|
* Generated bundle index. Do not edit.
|
|
10634
11625
|
*/
|
|
10635
11626
|
|
|
10636
|
-
export { NH_ASSISTANT_ACCESS_POLICY, NH_ASSISTANT_CONFIG, NH_ASSISTANT_CONVERSATION_FRAGMENT, NH_ASSISTANT_FEEDBACK_CATEGORIES, NH_ASSISTANT_FEEDBACK_GROUPS, NH_ASSISTANT_FETCH, NH_ASSISTANT_ICONS, NH_ASSISTANT_JOIN_FRAGMENT, NH_ASSISTANT_MAX_BULK_FEEDBACK, NH_ASSISTANT_MAX_CUSTOM_INSTRUCTIONS, NH_ASSISTANT_MAX_FEEDBACK_TEXT, NH_ASSISTANT_MAX_PROPOSAL_LOOKUP, NH_ASSISTANT_MAX_SUBJECT_SUMMARY, NH_ASSISTANT_MCP_AUTHORIZATION_MESSAGE, NH_ASSISTANT_MCP_AUTHORIZATION_WINDOW, 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_SSE_EVENT_TYPES, NH_ASSISTANT_SUBJECT_SEARCH, NH_ASSISTANT_SYSTEM_GLOBAL_ID, NH_ASSISTANT_SYSTEM_SUBJECT_TYPE, NH_ASSISTANT_TRANSLATIONS, 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, normalizeNhAssistantClientContext, provideNhAssistant, 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 };
|
|
11627
|
+
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 };
|
|
10637
11628
|
//# sourceMappingURL=newheap-platform-ai-chat.mjs.map
|