@m6d/cortex-client 1.1.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, inject, NgZone, Injectable, signal, DestroyRef, Pipe, input, ChangeDetectionStrategy, Component, computed, forwardRef, effect, viewChild, model, Injector } from '@angular/core';
2
+ import { InjectionToken, inject, NgZone, Injectable, signal, DestroyRef, Pipe, input, ViewEncapsulation, ChangeDetectionStrategy, Component, computed, forwardRef, effect, viewChild, model, Injector } from '@angular/core';
3
3
  import { Chat } from '@ai-sdk/angular';
4
4
  import { DefaultChatTransport, lastAssistantMessageIsCompleteWithToolCalls, isStaticToolUIPart, generateId } from 'ai';
5
5
  import { Observable, retry, timer, share, Subject, of, defer, finalize, tap, switchMap, map, concatMap } from 'rxjs';
@@ -7,11 +7,11 @@ import { fromPromise } from 'rxjs/internal/observable/innerFrom';
7
7
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
8
8
  import * as i1$1 from '@angular/forms';
9
9
  import { FormControl, Validators, FormBuilder, ReactiveFormsModule, NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
10
- import { NgClass, NgTemplateOutlet } from '@angular/common';
11
10
  import { TranslatePipe, TranslateService, provideTranslateService, provideTranslateLoader } from '@ngx-translate/core';
12
11
  import { marked } from 'marked';
13
12
  import DOMPurify from 'dompurify';
14
13
  import * as i1 from '@angular/platform-browser';
14
+ import { NgClass, NgTemplateOutlet, NgComponentOutlet } from '@angular/common';
15
15
  import hljs from 'highlight.js/lib/core';
16
16
  import javascript from 'highlight.js/lib/languages/javascript';
17
17
  import json from 'highlight.js/lib/languages/json';
@@ -83,6 +83,7 @@ class CortexChatService {
83
83
  isAgentWorking = signal(false, ...(ngDevMode ? [{ debugName: "isAgentWorking" }] : /* istanbul ignore next */ []));
84
84
  hasPendingToolCalls = signal(false, ...(ngDevMode ? [{ debugName: "hasPendingToolCalls" }] : /* istanbul ignore next */ []));
85
85
  events = new Subject();
86
+ activeThreadSelectionId = 0;
86
87
  config = inject(CORTEX_CLIENT_CONFIG);
87
88
  wsService = inject(CortexClientWebSocketService);
88
89
  destroyRef = inject(DestroyRef);
@@ -94,16 +95,21 @@ class CortexChatService {
94
95
  return this.events.asObservable();
95
96
  }
96
97
  selectThread(thread, options) {
97
- if (this.isLoadingMessages())
98
- return of();
99
- if (this.selectedThread()?.id === thread.id)
98
+ if (this.selectedThread()?.id === thread.id && this.chat() && !this.isLoadingMessages())
100
99
  return of();
100
+ const selectionId = ++this.activeThreadSelectionId;
101
101
  return (options?.skipLoadingMessages
102
102
  ? of([])
103
103
  : defer(() => {
104
104
  this.isLoadingMessages.set(true);
105
- return fromPromise(this.fetchJson(`/threads/${thread.id}/messages`)).pipe(finalize(() => this.isLoadingMessages.set(false)));
105
+ return fromPromise(this.fetchJson(`/threads/${thread.id}/messages`)).pipe(finalize(() => {
106
+ if (selectionId === this.activeThreadSelectionId) {
107
+ this.isLoadingMessages.set(false);
108
+ }
109
+ }));
106
110
  })).pipe(tap((messages) => {
111
+ if (selectionId !== this.activeThreadSelectionId)
112
+ return;
107
113
  const chat = new Chat({
108
114
  id: thread.id,
109
115
  sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls,
@@ -113,14 +119,13 @@ class CortexChatService {
113
119
  onToolCall: async ({ toolCall }) => {
114
120
  if (!this.config.onToolCall)
115
121
  return;
116
- const tc = toolCall;
117
- const result = this.config.onToolCall(tc);
122
+ const result = this.config.onToolCall(toolCall);
118
123
  if (result == null)
119
124
  return;
120
125
  const output = result instanceof Promise ? await result : result;
121
126
  chat.addToolOutput({
122
- tool: tc.toolName,
123
- toolCallId: tc.toolCallId,
127
+ tool: toolCall.toolName,
128
+ toolCallId: toolCall.toolCallId,
124
129
  output,
125
130
  });
126
131
  },
@@ -130,20 +135,8 @@ class CortexChatService {
130
135
  this.events.next('onError');
131
136
  },
132
137
  onFinish: () => {
133
- const lastAssistantMsg = [...chat.messages]
134
- .reverse()
135
- .find((m) => m.role === 'assistant');
136
- const pending = lastAssistantMsg?.parts?.some((p) => p.type.startsWith('tool-') &&
137
- p.state !== 'output-available' &&
138
- p.state !== 'output-error' &&
139
- p.state !== 'output-denied');
140
- if (pending) {
141
- this.hasPendingToolCalls.set(true);
142
- }
143
- else {
144
- this.isAgentWorking.set(false);
145
- this.hasPendingToolCalls.set(false);
146
- }
138
+ this.isAgentWorking.set(false);
139
+ this.hasPendingToolCalls.set(this.hasIncompleteToolCalls(chat.messages));
147
140
  this.events.next('onFinish');
148
141
  },
149
142
  messages: messages,
@@ -167,27 +160,23 @@ class CortexChatService {
167
160
  fixChat(chat);
168
161
  this.chat.set(chat);
169
162
  this.selectedThread.set(thread);
170
- // Check if loaded messages have pending tool calls
171
- const lastAssistantMsg = [...messages].reverse().find((m) => m.role === 'assistant');
172
- const pending = lastAssistantMsg?.parts?.some((p) => p.type.startsWith('tool-') &&
173
- p.state !== 'output-available' &&
174
- p.state !== 'output-error' &&
175
- p.state !== 'output-denied');
176
- if (pending) {
177
- this.hasPendingToolCalls.set(true);
178
- }
163
+ this.syncConversationState(messages);
179
164
  this.events.next('onThreadSelected');
180
165
  }));
181
166
  }
182
167
  deselectThread() {
183
- if (!this.selectedThread())
184
- return;
168
+ this.activeThreadSelectionId += 1;
185
169
  this.abort();
170
+ this.isLoadingMessages.set(false);
171
+ this.isAgentWorking.set(false);
172
+ this.hasPendingToolCalls.set(false);
173
+ if (!this.selectedThread() && !this.chat())
174
+ return;
186
175
  this.selectedThread.set(undefined);
187
176
  this.chat.set(undefined);
188
177
  }
189
178
  send(prompt) {
190
- if (this.isAgentWorking())
179
+ if (this.isAgentWorking() || this.hasPendingToolCalls())
191
180
  return of();
192
181
  const ensureThread = this.selectedThread()
193
182
  ? of(undefined)
@@ -208,9 +197,12 @@ class CortexChatService {
208
197
  if (!threadId)
209
198
  return;
210
199
  this.reloadMessages(threadId).subscribe((messages) => {
211
- if (!this.chat())
200
+ if (this.chat() !== chat)
212
201
  return;
213
- this.chat().messages = messages;
202
+ if (this.selectedThread()?.id !== threadId)
203
+ return;
204
+ chat.messages = messages;
205
+ this.syncConversationState(messages);
214
206
  });
215
207
  }
216
208
  deleteThread(threadId) {
@@ -243,6 +235,39 @@ class CortexChatService {
243
235
  reloadMessages(threadId) {
244
236
  return fromPromise(this.fetchJson(`/threads/${threadId}/messages`));
245
237
  }
238
+ syncConversationState(messages) {
239
+ this.isAgentWorking.set(false);
240
+ this.hasPendingToolCalls.set(this.hasIncompleteToolCalls(messages));
241
+ }
242
+ hasIncompleteToolCalls(messages) {
243
+ const lastAssistantMessage = this.getLastAssistantMessage(messages);
244
+ if (!lastAssistantMessage)
245
+ return false;
246
+ let lastStepStartIndex = -1;
247
+ for (const [index, part] of lastAssistantMessage.parts.entries()) {
248
+ if (part.type === 'step-start') {
249
+ lastStepStartIndex = index;
250
+ }
251
+ }
252
+ const toolParts = lastAssistantMessage.parts
253
+ .slice(lastStepStartIndex + 1)
254
+ .filter((part) => part.type.startsWith('tool-') &&
255
+ !part.providerExecuted);
256
+ return (toolParts.length > 0 &&
257
+ toolParts.some((part) => {
258
+ const state = part.state;
259
+ return (state !== 'output-available' && state !== 'output-error' && state !== 'output-denied');
260
+ }));
261
+ }
262
+ getLastAssistantMessage(messages) {
263
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
264
+ const message = messages[index];
265
+ if (message.role === 'assistant') {
266
+ return message;
267
+ }
268
+ }
269
+ return undefined;
270
+ }
246
271
  resolveUrl(path) {
247
272
  const baseUrl = this.config.transport.baseUrl;
248
273
  const base = typeof baseUrl === 'string' ? baseUrl : baseUrl();
@@ -325,21 +350,21 @@ class MessageTextPartComponent {
325
350
  role = input.required(...(ngDevMode ? [{ debugName: "role" }] : /* istanbul ignore next */ []));
326
351
  textPart = input.required(...(ngDevMode ? [{ debugName: "textPart" }] : /* istanbul ignore next */ []));
327
352
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: MessageTextPartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
328
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.3", type: MessageTextPartComponent, isStandalone: true, selector: "cortex-message-text-part", inputs: { role: { classPropertyName: "role", publicName: "role", isSignal: true, isRequired: true, transformFunction: null }, textPart: { classPropertyName: "textPart", publicName: "textPart", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div\n class=\"w-full flex flex-col\"\n [ngClass]=\"{\n 'items-end': role() === 'assistant',\n 'items-start': role() === 'user',\n }\"\n>\n <div\n class=\"max-w-4/5 text-[13px] leading-relaxed p-4 rounded-2xl border border-slate-200 prose\"\n [ngClass]=\"{\n 'bg-slate-100 text-slate-700 ltr:rounded-br-none rtl:rounded-bl-none': role() === 'assistant',\n 'bg-slate-50 text-slate-700 ltr:rounded-bl-none rtl:rounded-br-none': role() === 'user',\n }\"\n [innerHTML]=\"textPart().text | marked\"\n ></div>\n</div>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: MarkedPipe, name: "marked" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
353
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.3", type: MessageTextPartComponent, isStandalone: true, selector: "cortex-message-text-part", inputs: { role: { classPropertyName: "role", publicName: "role", isSignal: true, isRequired: true, transformFunction: null }, textPart: { classPropertyName: "textPart", publicName: "textPart", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div\n class=\"cortex-text-part\"\n [class.cortex-text-part--assistant]=\"role() === 'assistant'\"\n [class.cortex-text-part--user]=\"role() === 'user'\"\n>\n <div\n class=\"cortex-text-bubble\"\n [class.cortex-text-bubble--assistant]=\"role() === 'assistant'\"\n [class.cortex-text-bubble--user]=\"role() === 'user'\"\n [innerHTML]=\"textPart().text | marked\"\n ></div>\n</div>\n", styles: [".cortex-text-part{width:100%;display:flex;flex-direction:column}.cortex-text-part--assistant{align-items:flex-end}.cortex-text-part--user{align-items:flex-start}[dir=rtl] .cortex-text-part--assistant{align-items:flex-end}[dir=rtl] .cortex-text-part--user{align-items:flex-start}.cortex-text-bubble{max-width:80%;font-size:.8125rem;line-height:1.625;padding:1rem;border-radius:1rem;border:1px solid #e2e8f0;color:#334155}.cortex-text-bubble--assistant{background:#f1f5f9;border-bottom-right-radius:0}[dir=rtl] .cortex-text-bubble--assistant{border-bottom-right-radius:1rem;border-bottom-left-radius:0}.cortex-text-bubble--user{background:#f8fafc;border-bottom-left-radius:0}[dir=rtl] .cortex-text-bubble--user{border-bottom-left-radius:1rem;border-bottom-right-radius:0}.cortex-text-bubble h1,.cortex-text-bubble h2,.cortex-text-bubble h3,.cortex-text-bubble h4,.cortex-text-bubble h5,.cortex-text-bubble h6{margin-top:1.25em;margin-bottom:.5em;font-weight:600;line-height:1.3;color:#1e293b}.cortex-text-bubble h1:first-child,.cortex-text-bubble h2:first-child,.cortex-text-bubble h3:first-child,.cortex-text-bubble h4:first-child,.cortex-text-bubble h5:first-child,.cortex-text-bubble h6:first-child{margin-top:0}.cortex-text-bubble h1{font-size:1.25em}.cortex-text-bubble h2{font-size:1.125em}.cortex-text-bubble h3{font-size:1em}.cortex-text-bubble p{margin-top:.75em;margin-bottom:.75em}.cortex-text-bubble p:first-child{margin-top:0}.cortex-text-bubble p:last-child{margin-bottom:0}.cortex-text-bubble ul,.cortex-text-bubble ol{margin-top:.75em;margin-bottom:.75em;padding-inline-start:1.5em}.cortex-text-bubble ul{list-style-type:disc}.cortex-text-bubble ol{list-style-type:decimal}.cortex-text-bubble li{margin-top:.25em;margin-bottom:.25em}.cortex-text-bubble code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:.875em;background:#e2e8f0;padding:.125em .375em;border-radius:.25rem}.cortex-text-bubble pre{margin-top:.75em;margin-bottom:.75em;padding:.75em 1em;background:#1e293b;border-radius:.5rem;overflow-x:auto;font-size:.8125em;line-height:1.6}.cortex-text-bubble pre code{background:transparent;padding:0;color:#e2e8f0}.cortex-text-bubble blockquote{margin-top:.75em;margin-bottom:.75em;padding-inline-start:1em;border-inline-start:3px solid #cbd5e1;color:#64748b;font-style:italic}.cortex-text-bubble a{color:#2563eb;text-decoration:underline}.cortex-text-bubble a:hover{color:#1d4ed8}.cortex-text-bubble table{width:100%;border-collapse:collapse;margin-top:.75em;margin-bottom:.75em;font-size:.875em}.cortex-text-bubble th,.cortex-text-bubble td{border:1px solid #e2e8f0;padding:.375em .75em;text-align:start}.cortex-text-bubble th{background:#f8fafc;font-weight:600}.cortex-text-bubble hr{margin-top:1.5em;margin-bottom:1.5em;border:none;border-top:1px solid #e2e8f0}.cortex-text-bubble img{max-width:100%;height:auto;border-radius:.375rem}\n"], dependencies: [{ kind: "pipe", type: MarkedPipe, name: "marked" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
329
354
  }
330
355
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: MessageTextPartComponent, decorators: [{
331
356
  type: Component,
332
- args: [{ selector: 'cortex-message-text-part', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgClass, MarkedPipe], template: "<div\n class=\"w-full flex flex-col\"\n [ngClass]=\"{\n 'items-end': role() === 'assistant',\n 'items-start': role() === 'user',\n }\"\n>\n <div\n class=\"max-w-4/5 text-[13px] leading-relaxed p-4 rounded-2xl border border-slate-200 prose\"\n [ngClass]=\"{\n 'bg-slate-100 text-slate-700 ltr:rounded-br-none rtl:rounded-bl-none': role() === 'assistant',\n 'bg-slate-50 text-slate-700 ltr:rounded-bl-none rtl:rounded-br-none': role() === 'user',\n }\"\n [innerHTML]=\"textPart().text | marked\"\n ></div>\n</div>\n" }]
357
+ args: [{ selector: 'cortex-message-text-part', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [MarkedPipe], template: "<div\n class=\"cortex-text-part\"\n [class.cortex-text-part--assistant]=\"role() === 'assistant'\"\n [class.cortex-text-part--user]=\"role() === 'user'\"\n>\n <div\n class=\"cortex-text-bubble\"\n [class.cortex-text-bubble--assistant]=\"role() === 'assistant'\"\n [class.cortex-text-bubble--user]=\"role() === 'user'\"\n [innerHTML]=\"textPart().text | marked\"\n ></div>\n</div>\n", styles: [".cortex-text-part{width:100%;display:flex;flex-direction:column}.cortex-text-part--assistant{align-items:flex-end}.cortex-text-part--user{align-items:flex-start}[dir=rtl] .cortex-text-part--assistant{align-items:flex-end}[dir=rtl] .cortex-text-part--user{align-items:flex-start}.cortex-text-bubble{max-width:80%;font-size:.8125rem;line-height:1.625;padding:1rem;border-radius:1rem;border:1px solid #e2e8f0;color:#334155}.cortex-text-bubble--assistant{background:#f1f5f9;border-bottom-right-radius:0}[dir=rtl] .cortex-text-bubble--assistant{border-bottom-right-radius:1rem;border-bottom-left-radius:0}.cortex-text-bubble--user{background:#f8fafc;border-bottom-left-radius:0}[dir=rtl] .cortex-text-bubble--user{border-bottom-left-radius:1rem;border-bottom-right-radius:0}.cortex-text-bubble h1,.cortex-text-bubble h2,.cortex-text-bubble h3,.cortex-text-bubble h4,.cortex-text-bubble h5,.cortex-text-bubble h6{margin-top:1.25em;margin-bottom:.5em;font-weight:600;line-height:1.3;color:#1e293b}.cortex-text-bubble h1:first-child,.cortex-text-bubble h2:first-child,.cortex-text-bubble h3:first-child,.cortex-text-bubble h4:first-child,.cortex-text-bubble h5:first-child,.cortex-text-bubble h6:first-child{margin-top:0}.cortex-text-bubble h1{font-size:1.25em}.cortex-text-bubble h2{font-size:1.125em}.cortex-text-bubble h3{font-size:1em}.cortex-text-bubble p{margin-top:.75em;margin-bottom:.75em}.cortex-text-bubble p:first-child{margin-top:0}.cortex-text-bubble p:last-child{margin-bottom:0}.cortex-text-bubble ul,.cortex-text-bubble ol{margin-top:.75em;margin-bottom:.75em;padding-inline-start:1.5em}.cortex-text-bubble ul{list-style-type:disc}.cortex-text-bubble ol{list-style-type:decimal}.cortex-text-bubble li{margin-top:.25em;margin-bottom:.25em}.cortex-text-bubble code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:.875em;background:#e2e8f0;padding:.125em .375em;border-radius:.25rem}.cortex-text-bubble pre{margin-top:.75em;margin-bottom:.75em;padding:.75em 1em;background:#1e293b;border-radius:.5rem;overflow-x:auto;font-size:.8125em;line-height:1.6}.cortex-text-bubble pre code{background:transparent;padding:0;color:#e2e8f0}.cortex-text-bubble blockquote{margin-top:.75em;margin-bottom:.75em;padding-inline-start:1em;border-inline-start:3px solid #cbd5e1;color:#64748b;font-style:italic}.cortex-text-bubble a{color:#2563eb;text-decoration:underline}.cortex-text-bubble a:hover{color:#1d4ed8}.cortex-text-bubble table{width:100%;border-collapse:collapse;margin-top:.75em;margin-bottom:.75em;font-size:.875em}.cortex-text-bubble th,.cortex-text-bubble td{border:1px solid #e2e8f0;padding:.375em .75em;text-align:start}.cortex-text-bubble th{background:#f8fafc;font-weight:600}.cortex-text-bubble hr{margin-top:1.5em;margin-bottom:1.5em;border:none;border-top:1px solid #e2e8f0}.cortex-text-bubble img{max-width:100%;height:auto;border-radius:.375rem}\n"] }]
333
358
  }], propDecorators: { role: [{ type: i0.Input, args: [{ isSignal: true, alias: "role", required: true }] }], textPart: [{ type: i0.Input, args: [{ isSignal: true, alias: "textPart", required: true }] }] } });
334
359
 
335
360
  class MessageReasoningPartComponent {
336
361
  reasoningPart = input.required(...(ngDevMode ? [{ debugName: "reasoningPart" }] : /* istanbul ignore next */ []));
337
362
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: MessageReasoningPartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
338
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.3", type: MessageReasoningPartComponent, isStandalone: true, selector: "cortex-message-reasoning-part", inputs: { reasoningPart: { classPropertyName: "reasoningPart", publicName: "reasoningPart", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "@let p = reasoningPart();\n<details class=\"group w-full rounded-lg border border-slate-200 bg-slate-50 overflow-hidden\">\n <summary\n class=\"list-none [&::-webkit-details-marker]:hidden cursor-pointer select-none px-4 py-3 flex items-center gap-3\"\n >\n <div class=\"flex items-center gap-2 min-w-0 flex-1\">\n <span\n class=\"inline-flex h-6 w-6 items-center justify-center rounded bg-indigo-100 text-indigo-600\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 20 20\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M10 2C6.686 2 4 4.686 4 8c0 1.655.672 3.154 1.757 4.243.362.363.576.858.576 1.371V14.5a1 1 0 0 0 1 1h5.334a1 1 0 0 0 1-1v-.886c0-.513.214-1.008.576-1.371A5.978 5.978 0 0 0 16 8c0-3.314-2.686-6-6-6Z\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n <path\n d=\"M7.5 17.5h5M8.5 8a2 2 0 0 1 2-2\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n\n <div class=\"min-w-0\">\n <div class=\"flex items-center gap-2\">\n <div class=\"font-medium text-slate-700 text-sm\">Reasoning</div>\n @if (p.state) {\n <span\n class=\"inline-flex items-center rounded px-1.5 py-0.5 text-[11px] font-medium\"\n [class.bg-amber-100.text-amber-700]=\"p.state === 'streaming'\"\n [class.bg-emerald-100.text-emerald-700]=\"p.state === 'done'\"\n >\n {{ p.state === 'streaming' ? 'Streaming' : 'Done' }}\n </span>\n }\n </div>\n </div>\n </div>\n\n <span\n class=\"ml-auto inline-flex h-6 w-6 items-center justify-center rounded text-slate-400 transition group-open:rotate-180\"\n aria-hidden=\"true\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path\n d=\"m5.75 8.25 4.25 4.25 4.25-4.25\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n </summary>\n\n <div class=\"px-4 pb-4\">\n <div class=\"mt-1 rounded bg-white border border-slate-200 p-3\">\n <pre\n class=\"m-0 whitespace-pre-wrap wrap-break-word font-mono text-xs leading-relaxed text-slate-700\"\n >{{ p.text.trim() ? p.text : 'No reasoning provided.' }}</pre\n >\n </div>\n </div>\n</details>\n", changeDetection: i0.ChangeDetectionStrategy.OnPush });
363
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.3", type: MessageReasoningPartComponent, isStandalone: true, selector: "cortex-message-reasoning-part", inputs: { reasoningPart: { classPropertyName: "reasoningPart", publicName: "reasoningPart", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "@let p = reasoningPart();\n<details class=\"cortex-reasoning-details\">\n <summary class=\"cortex-reasoning-details__summary\">\n <div class=\"cortex-reasoning-details__header\">\n <span class=\"cortex-reasoning-details__icon\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 20 20\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M10 2C6.686 2 4 4.686 4 8c0 1.655.672 3.154 1.757 4.243.362.363.576.858.576 1.371V14.5a1 1 0 0 0 1 1h5.334a1 1 0 0 0 1-1v-.886c0-.513.214-1.008.576-1.371A5.978 5.978 0 0 0 16 8c0-3.314-2.686-6-6-6Z\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n <path\n d=\"M7.5 17.5h5M8.5 8a2 2 0 0 1 2-2\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n\n <div class=\"cortex-reasoning-details__title-group\">\n <div class=\"cortex-reasoning-details__title-row\">\n <div class=\"cortex-reasoning-details__title\">Reasoning</div>\n @if (p.state) {\n <span\n class=\"cortex-reasoning-details__badge\"\n [ngClass]=\"{\n 'cortex-reasoning-details__badge--streaming': p.state === 'streaming',\n 'cortex-reasoning-details__badge--done': p.state === 'done',\n }\"\n >\n {{ p.state === 'streaming' ? 'Streaming' : 'Done' }}\n </span>\n }\n </div>\n </div>\n </div>\n\n <span class=\"cortex-reasoning-details__chevron\" aria-hidden=\"true\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path\n d=\"m5.75 8.25 4.25 4.25 4.25-4.25\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n </summary>\n\n <div class=\"cortex-reasoning-details__body\">\n <div class=\"cortex-reasoning-details__content\">\n <pre class=\"cortex-reasoning-details__pre\">{{\n p.text.trim() ? p.text : 'No reasoning provided.'\n }}</pre>\n </div>\n </div>\n</details>\n", styles: [".cortex-reasoning-details{width:100%;border-radius:.5rem;border:1px solid #e2e8f0;background:#f8fafc;overflow:hidden}.cortex-reasoning-details__summary{list-style:none;cursor:pointer;-webkit-user-select:none;user-select:none;padding:.75rem 1rem;display:flex;align-items:center;gap:.75rem}.cortex-reasoning-details__summary::-webkit-details-marker{display:none}.cortex-reasoning-details__header{display:flex;align-items:center;gap:.5rem;min-width:0;flex:1}.cortex-reasoning-details__icon{display:inline-flex;height:1.5rem;width:1.5rem;align-items:center;justify-content:center;border-radius:.25rem;background:#e0e7ff;color:#4f46e5}.cortex-reasoning-details__title-group{min-width:0}.cortex-reasoning-details__title-row{display:flex;align-items:center;gap:.5rem}.cortex-reasoning-details__title{font-weight:500;color:#334155;font-size:.875rem}.cortex-reasoning-details__badge{display:inline-flex;align-items:center;border-radius:.25rem;padding:.125rem .375rem;font-size:.6875rem;font-weight:500}.cortex-reasoning-details__badge--streaming{background:#fef3c7;color:#b45309}.cortex-reasoning-details__badge--done{background:#d1fae5;color:#047857}.cortex-reasoning-details__chevron{margin-inline-start:auto;display:inline-flex;height:1.5rem;width:1.5rem;align-items:center;justify-content:center;border-radius:.25rem;color:#94a3b8;transition:transform .2s}details[open]>summary>.cortex-reasoning-details__chevron{transform:rotate(180deg)}.cortex-reasoning-details__body{padding:0 1rem 1rem}.cortex-reasoning-details__content{margin-top:.25rem;border-radius:.25rem;background:#fff;border:1px solid #e2e8f0;padding:.75rem}.cortex-reasoning-details__pre{margin:0;white-space:pre-wrap;overflow-wrap:break-word;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:.75rem;line-height:1.625;color:#334155}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
339
364
  }
340
365
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: MessageReasoningPartComponent, decorators: [{
341
366
  type: Component,
342
- args: [{ selector: 'cortex-message-reasoning-part', changeDetection: ChangeDetectionStrategy.OnPush, template: "@let p = reasoningPart();\n<details class=\"group w-full rounded-lg border border-slate-200 bg-slate-50 overflow-hidden\">\n <summary\n class=\"list-none [&::-webkit-details-marker]:hidden cursor-pointer select-none px-4 py-3 flex items-center gap-3\"\n >\n <div class=\"flex items-center gap-2 min-w-0 flex-1\">\n <span\n class=\"inline-flex h-6 w-6 items-center justify-center rounded bg-indigo-100 text-indigo-600\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 20 20\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M10 2C6.686 2 4 4.686 4 8c0 1.655.672 3.154 1.757 4.243.362.363.576.858.576 1.371V14.5a1 1 0 0 0 1 1h5.334a1 1 0 0 0 1-1v-.886c0-.513.214-1.008.576-1.371A5.978 5.978 0 0 0 16 8c0-3.314-2.686-6-6-6Z\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n <path\n d=\"M7.5 17.5h5M8.5 8a2 2 0 0 1 2-2\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n\n <div class=\"min-w-0\">\n <div class=\"flex items-center gap-2\">\n <div class=\"font-medium text-slate-700 text-sm\">Reasoning</div>\n @if (p.state) {\n <span\n class=\"inline-flex items-center rounded px-1.5 py-0.5 text-[11px] font-medium\"\n [class.bg-amber-100.text-amber-700]=\"p.state === 'streaming'\"\n [class.bg-emerald-100.text-emerald-700]=\"p.state === 'done'\"\n >\n {{ p.state === 'streaming' ? 'Streaming' : 'Done' }}\n </span>\n }\n </div>\n </div>\n </div>\n\n <span\n class=\"ml-auto inline-flex h-6 w-6 items-center justify-center rounded text-slate-400 transition group-open:rotate-180\"\n aria-hidden=\"true\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path\n d=\"m5.75 8.25 4.25 4.25 4.25-4.25\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n </summary>\n\n <div class=\"px-4 pb-4\">\n <div class=\"mt-1 rounded bg-white border border-slate-200 p-3\">\n <pre\n class=\"m-0 whitespace-pre-wrap wrap-break-word font-mono text-xs leading-relaxed text-slate-700\"\n >{{ p.text.trim() ? p.text : 'No reasoning provided.' }}</pre\n >\n </div>\n </div>\n</details>\n" }]
367
+ args: [{ selector: 'cortex-message-reasoning-part', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [NgClass], template: "@let p = reasoningPart();\n<details class=\"cortex-reasoning-details\">\n <summary class=\"cortex-reasoning-details__summary\">\n <div class=\"cortex-reasoning-details__header\">\n <span class=\"cortex-reasoning-details__icon\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 20 20\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M10 2C6.686 2 4 4.686 4 8c0 1.655.672 3.154 1.757 4.243.362.363.576.858.576 1.371V14.5a1 1 0 0 0 1 1h5.334a1 1 0 0 0 1-1v-.886c0-.513.214-1.008.576-1.371A5.978 5.978 0 0 0 16 8c0-3.314-2.686-6-6-6Z\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n <path\n d=\"M7.5 17.5h5M8.5 8a2 2 0 0 1 2-2\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n\n <div class=\"cortex-reasoning-details__title-group\">\n <div class=\"cortex-reasoning-details__title-row\">\n <div class=\"cortex-reasoning-details__title\">Reasoning</div>\n @if (p.state) {\n <span\n class=\"cortex-reasoning-details__badge\"\n [ngClass]=\"{\n 'cortex-reasoning-details__badge--streaming': p.state === 'streaming',\n 'cortex-reasoning-details__badge--done': p.state === 'done',\n }\"\n >\n {{ p.state === 'streaming' ? 'Streaming' : 'Done' }}\n </span>\n }\n </div>\n </div>\n </div>\n\n <span class=\"cortex-reasoning-details__chevron\" aria-hidden=\"true\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path\n d=\"m5.75 8.25 4.25 4.25 4.25-4.25\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n </summary>\n\n <div class=\"cortex-reasoning-details__body\">\n <div class=\"cortex-reasoning-details__content\">\n <pre class=\"cortex-reasoning-details__pre\">{{\n p.text.trim() ? p.text : 'No reasoning provided.'\n }}</pre>\n </div>\n </div>\n</details>\n", styles: [".cortex-reasoning-details{width:100%;border-radius:.5rem;border:1px solid #e2e8f0;background:#f8fafc;overflow:hidden}.cortex-reasoning-details__summary{list-style:none;cursor:pointer;-webkit-user-select:none;user-select:none;padding:.75rem 1rem;display:flex;align-items:center;gap:.75rem}.cortex-reasoning-details__summary::-webkit-details-marker{display:none}.cortex-reasoning-details__header{display:flex;align-items:center;gap:.5rem;min-width:0;flex:1}.cortex-reasoning-details__icon{display:inline-flex;height:1.5rem;width:1.5rem;align-items:center;justify-content:center;border-radius:.25rem;background:#e0e7ff;color:#4f46e5}.cortex-reasoning-details__title-group{min-width:0}.cortex-reasoning-details__title-row{display:flex;align-items:center;gap:.5rem}.cortex-reasoning-details__title{font-weight:500;color:#334155;font-size:.875rem}.cortex-reasoning-details__badge{display:inline-flex;align-items:center;border-radius:.25rem;padding:.125rem .375rem;font-size:.6875rem;font-weight:500}.cortex-reasoning-details__badge--streaming{background:#fef3c7;color:#b45309}.cortex-reasoning-details__badge--done{background:#d1fae5;color:#047857}.cortex-reasoning-details__chevron{margin-inline-start:auto;display:inline-flex;height:1.5rem;width:1.5rem;align-items:center;justify-content:center;border-radius:.25rem;color:#94a3b8;transition:transform .2s}details[open]>summary>.cortex-reasoning-details__chevron{transform:rotate(180deg)}.cortex-reasoning-details__body{padding:0 1rem 1rem}.cortex-reasoning-details__content{margin-top:.25rem;border-radius:.25rem;background:#fff;border:1px solid #e2e8f0;padding:.75rem}.cortex-reasoning-details__pre{margin:0;white-space:pre-wrap;overflow-wrap:break-word;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:.75rem;line-height:1.625;color:#334155}\n"] }]
343
368
  }], propDecorators: { reasoningPart: [{ type: i0.Input, args: [{ isSignal: true, alias: "reasoningPart", required: true }] }] } });
344
369
 
345
370
  hljs.registerLanguage('javascript', javascript);
@@ -463,11 +488,11 @@ class JsonTreeComponent {
463
488
  return value;
464
489
  }
465
490
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: JsonTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
466
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.3", type: JsonTreeComponent, isStandalone: true, selector: "cortex-json-tree", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, expandDepth: { classPropertyName: "expandDepth", publicName: "expandDepth", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block font-mono text-[12px] leading-5 select-text" }, ngImport: i0, template: "<!-- Entry point: parse and render the root value -->\n<ng-container\n *ngTemplateOutlet=\"valueTemplate; context: { $implicit: parsed(data()), path: '$', depth: 0 }\"\n/>\n\n<!-- Recursive value renderer -->\n<ng-template #valueTemplate let-value let-path=\"path\" let-depth=\"depth\">\n @if (isObject(value)) {\n @if (objectLength(value) === 0) {\n <span class=\"jt-bracket\">{{ '{' }}{{ '}' }}</span>\n } @else {\n <span class=\"jt-toggle\" (click)=\"toggle(path, depth); $event.stopPropagation()\" role=\"button\">\n <span class=\"jt-arrow\" [class.jt-arrow--collapsed]=\"isCollapsed(path, depth)\">&#9662;</span>\n <span class=\"jt-bracket\">{{ '{' }}</span>\n </span>\n @if (isCollapsed(path, depth)) {\n <span\n class=\"jt-collapsed-hint\"\n (click)=\"toggle(path, depth); $event.stopPropagation()\"\n role=\"button\"\n >{{ objectLength(value) }}\n {{ objectLength(value) === 1 ? 'property' : 'properties' }}</span\n ><span class=\"jt-bracket\">{{ '}' }}</span>\n } @else {\n <div class=\"jt-indent\">\n @for (entry of objectEntries(value); track entry[0]; let last = $last) {\n <div class=\"jt-line\">\n <span class=\"jt-key\">\"{{ entry[0] }}\"</span><span class=\"jt-colon\">: </span>\n @if (isContainer(entry[1])) {\n <ng-container\n *ngTemplateOutlet=\"\n valueTemplate;\n context: { $implicit: entry[1], path: path + '.' + entry[0], depth: depth + 1 }\n \"\n />\n } @else {\n <span [class]=\"primitiveClass(entry[1])\">{{ formatPrimitive(entry[1]) }}</span>\n }\n @if (!last) {\n <span class=\"jt-comma\">,</span>\n }\n </div>\n }\n </div>\n <span class=\"jt-bracket\">{{ '}' }}</span>\n }\n }\n } @else if (isArray(value)) {\n @if (arrayLength(value) === 0) {\n <span class=\"jt-bracket\">[]</span>\n } @else {\n <span class=\"jt-toggle\" (click)=\"toggle(path, depth); $event.stopPropagation()\" role=\"button\">\n <span class=\"jt-arrow\" [class.jt-arrow--collapsed]=\"isCollapsed(path, depth)\">&#9662;</span>\n <span class=\"jt-bracket\">[</span>\n </span>\n @if (isCollapsed(path, depth)) {\n <span\n class=\"jt-collapsed-hint\"\n (click)=\"toggle(path, depth); $event.stopPropagation()\"\n role=\"button\"\n >{{ arrayLength(value) }} {{ arrayLength(value) === 1 ? 'item' : 'items' }}</span\n ><span class=\"jt-bracket\">]</span>\n } @else {\n <div class=\"jt-indent\">\n @for (item of arrayItems(value); track $index; let last = $last) {\n <div class=\"jt-line\">\n @if (isContainer(item)) {\n <ng-container\n *ngTemplateOutlet=\"\n valueTemplate;\n context: { $implicit: item, path: path + '[' + $index + ']', depth: depth + 1 }\n \"\n />\n } @else {\n <span [class]=\"primitiveClass(item)\">{{ formatPrimitive(item) }}</span>\n }\n @if (!last) {\n <span class=\"jt-comma\">,</span>\n }\n </div>\n }\n </div>\n <span class=\"jt-bracket\">]</span>\n }\n }\n } @else {\n <span [class]=\"primitiveClass(value)\">{{ formatPrimitive(value) }}</span>\n }\n</ng-template>\n", styles: [":host{white-space:pre-wrap;word-break:break-word}.jt-indent{padding-inline-start:1.25em;border-inline-start:1px solid #e2e8f0;margin-inline-start:.3em}.jt-line{position:relative}.jt-toggle{cursor:pointer;-webkit-user-select:none;user-select:none}.jt-toggle:hover .jt-arrow{color:#475569}.jt-arrow{display:inline-block;width:1em;text-align:center;color:#94a3b8;font-size:.7em;transition:transform .15s ease;vertical-align:middle}.jt-arrow--collapsed{transform:rotate(-90deg)}.jt-bracket{color:#64748b}.jt-key{color:#6366f1}.jt-colon{color:#64748b}.jt-comma{color:#94a3b8}.jt-string{color:#059669}.jt-number{color:#2563eb}.jt-boolean{color:#d97706}.jt-null{color:#94a3b8;font-style:italic}.jt-collapsed-hint{display:inline-block;padding:0 .4em;margin:0 .15em;font-size:10px;line-height:1.5;color:#94a3b8;background:#f8fafc;border:1px solid #e2e8f0;border-radius:3px;font-style:italic;cursor:pointer;-webkit-user-select:none;user-select:none;vertical-align:middle}.jt-collapsed-hint:hover{color:#64748b;border-color:#cbd5e1;background:#f1f5f9}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
491
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.3", type: JsonTreeComponent, isStandalone: true, selector: "cortex-json-tree", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: true, transformFunction: null }, expandDepth: { classPropertyName: "expandDepth", publicName: "expandDepth", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "cortex-json-tree" }, ngImport: i0, template: "<!-- Entry point: parse and render the root value -->\n<ng-container\n *ngTemplateOutlet=\"valueTemplate; context: { $implicit: parsed(data()), path: '$', depth: 0 }\"\n/>\n\n<!-- Recursive value renderer -->\n<ng-template #valueTemplate let-value let-path=\"path\" let-depth=\"depth\">\n @if (isObject(value)) {\n @if (objectLength(value) === 0) {\n <span class=\"jt-bracket\">{{ '{' }}{{ '}' }}</span>\n } @else {\n <span class=\"jt-toggle\" (click)=\"toggle(path, depth); $event.stopPropagation()\" role=\"button\">\n <span class=\"jt-arrow\" [class.jt-arrow--collapsed]=\"isCollapsed(path, depth)\">&#9662;</span>\n <span class=\"jt-bracket\">{{ '{' }}</span>\n </span>\n @if (isCollapsed(path, depth)) {\n <span\n class=\"jt-collapsed-hint\"\n (click)=\"toggle(path, depth); $event.stopPropagation()\"\n role=\"button\"\n >{{ objectLength(value) }}\n {{ objectLength(value) === 1 ? 'property' : 'properties' }}</span\n ><span class=\"jt-bracket\">{{ '}' }}</span>\n } @else {\n <div class=\"jt-indent\">\n @for (entry of objectEntries(value); track entry[0]; let last = $last) {\n <div class=\"jt-line\">\n <span class=\"jt-key\">\"{{ entry[0] }}\"</span><span class=\"jt-colon\">: </span>\n @if (isContainer(entry[1])) {\n <ng-container\n *ngTemplateOutlet=\"\n valueTemplate;\n context: { $implicit: entry[1], path: path + '.' + entry[0], depth: depth + 1 }\n \"\n />\n } @else {\n <span [class]=\"primitiveClass(entry[1])\">{{ formatPrimitive(entry[1]) }}</span>\n }\n @if (!last) {\n <span class=\"jt-comma\">,</span>\n }\n </div>\n }\n </div>\n <span class=\"jt-bracket\">{{ '}' }}</span>\n }\n }\n } @else if (isArray(value)) {\n @if (arrayLength(value) === 0) {\n <span class=\"jt-bracket\">[]</span>\n } @else {\n <span class=\"jt-toggle\" (click)=\"toggle(path, depth); $event.stopPropagation()\" role=\"button\">\n <span class=\"jt-arrow\" [class.jt-arrow--collapsed]=\"isCollapsed(path, depth)\">&#9662;</span>\n <span class=\"jt-bracket\">[</span>\n </span>\n @if (isCollapsed(path, depth)) {\n <span\n class=\"jt-collapsed-hint\"\n (click)=\"toggle(path, depth); $event.stopPropagation()\"\n role=\"button\"\n >{{ arrayLength(value) }} {{ arrayLength(value) === 1 ? 'item' : 'items' }}</span\n ><span class=\"jt-bracket\">]</span>\n } @else {\n <div class=\"jt-indent\">\n @for (item of arrayItems(value); track $index; let last = $last) {\n <div class=\"jt-line\">\n @if (isContainer(item)) {\n <ng-container\n *ngTemplateOutlet=\"\n valueTemplate;\n context: { $implicit: item, path: path + '[' + $index + ']', depth: depth + 1 }\n \"\n />\n } @else {\n <span [class]=\"primitiveClass(item)\">{{ formatPrimitive(item) }}</span>\n }\n @if (!last) {\n <span class=\"jt-comma\">,</span>\n }\n </div>\n }\n </div>\n <span class=\"jt-bracket\">]</span>\n }\n }\n } @else {\n <span [class]=\"primitiveClass(value)\">{{ formatPrimitive(value) }}</span>\n }\n</ng-template>\n", styles: [".cortex-json-tree{display:block;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:.75rem;line-height:1.25rem;-webkit-user-select:text;user-select:text;white-space:pre-wrap;word-break:break-word}.jt-indent{padding-inline-start:1.25em;border-inline-start:1px solid #e2e8f0;margin-inline-start:.3em}.jt-line{position:relative}.jt-toggle{cursor:pointer;-webkit-user-select:none;user-select:none}.jt-toggle:hover .jt-arrow{color:#475569}.jt-arrow{display:inline-block;width:1em;text-align:center;color:#94a3b8;font-size:.7em;transition:transform .15s ease;vertical-align:middle}.jt-arrow--collapsed{transform:rotate(-90deg)}.jt-bracket{color:#64748b}.jt-key{color:#6366f1}.jt-colon{color:#64748b}.jt-comma{color:#94a3b8}.jt-string{color:#059669}.jt-number{color:#2563eb}.jt-boolean{color:#d97706}.jt-null{color:#94a3b8;font-style:italic}.jt-collapsed-hint{display:inline-block;padding:0 .4em;margin:0 .15em;font-size:10px;line-height:1.5;color:#94a3b8;background:#f8fafc;border:1px solid #e2e8f0;border-radius:3px;font-style:italic;cursor:pointer;-webkit-user-select:none;user-select:none;vertical-align:middle}.jt-collapsed-hint:hover{color:#64748b;border-color:#cbd5e1;background:#f1f5f9}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
467
492
  }
468
493
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: JsonTreeComponent, decorators: [{
469
494
  type: Component,
470
- args: [{ selector: 'cortex-json-tree', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgTemplateOutlet], host: { class: 'block font-mono text-[12px] leading-5 select-text' }, template: "<!-- Entry point: parse and render the root value -->\n<ng-container\n *ngTemplateOutlet=\"valueTemplate; context: { $implicit: parsed(data()), path: '$', depth: 0 }\"\n/>\n\n<!-- Recursive value renderer -->\n<ng-template #valueTemplate let-value let-path=\"path\" let-depth=\"depth\">\n @if (isObject(value)) {\n @if (objectLength(value) === 0) {\n <span class=\"jt-bracket\">{{ '{' }}{{ '}' }}</span>\n } @else {\n <span class=\"jt-toggle\" (click)=\"toggle(path, depth); $event.stopPropagation()\" role=\"button\">\n <span class=\"jt-arrow\" [class.jt-arrow--collapsed]=\"isCollapsed(path, depth)\">&#9662;</span>\n <span class=\"jt-bracket\">{{ '{' }}</span>\n </span>\n @if (isCollapsed(path, depth)) {\n <span\n class=\"jt-collapsed-hint\"\n (click)=\"toggle(path, depth); $event.stopPropagation()\"\n role=\"button\"\n >{{ objectLength(value) }}\n {{ objectLength(value) === 1 ? 'property' : 'properties' }}</span\n ><span class=\"jt-bracket\">{{ '}' }}</span>\n } @else {\n <div class=\"jt-indent\">\n @for (entry of objectEntries(value); track entry[0]; let last = $last) {\n <div class=\"jt-line\">\n <span class=\"jt-key\">\"{{ entry[0] }}\"</span><span class=\"jt-colon\">: </span>\n @if (isContainer(entry[1])) {\n <ng-container\n *ngTemplateOutlet=\"\n valueTemplate;\n context: { $implicit: entry[1], path: path + '.' + entry[0], depth: depth + 1 }\n \"\n />\n } @else {\n <span [class]=\"primitiveClass(entry[1])\">{{ formatPrimitive(entry[1]) }}</span>\n }\n @if (!last) {\n <span class=\"jt-comma\">,</span>\n }\n </div>\n }\n </div>\n <span class=\"jt-bracket\">{{ '}' }}</span>\n }\n }\n } @else if (isArray(value)) {\n @if (arrayLength(value) === 0) {\n <span class=\"jt-bracket\">[]</span>\n } @else {\n <span class=\"jt-toggle\" (click)=\"toggle(path, depth); $event.stopPropagation()\" role=\"button\">\n <span class=\"jt-arrow\" [class.jt-arrow--collapsed]=\"isCollapsed(path, depth)\">&#9662;</span>\n <span class=\"jt-bracket\">[</span>\n </span>\n @if (isCollapsed(path, depth)) {\n <span\n class=\"jt-collapsed-hint\"\n (click)=\"toggle(path, depth); $event.stopPropagation()\"\n role=\"button\"\n >{{ arrayLength(value) }} {{ arrayLength(value) === 1 ? 'item' : 'items' }}</span\n ><span class=\"jt-bracket\">]</span>\n } @else {\n <div class=\"jt-indent\">\n @for (item of arrayItems(value); track $index; let last = $last) {\n <div class=\"jt-line\">\n @if (isContainer(item)) {\n <ng-container\n *ngTemplateOutlet=\"\n valueTemplate;\n context: { $implicit: item, path: path + '[' + $index + ']', depth: depth + 1 }\n \"\n />\n } @else {\n <span [class]=\"primitiveClass(item)\">{{ formatPrimitive(item) }}</span>\n }\n @if (!last) {\n <span class=\"jt-comma\">,</span>\n }\n </div>\n }\n </div>\n <span class=\"jt-bracket\">]</span>\n }\n }\n } @else {\n <span [class]=\"primitiveClass(value)\">{{ formatPrimitive(value) }}</span>\n }\n</ng-template>\n", styles: [":host{white-space:pre-wrap;word-break:break-word}.jt-indent{padding-inline-start:1.25em;border-inline-start:1px solid #e2e8f0;margin-inline-start:.3em}.jt-line{position:relative}.jt-toggle{cursor:pointer;-webkit-user-select:none;user-select:none}.jt-toggle:hover .jt-arrow{color:#475569}.jt-arrow{display:inline-block;width:1em;text-align:center;color:#94a3b8;font-size:.7em;transition:transform .15s ease;vertical-align:middle}.jt-arrow--collapsed{transform:rotate(-90deg)}.jt-bracket{color:#64748b}.jt-key{color:#6366f1}.jt-colon{color:#64748b}.jt-comma{color:#94a3b8}.jt-string{color:#059669}.jt-number{color:#2563eb}.jt-boolean{color:#d97706}.jt-null{color:#94a3b8;font-style:italic}.jt-collapsed-hint{display:inline-block;padding:0 .4em;margin:0 .15em;font-size:10px;line-height:1.5;color:#94a3b8;background:#f8fafc;border:1px solid #e2e8f0;border-radius:3px;font-style:italic;cursor:pointer;-webkit-user-select:none;user-select:none;vertical-align:middle}.jt-collapsed-hint:hover{color:#64748b;border-color:#cbd5e1;background:#f1f5f9}\n"] }]
495
+ args: [{ selector: 'cortex-json-tree', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [NgTemplateOutlet], host: { class: 'cortex-json-tree' }, template: "<!-- Entry point: parse and render the root value -->\n<ng-container\n *ngTemplateOutlet=\"valueTemplate; context: { $implicit: parsed(data()), path: '$', depth: 0 }\"\n/>\n\n<!-- Recursive value renderer -->\n<ng-template #valueTemplate let-value let-path=\"path\" let-depth=\"depth\">\n @if (isObject(value)) {\n @if (objectLength(value) === 0) {\n <span class=\"jt-bracket\">{{ '{' }}{{ '}' }}</span>\n } @else {\n <span class=\"jt-toggle\" (click)=\"toggle(path, depth); $event.stopPropagation()\" role=\"button\">\n <span class=\"jt-arrow\" [class.jt-arrow--collapsed]=\"isCollapsed(path, depth)\">&#9662;</span>\n <span class=\"jt-bracket\">{{ '{' }}</span>\n </span>\n @if (isCollapsed(path, depth)) {\n <span\n class=\"jt-collapsed-hint\"\n (click)=\"toggle(path, depth); $event.stopPropagation()\"\n role=\"button\"\n >{{ objectLength(value) }}\n {{ objectLength(value) === 1 ? 'property' : 'properties' }}</span\n ><span class=\"jt-bracket\">{{ '}' }}</span>\n } @else {\n <div class=\"jt-indent\">\n @for (entry of objectEntries(value); track entry[0]; let last = $last) {\n <div class=\"jt-line\">\n <span class=\"jt-key\">\"{{ entry[0] }}\"</span><span class=\"jt-colon\">: </span>\n @if (isContainer(entry[1])) {\n <ng-container\n *ngTemplateOutlet=\"\n valueTemplate;\n context: { $implicit: entry[1], path: path + '.' + entry[0], depth: depth + 1 }\n \"\n />\n } @else {\n <span [class]=\"primitiveClass(entry[1])\">{{ formatPrimitive(entry[1]) }}</span>\n }\n @if (!last) {\n <span class=\"jt-comma\">,</span>\n }\n </div>\n }\n </div>\n <span class=\"jt-bracket\">{{ '}' }}</span>\n }\n }\n } @else if (isArray(value)) {\n @if (arrayLength(value) === 0) {\n <span class=\"jt-bracket\">[]</span>\n } @else {\n <span class=\"jt-toggle\" (click)=\"toggle(path, depth); $event.stopPropagation()\" role=\"button\">\n <span class=\"jt-arrow\" [class.jt-arrow--collapsed]=\"isCollapsed(path, depth)\">&#9662;</span>\n <span class=\"jt-bracket\">[</span>\n </span>\n @if (isCollapsed(path, depth)) {\n <span\n class=\"jt-collapsed-hint\"\n (click)=\"toggle(path, depth); $event.stopPropagation()\"\n role=\"button\"\n >{{ arrayLength(value) }} {{ arrayLength(value) === 1 ? 'item' : 'items' }}</span\n ><span class=\"jt-bracket\">]</span>\n } @else {\n <div class=\"jt-indent\">\n @for (item of arrayItems(value); track $index; let last = $last) {\n <div class=\"jt-line\">\n @if (isContainer(item)) {\n <ng-container\n *ngTemplateOutlet=\"\n valueTemplate;\n context: { $implicit: item, path: path + '[' + $index + ']', depth: depth + 1 }\n \"\n />\n } @else {\n <span [class]=\"primitiveClass(item)\">{{ formatPrimitive(item) }}</span>\n }\n @if (!last) {\n <span class=\"jt-comma\">,</span>\n }\n </div>\n }\n </div>\n <span class=\"jt-bracket\">]</span>\n }\n }\n } @else {\n <span [class]=\"primitiveClass(value)\">{{ formatPrimitive(value) }}</span>\n }\n</ng-template>\n", styles: [".cortex-json-tree{display:block;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:.75rem;line-height:1.25rem;-webkit-user-select:text;user-select:text;white-space:pre-wrap;word-break:break-word}.jt-indent{padding-inline-start:1.25em;border-inline-start:1px solid #e2e8f0;margin-inline-start:.3em}.jt-line{position:relative}.jt-toggle{cursor:pointer;-webkit-user-select:none;user-select:none}.jt-toggle:hover .jt-arrow{color:#475569}.jt-arrow{display:inline-block;width:1em;text-align:center;color:#94a3b8;font-size:.7em;transition:transform .15s ease;vertical-align:middle}.jt-arrow--collapsed{transform:rotate(-90deg)}.jt-bracket{color:#64748b}.jt-key{color:#6366f1}.jt-colon{color:#64748b}.jt-comma{color:#94a3b8}.jt-string{color:#059669}.jt-number{color:#2563eb}.jt-boolean{color:#d97706}.jt-null{color:#94a3b8;font-style:italic}.jt-collapsed-hint{display:inline-block;padding:0 .4em;margin:0 .15em;font-size:10px;line-height:1.5;color:#94a3b8;background:#f8fafc;border:1px solid #e2e8f0;border-radius:3px;font-style:italic;cursor:pointer;-webkit-user-select:none;user-select:none;vertical-align:middle}.jt-collapsed-hint:hover{color:#64748b;border-color:#cbd5e1;background:#f1f5f9}\n"] }]
471
496
  }], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: true }] }], expandDepth: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandDepth", required: false }] }] } });
472
497
 
473
498
  class MessageToolCallPartComponent {
@@ -516,11 +541,11 @@ class MessageToolCallPartComponent {
516
541
  return type.startsWith('tool-') ? type.slice('tool-'.length) : type;
517
542
  }
518
543
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: MessageToolCallPartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
519
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.3", type: MessageToolCallPartComponent, isStandalone: true, selector: "cortex-message-tool-call-part", inputs: { toolCallPart: { classPropertyName: "toolCallPart", publicName: "toolCallPart", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<details class=\"group w-full rounded-lg border border-slate-200 bg-slate-50 overflow-hidden\">\n <summary\n class=\"list-none [&::-webkit-details-marker]:hidden cursor-pointer select-none px-4 py-3 group-open:border-b group-open:border-slate-200\"\n >\n <div class=\"flex items-start gap-3\">\n <div\n class=\"shrink-0 mt-0.5 size-7 rounded bg-slate-800 text-white flex items-center justify-center\"\n aria-hidden=\"true\"\n >\n <svg viewBox=\"0 0 20 20\" class=\"size-4\" fill=\"none\">\n <path\n d=\"M6.5 6.5 3.75 10l2.75 3.5M13.5 6.5 16.25 10l-2.75 3.5M11.25 5.75 8.75 14.25\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </div>\n\n <div class=\"min-w-0 flex-1\">\n <div class=\"flex items-center gap-2\">\n <div class=\"min-w-0 flex-1\">\n <div class=\"flex items-center gap-2 min-w-0\">\n <div\n class=\"font-medium text-slate-800 text-sm truncate\"\n [attr.title]=\"toolCallPart().title || null\"\n >\n {{ toolCallPart().title ?? 'Tool call' }}\n </div>\n @if (toolName()) {\n <span\n class=\"hidden sm:inline-flex items-center rounded bg-slate-200 px-1.5 py-0.5 text-[11px] font-medium text-slate-600 font-mono\"\n [attr.title]=\"toolCallPart().type || null\"\n >{{ toolName() }}</span\n >\n }\n </div>\n <div class=\"mt-0.5 text-[11px] text-slate-500 break-all\">\n <span class=\"font-medium text-slate-600\">ID</span>\n <span class=\"font-mono\"> {{ toolCallPart().toolCallId }}</span>\n @if (toolCallPart().providerExecuted) {\n <span class=\"ml-2 inline-flex items-center gap-1 text-emerald-700\">\n <span class=\"size-1.5 rounded-full bg-emerald-500\"></span>\n provider\n </span>\n }\n </div>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <span\n class=\"inline-flex items-center gap-1.5 rounded px-2 py-1 text-[11px] font-medium border\"\n [ngClass]=\"{\n 'bg-emerald-50 text-emerald-700 border-emerald-200':\n toolCallPart().state === 'output-available',\n 'bg-red-50 text-red-700 border-red-200': toolCallPart().state === 'output-error',\n 'bg-slate-100 text-slate-600 border-slate-200':\n toolCallPart().state === 'output-denied',\n 'bg-violet-50 text-violet-700 border-violet-200':\n toolCallPart().state === 'approval-requested' ||\n toolCallPart().state === 'approval-responded',\n 'bg-amber-50 text-amber-700 border-amber-200':\n toolCallPart().state === 'input-streaming' ||\n toolCallPart().state === 'input-available',\n }\"\n >\n @switch (toolCallPart().state) {\n @case ('input-streaming') {\n <span class=\"size-1.5 rounded-full bg-amber-500 animate-pulse\"></span>\n Calling\n }\n @case ('input-available') {\n <span class=\"size-1.5 rounded-full bg-amber-500\"></span> Input ready\n }\n @case ('approval-requested') {\n <span class=\"size-1.5 rounded-full bg-violet-500 animate-pulse\"></span>\n Needs approval\n }\n @case ('approval-responded') {\n @if (toolCallPart().approval?.approved) {\n <span class=\"size-1.5 rounded-full bg-emerald-500\"></span> Approved\n } @else {\n <span class=\"size-1.5 rounded-full bg-slate-500\"></span> Responded\n }\n }\n @case ('output-available') {\n <span class=\"size-1.5 rounded-full bg-emerald-500\"></span>\n {{ 'translate_completed' | translate }}\n }\n @case ('output-error') {\n <span class=\"size-1.5 rounded-full bg-red-500\"></span> Error\n }\n @case ('output-denied') {\n <span class=\"size-1.5 rounded-full bg-slate-500\"></span> Denied\n }\n }\n </span>\n\n <span\n class=\"inline-flex size-6 items-center justify-center rounded text-slate-400 transition group-open:rotate-180\"\n aria-hidden=\"true\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path\n d=\"m5.75 8.25 4.25 4.25 4.25-4.25\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n </div>\n </div>\n </div>\n </div>\n </summary>\n\n <div class=\"bg-white px-4 pb-4 pt-3\">\n <div class=\"grid gap-3\">\n @for (snippet of codeSnippets(); track snippet.key) {\n <div class=\"rounded-lg border border-slate-200 overflow-hidden\">\n <div\n class=\"px-3 py-2 flex items-center justify-between border-b border-slate-200 bg-slate-50\"\n >\n <div class=\"text-[11px] font-medium text-slate-600\">\n {{ snippet.key }}\n </div>\n <div class=\"text-[11px] text-slate-400 font-mono\">\n {{ snippet.lang }}\n </div>\n </div>\n <pre\n dir=\"ltr\"\n class=\"m-0 max-h-80 overflow-auto p-3 text-[12px] leading-5 font-mono whitespace-pre wrap-break-word\"\n ><code class=\"hljs\" [innerHTML]=\"snippet.value | highlight : snippet.lang\"></code></pre>\n </div>\n }\n\n @if (remainingInput(); as remaining) {\n <div class=\"rounded-lg border border-slate-200 overflow-hidden\">\n <div\n class=\"px-3 py-2 flex items-center justify-between border-b border-slate-200 bg-slate-50\"\n >\n <div class=\"text-[11px] font-medium text-slate-600\">Input</div>\n <div class=\"text-[11px] text-slate-400 font-mono\">json</div>\n </div>\n <div dir=\"ltr\" class=\"max-h-64 overflow-auto p-3\">\n <cortex-json-tree [data]=\"remaining\" [expandDepth]=\"2\" />\n </div>\n </div>\n }\n\n @if (toolCallPart().state === 'output-available') {\n <div class=\"rounded-lg border border-emerald-200 overflow-hidden\">\n <div\n class=\"px-3 py-2 flex items-center justify-between border-b border-emerald-200 bg-emerald-50\"\n >\n <div class=\"text-[11px] font-medium text-emerald-700\">Output</div>\n <div class=\"text-[11px] text-emerald-600 font-mono\">json</div>\n </div>\n <div dir=\"ltr\" class=\"max-h-64 overflow-auto p-3\">\n <cortex-json-tree [data]=\"toolCallPart().output\" [expandDepth]=\"2\" />\n </div>\n </div>\n }\n\n @if (toolCallPart().state === 'output-error') {\n <div class=\"rounded-lg border border-red-200 overflow-hidden\">\n <div\n class=\"px-3 py-2 flex items-center justify-between border-b border-red-200 bg-red-50\"\n >\n <div class=\"text-[11px] font-medium text-red-700\">Error</div>\n <div class=\"text-[11px] text-red-600 font-mono\">text</div>\n </div>\n <pre\n dir=\"ltr\"\n class=\"m-0 max-h-64 overflow-auto p-3 text-[12px] leading-5 font-mono text-red-800 whitespace-pre-wrap wrap-break-word\"\n >{{ toolCallPart().errorText }}</pre\n >\n </div>\n }\n\n @if (toolCallPart().state === 'output-denied') {\n <div class=\"rounded-lg border border-slate-200 overflow-hidden\">\n <div\n class=\"px-3 py-2 flex items-center justify-between border-b border-slate-200 bg-slate-50\"\n >\n <div class=\"text-[11px] font-medium text-slate-600\">Denied</div>\n <div class=\"text-[11px] text-slate-500 font-mono\">approval</div>\n </div>\n <div class=\"p-3 text-[12px] text-slate-700\">\n Tool execution was denied.\n @if (toolCallPart().approval?.reason) {\n <div class=\"mt-2 rounded bg-slate-50 border border-slate-200 p-2\">\n <div class=\"text-[11px] font-medium text-slate-600\">Reason</div>\n <div class=\"mt-1 font-mono text-[12px] text-slate-700 wrap-break-word\">\n {{ toolCallPart().approval?.reason }}\n </div>\n </div>\n }\n </div>\n </div>\n }\n\n @if (toolCallPart().state === 'approval-requested') {\n <div class=\"rounded-lg border border-violet-200 overflow-hidden\">\n <div\n class=\"px-3 py-2 flex items-center justify-between border-b border-violet-200 bg-violet-50\"\n >\n <div class=\"text-[11px] font-medium text-violet-700\">Approval requested</div>\n <div class=\"text-[11px] text-violet-600 font-mono\">\n {{ toolCallPart().approval?.id }}\n </div>\n </div>\n <div class=\"p-3 text-[12px] text-violet-800\">\n Waiting for approval to execute this tool.\n </div>\n </div>\n }\n\n @if (toolCallPart().state === 'approval-responded') {\n <div class=\"rounded-lg border border-violet-200 overflow-hidden\">\n <div\n class=\"px-3 py-2 flex items-center justify-between border-b border-violet-200 bg-violet-50\"\n >\n <div class=\"text-[11px] font-medium text-violet-700\">Approval response</div>\n <div class=\"text-[11px] text-violet-600 font-mono\">\n {{ toolCallPart().approval?.id }}\n </div>\n </div>\n <div class=\"p-3 text-[12px] text-violet-800\">\n @if (toolCallPart().approval?.approved) {\n Approved.\n } @else {\n Response received.\n }\n @if (toolCallPart().approval?.reason) {\n <div class=\"mt-2 rounded bg-violet-50 border border-violet-200 p-2\">\n <div class=\"text-[11px] font-medium text-violet-700\">Reason</div>\n <div class=\"mt-1 font-mono text-[12px] text-violet-800 wrap-break-word\">\n {{ toolCallPart().approval?.reason }}\n </div>\n </div>\n }\n </div>\n </div>\n }\n </div>\n </div>\n</details>\n", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: JsonTreeComponent, selector: "cortex-json-tree", inputs: ["data", "expandDepth"] }, { kind: "pipe", type: HighlightPipe, name: "highlight" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
544
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.3", type: MessageToolCallPartComponent, isStandalone: true, selector: "cortex-message-tool-call-part", inputs: { toolCallPart: { classPropertyName: "toolCallPart", publicName: "toolCallPart", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<details class=\"cortex-tool-details\">\n <summary class=\"cortex-tool-details__summary\">\n <div class=\"cortex-tool-details__header\">\n <div class=\"cortex-tool-details__icon-box\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 20 20\" class=\"cortex-tool-details__icon-svg\" fill=\"none\">\n <path\n d=\"M6.5 6.5 3.75 10l2.75 3.5M13.5 6.5 16.25 10l-2.75 3.5M11.25 5.75 8.75 14.25\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </div>\n\n <div class=\"cortex-tool-details__info\">\n <div class=\"cortex-tool-details__title-row\">\n <div class=\"cortex-tool-details__name-row\">\n <div class=\"cortex-tool-details__name\" [attr.title]=\"toolCallPart().title || null\">\n {{ toolCallPart().title ?? 'Tool call' }}\n </div>\n @if (toolName()) {\n <span\n class=\"cortex-tool-details__tool-badge\"\n [attr.title]=\"toolCallPart().type || null\"\n >{{ toolName() }}</span\n >\n }\n </div>\n <div class=\"cortex-tool-details__id-line\">\n <span class=\"cortex-tool-details__id-label\">ID</span>\n <span class=\"cortex-tool-details__id-value\"> {{ toolCallPart().toolCallId }}</span>\n @if (toolCallPart().providerExecuted) {\n <span class=\"cortex-tool-details__provider-badge\">\n <span class=\"cortex-tool-details__provider-dot\"></span>\n provider\n </span>\n }\n </div>\n </div>\n\n <div class=\"cortex-tool-details__actions\">\n <span\n class=\"cortex-tool-details__state-badge\"\n [ngClass]=\"{\n 'cortex-tool-details__state-badge--success':\n toolCallPart().state === 'output-available',\n 'cortex-tool-details__state-badge--error': toolCallPart().state === 'output-error',\n 'cortex-tool-details__state-badge--denied': toolCallPart().state === 'output-denied',\n 'cortex-tool-details__state-badge--approval':\n toolCallPart().state === 'approval-requested' ||\n toolCallPart().state === 'approval-responded',\n 'cortex-tool-details__state-badge--pending':\n toolCallPart().state === 'input-streaming' ||\n toolCallPart().state === 'input-available',\n }\"\n >\n @switch (toolCallPart().state) {\n @case ('input-streaming') {\n <span\n class=\"cortex-tool-details__status-dot cortex-tool-details__status-dot--amber cortex-tool-details__status-dot--pulse\"\n ></span>\n Calling\n }\n @case ('input-available') {\n <span\n class=\"cortex-tool-details__status-dot cortex-tool-details__status-dot--amber\"\n ></span>\n Input ready\n }\n @case ('approval-requested') {\n <span\n class=\"cortex-tool-details__status-dot cortex-tool-details__status-dot--violet cortex-tool-details__status-dot--pulse\"\n ></span>\n Needs approval\n }\n @case ('approval-responded') {\n @if (toolCallPart().approval?.approved) {\n <span\n class=\"cortex-tool-details__status-dot cortex-tool-details__status-dot--emerald\"\n ></span>\n Approved\n } @else {\n <span\n class=\"cortex-tool-details__status-dot cortex-tool-details__status-dot--slate\"\n ></span>\n Responded\n }\n }\n @case ('output-available') {\n <span\n class=\"cortex-tool-details__status-dot cortex-tool-details__status-dot--emerald\"\n ></span>\n {{ 'translate_completed' | translate }}\n }\n @case ('output-error') {\n <span\n class=\"cortex-tool-details__status-dot cortex-tool-details__status-dot--red\"\n ></span>\n Error\n }\n @case ('output-denied') {\n <span\n class=\"cortex-tool-details__status-dot cortex-tool-details__status-dot--slate\"\n ></span>\n Denied\n }\n }\n </span>\n\n <span class=\"cortex-tool-details__chevron\" aria-hidden=\"true\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path\n d=\"m5.75 8.25 4.25 4.25 4.25-4.25\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n </div>\n </div>\n </div>\n </summary>\n\n <div class=\"cortex-tool-details__body\">\n <div class=\"cortex-tool-details__panels\">\n @for (snippet of codeSnippets(); track snippet.key) {\n <div class=\"cortex-tool-details__panel\">\n <div class=\"cortex-tool-details__panel-header\">\n <div class=\"cortex-tool-details__panel-label\">{{ snippet.key }}</div>\n <div class=\"cortex-tool-details__panel-lang\">{{ snippet.lang }}</div>\n </div>\n <pre\n dir=\"ltr\"\n class=\"cortex-tool-details__code-pre\"\n ><code class=\"hljs\" [innerHTML]=\"snippet.value | highlight : snippet.lang\"></code></pre>\n </div>\n }\n\n @if (remainingInput(); as remaining) {\n <div class=\"cortex-tool-details__panel\">\n <div class=\"cortex-tool-details__panel-header\">\n <div class=\"cortex-tool-details__panel-label\">Input</div>\n <div class=\"cortex-tool-details__panel-lang\">json</div>\n </div>\n <div dir=\"ltr\" class=\"cortex-tool-details__tree-container\">\n <cortex-json-tree [data]=\"remaining\" [expandDepth]=\"2\" />\n </div>\n </div>\n }\n\n @if (toolCallPart().state === 'output-available') {\n <div class=\"cortex-tool-details__panel cortex-tool-details__panel--success\">\n <div class=\"cortex-tool-details__panel-header cortex-tool-details__panel-header--success\">\n <div class=\"cortex-tool-details__panel-label cortex-tool-details__panel-label--success\">\n Output\n </div>\n <div class=\"cortex-tool-details__panel-lang cortex-tool-details__panel-lang--success\">\n json\n </div>\n </div>\n <div dir=\"ltr\" class=\"cortex-tool-details__tree-container\">\n <cortex-json-tree [data]=\"toolCallPart().output\" [expandDepth]=\"2\" />\n </div>\n </div>\n }\n\n @if (toolCallPart().state === 'output-error') {\n <div class=\"cortex-tool-details__panel cortex-tool-details__panel--error\">\n <div class=\"cortex-tool-details__panel-header cortex-tool-details__panel-header--error\">\n <div class=\"cortex-tool-details__panel-label cortex-tool-details__panel-label--error\">\n Error\n </div>\n <div class=\"cortex-tool-details__panel-lang cortex-tool-details__panel-lang--error\">\n text\n </div>\n </div>\n <pre dir=\"ltr\" class=\"cortex-tool-details__error-pre\">{{ toolCallPart().errorText }}</pre>\n </div>\n }\n\n @if (toolCallPart().state === 'output-denied') {\n <div class=\"cortex-tool-details__panel\">\n <div class=\"cortex-tool-details__panel-header\">\n <div class=\"cortex-tool-details__panel-label\">Denied</div>\n <div class=\"cortex-tool-details__panel-lang\">approval</div>\n </div>\n <div class=\"cortex-tool-details__denied-body\">\n Tool execution was denied.\n @if (toolCallPart().approval?.reason) {\n <div class=\"cortex-tool-details__reason-box\">\n <div class=\"cortex-tool-details__reason-label\">Reason</div>\n <div class=\"cortex-tool-details__reason-text\">\n {{ toolCallPart().approval?.reason }}\n </div>\n </div>\n }\n </div>\n </div>\n }\n\n @if (toolCallPart().state === 'approval-requested') {\n <div class=\"cortex-tool-details__panel cortex-tool-details__panel--approval\">\n <div\n class=\"cortex-tool-details__panel-header cortex-tool-details__panel-header--approval\"\n >\n <div\n class=\"cortex-tool-details__panel-label cortex-tool-details__panel-label--approval\"\n >\n Approval requested\n </div>\n <div class=\"cortex-tool-details__panel-lang cortex-tool-details__panel-lang--approval\">\n {{ toolCallPart().approval?.id }}\n </div>\n </div>\n <div class=\"cortex-tool-details__approval-body\">\n Waiting for approval to execute this tool.\n </div>\n </div>\n }\n\n @if (toolCallPart().state === 'approval-responded') {\n <div class=\"cortex-tool-details__panel cortex-tool-details__panel--approval\">\n <div\n class=\"cortex-tool-details__panel-header cortex-tool-details__panel-header--approval\"\n >\n <div\n class=\"cortex-tool-details__panel-label cortex-tool-details__panel-label--approval\"\n >\n Approval response\n </div>\n <div class=\"cortex-tool-details__panel-lang cortex-tool-details__panel-lang--approval\">\n {{ toolCallPart().approval?.id }}\n </div>\n </div>\n <div class=\"cortex-tool-details__approval-body\">\n @if (toolCallPart().approval?.approved) {\n Approved.\n } @else {\n Response received.\n }\n @if (toolCallPart().approval?.reason) {\n <div\n class=\"cortex-tool-details__reason-box cortex-tool-details__reason-box--approval\"\n >\n <div\n class=\"cortex-tool-details__reason-label cortex-tool-details__reason-label--approval\"\n >\n Reason\n </div>\n <div\n class=\"cortex-tool-details__reason-text cortex-tool-details__reason-text--approval\"\n >\n {{ toolCallPart().approval?.reason }}\n </div>\n </div>\n }\n </div>\n </div>\n }\n </div>\n </div>\n</details>\n", styles: [".cortex-tool-details{width:100%;border-radius:.5rem;border:1px solid #e2e8f0;background:#f8fafc;overflow:hidden}.cortex-tool-details__summary{list-style:none;cursor:pointer;-webkit-user-select:none;user-select:none;padding:.75rem 1rem}.cortex-tool-details__summary::-webkit-details-marker{display:none}details[open]>.cortex-tool-details__summary{border-bottom:1px solid #e2e8f0}.cortex-tool-details__header{display:flex;align-items:flex-start;gap:.75rem}.cortex-tool-details__icon-box{flex-shrink:0;margin-top:.125rem;width:1.75rem;height:1.75rem;border-radius:.25rem;background:#1e293b;color:#fff;display:flex;align-items:center;justify-content:center}.cortex-tool-details__icon-svg{width:1rem;height:1rem}.cortex-tool-details__info{min-width:0;flex:1;display:flex;align-items:center;gap:.5rem}.cortex-tool-details__title-row{min-width:0;flex:1}.cortex-tool-details__name-row{display:flex;align-items:center;gap:.5rem;min-width:0}.cortex-tool-details__name{font-weight:500;color:#1e293b;font-size:.875rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cortex-tool-details__tool-badge{display:none;align-items:center;border-radius:.25rem;background:#e2e8f0;padding:.125rem .375rem;font-size:.6875rem;font-weight:500;color:#475569;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace}@media(min-width:640px){.cortex-tool-details__tool-badge{display:inline-flex}}.cortex-tool-details__id-line{margin-top:.125rem;font-size:.6875rem;color:#64748b;word-break:break-all}.cortex-tool-details__id-label{font-weight:500;color:#475569}.cortex-tool-details__id-value{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace}.cortex-tool-details__provider-badge{margin-inline-start:.5rem;display:inline-flex;align-items:center;gap:.25rem;color:#047857}.cortex-tool-details__provider-dot{width:.375rem;height:.375rem;border-radius:9999px;background:#10b981}.cortex-tool-details__actions{display:flex;align-items:center;gap:.5rem}.cortex-tool-details__state-badge{display:inline-flex;align-items:center;gap:.375rem;border-radius:.25rem;padding:.25rem .5rem;font-size:.6875rem;font-weight:500;border:1px solid}.cortex-tool-details__state-badge--success{background:#ecfdf5;color:#047857;border-color:#a7f3d0}.cortex-tool-details__state-badge--error{background:#fef2f2;color:#b91c1c;border-color:#fecaca}.cortex-tool-details__state-badge--denied{background:#f1f5f9;color:#475569;border-color:#e2e8f0}.cortex-tool-details__state-badge--approval{background:#f5f3ff;color:#6d28d9;border-color:#ddd6fe}.cortex-tool-details__state-badge--pending{background:#fffbeb;color:#b45309;border-color:#fde68a}.cortex-tool-details__status-dot{width:.375rem;height:.375rem;border-radius:9999px}.cortex-tool-details__status-dot--amber{background:#f59e0b}.cortex-tool-details__status-dot--violet{background:#8b5cf6}.cortex-tool-details__status-dot--emerald{background:#10b981}.cortex-tool-details__status-dot--red{background:#ef4444}.cortex-tool-details__status-dot--slate{background:#64748b}.cortex-tool-details__status-dot--pulse{animation:cortex-tool-status-pulse 2s ease-in-out infinite}@keyframes cortex-tool-status-pulse{0%,to{opacity:1}50%{opacity:.4}}.cortex-tool-details__chevron{display:inline-flex;width:1.5rem;height:1.5rem;align-items:center;justify-content:center;border-radius:.25rem;color:#94a3b8;transition:transform .2s}details[open]>summary .cortex-tool-details__chevron{transform:rotate(180deg)}.cortex-tool-details__body{background:#fff;padding:.75rem 1rem 1rem}.cortex-tool-details__panels{display:grid;gap:.75rem}.cortex-tool-details__panel{border-radius:.5rem;border:1px solid #e2e8f0;overflow:hidden}.cortex-tool-details__panel--success{border-color:#a7f3d0}.cortex-tool-details__panel--error{border-color:#fecaca}.cortex-tool-details__panel--approval{border-color:#ddd6fe}.cortex-tool-details__panel-header{padding:.5rem .75rem;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #e2e8f0;background:#f8fafc}.cortex-tool-details__panel-header--success{border-bottom-color:#a7f3d0;background:#ecfdf5}.cortex-tool-details__panel-header--error{border-bottom-color:#fecaca;background:#fef2f2}.cortex-tool-details__panel-header--approval{border-bottom-color:#ddd6fe;background:#f5f3ff}.cortex-tool-details__panel-label{font-size:.6875rem;font-weight:500;color:#475569}.cortex-tool-details__panel-label--success{color:#047857}.cortex-tool-details__panel-label--error{color:#b91c1c}.cortex-tool-details__panel-label--approval{color:#6d28d9}.cortex-tool-details__panel-lang{font-size:.6875rem;color:#94a3b8;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace}.cortex-tool-details__panel-lang--success{color:#059669}.cortex-tool-details__panel-lang--error{color:#dc2626}.cortex-tool-details__panel-lang--approval{color:#7c3aed}.cortex-tool-details__code-pre{margin:0;max-height:20rem;overflow:auto;padding:.75rem;font-size:.75rem;line-height:1.25rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;white-space:pre;overflow-wrap:break-word}.cortex-tool-details__tree-container{max-height:16rem;overflow:auto;padding:.75rem}.cortex-tool-details__error-pre{margin:0;max-height:16rem;overflow:auto;padding:.75rem;font-size:.75rem;line-height:1.25rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;color:#991b1b;white-space:pre-wrap;overflow-wrap:break-word}.cortex-tool-details__denied-body{padding:.75rem;font-size:.75rem;color:#334155}.cortex-tool-details__approval-body{padding:.75rem;font-size:.75rem;color:#5b21b6}.cortex-tool-details__reason-box{margin-top:.5rem;border-radius:.25rem;background:#f8fafc;border:1px solid #e2e8f0;padding:.5rem}.cortex-tool-details__reason-box--approval{background:#f5f3ff;border-color:#ddd6fe}.cortex-tool-details__reason-label{font-size:.6875rem;font-weight:500;color:#475569}.cortex-tool-details__reason-label--approval{color:#6d28d9}.cortex-tool-details__reason-text{margin-top:.25rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:.75rem;color:#334155;overflow-wrap:break-word}.cortex-tool-details__reason-text--approval{color:#5b21b6}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: JsonTreeComponent, selector: "cortex-json-tree", inputs: ["data", "expandDepth"] }, { kind: "pipe", type: HighlightPipe, name: "highlight" }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
520
545
  }
521
546
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: MessageToolCallPartComponent, decorators: [{
522
547
  type: Component,
523
- args: [{ selector: 'cortex-message-tool-call-part', changeDetection: ChangeDetectionStrategy.OnPush, imports: [HighlightPipe, NgClass, TranslatePipe, JsonTreeComponent], template: "<details class=\"group w-full rounded-lg border border-slate-200 bg-slate-50 overflow-hidden\">\n <summary\n class=\"list-none [&::-webkit-details-marker]:hidden cursor-pointer select-none px-4 py-3 group-open:border-b group-open:border-slate-200\"\n >\n <div class=\"flex items-start gap-3\">\n <div\n class=\"shrink-0 mt-0.5 size-7 rounded bg-slate-800 text-white flex items-center justify-center\"\n aria-hidden=\"true\"\n >\n <svg viewBox=\"0 0 20 20\" class=\"size-4\" fill=\"none\">\n <path\n d=\"M6.5 6.5 3.75 10l2.75 3.5M13.5 6.5 16.25 10l-2.75 3.5M11.25 5.75 8.75 14.25\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </div>\n\n <div class=\"min-w-0 flex-1\">\n <div class=\"flex items-center gap-2\">\n <div class=\"min-w-0 flex-1\">\n <div class=\"flex items-center gap-2 min-w-0\">\n <div\n class=\"font-medium text-slate-800 text-sm truncate\"\n [attr.title]=\"toolCallPart().title || null\"\n >\n {{ toolCallPart().title ?? 'Tool call' }}\n </div>\n @if (toolName()) {\n <span\n class=\"hidden sm:inline-flex items-center rounded bg-slate-200 px-1.5 py-0.5 text-[11px] font-medium text-slate-600 font-mono\"\n [attr.title]=\"toolCallPart().type || null\"\n >{{ toolName() }}</span\n >\n }\n </div>\n <div class=\"mt-0.5 text-[11px] text-slate-500 break-all\">\n <span class=\"font-medium text-slate-600\">ID</span>\n <span class=\"font-mono\"> {{ toolCallPart().toolCallId }}</span>\n @if (toolCallPart().providerExecuted) {\n <span class=\"ml-2 inline-flex items-center gap-1 text-emerald-700\">\n <span class=\"size-1.5 rounded-full bg-emerald-500\"></span>\n provider\n </span>\n }\n </div>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <span\n class=\"inline-flex items-center gap-1.5 rounded px-2 py-1 text-[11px] font-medium border\"\n [ngClass]=\"{\n 'bg-emerald-50 text-emerald-700 border-emerald-200':\n toolCallPart().state === 'output-available',\n 'bg-red-50 text-red-700 border-red-200': toolCallPart().state === 'output-error',\n 'bg-slate-100 text-slate-600 border-slate-200':\n toolCallPart().state === 'output-denied',\n 'bg-violet-50 text-violet-700 border-violet-200':\n toolCallPart().state === 'approval-requested' ||\n toolCallPart().state === 'approval-responded',\n 'bg-amber-50 text-amber-700 border-amber-200':\n toolCallPart().state === 'input-streaming' ||\n toolCallPart().state === 'input-available',\n }\"\n >\n @switch (toolCallPart().state) {\n @case ('input-streaming') {\n <span class=\"size-1.5 rounded-full bg-amber-500 animate-pulse\"></span>\n Calling\n }\n @case ('input-available') {\n <span class=\"size-1.5 rounded-full bg-amber-500\"></span> Input ready\n }\n @case ('approval-requested') {\n <span class=\"size-1.5 rounded-full bg-violet-500 animate-pulse\"></span>\n Needs approval\n }\n @case ('approval-responded') {\n @if (toolCallPart().approval?.approved) {\n <span class=\"size-1.5 rounded-full bg-emerald-500\"></span> Approved\n } @else {\n <span class=\"size-1.5 rounded-full bg-slate-500\"></span> Responded\n }\n }\n @case ('output-available') {\n <span class=\"size-1.5 rounded-full bg-emerald-500\"></span>\n {{ 'translate_completed' | translate }}\n }\n @case ('output-error') {\n <span class=\"size-1.5 rounded-full bg-red-500\"></span> Error\n }\n @case ('output-denied') {\n <span class=\"size-1.5 rounded-full bg-slate-500\"></span> Denied\n }\n }\n </span>\n\n <span\n class=\"inline-flex size-6 items-center justify-center rounded text-slate-400 transition group-open:rotate-180\"\n aria-hidden=\"true\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path\n d=\"m5.75 8.25 4.25 4.25 4.25-4.25\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n </div>\n </div>\n </div>\n </div>\n </summary>\n\n <div class=\"bg-white px-4 pb-4 pt-3\">\n <div class=\"grid gap-3\">\n @for (snippet of codeSnippets(); track snippet.key) {\n <div class=\"rounded-lg border border-slate-200 overflow-hidden\">\n <div\n class=\"px-3 py-2 flex items-center justify-between border-b border-slate-200 bg-slate-50\"\n >\n <div class=\"text-[11px] font-medium text-slate-600\">\n {{ snippet.key }}\n </div>\n <div class=\"text-[11px] text-slate-400 font-mono\">\n {{ snippet.lang }}\n </div>\n </div>\n <pre\n dir=\"ltr\"\n class=\"m-0 max-h-80 overflow-auto p-3 text-[12px] leading-5 font-mono whitespace-pre wrap-break-word\"\n ><code class=\"hljs\" [innerHTML]=\"snippet.value | highlight : snippet.lang\"></code></pre>\n </div>\n }\n\n @if (remainingInput(); as remaining) {\n <div class=\"rounded-lg border border-slate-200 overflow-hidden\">\n <div\n class=\"px-3 py-2 flex items-center justify-between border-b border-slate-200 bg-slate-50\"\n >\n <div class=\"text-[11px] font-medium text-slate-600\">Input</div>\n <div class=\"text-[11px] text-slate-400 font-mono\">json</div>\n </div>\n <div dir=\"ltr\" class=\"max-h-64 overflow-auto p-3\">\n <cortex-json-tree [data]=\"remaining\" [expandDepth]=\"2\" />\n </div>\n </div>\n }\n\n @if (toolCallPart().state === 'output-available') {\n <div class=\"rounded-lg border border-emerald-200 overflow-hidden\">\n <div\n class=\"px-3 py-2 flex items-center justify-between border-b border-emerald-200 bg-emerald-50\"\n >\n <div class=\"text-[11px] font-medium text-emerald-700\">Output</div>\n <div class=\"text-[11px] text-emerald-600 font-mono\">json</div>\n </div>\n <div dir=\"ltr\" class=\"max-h-64 overflow-auto p-3\">\n <cortex-json-tree [data]=\"toolCallPart().output\" [expandDepth]=\"2\" />\n </div>\n </div>\n }\n\n @if (toolCallPart().state === 'output-error') {\n <div class=\"rounded-lg border border-red-200 overflow-hidden\">\n <div\n class=\"px-3 py-2 flex items-center justify-between border-b border-red-200 bg-red-50\"\n >\n <div class=\"text-[11px] font-medium text-red-700\">Error</div>\n <div class=\"text-[11px] text-red-600 font-mono\">text</div>\n </div>\n <pre\n dir=\"ltr\"\n class=\"m-0 max-h-64 overflow-auto p-3 text-[12px] leading-5 font-mono text-red-800 whitespace-pre-wrap wrap-break-word\"\n >{{ toolCallPart().errorText }}</pre\n >\n </div>\n }\n\n @if (toolCallPart().state === 'output-denied') {\n <div class=\"rounded-lg border border-slate-200 overflow-hidden\">\n <div\n class=\"px-3 py-2 flex items-center justify-between border-b border-slate-200 bg-slate-50\"\n >\n <div class=\"text-[11px] font-medium text-slate-600\">Denied</div>\n <div class=\"text-[11px] text-slate-500 font-mono\">approval</div>\n </div>\n <div class=\"p-3 text-[12px] text-slate-700\">\n Tool execution was denied.\n @if (toolCallPart().approval?.reason) {\n <div class=\"mt-2 rounded bg-slate-50 border border-slate-200 p-2\">\n <div class=\"text-[11px] font-medium text-slate-600\">Reason</div>\n <div class=\"mt-1 font-mono text-[12px] text-slate-700 wrap-break-word\">\n {{ toolCallPart().approval?.reason }}\n </div>\n </div>\n }\n </div>\n </div>\n }\n\n @if (toolCallPart().state === 'approval-requested') {\n <div class=\"rounded-lg border border-violet-200 overflow-hidden\">\n <div\n class=\"px-3 py-2 flex items-center justify-between border-b border-violet-200 bg-violet-50\"\n >\n <div class=\"text-[11px] font-medium text-violet-700\">Approval requested</div>\n <div class=\"text-[11px] text-violet-600 font-mono\">\n {{ toolCallPart().approval?.id }}\n </div>\n </div>\n <div class=\"p-3 text-[12px] text-violet-800\">\n Waiting for approval to execute this tool.\n </div>\n </div>\n }\n\n @if (toolCallPart().state === 'approval-responded') {\n <div class=\"rounded-lg border border-violet-200 overflow-hidden\">\n <div\n class=\"px-3 py-2 flex items-center justify-between border-b border-violet-200 bg-violet-50\"\n >\n <div class=\"text-[11px] font-medium text-violet-700\">Approval response</div>\n <div class=\"text-[11px] text-violet-600 font-mono\">\n {{ toolCallPart().approval?.id }}\n </div>\n </div>\n <div class=\"p-3 text-[12px] text-violet-800\">\n @if (toolCallPart().approval?.approved) {\n Approved.\n } @else {\n Response received.\n }\n @if (toolCallPart().approval?.reason) {\n <div class=\"mt-2 rounded bg-violet-50 border border-violet-200 p-2\">\n <div class=\"text-[11px] font-medium text-violet-700\">Reason</div>\n <div class=\"mt-1 font-mono text-[12px] text-violet-800 wrap-break-word\">\n {{ toolCallPart().approval?.reason }}\n </div>\n </div>\n }\n </div>\n </div>\n }\n </div>\n </div>\n</details>\n" }]
548
+ args: [{ selector: 'cortex-message-tool-call-part', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [HighlightPipe, NgClass, TranslatePipe, JsonTreeComponent], template: "<details class=\"cortex-tool-details\">\n <summary class=\"cortex-tool-details__summary\">\n <div class=\"cortex-tool-details__header\">\n <div class=\"cortex-tool-details__icon-box\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 20 20\" class=\"cortex-tool-details__icon-svg\" fill=\"none\">\n <path\n d=\"M6.5 6.5 3.75 10l2.75 3.5M13.5 6.5 16.25 10l-2.75 3.5M11.25 5.75 8.75 14.25\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </div>\n\n <div class=\"cortex-tool-details__info\">\n <div class=\"cortex-tool-details__title-row\">\n <div class=\"cortex-tool-details__name-row\">\n <div class=\"cortex-tool-details__name\" [attr.title]=\"toolCallPart().title || null\">\n {{ toolCallPart().title ?? 'Tool call' }}\n </div>\n @if (toolName()) {\n <span\n class=\"cortex-tool-details__tool-badge\"\n [attr.title]=\"toolCallPart().type || null\"\n >{{ toolName() }}</span\n >\n }\n </div>\n <div class=\"cortex-tool-details__id-line\">\n <span class=\"cortex-tool-details__id-label\">ID</span>\n <span class=\"cortex-tool-details__id-value\"> {{ toolCallPart().toolCallId }}</span>\n @if (toolCallPart().providerExecuted) {\n <span class=\"cortex-tool-details__provider-badge\">\n <span class=\"cortex-tool-details__provider-dot\"></span>\n provider\n </span>\n }\n </div>\n </div>\n\n <div class=\"cortex-tool-details__actions\">\n <span\n class=\"cortex-tool-details__state-badge\"\n [ngClass]=\"{\n 'cortex-tool-details__state-badge--success':\n toolCallPart().state === 'output-available',\n 'cortex-tool-details__state-badge--error': toolCallPart().state === 'output-error',\n 'cortex-tool-details__state-badge--denied': toolCallPart().state === 'output-denied',\n 'cortex-tool-details__state-badge--approval':\n toolCallPart().state === 'approval-requested' ||\n toolCallPart().state === 'approval-responded',\n 'cortex-tool-details__state-badge--pending':\n toolCallPart().state === 'input-streaming' ||\n toolCallPart().state === 'input-available',\n }\"\n >\n @switch (toolCallPart().state) {\n @case ('input-streaming') {\n <span\n class=\"cortex-tool-details__status-dot cortex-tool-details__status-dot--amber cortex-tool-details__status-dot--pulse\"\n ></span>\n Calling\n }\n @case ('input-available') {\n <span\n class=\"cortex-tool-details__status-dot cortex-tool-details__status-dot--amber\"\n ></span>\n Input ready\n }\n @case ('approval-requested') {\n <span\n class=\"cortex-tool-details__status-dot cortex-tool-details__status-dot--violet cortex-tool-details__status-dot--pulse\"\n ></span>\n Needs approval\n }\n @case ('approval-responded') {\n @if (toolCallPart().approval?.approved) {\n <span\n class=\"cortex-tool-details__status-dot cortex-tool-details__status-dot--emerald\"\n ></span>\n Approved\n } @else {\n <span\n class=\"cortex-tool-details__status-dot cortex-tool-details__status-dot--slate\"\n ></span>\n Responded\n }\n }\n @case ('output-available') {\n <span\n class=\"cortex-tool-details__status-dot cortex-tool-details__status-dot--emerald\"\n ></span>\n {{ 'translate_completed' | translate }}\n }\n @case ('output-error') {\n <span\n class=\"cortex-tool-details__status-dot cortex-tool-details__status-dot--red\"\n ></span>\n Error\n }\n @case ('output-denied') {\n <span\n class=\"cortex-tool-details__status-dot cortex-tool-details__status-dot--slate\"\n ></span>\n Denied\n }\n }\n </span>\n\n <span class=\"cortex-tool-details__chevron\" aria-hidden=\"true\">\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path\n d=\"m5.75 8.25 4.25 4.25 4.25-4.25\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n </div>\n </div>\n </div>\n </summary>\n\n <div class=\"cortex-tool-details__body\">\n <div class=\"cortex-tool-details__panels\">\n @for (snippet of codeSnippets(); track snippet.key) {\n <div class=\"cortex-tool-details__panel\">\n <div class=\"cortex-tool-details__panel-header\">\n <div class=\"cortex-tool-details__panel-label\">{{ snippet.key }}</div>\n <div class=\"cortex-tool-details__panel-lang\">{{ snippet.lang }}</div>\n </div>\n <pre\n dir=\"ltr\"\n class=\"cortex-tool-details__code-pre\"\n ><code class=\"hljs\" [innerHTML]=\"snippet.value | highlight : snippet.lang\"></code></pre>\n </div>\n }\n\n @if (remainingInput(); as remaining) {\n <div class=\"cortex-tool-details__panel\">\n <div class=\"cortex-tool-details__panel-header\">\n <div class=\"cortex-tool-details__panel-label\">Input</div>\n <div class=\"cortex-tool-details__panel-lang\">json</div>\n </div>\n <div dir=\"ltr\" class=\"cortex-tool-details__tree-container\">\n <cortex-json-tree [data]=\"remaining\" [expandDepth]=\"2\" />\n </div>\n </div>\n }\n\n @if (toolCallPart().state === 'output-available') {\n <div class=\"cortex-tool-details__panel cortex-tool-details__panel--success\">\n <div class=\"cortex-tool-details__panel-header cortex-tool-details__panel-header--success\">\n <div class=\"cortex-tool-details__panel-label cortex-tool-details__panel-label--success\">\n Output\n </div>\n <div class=\"cortex-tool-details__panel-lang cortex-tool-details__panel-lang--success\">\n json\n </div>\n </div>\n <div dir=\"ltr\" class=\"cortex-tool-details__tree-container\">\n <cortex-json-tree [data]=\"toolCallPart().output\" [expandDepth]=\"2\" />\n </div>\n </div>\n }\n\n @if (toolCallPart().state === 'output-error') {\n <div class=\"cortex-tool-details__panel cortex-tool-details__panel--error\">\n <div class=\"cortex-tool-details__panel-header cortex-tool-details__panel-header--error\">\n <div class=\"cortex-tool-details__panel-label cortex-tool-details__panel-label--error\">\n Error\n </div>\n <div class=\"cortex-tool-details__panel-lang cortex-tool-details__panel-lang--error\">\n text\n </div>\n </div>\n <pre dir=\"ltr\" class=\"cortex-tool-details__error-pre\">{{ toolCallPart().errorText }}</pre>\n </div>\n }\n\n @if (toolCallPart().state === 'output-denied') {\n <div class=\"cortex-tool-details__panel\">\n <div class=\"cortex-tool-details__panel-header\">\n <div class=\"cortex-tool-details__panel-label\">Denied</div>\n <div class=\"cortex-tool-details__panel-lang\">approval</div>\n </div>\n <div class=\"cortex-tool-details__denied-body\">\n Tool execution was denied.\n @if (toolCallPart().approval?.reason) {\n <div class=\"cortex-tool-details__reason-box\">\n <div class=\"cortex-tool-details__reason-label\">Reason</div>\n <div class=\"cortex-tool-details__reason-text\">\n {{ toolCallPart().approval?.reason }}\n </div>\n </div>\n }\n </div>\n </div>\n }\n\n @if (toolCallPart().state === 'approval-requested') {\n <div class=\"cortex-tool-details__panel cortex-tool-details__panel--approval\">\n <div\n class=\"cortex-tool-details__panel-header cortex-tool-details__panel-header--approval\"\n >\n <div\n class=\"cortex-tool-details__panel-label cortex-tool-details__panel-label--approval\"\n >\n Approval requested\n </div>\n <div class=\"cortex-tool-details__panel-lang cortex-tool-details__panel-lang--approval\">\n {{ toolCallPart().approval?.id }}\n </div>\n </div>\n <div class=\"cortex-tool-details__approval-body\">\n Waiting for approval to execute this tool.\n </div>\n </div>\n }\n\n @if (toolCallPart().state === 'approval-responded') {\n <div class=\"cortex-tool-details__panel cortex-tool-details__panel--approval\">\n <div\n class=\"cortex-tool-details__panel-header cortex-tool-details__panel-header--approval\"\n >\n <div\n class=\"cortex-tool-details__panel-label cortex-tool-details__panel-label--approval\"\n >\n Approval response\n </div>\n <div class=\"cortex-tool-details__panel-lang cortex-tool-details__panel-lang--approval\">\n {{ toolCallPart().approval?.id }}\n </div>\n </div>\n <div class=\"cortex-tool-details__approval-body\">\n @if (toolCallPart().approval?.approved) {\n Approved.\n } @else {\n Response received.\n }\n @if (toolCallPart().approval?.reason) {\n <div\n class=\"cortex-tool-details__reason-box cortex-tool-details__reason-box--approval\"\n >\n <div\n class=\"cortex-tool-details__reason-label cortex-tool-details__reason-label--approval\"\n >\n Reason\n </div>\n <div\n class=\"cortex-tool-details__reason-text cortex-tool-details__reason-text--approval\"\n >\n {{ toolCallPart().approval?.reason }}\n </div>\n </div>\n }\n </div>\n </div>\n }\n </div>\n </div>\n</details>\n", styles: [".cortex-tool-details{width:100%;border-radius:.5rem;border:1px solid #e2e8f0;background:#f8fafc;overflow:hidden}.cortex-tool-details__summary{list-style:none;cursor:pointer;-webkit-user-select:none;user-select:none;padding:.75rem 1rem}.cortex-tool-details__summary::-webkit-details-marker{display:none}details[open]>.cortex-tool-details__summary{border-bottom:1px solid #e2e8f0}.cortex-tool-details__header{display:flex;align-items:flex-start;gap:.75rem}.cortex-tool-details__icon-box{flex-shrink:0;margin-top:.125rem;width:1.75rem;height:1.75rem;border-radius:.25rem;background:#1e293b;color:#fff;display:flex;align-items:center;justify-content:center}.cortex-tool-details__icon-svg{width:1rem;height:1rem}.cortex-tool-details__info{min-width:0;flex:1;display:flex;align-items:center;gap:.5rem}.cortex-tool-details__title-row{min-width:0;flex:1}.cortex-tool-details__name-row{display:flex;align-items:center;gap:.5rem;min-width:0}.cortex-tool-details__name{font-weight:500;color:#1e293b;font-size:.875rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cortex-tool-details__tool-badge{display:none;align-items:center;border-radius:.25rem;background:#e2e8f0;padding:.125rem .375rem;font-size:.6875rem;font-weight:500;color:#475569;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace}@media(min-width:640px){.cortex-tool-details__tool-badge{display:inline-flex}}.cortex-tool-details__id-line{margin-top:.125rem;font-size:.6875rem;color:#64748b;word-break:break-all}.cortex-tool-details__id-label{font-weight:500;color:#475569}.cortex-tool-details__id-value{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace}.cortex-tool-details__provider-badge{margin-inline-start:.5rem;display:inline-flex;align-items:center;gap:.25rem;color:#047857}.cortex-tool-details__provider-dot{width:.375rem;height:.375rem;border-radius:9999px;background:#10b981}.cortex-tool-details__actions{display:flex;align-items:center;gap:.5rem}.cortex-tool-details__state-badge{display:inline-flex;align-items:center;gap:.375rem;border-radius:.25rem;padding:.25rem .5rem;font-size:.6875rem;font-weight:500;border:1px solid}.cortex-tool-details__state-badge--success{background:#ecfdf5;color:#047857;border-color:#a7f3d0}.cortex-tool-details__state-badge--error{background:#fef2f2;color:#b91c1c;border-color:#fecaca}.cortex-tool-details__state-badge--denied{background:#f1f5f9;color:#475569;border-color:#e2e8f0}.cortex-tool-details__state-badge--approval{background:#f5f3ff;color:#6d28d9;border-color:#ddd6fe}.cortex-tool-details__state-badge--pending{background:#fffbeb;color:#b45309;border-color:#fde68a}.cortex-tool-details__status-dot{width:.375rem;height:.375rem;border-radius:9999px}.cortex-tool-details__status-dot--amber{background:#f59e0b}.cortex-tool-details__status-dot--violet{background:#8b5cf6}.cortex-tool-details__status-dot--emerald{background:#10b981}.cortex-tool-details__status-dot--red{background:#ef4444}.cortex-tool-details__status-dot--slate{background:#64748b}.cortex-tool-details__status-dot--pulse{animation:cortex-tool-status-pulse 2s ease-in-out infinite}@keyframes cortex-tool-status-pulse{0%,to{opacity:1}50%{opacity:.4}}.cortex-tool-details__chevron{display:inline-flex;width:1.5rem;height:1.5rem;align-items:center;justify-content:center;border-radius:.25rem;color:#94a3b8;transition:transform .2s}details[open]>summary .cortex-tool-details__chevron{transform:rotate(180deg)}.cortex-tool-details__body{background:#fff;padding:.75rem 1rem 1rem}.cortex-tool-details__panels{display:grid;gap:.75rem}.cortex-tool-details__panel{border-radius:.5rem;border:1px solid #e2e8f0;overflow:hidden}.cortex-tool-details__panel--success{border-color:#a7f3d0}.cortex-tool-details__panel--error{border-color:#fecaca}.cortex-tool-details__panel--approval{border-color:#ddd6fe}.cortex-tool-details__panel-header{padding:.5rem .75rem;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid #e2e8f0;background:#f8fafc}.cortex-tool-details__panel-header--success{border-bottom-color:#a7f3d0;background:#ecfdf5}.cortex-tool-details__panel-header--error{border-bottom-color:#fecaca;background:#fef2f2}.cortex-tool-details__panel-header--approval{border-bottom-color:#ddd6fe;background:#f5f3ff}.cortex-tool-details__panel-label{font-size:.6875rem;font-weight:500;color:#475569}.cortex-tool-details__panel-label--success{color:#047857}.cortex-tool-details__panel-label--error{color:#b91c1c}.cortex-tool-details__panel-label--approval{color:#6d28d9}.cortex-tool-details__panel-lang{font-size:.6875rem;color:#94a3b8;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace}.cortex-tool-details__panel-lang--success{color:#059669}.cortex-tool-details__panel-lang--error{color:#dc2626}.cortex-tool-details__panel-lang--approval{color:#7c3aed}.cortex-tool-details__code-pre{margin:0;max-height:20rem;overflow:auto;padding:.75rem;font-size:.75rem;line-height:1.25rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;white-space:pre;overflow-wrap:break-word}.cortex-tool-details__tree-container{max-height:16rem;overflow:auto;padding:.75rem}.cortex-tool-details__error-pre{margin:0;max-height:16rem;overflow:auto;padding:.75rem;font-size:.75rem;line-height:1.25rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;color:#991b1b;white-space:pre-wrap;overflow-wrap:break-word}.cortex-tool-details__denied-body{padding:.75rem;font-size:.75rem;color:#334155}.cortex-tool-details__approval-body{padding:.75rem;font-size:.75rem;color:#5b21b6}.cortex-tool-details__reason-box{margin-top:.5rem;border-radius:.25rem;background:#f8fafc;border:1px solid #e2e8f0;padding:.5rem}.cortex-tool-details__reason-box--approval{background:#f5f3ff;border-color:#ddd6fe}.cortex-tool-details__reason-label{font-size:.6875rem;font-weight:500;color:#475569}.cortex-tool-details__reason-label--approval{color:#6d28d9}.cortex-tool-details__reason-text{margin-top:.25rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:.75rem;color:#334155;overflow-wrap:break-word}.cortex-tool-details__reason-text--approval{color:#5b21b6}\n"] }]
524
549
  }], propDecorators: { toolCallPart: [{ type: i0.Input, args: [{ isSignal: true, alias: "toolCallPart", required: true }] }] } });
525
550
 
526
551
  class MessageCaptureFilesPartComponent {
@@ -618,11 +643,11 @@ class MessageCaptureFilesPartComponent {
618
643
  });
619
644
  }
620
645
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: MessageCaptureFilesPartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
621
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.3", type: MessageCaptureFilesPartComponent, isStandalone: true, selector: "cortex-message-capture-files-part", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null }, toolPart: { classPropertyName: "toolPart", publicName: "toolPart", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "@if (outputData(); as output) {\n <!-- \u2500\u2500 Result state \u2500\u2500 -->\n @if (output.status === 'success') {\n <div\n class=\"overflow-hidden rounded-2xl border border-emerald-200 bg-linear-to-b from-emerald-50/80 to-white\"\n >\n <div class=\"flex items-center gap-3 px-4 py-3 sm:px-5\">\n <div\n class=\"inline-flex size-8 shrink-0 items-center justify-center rounded-lg border border-emerald-200 bg-emerald-100 text-emerald-600\"\n >\n <svg viewBox=\"0 0 20 20\" class=\"size-4\" fill=\"none\">\n <path\n d=\"M5.5 10.5 L8.5 13.5 L14.5 7\"\n stroke=\"currentColor\"\n stroke-width=\"1.75\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </div>\n <div class=\"min-w-0\">\n <h3 class=\"text-[13px] font-semibold tracking-tight text-emerald-800\">\n {{ 'translate_files_attached' | translate: { count: data().files.length } }}\n </h3>\n </div>\n </div>\n\n <div class=\"border-t border-emerald-100 px-4 py-2.5 sm:px-5\">\n <ul class=\"flex flex-wrap gap-2\">\n @for (file of outputData()!.result!; track file.uploadId) {\n <li>\n <button\n type=\"button\"\n (click)=\"downloadFile(file.uploadId)\"\n class=\"inline-flex cursor-pointer items-center gap-1.5 rounded-lg border border-emerald-100 bg-white px-2.5 py-1.5 text-[12px] font-medium text-emerald-700 shadow-xs transition-all hover:border-emerald-200 hover:bg-emerald-50 hover:shadow-sm active:scale-[0.97]\"\n >\n <svg viewBox=\"0 0 16 16\" class=\"size-3.5 shrink-0 text-emerald-400\" fill=\"none\">\n <path\n d=\"M4 1.5h5.172a2 2 0 0 1 1.414.586l2.328 2.328a2 2 0 0 1 .586 1.414V12.5a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-9a2 2 0 0 1 2-2Z\"\n stroke=\"currentColor\"\n stroke-width=\"1.25\"\n />\n <path\n d=\"M9.5 1.5v2a2 2 0 0 0 2 2h2\"\n stroke=\"currentColor\"\n stroke-width=\"1.25\"\n stroke-linecap=\"round\"\n />\n </svg>\n <span class=\"truncate max-w-[180px]\">{{ data().files[$index].label }}</span>\n <svg viewBox=\"0 0 16 16\" class=\"size-3 shrink-0 text-emerald-300\" fill=\"none\">\n <path\n d=\"M8 3v7m0 0L5.5 7.5M8 10l2.5-2.5M3 13h10\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n </li>\n }\n </ul>\n </div>\n </div>\n } @else {\n <!-- Canceled state -->\n <div class=\"flex justify-center\">\n <div\n class=\"inline-flex items-center gap-2.5 rounded-lg border border-slate-200 bg-slate-50 px-4 py-2.5\"\n >\n <div\n class=\"inline-flex size-6 shrink-0 items-center justify-center rounded-md border border-slate-200 bg-slate-100 text-slate-400\"\n >\n <svg viewBox=\"0 0 20 20\" class=\"size-3\" fill=\"none\">\n <path d=\"M6 10h8\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" />\n </svg>\n </div>\n <span class=\"text-[13px] font-medium text-slate-400\">{{\n 'translate_file_upload_skipped' | translate\n }}</span>\n </div>\n </div>\n }\n} @else {\n <!-- \u2500\u2500 Upload form \u2500\u2500 -->\n <form [formGroup]=\"form()\" class=\"w-full\">\n @if (data().files.length === 1) {\n <!-- Single file: minimal inline -->\n <fieldset [formArray]=\"form().controls.files\">\n <div [formGroupName]=\"0\" class=\"rounded-xl border border-slate-200 bg-slate-50 p-3\">\n <label for=\"capture-file-0\" class=\"mb-1.5 block text-[12px] text-slate-500\">\n {{ data().files[0].label }}\n </label>\n <input hidden formControlName=\"id\" />\n <div class=\"flex items-center gap-2\">\n <cortex-file-input id=\"capture-file-0\" formControlName=\"file\" class=\"min-w-0 flex-1\" />\n <button\n (click)=\"cancel()\"\n [disabled]=\"isLoading()\"\n type=\"button\"\n class=\"shrink-0 text-[12px] font-medium text-slate-400 transition-colors hover:text-slate-600\"\n >\n {{ 'translate_cancel' | translate }}\n </button>\n <button\n (click)=\"submit()\"\n type=\"button\"\n class=\"inline-flex shrink-0 items-center rounded-md bg-slate-800 px-3 py-1.5 text-[12px] font-medium text-slate-100 transition-colors hover:bg-slate-700 active:bg-slate-900 disabled:cursor-not-allowed disabled:bg-slate-300\"\n [disabled]=\"form().invalid || isLoading()\"\n >\n @if (isLoading()) {\n <svg class=\"mr-1.5 size-3 animate-spin\" viewBox=\"0 0 16 16\" fill=\"none\">\n <circle\n cx=\"8\"\n cy=\"8\"\n r=\"6\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n class=\"opacity-25\"\n />\n <path\n d=\"M14 8a6 6 0 0 0-6-6\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n />\n </svg>\n {{ 'translate_uploading' | translate }}\n } @else {\n {{ 'translate_submit' | translate }}\n }\n </button>\n </div>\n </div>\n </fieldset>\n } @else {\n <!-- Multi-file: full form -->\n <div\n class=\"overflow-hidden rounded-2xl border border-slate-200 bg-gradient-to-b from-white to-slate-50\"\n >\n <div class=\"border-b border-slate-200 bg-white/85 px-4 py-3 sm:px-5\">\n <div class=\"flex items-start gap-3\">\n <div\n class=\"mt-0.5 inline-flex size-8 shrink-0 items-center justify-center rounded-lg border border-slate-200 bg-slate-100 text-slate-600\"\n aria-hidden=\"true\"\n >\n <svg viewBox=\"0 0 20 20\" class=\"size-4\" fill=\"none\">\n <path\n d=\"M4 13.5a3.5 3.5 0 0 1-.5-6.97 5.002 5.002 0 0 1 9.78-1.03A4.5 4.5 0 0 1 15.5 14\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n <path\n d=\"M10 10v7m0-7-2.5 2.5M10 10l2.5 2.5\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </div>\n\n <div class=\"min-w-0\">\n <h3 class=\"text-[13px] font-semibold tracking-tight text-slate-800\">\n {{ 'translate_attach_requested_files' | translate }}\n </h3>\n <p class=\"mt-0.5 text-[11px] text-slate-500\">\n {{ 'translate_n_files_required' | translate: { count: data().files.length } }}\n </p>\n </div>\n </div>\n </div>\n\n <fieldset [formArray]=\"form().controls.files\" class=\"grid gap-3 p-4 sm:p-5\">\n @for (file of form().controls.files.value; track $index) {\n <div\n [formGroupName]=\"$index\"\n class=\"rounded-xl border border-slate-200 bg-white p-3 transition-colors hover:border-slate-300\"\n >\n <div class=\"mb-2 flex items-center justify-between gap-2\">\n <label\n [for]=\"'capture-file-' + $index\"\n class=\"block text-[12px] font-medium leading-none text-slate-700\"\n >\n {{ data().files[$index].label }}\n </label>\n <span\n class=\"inline-flex items-center rounded border border-slate-200 bg-slate-50 px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-slate-500\"\n >\n {{ 'translate_required' | translate }}\n </span>\n </div>\n\n <input hidden formControlName=\"id\" />\n <cortex-file-input [id]=\"'capture-file-' + $index\" formControlName=\"file\" />\n </div>\n }\n </fieldset>\n\n <div\n class=\"flex items-center justify-end gap-2 border-t border-slate-200 bg-white/90 px-4 py-3 sm:px-5\"\n >\n <button\n (click)=\"cancel()\"\n [disabled]=\"isLoading()\"\n type=\"button\"\n class=\"inline-flex items-center rounded-md border border-slate-300 bg-white px-3 py-1.5 text-[12px] font-medium text-slate-600 transition-colors hover:bg-slate-50 active:bg-slate-100\"\n >\n {{ 'translate_cancel' | translate }}\n </button>\n <button\n (click)=\"submit()\"\n type=\"button\"\n class=\"inline-flex items-center rounded-md bg-slate-800 px-3 py-1.5 text-[12px] font-medium text-slate-100 transition-colors hover:bg-slate-700 active:bg-slate-900 disabled:cursor-not-allowed disabled:bg-slate-300\"\n [disabled]=\"form().invalid || isLoading()\"\n >\n @if (isLoading()) {\n <svg class=\"mr-1.5 size-3 animate-spin\" viewBox=\"0 0 16 16\" fill=\"none\">\n <circle\n cx=\"8\"\n cy=\"8\"\n r=\"6\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n class=\"opacity-25\"\n />\n <path\n d=\"M14 8a6 6 0 0 0-6-6\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n />\n </svg>\n {{ 'translate_uploading' | translate }}\n } @else {\n {{ 'translate_submit_files' | translate }}\n }\n </button>\n </div>\n </div>\n }\n </form>\n}\n", styles: [":host{display:block;width:100%}\n/*! tailwindcss v4.2.1 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "ngmodule", type: i0.forwardRef(() => ReactiveFormsModule) }, { kind: "directive", type: i0.forwardRef(() => i1$1.ɵNgNoValidate), selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i0.forwardRef(() => i1$1.DefaultValueAccessor), selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i0.forwardRef(() => i1$1.NgControlStatus), selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i0.forwardRef(() => i1$1.NgControlStatusGroup), selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i0.forwardRef(() => i1$1.FormGroupDirective), selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i0.forwardRef(() => i1$1.FormArrayDirective), selector: "[formArray]", inputs: ["formArray"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i0.forwardRef(() => i1$1.FormControlName), selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i0.forwardRef(() => i1$1.FormGroupName), selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "component", type: i0.forwardRef(() => FileInputComponent), selector: "cortex-file-input", inputs: ["allowedMimeTypes"] }, { kind: "pipe", type: i0.forwardRef(() => TranslatePipe), name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
646
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.3", type: MessageCaptureFilesPartComponent, isStandalone: true, selector: "cortex-message-capture-files-part", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null }, toolPart: { classPropertyName: "toolPart", publicName: "toolPart", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "cortex-capture-files" }, ngImport: i0, template: "@if (outputData(); as output) {\n <!-- Result state -->\n @if (output.status === 'success') {\n <div class=\"cortex-capture-success\">\n <div class=\"cortex-capture-success__header\">\n <div class=\"cortex-capture-success__icon\">\n <svg viewBox=\"0 0 20 20\" class=\"cortex-capture-success__icon-svg\" fill=\"none\">\n <path\n d=\"M5.5 10.5 L8.5 13.5 L14.5 7\"\n stroke=\"currentColor\"\n stroke-width=\"1.75\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </div>\n <div class=\"cortex-capture-success__title-wrap\">\n <h3 class=\"cortex-capture-success__title\">\n {{ 'translate_files_attached' | translate: { count: data().files.length } }}\n </h3>\n </div>\n </div>\n\n <div class=\"cortex-capture-success__file-list\">\n <ul class=\"cortex-capture-success__files\">\n @for (file of outputData()!.result!; track file.uploadId) {\n <li>\n <button\n type=\"button\"\n (click)=\"downloadFile(file.uploadId)\"\n class=\"cortex-capture-success__download-btn\"\n >\n <svg viewBox=\"0 0 16 16\" class=\"cortex-capture-success__file-icon\" fill=\"none\">\n <path\n d=\"M4 1.5h5.172a2 2 0 0 1 1.414.586l2.328 2.328a2 2 0 0 1 .586 1.414V12.5a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-9a2 2 0 0 1 2-2Z\"\n stroke=\"currentColor\"\n stroke-width=\"1.25\"\n />\n <path\n d=\"M9.5 1.5v2a2 2 0 0 0 2 2h2\"\n stroke=\"currentColor\"\n stroke-width=\"1.25\"\n stroke-linecap=\"round\"\n />\n </svg>\n <span class=\"cortex-capture-success__file-name\">{{\n data().files[$index].label\n }}</span>\n <svg viewBox=\"0 0 16 16\" class=\"cortex-capture-success__dl-icon\" fill=\"none\">\n <path\n d=\"M8 3v7m0 0L5.5 7.5M8 10l2.5-2.5M3 13h10\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n </li>\n }\n </ul>\n </div>\n </div>\n } @else {\n <!-- Canceled state -->\n <div class=\"cortex-capture-canceled-wrap\">\n <div class=\"cortex-capture-canceled\">\n <div class=\"cortex-capture-canceled__icon\">\n <svg viewBox=\"0 0 20 20\" class=\"cortex-capture-canceled__icon-svg\" fill=\"none\">\n <path d=\"M6 10h8\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" />\n </svg>\n </div>\n <span class=\"cortex-capture-canceled__text\">{{\n 'translate_file_upload_skipped' | translate\n }}</span>\n </div>\n </div>\n }\n} @else {\n <!-- Upload form -->\n <form [formGroup]=\"form()\" class=\"cortex-capture-form\">\n @if (data().files.length === 1) {\n <!-- Single file: minimal inline -->\n <fieldset [formArray]=\"form().controls.files\">\n <div [formGroupName]=\"0\" class=\"cortex-capture-form__single\">\n <label for=\"capture-file-0\" class=\"cortex-capture-form__label\">\n {{ data().files[0].label }}\n </label>\n <input hidden formControlName=\"id\" />\n <div class=\"cortex-capture-form__row\">\n <cortex-file-input\n id=\"capture-file-0\"\n formControlName=\"file\"\n class=\"cortex-capture-form__input\"\n />\n <button\n (click)=\"cancel()\"\n [disabled]=\"isLoading()\"\n type=\"button\"\n class=\"cortex-capture-form__cancel-link\"\n >\n {{ 'translate_cancel' | translate }}\n </button>\n <button\n (click)=\"submit()\"\n type=\"button\"\n class=\"cortex-capture-form__submit-btn\"\n [disabled]=\"form().invalid || isLoading()\"\n >\n @if (isLoading()) {\n <svg class=\"cortex-capture-form__spinner\" viewBox=\"0 0 16 16\" fill=\"none\">\n <circle\n cx=\"8\"\n cy=\"8\"\n r=\"6\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n class=\"cortex-capture-form__spinner-track\"\n />\n <path\n d=\"M14 8a6 6 0 0 0-6-6\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n />\n </svg>\n {{ 'translate_uploading' | translate }}\n } @else {\n {{ 'translate_submit' | translate }}\n }\n </button>\n </div>\n </div>\n </fieldset>\n } @else {\n <!-- Multi-file: full form -->\n <div class=\"cortex-capture-form__multi\">\n <div class=\"cortex-capture-form__multi-header\">\n <div class=\"cortex-capture-form__multi-header-row\">\n <div class=\"cortex-capture-form__multi-icon\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 20 20\" class=\"cortex-capture-form__multi-icon-svg\" fill=\"none\">\n <path\n d=\"M4 13.5a3.5 3.5 0 0 1-.5-6.97 5.002 5.002 0 0 1 9.78-1.03A4.5 4.5 0 0 1 15.5 14\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n <path\n d=\"M10 10v7m0-7-2.5 2.5M10 10l2.5 2.5\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </div>\n\n <div class=\"cortex-capture-form__multi-title-wrap\">\n <h3 class=\"cortex-capture-form__multi-title\">\n {{ 'translate_attach_requested_files' | translate }}\n </h3>\n <p class=\"cortex-capture-form__multi-subtitle\">\n {{ 'translate_n_files_required' | translate: { count: data().files.length } }}\n </p>\n </div>\n </div>\n </div>\n\n <fieldset [formArray]=\"form().controls.files\" class=\"cortex-capture-form__multi-fields\">\n @for (file of form().controls.files.value; track $index) {\n <div [formGroupName]=\"$index\" class=\"cortex-capture-form__multi-item\">\n <div class=\"cortex-capture-form__multi-item-header\">\n <label\n [for]=\"'capture-file-' + $index\"\n class=\"cortex-capture-form__multi-item-label\"\n >\n {{ data().files[$index].label }}\n </label>\n <span class=\"cortex-capture-form__required-badge\">\n {{ 'translate_required' | translate }}\n </span>\n </div>\n <input hidden formControlName=\"id\" />\n <cortex-file-input [id]=\"'capture-file-' + $index\" formControlName=\"file\" />\n </div>\n }\n </fieldset>\n\n <div class=\"cortex-capture-form__multi-footer\">\n <button\n (click)=\"cancel()\"\n [disabled]=\"isLoading()\"\n type=\"button\"\n class=\"cortex-capture-form__cancel-btn\"\n >\n {{ 'translate_cancel' | translate }}\n </button>\n <button\n (click)=\"submit()\"\n type=\"button\"\n class=\"cortex-capture-form__submit-btn\"\n [disabled]=\"form().invalid || isLoading()\"\n >\n @if (isLoading()) {\n <svg class=\"cortex-capture-form__spinner\" viewBox=\"0 0 16 16\" fill=\"none\">\n <circle\n cx=\"8\"\n cy=\"8\"\n r=\"6\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n class=\"cortex-capture-form__spinner-track\"\n />\n <path\n d=\"M14 8a6 6 0 0 0-6-6\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n />\n </svg>\n {{ 'translate_uploading' | translate }}\n } @else {\n {{ 'translate_submit_files' | translate }}\n }\n </button>\n </div>\n </div>\n }\n </form>\n}\n", styles: [".cortex-capture-files{display:block;width:100%}.cortex-file-input{display:flex;width:100%;cursor:pointer;align-items:center;border-radius:.5rem;border:1px solid #cbd5e1;background:#f8fafc;transition:border-color .15s}.cortex-file-input:hover{border-color:#94a3b8}.cortex-file-input:focus-within{box-shadow:0 0 0 2px #cbd5e1}.cortex-file-input__btn{margin:.25rem;flex-shrink:0;cursor:pointer;border-radius:.375rem;border:0;background:#1e293b;padding:.375rem .75rem;font-size:.6875rem;font-weight:600;letter-spacing:.025em;color:#f1f5f9}.cortex-file-input__btn:hover{background:#334155}.cortex-file-input__name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:0 .75rem;font-size:.75rem;color:#475569}.cortex-file-input__hidden{display:none}.cortex-capture-success{overflow:hidden;border-radius:1rem;border:1px solid #a7f3d0;background:linear-gradient(to bottom,#ecfdf5cc,#fff)}.cortex-capture-success__header{display:flex;align-items:center;gap:.75rem;padding:.75rem 1rem}@media(min-width:640px){.cortex-capture-success__header{padding:.75rem 1.25rem}}.cortex-capture-success__icon{display:inline-flex;width:2rem;height:2rem;flex-shrink:0;align-items:center;justify-content:center;border-radius:.5rem;border:1px solid #a7f3d0;background:#d1fae5;color:#059669}.cortex-capture-success__icon-svg{width:1rem;height:1rem}.cortex-capture-success__title-wrap{min-width:0}.cortex-capture-success__title{font-size:.8125rem;font-weight:600;letter-spacing:-.01em;color:#065f46;margin:0}.cortex-capture-success__file-list{border-top:1px solid #d1fae5;padding:.625rem 1rem}@media(min-width:640px){.cortex-capture-success__file-list{padding:.625rem 1.25rem}}.cortex-capture-success__files{display:flex;flex-wrap:wrap;gap:.5rem;list-style:none;margin:0;padding:0}.cortex-capture-success__download-btn{display:inline-flex;cursor:pointer;align-items:center;gap:.375rem;border-radius:.5rem;border:1px solid #d1fae5;background:#fff;padding:.375rem .625rem;font-size:.75rem;font-weight:500;color:#047857;box-shadow:0 1px 2px #0000000d;transition:all .15s}.cortex-capture-success__download-btn:hover{border-color:#a7f3d0;background:#ecfdf5;box-shadow:0 1px 3px #0000001a}.cortex-capture-success__download-btn:active{transform:scale(.97)}.cortex-capture-success__file-icon{width:.875rem;height:.875rem;flex-shrink:0;color:#6ee7b7}.cortex-capture-success__file-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:180px}.cortex-capture-success__dl-icon{width:.75rem;height:.75rem;flex-shrink:0;color:#86efac}.cortex-capture-canceled-wrap{display:flex;justify-content:center}.cortex-capture-canceled{display:inline-flex;align-items:center;gap:.625rem;border-radius:.5rem;border:1px solid #e2e8f0;background:#f8fafc;padding:.625rem 1rem}.cortex-capture-canceled__icon{display:inline-flex;width:1.5rem;height:1.5rem;flex-shrink:0;align-items:center;justify-content:center;border-radius:.375rem;border:1px solid #e2e8f0;background:#f1f5f9;color:#94a3b8}.cortex-capture-canceled__icon-svg{width:.75rem;height:.75rem}.cortex-capture-canceled__text{font-size:.8125rem;font-weight:500;color:#94a3b8}.cortex-capture-form{width:100%}.cortex-capture-form__single{border-radius:.75rem;border:1px solid #e2e8f0;background:#f8fafc;padding:.75rem}.cortex-capture-form__label{display:block;margin-bottom:.375rem;font-size:.75rem;color:#64748b}.cortex-capture-form__row{display:flex;align-items:center;gap:.5rem}.cortex-capture-form__input{min-width:0;flex:1}.cortex-capture-form__cancel-link{flex-shrink:0;font-size:.75rem;font-weight:500;color:#94a3b8;background:none;border:0;cursor:pointer;transition:color .15s}.cortex-capture-form__cancel-link:hover{color:#475569}.cortex-capture-form__submit-btn{display:inline-flex;flex-shrink:0;align-items:center;border-radius:.375rem;background:#1e293b;padding:.375rem .75rem;font-size:.75rem;font-weight:500;color:#f1f5f9;border:0;cursor:pointer;transition:background-color .15s}.cortex-capture-form__submit-btn:hover{background:#334155}.cortex-capture-form__submit-btn:active{background:#0f172a}.cortex-capture-form__submit-btn:disabled{cursor:not-allowed;background:#cbd5e1}.cortex-capture-form__spinner{width:.75rem;height:.75rem;margin-inline-end:.375rem;animation:cortex-capture-spin .8s linear infinite}.cortex-capture-form__spinner-track{opacity:.25}@keyframes cortex-capture-spin{to{transform:rotate(360deg)}}.cortex-capture-form__multi{overflow:hidden;border-radius:1rem;border:1px solid #e2e8f0;background:linear-gradient(to bottom,#fff,#f8fafc)}.cortex-capture-form__multi-header{border-bottom:1px solid #e2e8f0;background:#ffffffd9;padding:.75rem 1rem}@media(min-width:640px){.cortex-capture-form__multi-header{padding:.75rem 1.25rem}}.cortex-capture-form__multi-header-row{display:flex;align-items:flex-start;gap:.75rem}.cortex-capture-form__multi-icon{margin-top:.125rem;display:inline-flex;width:2rem;height:2rem;flex-shrink:0;align-items:center;justify-content:center;border-radius:.5rem;border:1px solid #e2e8f0;background:#f1f5f9;color:#475569}.cortex-capture-form__multi-icon-svg{width:1rem;height:1rem}.cortex-capture-form__multi-title-wrap{min-width:0}.cortex-capture-form__multi-title{font-size:.8125rem;font-weight:600;letter-spacing:-.01em;color:#1e293b;margin:0}.cortex-capture-form__multi-subtitle{margin-top:.125rem;font-size:.6875rem;color:#64748b;margin-bottom:0}.cortex-capture-form__multi-fields{display:grid;gap:.75rem;padding:1rem;border:0}@media(min-width:640px){.cortex-capture-form__multi-fields{padding:1.25rem}}.cortex-capture-form__multi-item{border-radius:.75rem;border:1px solid #e2e8f0;background:#fff;padding:.75rem;transition:border-color .15s}.cortex-capture-form__multi-item:hover{border-color:#cbd5e1}.cortex-capture-form__multi-item-header{margin-bottom:.5rem;display:flex;align-items:center;justify-content:space-between;gap:.5rem}.cortex-capture-form__multi-item-label{display:block;font-size:.75rem;font-weight:500;line-height:1;color:#334155}.cortex-capture-form__required-badge{display:inline-flex;align-items:center;border-radius:.25rem;border:1px solid #e2e8f0;background:#f8fafc;padding:.125rem .375rem;font-size:.625rem;font-weight:500;text-transform:uppercase;letter-spacing:.05em;color:#64748b}.cortex-capture-form__multi-footer{display:flex;align-items:center;justify-content:flex-end;gap:.5rem;border-top:1px solid #e2e8f0;background:#ffffffe6;padding:.75rem 1rem}@media(min-width:640px){.cortex-capture-form__multi-footer{padding:.75rem 1.25rem}}.cortex-capture-form__cancel-btn{display:inline-flex;align-items:center;border-radius:.375rem;border:1px solid #cbd5e1;background:#fff;padding:.375rem .75rem;font-size:.75rem;font-weight:500;color:#475569;cursor:pointer;transition:background-color .15s}.cortex-capture-form__cancel-btn:hover{background:#f8fafc}.cortex-capture-form__cancel-btn:active{background:#f1f5f9}\n"], dependencies: [{ kind: "ngmodule", type: i0.forwardRef(() => ReactiveFormsModule) }, { kind: "directive", type: i0.forwardRef(() => i1$1.ɵNgNoValidate), selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i0.forwardRef(() => i1$1.DefaultValueAccessor), selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i0.forwardRef(() => i1$1.NgControlStatus), selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i0.forwardRef(() => i1$1.NgControlStatusGroup), selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i0.forwardRef(() => i1$1.FormGroupDirective), selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i0.forwardRef(() => i1$1.FormArrayDirective), selector: "[formArray]", inputs: ["formArray"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i0.forwardRef(() => i1$1.FormControlName), selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i0.forwardRef(() => i1$1.FormGroupName), selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "component", type: i0.forwardRef(() => FileInputComponent), selector: "cortex-file-input", inputs: ["allowedMimeTypes"] }, { kind: "pipe", type: i0.forwardRef(() => TranslatePipe), name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
622
647
  }
623
648
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: MessageCaptureFilesPartComponent, decorators: [{
624
649
  type: Component,
625
- args: [{ selector: 'cortex-message-capture-files-part', changeDetection: ChangeDetectionStrategy.OnPush, imports: [ReactiveFormsModule, forwardRef(() => FileInputComponent), TranslatePipe], template: "@if (outputData(); as output) {\n <!-- \u2500\u2500 Result state \u2500\u2500 -->\n @if (output.status === 'success') {\n <div\n class=\"overflow-hidden rounded-2xl border border-emerald-200 bg-linear-to-b from-emerald-50/80 to-white\"\n >\n <div class=\"flex items-center gap-3 px-4 py-3 sm:px-5\">\n <div\n class=\"inline-flex size-8 shrink-0 items-center justify-center rounded-lg border border-emerald-200 bg-emerald-100 text-emerald-600\"\n >\n <svg viewBox=\"0 0 20 20\" class=\"size-4\" fill=\"none\">\n <path\n d=\"M5.5 10.5 L8.5 13.5 L14.5 7\"\n stroke=\"currentColor\"\n stroke-width=\"1.75\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </div>\n <div class=\"min-w-0\">\n <h3 class=\"text-[13px] font-semibold tracking-tight text-emerald-800\">\n {{ 'translate_files_attached' | translate: { count: data().files.length } }}\n </h3>\n </div>\n </div>\n\n <div class=\"border-t border-emerald-100 px-4 py-2.5 sm:px-5\">\n <ul class=\"flex flex-wrap gap-2\">\n @for (file of outputData()!.result!; track file.uploadId) {\n <li>\n <button\n type=\"button\"\n (click)=\"downloadFile(file.uploadId)\"\n class=\"inline-flex cursor-pointer items-center gap-1.5 rounded-lg border border-emerald-100 bg-white px-2.5 py-1.5 text-[12px] font-medium text-emerald-700 shadow-xs transition-all hover:border-emerald-200 hover:bg-emerald-50 hover:shadow-sm active:scale-[0.97]\"\n >\n <svg viewBox=\"0 0 16 16\" class=\"size-3.5 shrink-0 text-emerald-400\" fill=\"none\">\n <path\n d=\"M4 1.5h5.172a2 2 0 0 1 1.414.586l2.328 2.328a2 2 0 0 1 .586 1.414V12.5a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-9a2 2 0 0 1 2-2Z\"\n stroke=\"currentColor\"\n stroke-width=\"1.25\"\n />\n <path\n d=\"M9.5 1.5v2a2 2 0 0 0 2 2h2\"\n stroke=\"currentColor\"\n stroke-width=\"1.25\"\n stroke-linecap=\"round\"\n />\n </svg>\n <span class=\"truncate max-w-[180px]\">{{ data().files[$index].label }}</span>\n <svg viewBox=\"0 0 16 16\" class=\"size-3 shrink-0 text-emerald-300\" fill=\"none\">\n <path\n d=\"M8 3v7m0 0L5.5 7.5M8 10l2.5-2.5M3 13h10\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n </li>\n }\n </ul>\n </div>\n </div>\n } @else {\n <!-- Canceled state -->\n <div class=\"flex justify-center\">\n <div\n class=\"inline-flex items-center gap-2.5 rounded-lg border border-slate-200 bg-slate-50 px-4 py-2.5\"\n >\n <div\n class=\"inline-flex size-6 shrink-0 items-center justify-center rounded-md border border-slate-200 bg-slate-100 text-slate-400\"\n >\n <svg viewBox=\"0 0 20 20\" class=\"size-3\" fill=\"none\">\n <path d=\"M6 10h8\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" />\n </svg>\n </div>\n <span class=\"text-[13px] font-medium text-slate-400\">{{\n 'translate_file_upload_skipped' | translate\n }}</span>\n </div>\n </div>\n }\n} @else {\n <!-- \u2500\u2500 Upload form \u2500\u2500 -->\n <form [formGroup]=\"form()\" class=\"w-full\">\n @if (data().files.length === 1) {\n <!-- Single file: minimal inline -->\n <fieldset [formArray]=\"form().controls.files\">\n <div [formGroupName]=\"0\" class=\"rounded-xl border border-slate-200 bg-slate-50 p-3\">\n <label for=\"capture-file-0\" class=\"mb-1.5 block text-[12px] text-slate-500\">\n {{ data().files[0].label }}\n </label>\n <input hidden formControlName=\"id\" />\n <div class=\"flex items-center gap-2\">\n <cortex-file-input id=\"capture-file-0\" formControlName=\"file\" class=\"min-w-0 flex-1\" />\n <button\n (click)=\"cancel()\"\n [disabled]=\"isLoading()\"\n type=\"button\"\n class=\"shrink-0 text-[12px] font-medium text-slate-400 transition-colors hover:text-slate-600\"\n >\n {{ 'translate_cancel' | translate }}\n </button>\n <button\n (click)=\"submit()\"\n type=\"button\"\n class=\"inline-flex shrink-0 items-center rounded-md bg-slate-800 px-3 py-1.5 text-[12px] font-medium text-slate-100 transition-colors hover:bg-slate-700 active:bg-slate-900 disabled:cursor-not-allowed disabled:bg-slate-300\"\n [disabled]=\"form().invalid || isLoading()\"\n >\n @if (isLoading()) {\n <svg class=\"mr-1.5 size-3 animate-spin\" viewBox=\"0 0 16 16\" fill=\"none\">\n <circle\n cx=\"8\"\n cy=\"8\"\n r=\"6\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n class=\"opacity-25\"\n />\n <path\n d=\"M14 8a6 6 0 0 0-6-6\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n />\n </svg>\n {{ 'translate_uploading' | translate }}\n } @else {\n {{ 'translate_submit' | translate }}\n }\n </button>\n </div>\n </div>\n </fieldset>\n } @else {\n <!-- Multi-file: full form -->\n <div\n class=\"overflow-hidden rounded-2xl border border-slate-200 bg-gradient-to-b from-white to-slate-50\"\n >\n <div class=\"border-b border-slate-200 bg-white/85 px-4 py-3 sm:px-5\">\n <div class=\"flex items-start gap-3\">\n <div\n class=\"mt-0.5 inline-flex size-8 shrink-0 items-center justify-center rounded-lg border border-slate-200 bg-slate-100 text-slate-600\"\n aria-hidden=\"true\"\n >\n <svg viewBox=\"0 0 20 20\" class=\"size-4\" fill=\"none\">\n <path\n d=\"M4 13.5a3.5 3.5 0 0 1-.5-6.97 5.002 5.002 0 0 1 9.78-1.03A4.5 4.5 0 0 1 15.5 14\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n <path\n d=\"M10 10v7m0-7-2.5 2.5M10 10l2.5 2.5\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </div>\n\n <div class=\"min-w-0\">\n <h3 class=\"text-[13px] font-semibold tracking-tight text-slate-800\">\n {{ 'translate_attach_requested_files' | translate }}\n </h3>\n <p class=\"mt-0.5 text-[11px] text-slate-500\">\n {{ 'translate_n_files_required' | translate: { count: data().files.length } }}\n </p>\n </div>\n </div>\n </div>\n\n <fieldset [formArray]=\"form().controls.files\" class=\"grid gap-3 p-4 sm:p-5\">\n @for (file of form().controls.files.value; track $index) {\n <div\n [formGroupName]=\"$index\"\n class=\"rounded-xl border border-slate-200 bg-white p-3 transition-colors hover:border-slate-300\"\n >\n <div class=\"mb-2 flex items-center justify-between gap-2\">\n <label\n [for]=\"'capture-file-' + $index\"\n class=\"block text-[12px] font-medium leading-none text-slate-700\"\n >\n {{ data().files[$index].label }}\n </label>\n <span\n class=\"inline-flex items-center rounded border border-slate-200 bg-slate-50 px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-slate-500\"\n >\n {{ 'translate_required' | translate }}\n </span>\n </div>\n\n <input hidden formControlName=\"id\" />\n <cortex-file-input [id]=\"'capture-file-' + $index\" formControlName=\"file\" />\n </div>\n }\n </fieldset>\n\n <div\n class=\"flex items-center justify-end gap-2 border-t border-slate-200 bg-white/90 px-4 py-3 sm:px-5\"\n >\n <button\n (click)=\"cancel()\"\n [disabled]=\"isLoading()\"\n type=\"button\"\n class=\"inline-flex items-center rounded-md border border-slate-300 bg-white px-3 py-1.5 text-[12px] font-medium text-slate-600 transition-colors hover:bg-slate-50 active:bg-slate-100\"\n >\n {{ 'translate_cancel' | translate }}\n </button>\n <button\n (click)=\"submit()\"\n type=\"button\"\n class=\"inline-flex items-center rounded-md bg-slate-800 px-3 py-1.5 text-[12px] font-medium text-slate-100 transition-colors hover:bg-slate-700 active:bg-slate-900 disabled:cursor-not-allowed disabled:bg-slate-300\"\n [disabled]=\"form().invalid || isLoading()\"\n >\n @if (isLoading()) {\n <svg class=\"mr-1.5 size-3 animate-spin\" viewBox=\"0 0 16 16\" fill=\"none\">\n <circle\n cx=\"8\"\n cy=\"8\"\n r=\"6\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n class=\"opacity-25\"\n />\n <path\n d=\"M14 8a6 6 0 0 0-6-6\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n />\n </svg>\n {{ 'translate_uploading' | translate }}\n } @else {\n {{ 'translate_submit_files' | translate }}\n }\n </button>\n </div>\n </div>\n }\n </form>\n}\n", styles: [":host{display:block;width:100%}\n/*! tailwindcss v4.2.1 | MIT License | https://tailwindcss.com */\n"] }]
650
+ args: [{ selector: 'cortex-message-capture-files-part', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [ReactiveFormsModule, forwardRef(() => FileInputComponent), TranslatePipe], host: { class: 'cortex-capture-files' }, template: "@if (outputData(); as output) {\n <!-- Result state -->\n @if (output.status === 'success') {\n <div class=\"cortex-capture-success\">\n <div class=\"cortex-capture-success__header\">\n <div class=\"cortex-capture-success__icon\">\n <svg viewBox=\"0 0 20 20\" class=\"cortex-capture-success__icon-svg\" fill=\"none\">\n <path\n d=\"M5.5 10.5 L8.5 13.5 L14.5 7\"\n stroke=\"currentColor\"\n stroke-width=\"1.75\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </div>\n <div class=\"cortex-capture-success__title-wrap\">\n <h3 class=\"cortex-capture-success__title\">\n {{ 'translate_files_attached' | translate: { count: data().files.length } }}\n </h3>\n </div>\n </div>\n\n <div class=\"cortex-capture-success__file-list\">\n <ul class=\"cortex-capture-success__files\">\n @for (file of outputData()!.result!; track file.uploadId) {\n <li>\n <button\n type=\"button\"\n (click)=\"downloadFile(file.uploadId)\"\n class=\"cortex-capture-success__download-btn\"\n >\n <svg viewBox=\"0 0 16 16\" class=\"cortex-capture-success__file-icon\" fill=\"none\">\n <path\n d=\"M4 1.5h5.172a2 2 0 0 1 1.414.586l2.328 2.328a2 2 0 0 1 .586 1.414V12.5a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-9a2 2 0 0 1 2-2Z\"\n stroke=\"currentColor\"\n stroke-width=\"1.25\"\n />\n <path\n d=\"M9.5 1.5v2a2 2 0 0 0 2 2h2\"\n stroke=\"currentColor\"\n stroke-width=\"1.25\"\n stroke-linecap=\"round\"\n />\n </svg>\n <span class=\"cortex-capture-success__file-name\">{{\n data().files[$index].label\n }}</span>\n <svg viewBox=\"0 0 16 16\" class=\"cortex-capture-success__dl-icon\" fill=\"none\">\n <path\n d=\"M8 3v7m0 0L5.5 7.5M8 10l2.5-2.5M3 13h10\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n </li>\n }\n </ul>\n </div>\n </div>\n } @else {\n <!-- Canceled state -->\n <div class=\"cortex-capture-canceled-wrap\">\n <div class=\"cortex-capture-canceled\">\n <div class=\"cortex-capture-canceled__icon\">\n <svg viewBox=\"0 0 20 20\" class=\"cortex-capture-canceled__icon-svg\" fill=\"none\">\n <path d=\"M6 10h8\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" />\n </svg>\n </div>\n <span class=\"cortex-capture-canceled__text\">{{\n 'translate_file_upload_skipped' | translate\n }}</span>\n </div>\n </div>\n }\n} @else {\n <!-- Upload form -->\n <form [formGroup]=\"form()\" class=\"cortex-capture-form\">\n @if (data().files.length === 1) {\n <!-- Single file: minimal inline -->\n <fieldset [formArray]=\"form().controls.files\">\n <div [formGroupName]=\"0\" class=\"cortex-capture-form__single\">\n <label for=\"capture-file-0\" class=\"cortex-capture-form__label\">\n {{ data().files[0].label }}\n </label>\n <input hidden formControlName=\"id\" />\n <div class=\"cortex-capture-form__row\">\n <cortex-file-input\n id=\"capture-file-0\"\n formControlName=\"file\"\n class=\"cortex-capture-form__input\"\n />\n <button\n (click)=\"cancel()\"\n [disabled]=\"isLoading()\"\n type=\"button\"\n class=\"cortex-capture-form__cancel-link\"\n >\n {{ 'translate_cancel' | translate }}\n </button>\n <button\n (click)=\"submit()\"\n type=\"button\"\n class=\"cortex-capture-form__submit-btn\"\n [disabled]=\"form().invalid || isLoading()\"\n >\n @if (isLoading()) {\n <svg class=\"cortex-capture-form__spinner\" viewBox=\"0 0 16 16\" fill=\"none\">\n <circle\n cx=\"8\"\n cy=\"8\"\n r=\"6\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n class=\"cortex-capture-form__spinner-track\"\n />\n <path\n d=\"M14 8a6 6 0 0 0-6-6\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n />\n </svg>\n {{ 'translate_uploading' | translate }}\n } @else {\n {{ 'translate_submit' | translate }}\n }\n </button>\n </div>\n </div>\n </fieldset>\n } @else {\n <!-- Multi-file: full form -->\n <div class=\"cortex-capture-form__multi\">\n <div class=\"cortex-capture-form__multi-header\">\n <div class=\"cortex-capture-form__multi-header-row\">\n <div class=\"cortex-capture-form__multi-icon\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 20 20\" class=\"cortex-capture-form__multi-icon-svg\" fill=\"none\">\n <path\n d=\"M4 13.5a3.5 3.5 0 0 1-.5-6.97 5.002 5.002 0 0 1 9.78-1.03A4.5 4.5 0 0 1 15.5 14\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n <path\n d=\"M10 10v7m0-7-2.5 2.5M10 10l2.5 2.5\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </div>\n\n <div class=\"cortex-capture-form__multi-title-wrap\">\n <h3 class=\"cortex-capture-form__multi-title\">\n {{ 'translate_attach_requested_files' | translate }}\n </h3>\n <p class=\"cortex-capture-form__multi-subtitle\">\n {{ 'translate_n_files_required' | translate: { count: data().files.length } }}\n </p>\n </div>\n </div>\n </div>\n\n <fieldset [formArray]=\"form().controls.files\" class=\"cortex-capture-form__multi-fields\">\n @for (file of form().controls.files.value; track $index) {\n <div [formGroupName]=\"$index\" class=\"cortex-capture-form__multi-item\">\n <div class=\"cortex-capture-form__multi-item-header\">\n <label\n [for]=\"'capture-file-' + $index\"\n class=\"cortex-capture-form__multi-item-label\"\n >\n {{ data().files[$index].label }}\n </label>\n <span class=\"cortex-capture-form__required-badge\">\n {{ 'translate_required' | translate }}\n </span>\n </div>\n <input hidden formControlName=\"id\" />\n <cortex-file-input [id]=\"'capture-file-' + $index\" formControlName=\"file\" />\n </div>\n }\n </fieldset>\n\n <div class=\"cortex-capture-form__multi-footer\">\n <button\n (click)=\"cancel()\"\n [disabled]=\"isLoading()\"\n type=\"button\"\n class=\"cortex-capture-form__cancel-btn\"\n >\n {{ 'translate_cancel' | translate }}\n </button>\n <button\n (click)=\"submit()\"\n type=\"button\"\n class=\"cortex-capture-form__submit-btn\"\n [disabled]=\"form().invalid || isLoading()\"\n >\n @if (isLoading()) {\n <svg class=\"cortex-capture-form__spinner\" viewBox=\"0 0 16 16\" fill=\"none\">\n <circle\n cx=\"8\"\n cy=\"8\"\n r=\"6\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n class=\"cortex-capture-form__spinner-track\"\n />\n <path\n d=\"M14 8a6 6 0 0 0-6-6\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n />\n </svg>\n {{ 'translate_uploading' | translate }}\n } @else {\n {{ 'translate_submit_files' | translate }}\n }\n </button>\n </div>\n </div>\n }\n </form>\n}\n", styles: [".cortex-capture-files{display:block;width:100%}.cortex-file-input{display:flex;width:100%;cursor:pointer;align-items:center;border-radius:.5rem;border:1px solid #cbd5e1;background:#f8fafc;transition:border-color .15s}.cortex-file-input:hover{border-color:#94a3b8}.cortex-file-input:focus-within{box-shadow:0 0 0 2px #cbd5e1}.cortex-file-input__btn{margin:.25rem;flex-shrink:0;cursor:pointer;border-radius:.375rem;border:0;background:#1e293b;padding:.375rem .75rem;font-size:.6875rem;font-weight:600;letter-spacing:.025em;color:#f1f5f9}.cortex-file-input__btn:hover{background:#334155}.cortex-file-input__name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:0 .75rem;font-size:.75rem;color:#475569}.cortex-file-input__hidden{display:none}.cortex-capture-success{overflow:hidden;border-radius:1rem;border:1px solid #a7f3d0;background:linear-gradient(to bottom,#ecfdf5cc,#fff)}.cortex-capture-success__header{display:flex;align-items:center;gap:.75rem;padding:.75rem 1rem}@media(min-width:640px){.cortex-capture-success__header{padding:.75rem 1.25rem}}.cortex-capture-success__icon{display:inline-flex;width:2rem;height:2rem;flex-shrink:0;align-items:center;justify-content:center;border-radius:.5rem;border:1px solid #a7f3d0;background:#d1fae5;color:#059669}.cortex-capture-success__icon-svg{width:1rem;height:1rem}.cortex-capture-success__title-wrap{min-width:0}.cortex-capture-success__title{font-size:.8125rem;font-weight:600;letter-spacing:-.01em;color:#065f46;margin:0}.cortex-capture-success__file-list{border-top:1px solid #d1fae5;padding:.625rem 1rem}@media(min-width:640px){.cortex-capture-success__file-list{padding:.625rem 1.25rem}}.cortex-capture-success__files{display:flex;flex-wrap:wrap;gap:.5rem;list-style:none;margin:0;padding:0}.cortex-capture-success__download-btn{display:inline-flex;cursor:pointer;align-items:center;gap:.375rem;border-radius:.5rem;border:1px solid #d1fae5;background:#fff;padding:.375rem .625rem;font-size:.75rem;font-weight:500;color:#047857;box-shadow:0 1px 2px #0000000d;transition:all .15s}.cortex-capture-success__download-btn:hover{border-color:#a7f3d0;background:#ecfdf5;box-shadow:0 1px 3px #0000001a}.cortex-capture-success__download-btn:active{transform:scale(.97)}.cortex-capture-success__file-icon{width:.875rem;height:.875rem;flex-shrink:0;color:#6ee7b7}.cortex-capture-success__file-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:180px}.cortex-capture-success__dl-icon{width:.75rem;height:.75rem;flex-shrink:0;color:#86efac}.cortex-capture-canceled-wrap{display:flex;justify-content:center}.cortex-capture-canceled{display:inline-flex;align-items:center;gap:.625rem;border-radius:.5rem;border:1px solid #e2e8f0;background:#f8fafc;padding:.625rem 1rem}.cortex-capture-canceled__icon{display:inline-flex;width:1.5rem;height:1.5rem;flex-shrink:0;align-items:center;justify-content:center;border-radius:.375rem;border:1px solid #e2e8f0;background:#f1f5f9;color:#94a3b8}.cortex-capture-canceled__icon-svg{width:.75rem;height:.75rem}.cortex-capture-canceled__text{font-size:.8125rem;font-weight:500;color:#94a3b8}.cortex-capture-form{width:100%}.cortex-capture-form__single{border-radius:.75rem;border:1px solid #e2e8f0;background:#f8fafc;padding:.75rem}.cortex-capture-form__label{display:block;margin-bottom:.375rem;font-size:.75rem;color:#64748b}.cortex-capture-form__row{display:flex;align-items:center;gap:.5rem}.cortex-capture-form__input{min-width:0;flex:1}.cortex-capture-form__cancel-link{flex-shrink:0;font-size:.75rem;font-weight:500;color:#94a3b8;background:none;border:0;cursor:pointer;transition:color .15s}.cortex-capture-form__cancel-link:hover{color:#475569}.cortex-capture-form__submit-btn{display:inline-flex;flex-shrink:0;align-items:center;border-radius:.375rem;background:#1e293b;padding:.375rem .75rem;font-size:.75rem;font-weight:500;color:#f1f5f9;border:0;cursor:pointer;transition:background-color .15s}.cortex-capture-form__submit-btn:hover{background:#334155}.cortex-capture-form__submit-btn:active{background:#0f172a}.cortex-capture-form__submit-btn:disabled{cursor:not-allowed;background:#cbd5e1}.cortex-capture-form__spinner{width:.75rem;height:.75rem;margin-inline-end:.375rem;animation:cortex-capture-spin .8s linear infinite}.cortex-capture-form__spinner-track{opacity:.25}@keyframes cortex-capture-spin{to{transform:rotate(360deg)}}.cortex-capture-form__multi{overflow:hidden;border-radius:1rem;border:1px solid #e2e8f0;background:linear-gradient(to bottom,#fff,#f8fafc)}.cortex-capture-form__multi-header{border-bottom:1px solid #e2e8f0;background:#ffffffd9;padding:.75rem 1rem}@media(min-width:640px){.cortex-capture-form__multi-header{padding:.75rem 1.25rem}}.cortex-capture-form__multi-header-row{display:flex;align-items:flex-start;gap:.75rem}.cortex-capture-form__multi-icon{margin-top:.125rem;display:inline-flex;width:2rem;height:2rem;flex-shrink:0;align-items:center;justify-content:center;border-radius:.5rem;border:1px solid #e2e8f0;background:#f1f5f9;color:#475569}.cortex-capture-form__multi-icon-svg{width:1rem;height:1rem}.cortex-capture-form__multi-title-wrap{min-width:0}.cortex-capture-form__multi-title{font-size:.8125rem;font-weight:600;letter-spacing:-.01em;color:#1e293b;margin:0}.cortex-capture-form__multi-subtitle{margin-top:.125rem;font-size:.6875rem;color:#64748b;margin-bottom:0}.cortex-capture-form__multi-fields{display:grid;gap:.75rem;padding:1rem;border:0}@media(min-width:640px){.cortex-capture-form__multi-fields{padding:1.25rem}}.cortex-capture-form__multi-item{border-radius:.75rem;border:1px solid #e2e8f0;background:#fff;padding:.75rem;transition:border-color .15s}.cortex-capture-form__multi-item:hover{border-color:#cbd5e1}.cortex-capture-form__multi-item-header{margin-bottom:.5rem;display:flex;align-items:center;justify-content:space-between;gap:.5rem}.cortex-capture-form__multi-item-label{display:block;font-size:.75rem;font-weight:500;line-height:1;color:#334155}.cortex-capture-form__required-badge{display:inline-flex;align-items:center;border-radius:.25rem;border:1px solid #e2e8f0;background:#f8fafc;padding:.125rem .375rem;font-size:.625rem;font-weight:500;text-transform:uppercase;letter-spacing:.05em;color:#64748b}.cortex-capture-form__multi-footer{display:flex;align-items:center;justify-content:flex-end;gap:.5rem;border-top:1px solid #e2e8f0;background:#ffffffe6;padding:.75rem 1rem}@media(min-width:640px){.cortex-capture-form__multi-footer{padding:.75rem 1.25rem}}.cortex-capture-form__cancel-btn{display:inline-flex;align-items:center;border-radius:.375rem;border:1px solid #cbd5e1;background:#fff;padding:.375rem .75rem;font-size:.75rem;font-weight:500;color:#475569;cursor:pointer;transition:background-color .15s}.cortex-capture-form__cancel-btn:hover{background:#f8fafc}.cortex-capture-form__cancel-btn:active{background:#f1f5f9}\n"] }]
626
651
  }], propDecorators: { message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: true }] }], toolPart: [{ type: i0.Input, args: [{ isSignal: true, alias: "toolPart", required: true }] }] } });
627
652
  class FileInputComponent {
628
653
  allowedMimeTypes = input('*/*', ...(ngDevMode ? [{ debugName: "allowedMimeTypes" }] : /* istanbul ignore next */ []));
@@ -665,51 +690,33 @@ class FileInputComponent {
665
690
  multi: true,
666
691
  useExisting: forwardRef(() => FileInputComponent),
667
692
  },
668
- ], ngImport: i0, template: `<div
669
- class="flex w-full cursor-pointer items-center rounded-lg border border-slate-300 bg-slate-50 transition hover:border-slate-400 focus-within:ring-2 focus-within:ring-slate-300"
670
- >
671
- <button
672
- type="button"
673
- (click)="fileInput.click()"
674
- class="m-1 shrink-0 cursor-pointer rounded-md border-0 bg-slate-800 px-3 py-1.5 text-[11px] font-semibold tracking-wide text-slate-100 hover:bg-slate-700"
675
- >
693
+ ], ngImport: i0, template: `<div class="cortex-file-input">
694
+ <button type="button" (click)="fileInput.click()" class="cortex-file-input__btn">
676
695
  {{ 'translate_choose_file' | translate }}
677
696
  </button>
678
- <span class="truncate px-3 text-[12px] text-slate-600">{{
697
+ <span class="cortex-file-input__name">{{
679
698
  value()?.name ?? ('translate_no_file_chosen' | translate)
680
699
  }}</span>
681
- <input #fileInput (change)="loadFile($event)" type="file" class="hidden" />
682
- </div>`, isInline: true, dependencies: [{ kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
700
+ <input #fileInput (change)="loadFile($event)" type="file" class="cortex-file-input__hidden" />
701
+ </div>`, isInline: true, styles: [".cortex-capture-files{display:block;width:100%}.cortex-file-input{display:flex;width:100%;cursor:pointer;align-items:center;border-radius:.5rem;border:1px solid #cbd5e1;background:#f8fafc;transition:border-color .15s}.cortex-file-input:hover{border-color:#94a3b8}.cortex-file-input:focus-within{box-shadow:0 0 0 2px #cbd5e1}.cortex-file-input__btn{margin:.25rem;flex-shrink:0;cursor:pointer;border-radius:.375rem;border:0;background:#1e293b;padding:.375rem .75rem;font-size:.6875rem;font-weight:600;letter-spacing:.025em;color:#f1f5f9}.cortex-file-input__btn:hover{background:#334155}.cortex-file-input__name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:0 .75rem;font-size:.75rem;color:#475569}.cortex-file-input__hidden{display:none}.cortex-capture-success{overflow:hidden;border-radius:1rem;border:1px solid #a7f3d0;background:linear-gradient(to bottom,#ecfdf5cc,#fff)}.cortex-capture-success__header{display:flex;align-items:center;gap:.75rem;padding:.75rem 1rem}@media(min-width:640px){.cortex-capture-success__header{padding:.75rem 1.25rem}}.cortex-capture-success__icon{display:inline-flex;width:2rem;height:2rem;flex-shrink:0;align-items:center;justify-content:center;border-radius:.5rem;border:1px solid #a7f3d0;background:#d1fae5;color:#059669}.cortex-capture-success__icon-svg{width:1rem;height:1rem}.cortex-capture-success__title-wrap{min-width:0}.cortex-capture-success__title{font-size:.8125rem;font-weight:600;letter-spacing:-.01em;color:#065f46;margin:0}.cortex-capture-success__file-list{border-top:1px solid #d1fae5;padding:.625rem 1rem}@media(min-width:640px){.cortex-capture-success__file-list{padding:.625rem 1.25rem}}.cortex-capture-success__files{display:flex;flex-wrap:wrap;gap:.5rem;list-style:none;margin:0;padding:0}.cortex-capture-success__download-btn{display:inline-flex;cursor:pointer;align-items:center;gap:.375rem;border-radius:.5rem;border:1px solid #d1fae5;background:#fff;padding:.375rem .625rem;font-size:.75rem;font-weight:500;color:#047857;box-shadow:0 1px 2px #0000000d;transition:all .15s}.cortex-capture-success__download-btn:hover{border-color:#a7f3d0;background:#ecfdf5;box-shadow:0 1px 3px #0000001a}.cortex-capture-success__download-btn:active{transform:scale(.97)}.cortex-capture-success__file-icon{width:.875rem;height:.875rem;flex-shrink:0;color:#6ee7b7}.cortex-capture-success__file-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:180px}.cortex-capture-success__dl-icon{width:.75rem;height:.75rem;flex-shrink:0;color:#86efac}.cortex-capture-canceled-wrap{display:flex;justify-content:center}.cortex-capture-canceled{display:inline-flex;align-items:center;gap:.625rem;border-radius:.5rem;border:1px solid #e2e8f0;background:#f8fafc;padding:.625rem 1rem}.cortex-capture-canceled__icon{display:inline-flex;width:1.5rem;height:1.5rem;flex-shrink:0;align-items:center;justify-content:center;border-radius:.375rem;border:1px solid #e2e8f0;background:#f1f5f9;color:#94a3b8}.cortex-capture-canceled__icon-svg{width:.75rem;height:.75rem}.cortex-capture-canceled__text{font-size:.8125rem;font-weight:500;color:#94a3b8}.cortex-capture-form{width:100%}.cortex-capture-form__single{border-radius:.75rem;border:1px solid #e2e8f0;background:#f8fafc;padding:.75rem}.cortex-capture-form__label{display:block;margin-bottom:.375rem;font-size:.75rem;color:#64748b}.cortex-capture-form__row{display:flex;align-items:center;gap:.5rem}.cortex-capture-form__input{min-width:0;flex:1}.cortex-capture-form__cancel-link{flex-shrink:0;font-size:.75rem;font-weight:500;color:#94a3b8;background:none;border:0;cursor:pointer;transition:color .15s}.cortex-capture-form__cancel-link:hover{color:#475569}.cortex-capture-form__submit-btn{display:inline-flex;flex-shrink:0;align-items:center;border-radius:.375rem;background:#1e293b;padding:.375rem .75rem;font-size:.75rem;font-weight:500;color:#f1f5f9;border:0;cursor:pointer;transition:background-color .15s}.cortex-capture-form__submit-btn:hover{background:#334155}.cortex-capture-form__submit-btn:active{background:#0f172a}.cortex-capture-form__submit-btn:disabled{cursor:not-allowed;background:#cbd5e1}.cortex-capture-form__spinner{width:.75rem;height:.75rem;margin-inline-end:.375rem;animation:cortex-capture-spin .8s linear infinite}.cortex-capture-form__spinner-track{opacity:.25}@keyframes cortex-capture-spin{to{transform:rotate(360deg)}}.cortex-capture-form__multi{overflow:hidden;border-radius:1rem;border:1px solid #e2e8f0;background:linear-gradient(to bottom,#fff,#f8fafc)}.cortex-capture-form__multi-header{border-bottom:1px solid #e2e8f0;background:#ffffffd9;padding:.75rem 1rem}@media(min-width:640px){.cortex-capture-form__multi-header{padding:.75rem 1.25rem}}.cortex-capture-form__multi-header-row{display:flex;align-items:flex-start;gap:.75rem}.cortex-capture-form__multi-icon{margin-top:.125rem;display:inline-flex;width:2rem;height:2rem;flex-shrink:0;align-items:center;justify-content:center;border-radius:.5rem;border:1px solid #e2e8f0;background:#f1f5f9;color:#475569}.cortex-capture-form__multi-icon-svg{width:1rem;height:1rem}.cortex-capture-form__multi-title-wrap{min-width:0}.cortex-capture-form__multi-title{font-size:.8125rem;font-weight:600;letter-spacing:-.01em;color:#1e293b;margin:0}.cortex-capture-form__multi-subtitle{margin-top:.125rem;font-size:.6875rem;color:#64748b;margin-bottom:0}.cortex-capture-form__multi-fields{display:grid;gap:.75rem;padding:1rem;border:0}@media(min-width:640px){.cortex-capture-form__multi-fields{padding:1.25rem}}.cortex-capture-form__multi-item{border-radius:.75rem;border:1px solid #e2e8f0;background:#fff;padding:.75rem;transition:border-color .15s}.cortex-capture-form__multi-item:hover{border-color:#cbd5e1}.cortex-capture-form__multi-item-header{margin-bottom:.5rem;display:flex;align-items:center;justify-content:space-between;gap:.5rem}.cortex-capture-form__multi-item-label{display:block;font-size:.75rem;font-weight:500;line-height:1;color:#334155}.cortex-capture-form__required-badge{display:inline-flex;align-items:center;border-radius:.25rem;border:1px solid #e2e8f0;background:#f8fafc;padding:.125rem .375rem;font-size:.625rem;font-weight:500;text-transform:uppercase;letter-spacing:.05em;color:#64748b}.cortex-capture-form__multi-footer{display:flex;align-items:center;justify-content:flex-end;gap:.5rem;border-top:1px solid #e2e8f0;background:#ffffffe6;padding:.75rem 1rem}@media(min-width:640px){.cortex-capture-form__multi-footer{padding:.75rem 1.25rem}}.cortex-capture-form__cancel-btn{display:inline-flex;align-items:center;border-radius:.375rem;border:1px solid #cbd5e1;background:#fff;padding:.375rem .75rem;font-size:.75rem;font-weight:500;color:#475569;cursor:pointer;transition:background-color .15s}.cortex-capture-form__cancel-btn:hover{background:#f8fafc}.cortex-capture-form__cancel-btn:active{background:#f1f5f9}\n"], dependencies: [{ kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
683
702
  }
684
703
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: FileInputComponent, decorators: [{
685
704
  type: Component,
686
- args: [{
687
- selector: 'cortex-file-input',
688
- template: `<div
689
- class="flex w-full cursor-pointer items-center rounded-lg border border-slate-300 bg-slate-50 transition hover:border-slate-400 focus-within:ring-2 focus-within:ring-slate-300"
690
- >
691
- <button
692
- type="button"
693
- (click)="fileInput.click()"
694
- class="m-1 shrink-0 cursor-pointer rounded-md border-0 bg-slate-800 px-3 py-1.5 text-[11px] font-semibold tracking-wide text-slate-100 hover:bg-slate-700"
695
- >
705
+ args: [{ selector: 'cortex-file-input', template: `<div class="cortex-file-input">
706
+ <button type="button" (click)="fileInput.click()" class="cortex-file-input__btn">
696
707
  {{ 'translate_choose_file' | translate }}
697
708
  </button>
698
- <span class="truncate px-3 text-[12px] text-slate-600">{{
709
+ <span class="cortex-file-input__name">{{
699
710
  value()?.name ?? ('translate_no_file_chosen' | translate)
700
711
  }}</span>
701
- <input #fileInput (change)="loadFile($event)" type="file" class="hidden" />
702
- </div>`,
703
- changeDetection: ChangeDetectionStrategy.OnPush,
704
- imports: [TranslatePipe],
705
- providers: [
712
+ <input #fileInput (change)="loadFile($event)" type="file" class="cortex-file-input__hidden" />
713
+ </div>`, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [TranslatePipe], providers: [
706
714
  {
707
715
  provide: NG_VALUE_ACCESSOR,
708
716
  multi: true,
709
717
  useExisting: forwardRef(() => FileInputComponent),
710
718
  },
711
- ],
712
- }]
719
+ ], styles: [".cortex-capture-files{display:block;width:100%}.cortex-file-input{display:flex;width:100%;cursor:pointer;align-items:center;border-radius:.5rem;border:1px solid #cbd5e1;background:#f8fafc;transition:border-color .15s}.cortex-file-input:hover{border-color:#94a3b8}.cortex-file-input:focus-within{box-shadow:0 0 0 2px #cbd5e1}.cortex-file-input__btn{margin:.25rem;flex-shrink:0;cursor:pointer;border-radius:.375rem;border:0;background:#1e293b;padding:.375rem .75rem;font-size:.6875rem;font-weight:600;letter-spacing:.025em;color:#f1f5f9}.cortex-file-input__btn:hover{background:#334155}.cortex-file-input__name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:0 .75rem;font-size:.75rem;color:#475569}.cortex-file-input__hidden{display:none}.cortex-capture-success{overflow:hidden;border-radius:1rem;border:1px solid #a7f3d0;background:linear-gradient(to bottom,#ecfdf5cc,#fff)}.cortex-capture-success__header{display:flex;align-items:center;gap:.75rem;padding:.75rem 1rem}@media(min-width:640px){.cortex-capture-success__header{padding:.75rem 1.25rem}}.cortex-capture-success__icon{display:inline-flex;width:2rem;height:2rem;flex-shrink:0;align-items:center;justify-content:center;border-radius:.5rem;border:1px solid #a7f3d0;background:#d1fae5;color:#059669}.cortex-capture-success__icon-svg{width:1rem;height:1rem}.cortex-capture-success__title-wrap{min-width:0}.cortex-capture-success__title{font-size:.8125rem;font-weight:600;letter-spacing:-.01em;color:#065f46;margin:0}.cortex-capture-success__file-list{border-top:1px solid #d1fae5;padding:.625rem 1rem}@media(min-width:640px){.cortex-capture-success__file-list{padding:.625rem 1.25rem}}.cortex-capture-success__files{display:flex;flex-wrap:wrap;gap:.5rem;list-style:none;margin:0;padding:0}.cortex-capture-success__download-btn{display:inline-flex;cursor:pointer;align-items:center;gap:.375rem;border-radius:.5rem;border:1px solid #d1fae5;background:#fff;padding:.375rem .625rem;font-size:.75rem;font-weight:500;color:#047857;box-shadow:0 1px 2px #0000000d;transition:all .15s}.cortex-capture-success__download-btn:hover{border-color:#a7f3d0;background:#ecfdf5;box-shadow:0 1px 3px #0000001a}.cortex-capture-success__download-btn:active{transform:scale(.97)}.cortex-capture-success__file-icon{width:.875rem;height:.875rem;flex-shrink:0;color:#6ee7b7}.cortex-capture-success__file-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:180px}.cortex-capture-success__dl-icon{width:.75rem;height:.75rem;flex-shrink:0;color:#86efac}.cortex-capture-canceled-wrap{display:flex;justify-content:center}.cortex-capture-canceled{display:inline-flex;align-items:center;gap:.625rem;border-radius:.5rem;border:1px solid #e2e8f0;background:#f8fafc;padding:.625rem 1rem}.cortex-capture-canceled__icon{display:inline-flex;width:1.5rem;height:1.5rem;flex-shrink:0;align-items:center;justify-content:center;border-radius:.375rem;border:1px solid #e2e8f0;background:#f1f5f9;color:#94a3b8}.cortex-capture-canceled__icon-svg{width:.75rem;height:.75rem}.cortex-capture-canceled__text{font-size:.8125rem;font-weight:500;color:#94a3b8}.cortex-capture-form{width:100%}.cortex-capture-form__single{border-radius:.75rem;border:1px solid #e2e8f0;background:#f8fafc;padding:.75rem}.cortex-capture-form__label{display:block;margin-bottom:.375rem;font-size:.75rem;color:#64748b}.cortex-capture-form__row{display:flex;align-items:center;gap:.5rem}.cortex-capture-form__input{min-width:0;flex:1}.cortex-capture-form__cancel-link{flex-shrink:0;font-size:.75rem;font-weight:500;color:#94a3b8;background:none;border:0;cursor:pointer;transition:color .15s}.cortex-capture-form__cancel-link:hover{color:#475569}.cortex-capture-form__submit-btn{display:inline-flex;flex-shrink:0;align-items:center;border-radius:.375rem;background:#1e293b;padding:.375rem .75rem;font-size:.75rem;font-weight:500;color:#f1f5f9;border:0;cursor:pointer;transition:background-color .15s}.cortex-capture-form__submit-btn:hover{background:#334155}.cortex-capture-form__submit-btn:active{background:#0f172a}.cortex-capture-form__submit-btn:disabled{cursor:not-allowed;background:#cbd5e1}.cortex-capture-form__spinner{width:.75rem;height:.75rem;margin-inline-end:.375rem;animation:cortex-capture-spin .8s linear infinite}.cortex-capture-form__spinner-track{opacity:.25}@keyframes cortex-capture-spin{to{transform:rotate(360deg)}}.cortex-capture-form__multi{overflow:hidden;border-radius:1rem;border:1px solid #e2e8f0;background:linear-gradient(to bottom,#fff,#f8fafc)}.cortex-capture-form__multi-header{border-bottom:1px solid #e2e8f0;background:#ffffffd9;padding:.75rem 1rem}@media(min-width:640px){.cortex-capture-form__multi-header{padding:.75rem 1.25rem}}.cortex-capture-form__multi-header-row{display:flex;align-items:flex-start;gap:.75rem}.cortex-capture-form__multi-icon{margin-top:.125rem;display:inline-flex;width:2rem;height:2rem;flex-shrink:0;align-items:center;justify-content:center;border-radius:.5rem;border:1px solid #e2e8f0;background:#f1f5f9;color:#475569}.cortex-capture-form__multi-icon-svg{width:1rem;height:1rem}.cortex-capture-form__multi-title-wrap{min-width:0}.cortex-capture-form__multi-title{font-size:.8125rem;font-weight:600;letter-spacing:-.01em;color:#1e293b;margin:0}.cortex-capture-form__multi-subtitle{margin-top:.125rem;font-size:.6875rem;color:#64748b;margin-bottom:0}.cortex-capture-form__multi-fields{display:grid;gap:.75rem;padding:1rem;border:0}@media(min-width:640px){.cortex-capture-form__multi-fields{padding:1.25rem}}.cortex-capture-form__multi-item{border-radius:.75rem;border:1px solid #e2e8f0;background:#fff;padding:.75rem;transition:border-color .15s}.cortex-capture-form__multi-item:hover{border-color:#cbd5e1}.cortex-capture-form__multi-item-header{margin-bottom:.5rem;display:flex;align-items:center;justify-content:space-between;gap:.5rem}.cortex-capture-form__multi-item-label{display:block;font-size:.75rem;font-weight:500;line-height:1;color:#334155}.cortex-capture-form__required-badge{display:inline-flex;align-items:center;border-radius:.25rem;border:1px solid #e2e8f0;background:#f8fafc;padding:.125rem .375rem;font-size:.625rem;font-weight:500;text-transform:uppercase;letter-spacing:.05em;color:#64748b}.cortex-capture-form__multi-footer{display:flex;align-items:center;justify-content:flex-end;gap:.5rem;border-top:1px solid #e2e8f0;background:#ffffffe6;padding:.75rem 1rem}@media(min-width:640px){.cortex-capture-form__multi-footer{padding:.75rem 1.25rem}}.cortex-capture-form__cancel-btn{display:inline-flex;align-items:center;border-radius:.375rem;border:1px solid #cbd5e1;background:#fff;padding:.375rem .75rem;font-size:.75rem;font-weight:500;color:#475569;cursor:pointer;transition:background-color .15s}.cortex-capture-form__cancel-btn:hover{background:#f8fafc}.cortex-capture-form__cancel-btn:active{background:#f1f5f9}\n"] }]
713
720
  }], propDecorators: { allowedMimeTypes: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowedMimeTypes", required: false }] }] } });
714
721
  async function convertFileToBase64(file) {
715
722
  return new Promise((resolve, reject) => {
@@ -790,21 +797,21 @@ class MessageToolCallAnimatedComponent {
790
797
  return Math.abs(hash) % STATUS_COUNT;
791
798
  }, ...(ngDevMode ? [{ debugName: "stableIndex" }] : /* istanbul ignore next */ []));
792
799
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: MessageToolCallAnimatedComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
793
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.3", type: MessageToolCallAnimatedComponent, isStandalone: true, selector: "cortex-message-tool-call-animated", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null }, toolCallPart: { classPropertyName: "toolCallPart", publicName: "toolCallPart", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "block w-full" }, ngImport: i0, template: "@if (customComponent(); as custom) {\n <ng-container\n *ngComponentOutlet=\"custom; inputs: { toolCallPart: toolCallPart(), message: message() }\"\n />\n} @else {\n @switch (toolCallPart().type) {\n @case ('tool-captureFiles') {\n <cortex-message-capture-files-part [toolPart]=\"toolCallPart()\" [message]=\"message()\" />\n }\n @default {\n @let state = animState();\n @let active = isActive();\n\n <div class=\"flex justify-center\">\n <div\n class=\"inline-flex items-center gap-2.5 py-2.5 px-4 rounded-lg border relative overflow-hidden transition-colors duration-300\"\n [ngClass]=\"{\n 'border-indigo-200 bg-indigo-50': active,\n 'border-emerald-200 bg-emerald-50': state === 'complete',\n 'border-red-200 bg-red-50': state === 'error',\n 'border-slate-200 bg-slate-50 opacity-60': state === 'denied',\n }\"\n >\n <!-- Icon box -->\n <div\n class=\"relative size-6 flex items-center justify-center rounded-md border shrink-0 transition-colors duration-300\"\n [ngClass]=\"{\n 'bg-indigo-100 border-indigo-200': active,\n 'bg-emerald-100 border-emerald-200': state === 'complete',\n 'bg-red-100 border-red-200': state === 'error',\n 'bg-slate-100 border-slate-200': state === 'denied',\n }\"\n >\n <!-- Spinner -->\n <div\n class=\"absolute size-3 border-[1.5px] border-indigo-200 border-t-indigo-500 rounded-full animate-spin transition-opacity duration-200\"\n [ngClass]=\"active ? 'opacity-100' : 'opacity-0'\"\n ></div>\n <!-- Check -->\n <svg\n class=\"absolute size-3 transition-all duration-200\"\n [ngClass]=\"\n state === 'complete'\n ? 'opacity-100 scale-100 text-emerald-600'\n : 'opacity-0 scale-[0.6]'\n \"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n >\n <path\n d=\"M5.5 10.5 L8.5 13.5 L14.5 7\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n <!-- Error -->\n <svg\n class=\"absolute size-3 transition-all duration-200\"\n [ngClass]=\"\n state === 'error' ? 'opacity-100 scale-100 text-red-600' : 'opacity-0 scale-[0.6]'\n \"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n >\n <path\n d=\"M6.5 6.5 L13.5 13.5 M13.5 6.5 L6.5 13.5\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n />\n </svg>\n <!-- Denied -->\n <svg\n class=\"absolute size-3 transition-all duration-200\"\n [ngClass]=\"\n state === 'denied'\n ? 'opacity-100 scale-100 text-slate-400'\n : 'opacity-0 scale-[0.6]'\n \"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n >\n <path\n d=\"M6 10 L14 10\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n\n <!-- Title -->\n <span\n class=\"text-[13px] font-medium whitespace-nowrap overflow-hidden text-ellipsis transition-colors duration-300\"\n [ngClass]=\"{\n 'text-indigo-700': active,\n 'text-emerald-700': state === 'complete',\n 'text-red-600': state === 'error',\n 'text-slate-400': state === 'denied',\n }\"\n >\n {{ displayTitleKey() | translate }}\n </span>\n\n <!-- Bottom bar -->\n <div class=\"absolute bottom-0 inset-x-0 h-0.5 overflow-hidden\">\n <div\n class=\"h-full w-full origin-left\"\n [ngClass]=\"{\n 'bg-indigo-400 animate-bar-slide': active,\n 'bg-emerald-400 animate-bar-fill': state === 'complete',\n 'bg-red-400 animate-bar-fill': state === 'error',\n 'opacity-0': state === 'denied',\n }\"\n ></div>\n </div>\n </div>\n </div>\n }\n }\n}\n", styles: [".animate-bar-slide{animation:bar-slide 1.5s ease-in-out infinite}.animate-bar-fill{animation:bar-fill .3s ease-out both}@keyframes bar-slide{0%{transform:translate(-100%)}to{transform:translate(100%)}}@keyframes bar-fill{0%{transform:scaleX(0)}to{transform:scaleX(1)}}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: MessageCaptureFilesPartComponent, selector: "cortex-message-capture-files-part", inputs: ["message", "toolPart"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
800
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.3", type: MessageToolCallAnimatedComponent, isStandalone: true, selector: "cortex-message-tool-call-animated", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null }, toolCallPart: { classPropertyName: "toolCallPart", publicName: "toolCallPart", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "cortex-tool-call-animated" }, ngImport: i0, template: "@if (customComponent(); as custom) {\n <ng-container\n *ngComponentOutlet=\"custom; inputs: { toolCallPart: toolCallPart(), message: message() }\"\n />\n} @else {\n @switch (toolCallPart().type) {\n @case ('tool-captureFiles') {\n <cortex-message-capture-files-part [toolPart]=\"toolCallPart()\" [message]=\"message()\" />\n }\n @default {\n @let state = animState();\n @let active = isActive();\n\n <div class=\"cortex-tool-pill-wrap\">\n <div\n class=\"cortex-tool-pill\"\n [class.cortex-tool-pill--active]=\"active\"\n [class.cortex-tool-pill--complete]=\"state === 'complete'\"\n [class.cortex-tool-pill--error]=\"state === 'error'\"\n [class.cortex-tool-pill--denied]=\"state === 'denied'\"\n >\n <!-- Icon box -->\n <div\n class=\"cortex-tool-pill__icon\"\n [class.cortex-tool-pill__icon--active]=\"active\"\n [class.cortex-tool-pill__icon--complete]=\"state === 'complete'\"\n [class.cortex-tool-pill__icon--error]=\"state === 'error'\"\n [class.cortex-tool-pill__icon--denied]=\"state === 'denied'\"\n >\n <!-- Spinner -->\n <div\n class=\"cortex-tool-pill__spinner\"\n [class.cortex-tool-pill__spinner--visible]=\"active\"\n ></div>\n <!-- Check -->\n <svg\n class=\"cortex-tool-pill__svg\"\n [class.cortex-tool-pill__svg--visible]=\"state === 'complete'\"\n [class.cortex-tool-pill__svg--check]=\"state === 'complete'\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n >\n <path\n d=\"M5.5 10.5 L8.5 13.5 L14.5 7\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n <!-- Error -->\n <svg\n class=\"cortex-tool-pill__svg\"\n [class.cortex-tool-pill__svg--visible]=\"state === 'error'\"\n [class.cortex-tool-pill__svg--error]=\"state === 'error'\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n >\n <path\n d=\"M6.5 6.5 L13.5 13.5 M13.5 6.5 L6.5 13.5\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n />\n </svg>\n <!-- Denied -->\n <svg\n class=\"cortex-tool-pill__svg\"\n [class.cortex-tool-pill__svg--visible]=\"state === 'denied'\"\n [class.cortex-tool-pill__svg--denied]=\"state === 'denied'\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n >\n <path\n d=\"M6 10 L14 10\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n\n <!-- Title -->\n <span\n class=\"cortex-tool-pill__title\"\n [class.cortex-tool-pill__title--active]=\"active\"\n [class.cortex-tool-pill__title--complete]=\"state === 'complete'\"\n [class.cortex-tool-pill__title--error]=\"state === 'error'\"\n [class.cortex-tool-pill__title--denied]=\"state === 'denied'\"\n >\n {{ displayTitleKey() | translate }}\n </span>\n\n <!-- Bottom bar -->\n <div class=\"cortex-tool-pill__bar-track\">\n <div\n class=\"cortex-tool-pill__bar\"\n [class.cortex-tool-pill__bar--active]=\"active\"\n [class.cortex-tool-pill__bar--complete]=\"state === 'complete'\"\n [class.cortex-tool-pill__bar--error]=\"state === 'error'\"\n [class.cortex-tool-pill__bar--denied]=\"state === 'denied'\"\n ></div>\n </div>\n </div>\n </div>\n }\n }\n}\n", styles: [".cortex-tool-call-animated{display:block;width:100%}.cortex-tool-pill-wrap{display:flex;justify-content:center}.cortex-tool-pill{display:inline-flex;align-items:center;gap:.625rem;padding:.625rem 1rem;border-radius:.5rem;border:1px solid;position:relative;overflow:hidden;transition:background-color .3s,border-color .3s}.cortex-tool-pill--active{border-color:#c7d2fe;background:#eef2ff}.cortex-tool-pill--complete{border-color:#a7f3d0;background:#ecfdf5}.cortex-tool-pill--error{border-color:#fecaca;background:#fef2f2}.cortex-tool-pill--denied{border-color:#e2e8f0;background:#f8fafc;opacity:.6}.cortex-tool-pill__icon{position:relative;width:1.5rem;height:1.5rem;display:flex;align-items:center;justify-content:center;border-radius:.375rem;border:1px solid;flex-shrink:0;transition:background-color .3s,border-color .3s}.cortex-tool-pill__icon--active{background:#e0e7ff;border-color:#c7d2fe}.cortex-tool-pill__icon--complete{background:#d1fae5;border-color:#a7f3d0}.cortex-tool-pill__icon--error{background:#fee2e2;border-color:#fecaca}.cortex-tool-pill__icon--denied{background:#f1f5f9;border-color:#e2e8f0}.cortex-tool-pill__spinner{position:absolute;width:.75rem;height:.75rem;border:1.5px solid #c7d2fe;border-top-color:#6366f1;border-radius:9999px;opacity:0;transition:opacity .2s}.cortex-tool-pill__spinner--visible{opacity:1;animation:cortex-tool-spin .8s linear infinite}@keyframes cortex-tool-spin{to{transform:rotate(360deg)}}.cortex-tool-pill__svg{position:absolute;width:.75rem;height:.75rem;opacity:0;transform:scale(.6);transition:opacity .2s,transform .2s}.cortex-tool-pill__svg--visible{opacity:1;transform:scale(1)}.cortex-tool-pill__svg--check{color:#059669}.cortex-tool-pill__svg--error{color:#dc2626}.cortex-tool-pill__svg--denied{color:#94a3b8}.cortex-tool-pill__title{font-size:.8125rem;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:color .3s}.cortex-tool-pill__title--active{color:#4338ca}.cortex-tool-pill__title--complete{color:#047857}.cortex-tool-pill__title--error{color:#dc2626}.cortex-tool-pill__title--denied{color:#94a3b8}.cortex-tool-pill__bar-track{position:absolute;bottom:0;left:0;right:0;height:2px;overflow:hidden}.cortex-tool-pill__bar{height:100%;width:100%;transform-origin:left}.cortex-tool-pill__bar--active{background:#818cf8;animation:cortex-bar-slide 1.5s ease-in-out infinite}.cortex-tool-pill__bar--complete{background:#34d399;animation:cortex-bar-fill .3s ease-out both}.cortex-tool-pill__bar--error{background:#f87171;animation:cortex-bar-fill .3s ease-out both}.cortex-tool-pill__bar--denied{opacity:0}@keyframes cortex-bar-slide{0%{transform:translate(-100%)}to{transform:translate(100%)}}@keyframes cortex-bar-fill{0%{transform:scaleX(0)}to{transform:scaleX(1)}}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: MessageCaptureFilesPartComponent, selector: "cortex-message-capture-files-part", inputs: ["message", "toolPart"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
794
801
  }
795
802
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: MessageToolCallAnimatedComponent, decorators: [{
796
803
  type: Component,
797
- args: [{ selector: 'cortex-message-tool-call-animated', imports: [NgClass, MessageCaptureFilesPartComponent, TranslatePipe], host: { class: 'block w-full' }, changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (customComponent(); as custom) {\n <ng-container\n *ngComponentOutlet=\"custom; inputs: { toolCallPart: toolCallPart(), message: message() }\"\n />\n} @else {\n @switch (toolCallPart().type) {\n @case ('tool-captureFiles') {\n <cortex-message-capture-files-part [toolPart]=\"toolCallPart()\" [message]=\"message()\" />\n }\n @default {\n @let state = animState();\n @let active = isActive();\n\n <div class=\"flex justify-center\">\n <div\n class=\"inline-flex items-center gap-2.5 py-2.5 px-4 rounded-lg border relative overflow-hidden transition-colors duration-300\"\n [ngClass]=\"{\n 'border-indigo-200 bg-indigo-50': active,\n 'border-emerald-200 bg-emerald-50': state === 'complete',\n 'border-red-200 bg-red-50': state === 'error',\n 'border-slate-200 bg-slate-50 opacity-60': state === 'denied',\n }\"\n >\n <!-- Icon box -->\n <div\n class=\"relative size-6 flex items-center justify-center rounded-md border shrink-0 transition-colors duration-300\"\n [ngClass]=\"{\n 'bg-indigo-100 border-indigo-200': active,\n 'bg-emerald-100 border-emerald-200': state === 'complete',\n 'bg-red-100 border-red-200': state === 'error',\n 'bg-slate-100 border-slate-200': state === 'denied',\n }\"\n >\n <!-- Spinner -->\n <div\n class=\"absolute size-3 border-[1.5px] border-indigo-200 border-t-indigo-500 rounded-full animate-spin transition-opacity duration-200\"\n [ngClass]=\"active ? 'opacity-100' : 'opacity-0'\"\n ></div>\n <!-- Check -->\n <svg\n class=\"absolute size-3 transition-all duration-200\"\n [ngClass]=\"\n state === 'complete'\n ? 'opacity-100 scale-100 text-emerald-600'\n : 'opacity-0 scale-[0.6]'\n \"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n >\n <path\n d=\"M5.5 10.5 L8.5 13.5 L14.5 7\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n <!-- Error -->\n <svg\n class=\"absolute size-3 transition-all duration-200\"\n [ngClass]=\"\n state === 'error' ? 'opacity-100 scale-100 text-red-600' : 'opacity-0 scale-[0.6]'\n \"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n >\n <path\n d=\"M6.5 6.5 L13.5 13.5 M13.5 6.5 L6.5 13.5\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n />\n </svg>\n <!-- Denied -->\n <svg\n class=\"absolute size-3 transition-all duration-200\"\n [ngClass]=\"\n state === 'denied'\n ? 'opacity-100 scale-100 text-slate-400'\n : 'opacity-0 scale-[0.6]'\n \"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n >\n <path\n d=\"M6 10 L14 10\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n\n <!-- Title -->\n <span\n class=\"text-[13px] font-medium whitespace-nowrap overflow-hidden text-ellipsis transition-colors duration-300\"\n [ngClass]=\"{\n 'text-indigo-700': active,\n 'text-emerald-700': state === 'complete',\n 'text-red-600': state === 'error',\n 'text-slate-400': state === 'denied',\n }\"\n >\n {{ displayTitleKey() | translate }}\n </span>\n\n <!-- Bottom bar -->\n <div class=\"absolute bottom-0 inset-x-0 h-0.5 overflow-hidden\">\n <div\n class=\"h-full w-full origin-left\"\n [ngClass]=\"{\n 'bg-indigo-400 animate-bar-slide': active,\n 'bg-emerald-400 animate-bar-fill': state === 'complete',\n 'bg-red-400 animate-bar-fill': state === 'error',\n 'opacity-0': state === 'denied',\n }\"\n ></div>\n </div>\n </div>\n </div>\n }\n }\n}\n", styles: [".animate-bar-slide{animation:bar-slide 1.5s ease-in-out infinite}.animate-bar-fill{animation:bar-fill .3s ease-out both}@keyframes bar-slide{0%{transform:translate(-100%)}to{transform:translate(100%)}}@keyframes bar-fill{0%{transform:scaleX(0)}to{transform:scaleX(1)}}\n"] }]
804
+ args: [{ selector: 'cortex-message-tool-call-animated', imports: [NgComponentOutlet, MessageCaptureFilesPartComponent, TranslatePipe], host: { class: 'cortex-tool-call-animated' }, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (customComponent(); as custom) {\n <ng-container\n *ngComponentOutlet=\"custom; inputs: { toolCallPart: toolCallPart(), message: message() }\"\n />\n} @else {\n @switch (toolCallPart().type) {\n @case ('tool-captureFiles') {\n <cortex-message-capture-files-part [toolPart]=\"toolCallPart()\" [message]=\"message()\" />\n }\n @default {\n @let state = animState();\n @let active = isActive();\n\n <div class=\"cortex-tool-pill-wrap\">\n <div\n class=\"cortex-tool-pill\"\n [class.cortex-tool-pill--active]=\"active\"\n [class.cortex-tool-pill--complete]=\"state === 'complete'\"\n [class.cortex-tool-pill--error]=\"state === 'error'\"\n [class.cortex-tool-pill--denied]=\"state === 'denied'\"\n >\n <!-- Icon box -->\n <div\n class=\"cortex-tool-pill__icon\"\n [class.cortex-tool-pill__icon--active]=\"active\"\n [class.cortex-tool-pill__icon--complete]=\"state === 'complete'\"\n [class.cortex-tool-pill__icon--error]=\"state === 'error'\"\n [class.cortex-tool-pill__icon--denied]=\"state === 'denied'\"\n >\n <!-- Spinner -->\n <div\n class=\"cortex-tool-pill__spinner\"\n [class.cortex-tool-pill__spinner--visible]=\"active\"\n ></div>\n <!-- Check -->\n <svg\n class=\"cortex-tool-pill__svg\"\n [class.cortex-tool-pill__svg--visible]=\"state === 'complete'\"\n [class.cortex-tool-pill__svg--check]=\"state === 'complete'\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n >\n <path\n d=\"M5.5 10.5 L8.5 13.5 L14.5 7\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n <!-- Error -->\n <svg\n class=\"cortex-tool-pill__svg\"\n [class.cortex-tool-pill__svg--visible]=\"state === 'error'\"\n [class.cortex-tool-pill__svg--error]=\"state === 'error'\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n >\n <path\n d=\"M6.5 6.5 L13.5 13.5 M13.5 6.5 L6.5 13.5\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n />\n </svg>\n <!-- Denied -->\n <svg\n class=\"cortex-tool-pill__svg\"\n [class.cortex-tool-pill__svg--visible]=\"state === 'denied'\"\n [class.cortex-tool-pill__svg--denied]=\"state === 'denied'\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n >\n <path\n d=\"M6 10 L14 10\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n\n <!-- Title -->\n <span\n class=\"cortex-tool-pill__title\"\n [class.cortex-tool-pill__title--active]=\"active\"\n [class.cortex-tool-pill__title--complete]=\"state === 'complete'\"\n [class.cortex-tool-pill__title--error]=\"state === 'error'\"\n [class.cortex-tool-pill__title--denied]=\"state === 'denied'\"\n >\n {{ displayTitleKey() | translate }}\n </span>\n\n <!-- Bottom bar -->\n <div class=\"cortex-tool-pill__bar-track\">\n <div\n class=\"cortex-tool-pill__bar\"\n [class.cortex-tool-pill__bar--active]=\"active\"\n [class.cortex-tool-pill__bar--complete]=\"state === 'complete'\"\n [class.cortex-tool-pill__bar--error]=\"state === 'error'\"\n [class.cortex-tool-pill__bar--denied]=\"state === 'denied'\"\n ></div>\n </div>\n </div>\n </div>\n }\n }\n}\n", styles: [".cortex-tool-call-animated{display:block;width:100%}.cortex-tool-pill-wrap{display:flex;justify-content:center}.cortex-tool-pill{display:inline-flex;align-items:center;gap:.625rem;padding:.625rem 1rem;border-radius:.5rem;border:1px solid;position:relative;overflow:hidden;transition:background-color .3s,border-color .3s}.cortex-tool-pill--active{border-color:#c7d2fe;background:#eef2ff}.cortex-tool-pill--complete{border-color:#a7f3d0;background:#ecfdf5}.cortex-tool-pill--error{border-color:#fecaca;background:#fef2f2}.cortex-tool-pill--denied{border-color:#e2e8f0;background:#f8fafc;opacity:.6}.cortex-tool-pill__icon{position:relative;width:1.5rem;height:1.5rem;display:flex;align-items:center;justify-content:center;border-radius:.375rem;border:1px solid;flex-shrink:0;transition:background-color .3s,border-color .3s}.cortex-tool-pill__icon--active{background:#e0e7ff;border-color:#c7d2fe}.cortex-tool-pill__icon--complete{background:#d1fae5;border-color:#a7f3d0}.cortex-tool-pill__icon--error{background:#fee2e2;border-color:#fecaca}.cortex-tool-pill__icon--denied{background:#f1f5f9;border-color:#e2e8f0}.cortex-tool-pill__spinner{position:absolute;width:.75rem;height:.75rem;border:1.5px solid #c7d2fe;border-top-color:#6366f1;border-radius:9999px;opacity:0;transition:opacity .2s}.cortex-tool-pill__spinner--visible{opacity:1;animation:cortex-tool-spin .8s linear infinite}@keyframes cortex-tool-spin{to{transform:rotate(360deg)}}.cortex-tool-pill__svg{position:absolute;width:.75rem;height:.75rem;opacity:0;transform:scale(.6);transition:opacity .2s,transform .2s}.cortex-tool-pill__svg--visible{opacity:1;transform:scale(1)}.cortex-tool-pill__svg--check{color:#059669}.cortex-tool-pill__svg--error{color:#dc2626}.cortex-tool-pill__svg--denied{color:#94a3b8}.cortex-tool-pill__title{font-size:.8125rem;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:color .3s}.cortex-tool-pill__title--active{color:#4338ca}.cortex-tool-pill__title--complete{color:#047857}.cortex-tool-pill__title--error{color:#dc2626}.cortex-tool-pill__title--denied{color:#94a3b8}.cortex-tool-pill__bar-track{position:absolute;bottom:0;left:0;right:0;height:2px;overflow:hidden}.cortex-tool-pill__bar{height:100%;width:100%;transform-origin:left}.cortex-tool-pill__bar--active{background:#818cf8;animation:cortex-bar-slide 1.5s ease-in-out infinite}.cortex-tool-pill__bar--complete{background:#34d399;animation:cortex-bar-fill .3s ease-out both}.cortex-tool-pill__bar--error{background:#f87171;animation:cortex-bar-fill .3s ease-out both}.cortex-tool-pill__bar--denied{opacity:0}@keyframes cortex-bar-slide{0%{transform:translate(-100%)}to{transform:translate(100%)}}@keyframes cortex-bar-fill{0%{transform:scaleX(0)}to{transform:scaleX(1)}}\n"] }]
798
805
  }], propDecorators: { message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: true }] }], toolCallPart: [{ type: i0.Input, args: [{ isSignal: true, alias: "toolCallPart", required: true }] }] } });
799
806
 
800
807
  class MessageReasoningAnimatedComponent {
801
808
  reasoningPart = input.required(...(ngDevMode ? [{ debugName: "reasoningPart" }] : /* istanbul ignore next */ []));
802
809
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: MessageReasoningAnimatedComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
803
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.3", type: MessageReasoningAnimatedComponent, isStandalone: true, selector: "cortex-message-reasoning-animated", inputs: { reasoningPart: { classPropertyName: "reasoningPart", publicName: "reasoningPart", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "flex justify-center" }, ngImport: i0, template: "@let p = reasoningPart();\n@let streaming = p.state === 'streaming';\n\n<div\n class=\"inline-flex items-center gap-2.5 py-2 pl-3 pr-4 rounded-lg border transition-colors duration-300\"\n [ngClass]=\"streaming ? 'border-indigo-200 bg-indigo-50' : 'border-slate-200 bg-slate-50'\"\n>\n <div class=\"flex items-center gap-1\">\n <span\n class=\"size-[5px] rounded-full transition-all duration-400\"\n [ngClass]=\"streaming ? 'animate-dot-pulse bg-indigo-300' : 'bg-slate-300 opacity-40'\"\n ></span>\n <span\n class=\"size-[5px] rounded-full transition-all duration-400\"\n [ngClass]=\"streaming ? 'animate-dot-pulse bg-indigo-300' : 'bg-slate-300 opacity-40'\"\n [style.animation-delay]=\"streaming ? '200ms' : '0ms'\"\n ></span>\n <span\n class=\"size-[5px] rounded-full transition-all duration-400\"\n [ngClass]=\"streaming ? 'animate-dot-pulse bg-indigo-300' : 'bg-slate-300 opacity-40'\"\n [style.animation-delay]=\"streaming ? '400ms' : '0ms'\"\n ></span>\n <span\n class=\"size-[5px] rounded-full transition-all duration-400\"\n [ngClass]=\"streaming ? 'animate-dot-pulse bg-indigo-300' : 'bg-slate-300 opacity-40'\"\n [style.animation-delay]=\"streaming ? '600ms' : '0ms'\"\n ></span>\n </div>\n\n <span\n class=\"text-[13px] font-medium transition-colors duration-300\"\n [ngClass]=\"streaming ? 'text-indigo-600' : 'text-slate-400'\"\n >\n {{ streaming ? ('translate_thinking' | translate) : ('translate_reasoned' | translate) }}\n </span>\n</div>\n", styles: [".animate-dot-pulse{animation:dot-pulse 1.4s ease-in-out infinite}@keyframes dot-pulse{0%,to{opacity:.3;transform:scale(.8)}40%{opacity:1;transform:scale(1.3);background-color:#6366f1}}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
810
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.3", type: MessageReasoningAnimatedComponent, isStandalone: true, selector: "cortex-message-reasoning-animated", inputs: { reasoningPart: { classPropertyName: "reasoningPart", publicName: "reasoningPart", isSignal: true, isRequired: true, transformFunction: null } }, host: { classAttribute: "cortex-reasoning-animated" }, ngImport: i0, template: "@let p = reasoningPart();\n@let streaming = p.state === 'streaming';\n\n<div\n class=\"cortex-reasoning-pill\"\n [class.cortex-reasoning-pill--streaming]=\"streaming\"\n [class.cortex-reasoning-pill--done]=\"!streaming\"\n>\n <div class=\"cortex-reasoning-pill__dots\">\n <span class=\"cortex-reasoning-dot\" [class.cortex-reasoning-dot--active]=\"streaming\"></span>\n <span\n class=\"cortex-reasoning-dot\"\n [class.cortex-reasoning-dot--active]=\"streaming\"\n [style.animation-delay]=\"streaming ? '200ms' : '0ms'\"\n ></span>\n <span\n class=\"cortex-reasoning-dot\"\n [class.cortex-reasoning-dot--active]=\"streaming\"\n [style.animation-delay]=\"streaming ? '400ms' : '0ms'\"\n ></span>\n <span\n class=\"cortex-reasoning-dot\"\n [class.cortex-reasoning-dot--active]=\"streaming\"\n [style.animation-delay]=\"streaming ? '600ms' : '0ms'\"\n ></span>\n </div>\n\n <span\n class=\"cortex-reasoning-pill__text\"\n [class.cortex-reasoning-pill__text--streaming]=\"streaming\"\n [class.cortex-reasoning-pill__text--done]=\"!streaming\"\n >\n {{ streaming ? ('translate_thinking' | translate) : ('translate_reasoned' | translate) }}\n </span>\n</div>\n", styles: [".cortex-reasoning-animated{display:flex;justify-content:center}.cortex-reasoning-pill{display:inline-flex;align-items:center;gap:.625rem;padding:.5rem 1rem .5rem .75rem;border-radius:.5rem;border:1px solid;transition:background-color .3s,border-color .3s}.cortex-reasoning-pill--streaming{border-color:#c7d2fe;background:#eef2ff}.cortex-reasoning-pill--done{border-color:#e2e8f0;background:#f8fafc}.cortex-reasoning-pill__dots{display:flex;align-items:center;gap:.25rem}.cortex-reasoning-dot{width:5px;height:5px;border-radius:9999px;transition:all .4s;background:#cbd5e1;opacity:.4}.cortex-reasoning-dot--active{background:#a5b4fc;opacity:1;animation:cortex-reasoning-dot-pulse 1.4s ease-in-out infinite}@keyframes cortex-reasoning-dot-pulse{0%,to{opacity:.3;transform:scale(.8)}40%{opacity:1;transform:scale(1.3);background-color:#6366f1}}.cortex-reasoning-pill__text{font-size:.8125rem;font-weight:500;transition:color .3s}.cortex-reasoning-pill__text--streaming{color:#4f46e5}.cortex-reasoning-pill__text--done{color:#94a3b8}\n"], dependencies: [{ kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
804
811
  }
805
812
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: MessageReasoningAnimatedComponent, decorators: [{
806
813
  type: Component,
807
- args: [{ selector: 'cortex-message-reasoning-animated', imports: [NgClass, TranslatePipe], host: { class: 'flex justify-center' }, changeDetection: ChangeDetectionStrategy.OnPush, template: "@let p = reasoningPart();\n@let streaming = p.state === 'streaming';\n\n<div\n class=\"inline-flex items-center gap-2.5 py-2 pl-3 pr-4 rounded-lg border transition-colors duration-300\"\n [ngClass]=\"streaming ? 'border-indigo-200 bg-indigo-50' : 'border-slate-200 bg-slate-50'\"\n>\n <div class=\"flex items-center gap-1\">\n <span\n class=\"size-[5px] rounded-full transition-all duration-400\"\n [ngClass]=\"streaming ? 'animate-dot-pulse bg-indigo-300' : 'bg-slate-300 opacity-40'\"\n ></span>\n <span\n class=\"size-[5px] rounded-full transition-all duration-400\"\n [ngClass]=\"streaming ? 'animate-dot-pulse bg-indigo-300' : 'bg-slate-300 opacity-40'\"\n [style.animation-delay]=\"streaming ? '200ms' : '0ms'\"\n ></span>\n <span\n class=\"size-[5px] rounded-full transition-all duration-400\"\n [ngClass]=\"streaming ? 'animate-dot-pulse bg-indigo-300' : 'bg-slate-300 opacity-40'\"\n [style.animation-delay]=\"streaming ? '400ms' : '0ms'\"\n ></span>\n <span\n class=\"size-[5px] rounded-full transition-all duration-400\"\n [ngClass]=\"streaming ? 'animate-dot-pulse bg-indigo-300' : 'bg-slate-300 opacity-40'\"\n [style.animation-delay]=\"streaming ? '600ms' : '0ms'\"\n ></span>\n </div>\n\n <span\n class=\"text-[13px] font-medium transition-colors duration-300\"\n [ngClass]=\"streaming ? 'text-indigo-600' : 'text-slate-400'\"\n >\n {{ streaming ? ('translate_thinking' | translate) : ('translate_reasoned' | translate) }}\n </span>\n</div>\n", styles: [".animate-dot-pulse{animation:dot-pulse 1.4s ease-in-out infinite}@keyframes dot-pulse{0%,to{opacity:.3;transform:scale(.8)}40%{opacity:1;transform:scale(1.3);background-color:#6366f1}}\n"] }]
814
+ args: [{ selector: 'cortex-message-reasoning-animated', imports: [TranslatePipe], host: { class: 'cortex-reasoning-animated' }, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "@let p = reasoningPart();\n@let streaming = p.state === 'streaming';\n\n<div\n class=\"cortex-reasoning-pill\"\n [class.cortex-reasoning-pill--streaming]=\"streaming\"\n [class.cortex-reasoning-pill--done]=\"!streaming\"\n>\n <div class=\"cortex-reasoning-pill__dots\">\n <span class=\"cortex-reasoning-dot\" [class.cortex-reasoning-dot--active]=\"streaming\"></span>\n <span\n class=\"cortex-reasoning-dot\"\n [class.cortex-reasoning-dot--active]=\"streaming\"\n [style.animation-delay]=\"streaming ? '200ms' : '0ms'\"\n ></span>\n <span\n class=\"cortex-reasoning-dot\"\n [class.cortex-reasoning-dot--active]=\"streaming\"\n [style.animation-delay]=\"streaming ? '400ms' : '0ms'\"\n ></span>\n <span\n class=\"cortex-reasoning-dot\"\n [class.cortex-reasoning-dot--active]=\"streaming\"\n [style.animation-delay]=\"streaming ? '600ms' : '0ms'\"\n ></span>\n </div>\n\n <span\n class=\"cortex-reasoning-pill__text\"\n [class.cortex-reasoning-pill__text--streaming]=\"streaming\"\n [class.cortex-reasoning-pill__text--done]=\"!streaming\"\n >\n {{ streaming ? ('translate_thinking' | translate) : ('translate_reasoned' | translate) }}\n </span>\n</div>\n", styles: [".cortex-reasoning-animated{display:flex;justify-content:center}.cortex-reasoning-pill{display:inline-flex;align-items:center;gap:.625rem;padding:.5rem 1rem .5rem .75rem;border-radius:.5rem;border:1px solid;transition:background-color .3s,border-color .3s}.cortex-reasoning-pill--streaming{border-color:#c7d2fe;background:#eef2ff}.cortex-reasoning-pill--done{border-color:#e2e8f0;background:#f8fafc}.cortex-reasoning-pill__dots{display:flex;align-items:center;gap:.25rem}.cortex-reasoning-dot{width:5px;height:5px;border-radius:9999px;transition:all .4s;background:#cbd5e1;opacity:.4}.cortex-reasoning-dot--active{background:#a5b4fc;opacity:1;animation:cortex-reasoning-dot-pulse 1.4s ease-in-out infinite}@keyframes cortex-reasoning-dot-pulse{0%,to{opacity:.3;transform:scale(.8)}40%{opacity:1;transform:scale(1.3);background-color:#6366f1}}.cortex-reasoning-pill__text{font-size:.8125rem;font-weight:500;transition:color .3s}.cortex-reasoning-pill__text--streaming{color:#4f46e5}.cortex-reasoning-pill__text--done{color:#94a3b8}\n"] }]
808
815
  }], propDecorators: { reasoningPart: [{ type: i0.Input, args: [{ isSignal: true, alias: "reasoningPart", required: true }] }] } });
809
816
 
810
817
  class MessagePartComponent {
@@ -822,7 +829,7 @@ class MessagePartComponent {
822
829
  return toolName ? (this.config.toolComponents?.[toolName] ?? null) : null;
823
830
  }, ...(ngDevMode ? [{ debugName: "customToolComponent" }] : /* istanbul ignore next */ []));
824
831
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: MessagePartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
825
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.3", type: MessagePartComponent, isStandalone: true, selector: "cortex-message-part", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null }, part: { classPropertyName: "part", publicName: "part", isSignal: true, isRequired: true, transformFunction: null }, debugMode: { classPropertyName: "debugMode", publicName: "debugMode", isSignal: true, isRequired: false, transformFunction: null }, animate: { classPropertyName: "animate", publicName: "animate", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.animate-part-enter": "animate()" } }, ngImport: i0, template: "@let myPart = part();\n@switch (myPart.type) {\n @case ('text') {\n <cortex-message-text-part [textPart]=\"myPart\" [role]=\"message().role\" />\n }\n @case ('reasoning') {\n @if (debugMode()) {\n <cortex-message-reasoning-part [reasoningPart]=\"myPart\" />\n } @else {\n <cortex-message-reasoning-animated [reasoningPart]=\"myPart\" />\n }\n }\n @default {\n @if (isStaticToolUIPart(myPart)) {\n @if (!debugMode() || customToolComponent()) {\n <cortex-message-tool-call-animated [toolCallPart]=\"myPart\" [message]=\"message()\" />\n } @else {\n <cortex-message-tool-call-part [toolCallPart]=\"myPart\" />\n }\n } @else {\n <p class=\"text-center text-xs text-slate-400\">Unhandled type: {{ part().type }}</p>\n }\n }\n}\n", styles: [":host{display:block}:host:empty{display:none}:host.animate-part-enter{animation:partFadeInUp .2s cubic-bezier(.34,1.56,.64,1) both}@keyframes partFadeInUp{0%{opacity:0;transform:translateY(30px)}to{opacity:1;transform:translateY(0)}}\n/*! tailwindcss v4.2.1 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "component", type: MessageTextPartComponent, selector: "cortex-message-text-part", inputs: ["role", "textPart"] }, { kind: "component", type: MessageReasoningPartComponent, selector: "cortex-message-reasoning-part", inputs: ["reasoningPart"] }, { kind: "component", type: MessageToolCallPartComponent, selector: "cortex-message-tool-call-part", inputs: ["toolCallPart"] }, { kind: "component", type: MessageToolCallAnimatedComponent, selector: "cortex-message-tool-call-animated", inputs: ["message", "toolCallPart"] }, { kind: "component", type: MessageReasoningAnimatedComponent, selector: "cortex-message-reasoning-animated", inputs: ["reasoningPart"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
832
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.3", type: MessagePartComponent, isStandalone: true, selector: "cortex-message-part", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null }, part: { classPropertyName: "part", publicName: "part", isSignal: true, isRequired: true, transformFunction: null }, debugMode: { classPropertyName: "debugMode", publicName: "debugMode", isSignal: true, isRequired: false, transformFunction: null }, animate: { classPropertyName: "animate", publicName: "animate", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.cortex-message-part--animated": "animate()" }, classAttribute: "cortex-message-part" }, ngImport: i0, template: "@let myPart = part();\n@switch (myPart.type) {\n @case ('text') {\n <cortex-message-text-part [textPart]=\"myPart\" [role]=\"message().role\" />\n }\n @case ('reasoning') {\n @if (debugMode()) {\n <cortex-message-reasoning-part [reasoningPart]=\"myPart\" />\n } @else {\n <cortex-message-reasoning-animated [reasoningPart]=\"myPart\" />\n }\n }\n @default {\n @if (isStaticToolUIPart(myPart)) {\n @if (!debugMode() || customToolComponent()) {\n <cortex-message-tool-call-animated [toolCallPart]=\"myPart\" [message]=\"message()\" />\n } @else {\n <cortex-message-tool-call-part [toolCallPart]=\"myPart\" />\n }\n } @else {\n <p class=\"cortex-unhandled-type\">Unhandled type: {{ part().type }}</p>\n }\n }\n}\n", styles: [".cortex-message-part{display:block}.cortex-message-part:empty{display:none}.cortex-message-part--animated{animation:cortex-part-fade-in-up .2s cubic-bezier(.34,1.56,.64,1) both}@keyframes cortex-part-fade-in-up{0%{opacity:0;transform:translateY(30px)}to{opacity:1;transform:translateY(0)}}.cortex-unhandled-type{text-align:center;font-size:.75rem;color:#94a3b8}\n"], dependencies: [{ kind: "component", type: MessageTextPartComponent, selector: "cortex-message-text-part", inputs: ["role", "textPart"] }, { kind: "component", type: MessageReasoningPartComponent, selector: "cortex-message-reasoning-part", inputs: ["reasoningPart"] }, { kind: "component", type: MessageToolCallPartComponent, selector: "cortex-message-tool-call-part", inputs: ["toolCallPart"] }, { kind: "component", type: MessageToolCallAnimatedComponent, selector: "cortex-message-tool-call-animated", inputs: ["message", "toolCallPart"] }, { kind: "component", type: MessageReasoningAnimatedComponent, selector: "cortex-message-reasoning-animated", inputs: ["reasoningPart"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
826
833
  }
827
834
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: MessagePartComponent, decorators: [{
828
835
  type: Component,
@@ -832,9 +839,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.3", ngImpor
832
839
  MessageToolCallPartComponent,
833
840
  MessageToolCallAnimatedComponent,
834
841
  MessageReasoningAnimatedComponent,
835
- ], changeDetection: ChangeDetectionStrategy.OnPush, host: {
836
- '[class.animate-part-enter]': 'animate()',
837
- }, template: "@let myPart = part();\n@switch (myPart.type) {\n @case ('text') {\n <cortex-message-text-part [textPart]=\"myPart\" [role]=\"message().role\" />\n }\n @case ('reasoning') {\n @if (debugMode()) {\n <cortex-message-reasoning-part [reasoningPart]=\"myPart\" />\n } @else {\n <cortex-message-reasoning-animated [reasoningPart]=\"myPart\" />\n }\n }\n @default {\n @if (isStaticToolUIPart(myPart)) {\n @if (!debugMode() || customToolComponent()) {\n <cortex-message-tool-call-animated [toolCallPart]=\"myPart\" [message]=\"message()\" />\n } @else {\n <cortex-message-tool-call-part [toolCallPart]=\"myPart\" />\n }\n } @else {\n <p class=\"text-center text-xs text-slate-400\">Unhandled type: {{ part().type }}</p>\n }\n }\n}\n", styles: [":host{display:block}:host:empty{display:none}:host.animate-part-enter{animation:partFadeInUp .2s cubic-bezier(.34,1.56,.64,1) both}@keyframes partFadeInUp{0%{opacity:0;transform:translateY(30px)}to{opacity:1;transform:translateY(0)}}\n/*! tailwindcss v4.2.1 | MIT License | https://tailwindcss.com */\n"] }]
842
+ ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
843
+ class: 'cortex-message-part',
844
+ '[class.cortex-message-part--animated]': 'animate()',
845
+ }, template: "@let myPart = part();\n@switch (myPart.type) {\n @case ('text') {\n <cortex-message-text-part [textPart]=\"myPart\" [role]=\"message().role\" />\n }\n @case ('reasoning') {\n @if (debugMode()) {\n <cortex-message-reasoning-part [reasoningPart]=\"myPart\" />\n } @else {\n <cortex-message-reasoning-animated [reasoningPart]=\"myPart\" />\n }\n }\n @default {\n @if (isStaticToolUIPart(myPart)) {\n @if (!debugMode() || customToolComponent()) {\n <cortex-message-tool-call-animated [toolCallPart]=\"myPart\" [message]=\"message()\" />\n } @else {\n <cortex-message-tool-call-part [toolCallPart]=\"myPart\" />\n }\n } @else {\n <p class=\"cortex-unhandled-type\">Unhandled type: {{ part().type }}</p>\n }\n }\n}\n", styles: [".cortex-message-part{display:block}.cortex-message-part:empty{display:none}.cortex-message-part--animated{animation:cortex-part-fade-in-up .2s cubic-bezier(.34,1.56,.64,1) both}@keyframes cortex-part-fade-in-up{0%{opacity:0;transform:translateY(30px)}to{opacity:1;transform:translateY(0)}}.cortex-unhandled-type{text-align:center;font-size:.75rem;color:#94a3b8}\n"] }]
838
846
  }], propDecorators: { message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: true }] }], part: [{ type: i0.Input, args: [{ isSignal: true, alias: "part", required: true }] }], debugMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "debugMode", required: false }] }], animate: [{ type: i0.Input, args: [{ isSignal: true, alias: "animate", required: false }] }] } });
839
847
 
840
848
  class MessageComponent {
@@ -874,11 +882,11 @@ class MessageComponent {
874
882
  });
875
883
  }
876
884
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: MessageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
877
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.3", type: MessageComponent, isStandalone: true, selector: "cortex-message", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null }, debugMode: { classPropertyName: "debugMode", publicName: "debugMode", isSignal: true, isRequired: false, transformFunction: null }, animate: { classPropertyName: "animate", publicName: "animate", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (visibleParts().length) {\n <div class=\"w-full flex flex-col gap-2\">\n @for (part of visibleParts(); track $index) {\n <cortex-message-part\n [part]=\"part\"\n [message]=\"message()\"\n [debugMode]=\"debugMode()\"\n [animate]=\"animate()\"\n />\n }\n </div>\n}\n", styles: [":host:empty{display:none}\n/*! tailwindcss v4.2.1 | MIT License | https://tailwindcss.com */\n"], dependencies: [{ kind: "component", type: MessagePartComponent, selector: "cortex-message-part", inputs: ["message", "part", "debugMode", "animate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
885
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.3", type: MessageComponent, isStandalone: true, selector: "cortex-message", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null }, debugMode: { classPropertyName: "debugMode", publicName: "debugMode", isSignal: true, isRequired: false, transformFunction: null }, animate: { classPropertyName: "animate", publicName: "animate", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "cortex-message" }, ngImport: i0, template: "@if (visibleParts().length) {\n <div class=\"cortex-message-parts\">\n @for (part of visibleParts(); track $index) {\n <cortex-message-part\n [part]=\"part\"\n [message]=\"message()\"\n [debugMode]=\"debugMode()\"\n [animate]=\"animate()\"\n />\n }\n </div>\n}\n", styles: [".cortex-message:empty{display:none}.cortex-message-parts{width:100%;display:flex;flex-direction:column;gap:.5rem}\n"], dependencies: [{ kind: "component", type: MessagePartComponent, selector: "cortex-message-part", inputs: ["message", "part", "debugMode", "animate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
878
886
  }
879
887
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: MessageComponent, decorators: [{
880
888
  type: Component,
881
- args: [{ selector: 'cortex-message', changeDetection: ChangeDetectionStrategy.OnPush, imports: [MessagePartComponent], template: "@if (visibleParts().length) {\n <div class=\"w-full flex flex-col gap-2\">\n @for (part of visibleParts(); track $index) {\n <cortex-message-part\n [part]=\"part\"\n [message]=\"message()\"\n [debugMode]=\"debugMode()\"\n [animate]=\"animate()\"\n />\n }\n </div>\n}\n", styles: [":host:empty{display:none}\n/*! tailwindcss v4.2.1 | MIT License | https://tailwindcss.com */\n"] }]
889
+ args: [{ selector: 'cortex-message', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [MessagePartComponent], host: { class: 'cortex-message' }, template: "@if (visibleParts().length) {\n <div class=\"cortex-message-parts\">\n @for (part of visibleParts(); track $index) {\n <cortex-message-part\n [part]=\"part\"\n [message]=\"message()\"\n [debugMode]=\"debugMode()\"\n [animate]=\"animate()\"\n />\n }\n </div>\n}\n", styles: [".cortex-message:empty{display:none}.cortex-message-parts{width:100%;display:flex;flex-direction:column;gap:.5rem}\n"] }]
882
890
  }], ctorParameters: () => [], propDecorators: { message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: true }] }], debugMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "debugMode", required: false }] }], animate: [{ type: i0.Input, args: [{ isSignal: true, alias: "animate", required: false }] }] } });
883
891
 
884
892
  class MessageListComponent {
@@ -931,11 +939,11 @@ class MessageListComponent {
931
939
  }
932
940
  }
933
941
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: MessageListComponent, deps: [{ token: CortexChatService }, { token: i0.DestroyRef }], target: i0.ɵɵFactoryTarget.Component });
934
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.3", type: MessageListComponent, isStandalone: true, selector: "cortex-message-list", inputs: { debugMode: { classPropertyName: "debugMode", publicName: "debugMode", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "messagesContainer", first: true, predicate: ["messagesContainer"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"h-full overflow-y-auto w-full flex flex-col gap-2 p-4\"\n #messagesContainer\n (scroll)=\"onScroll()\"\n>\n @for (message of chatService.chat()?.messages; track message.id) {\n <cortex-message [message]=\"message\" [debugMode]=\"debugMode()\" [animate]=\"animateNewParts()\" />\n }\n</div>\n", dependencies: [{ kind: "component", type: MessageComponent, selector: "cortex-message", inputs: ["message", "debugMode", "animate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
942
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.3", type: MessageListComponent, isStandalone: true, selector: "cortex-message-list", inputs: { debugMode: { classPropertyName: "debugMode", publicName: "debugMode", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "messagesContainer", first: true, predicate: ["messagesContainer"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"cortex-message-list\" #messagesContainer (scroll)=\"onScroll()\">\n @for (message of chatService.chat()?.messages; track message.id) {\n <cortex-message [message]=\"message\" [debugMode]=\"debugMode()\" [animate]=\"animateNewParts()\" />\n }\n</div>\n", styles: [".cortex-message-list{height:100%;overflow-y:auto;width:100%;display:flex;flex-direction:column;gap:.5rem;padding:1rem}\n"], dependencies: [{ kind: "component", type: MessageComponent, selector: "cortex-message", inputs: ["message", "debugMode", "animate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
935
943
  }
936
944
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: MessageListComponent, decorators: [{
937
945
  type: Component,
938
- args: [{ selector: 'cortex-message-list', changeDetection: ChangeDetectionStrategy.OnPush, imports: [MessageComponent], template: "<div\n class=\"h-full overflow-y-auto w-full flex flex-col gap-2 p-4\"\n #messagesContainer\n (scroll)=\"onScroll()\"\n>\n @for (message of chatService.chat()?.messages; track message.id) {\n <cortex-message [message]=\"message\" [debugMode]=\"debugMode()\" [animate]=\"animateNewParts()\" />\n }\n</div>\n" }]
946
+ args: [{ selector: 'cortex-message-list', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [MessageComponent], template: "<div class=\"cortex-message-list\" #messagesContainer (scroll)=\"onScroll()\">\n @for (message of chatService.chat()?.messages; track message.id) {\n <cortex-message [message]=\"message\" [debugMode]=\"debugMode()\" [animate]=\"animateNewParts()\" />\n }\n</div>\n", styles: [".cortex-message-list{height:100%;overflow-y:auto;width:100%;display:flex;flex-direction:column;gap:.5rem;padding:1rem}\n"] }]
939
947
  }], ctorParameters: () => [{ type: CortexChatService }, { type: i0.DestroyRef }], propDecorators: { debugMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "debugMode", required: false }] }], messagesContainer: [{ type: i0.ViewChild, args: ['messagesContainer', { isSignal: true }] }] } });
940
948
 
941
949
  var translate_new$1 = "New";
@@ -1148,13 +1156,11 @@ class CortexChatWidgetComponent {
1148
1156
  text = model('', ...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
1149
1157
  debugMode = signal(false, ...(ngDevMode ? [{ debugName: "debugMode" }] : /* istanbul ignore next */ []));
1150
1158
  screen = signal('threads', ...(ngDevMode ? [{ debugName: "screen" }] : /* istanbul ignore next */ []));
1151
- theme = signal(undefined, ...(ngDevMode ? [{ debugName: "theme" }] : /* istanbul ignore next */ []));
1152
1159
  chatService;
1153
1160
  configRef = inject(CortexClientConfigRef);
1154
1161
  injector = inject(Injector);
1155
1162
  ngOnInit() {
1156
1163
  this.configRef.config = this.config();
1157
- this.theme.set(this.config().theme);
1158
1164
  this.chatService = this.injector.get(CortexChatService);
1159
1165
  const translateService = this.injector.get(TranslateService);
1160
1166
  translateService.use(this.config().locale);
@@ -1190,7 +1196,7 @@ class CortexChatWidgetComponent {
1190
1196
  this.screen.set('chat');
1191
1197
  }
1192
1198
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: CortexChatWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1193
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.3", type: CortexChatWidgetComponent, isStandalone: true, selector: "cortex-chat-widget", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { text: "textChange" }, host: { properties: { "style.--cortex-primary-color": "theme()?.primaryColor", "style.--cortex-bg-color": "theme()?.backgroundColor", "style.--cortex-surface-color": "theme()?.surfaceColor", "style.--cortex-text-color": "theme()?.textColor", "style.--cortex-border-radius": "theme()?.borderRadius" }, classAttribute: "block" }, providers: [
1199
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.3", type: CortexChatWidgetComponent, isStandalone: true, selector: "cortex-chat-widget", inputs: { config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: true, transformFunction: null }, text: { classPropertyName: "text", publicName: "text", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { text: "textChange" }, host: { classAttribute: "cortex-widget" }, providers: [
1194
1200
  CortexClientConfigRef,
1195
1201
  {
1196
1202
  provide: CORTEX_CLIENT_CONFIG,
@@ -1202,11 +1208,11 @@ class CortexChatWidgetComponent {
1202
1208
  fallbackLang: 'en',
1203
1209
  loader: provideTranslateLoader(CortexClientTranslateLoader),
1204
1210
  }),
1205
- ], ngImport: i0, template: "<div class=\"w-full h-full relative overflow-hidden bg-slate-5\">\n <!-- \u2550\u2550\u2550 SCREEN 1: THREADS \u2550\u2550\u2550 -->\n <div\n class=\"absolute inset-0 flex flex-col transition-transform duration-300 ease-out\"\n [ngClass]=\"\n screen() === 'threads' ? 'translate-x-0' : 'ltr:-translate-x-full rtl:translate-x-full'\n \"\n >\n <!-- Threads header -->\n <div class=\"shrink-0 px-5 pt-5 pb-4 flex items-center justify-between\">\n <div>\n <h2 class=\"text-[15px] font-semibold text-slate-800 tracking-tight\">\n {{ 'translate_threads' | translate }}\n </h2>\n <p class=\"text-[11px] text-slate-400 mt-0.5\">\n {{\n 'translate_n_conversations' | translate: { count: chatService.threads()?.length ?? 0 }\n }}\n </p>\n </div>\n <button\n (click)=\"newChat()\"\n class=\"inline-flex items-center gap-1.5 text-[12px] font-medium text-slate-100 bg-slate-800 px-3 py-1.5 rounded-md hover:bg-slate-700 active:bg-slate-900 transition-colors cursor-pointer\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path\n d=\"M8 3v10M3 8h10\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n />\n </svg>\n {{ 'translate_new' | translate }}\n </button>\n </div>\n\n <!-- Threads list -->\n <div class=\"flex-1 overflow-y-auto px-3 pb-3\">\n <!-- Threads loading skeleton -->\n @if (chatService.threads() === undefined) {\n @for (i of [1, 2, 3, 4]; track i) {\n <div class=\"flex items-center gap-3 px-3 py-3 mb-0.5\">\n <div class=\"skeleton shrink-0 size-8\"></div>\n <div class=\"flex-1 flex flex-col gap-1.5\">\n <div class=\"skeleton h-3.5\" [style.width]=\"40 + i * 12 + '%'\"></div>\n </div>\n </div>\n }\n } @else {\n @for (thread of chatService.threads(); track thread.id) {\n <button\n (click)=\"selectThread(thread)\"\n class=\"group w-full text-start px-3 py-3 rounded-lg flex items-center gap-3 transition-colors cursor-pointer mb-0.5\"\n [ngClass]=\"{\n 'bg-slate-200 text-slate-100': thread.id === chatService.selectedThread()?.id,\n 'text-slate-600 hover:bg-slate-100 active:bg-slate-150':\n thread.id !== chatService.selectedThread()?.id,\n }\"\n >\n <!-- Thread icon -->\n <div\n class=\"shrink-0 size-8 rounded-md flex items-center justify-center text-[11px] font-semibold rtl:transform rtl:rotate-180\"\n [ngClass]=\"{\n 'bg-slate-700 text-slate-300': thread.id === chatService.selectedThread()?.id,\n 'bg-slate-200 text-slate-500': thread.id !== chatService.selectedThread()?.id,\n }\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path\n d=\"M2.5 4.5h11M2.5 8h7M2.5 11.5h9\"\n stroke=\"currentColor\"\n stroke-width=\"1.3\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n\n <!-- Thread info -->\n <div class=\"min-w-0 flex-1\">\n <p\n class=\"text-[13px] font-medium truncate leading-tight\"\n [ngClass]=\"{\n 'text-slate-100': thread.id === chatService.selectedThread()?.id,\n 'text-slate-700': thread.id !== chatService.selectedThread()?.id,\n }\"\n >\n {{ thread.title ?? ('translate_untitled' | translate) }}\n </p>\n </div>\n\n <!-- Delete button -->\n <button\n (click)=\"$event.stopPropagation(); chatService.deleteThread(thread.id).subscribe()\"\n class=\"shrink-0 opacity-0 group-hover:opacity-100 transition-opacity size-7 rounded-md flex items-center justify-center cursor-pointer\"\n [ngClass]=\"{\n 'hover:bg-slate-600 text-slate-400': thread.id === chatService.selectedThread()?.id,\n 'hover:bg-slate-200 text-slate-400': thread.id !== chatService.selectedThread()?.id,\n }\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path\n d=\"M4 4l8 8M12 4l-8 8\"\n stroke=\"currentColor\"\n stroke-width=\"1.3\"\n stroke-linecap=\"round\"\n />\n </svg>\n </button>\n\n <!-- Arrow -->\n <svg\n class=\"shrink-0 transition-colors rtl:transform rtl:rotate-180\"\n [ngClass]=\"{\n 'text-slate-500': thread.id === chatService.selectedThread()?.id,\n 'text-slate-300 group-hover:text-slate-400':\n thread.id !== chatService.selectedThread()?.id,\n }\"\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n >\n <path\n d=\"M6 4l4 4-4 4\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n }\n\n @if (!chatService.threads()?.length) {\n <div class=\"flex flex-col items-center justify-center py-16 text-center\">\n <div class=\"size-10 rounded-lg bg-slate-200 flex items-center justify-center mb-3\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 16 16\" fill=\"none\" class=\"text-slate-400\">\n <path\n d=\"M2.5 4.5h11M2.5 8h7M2.5 11.5h9\"\n stroke=\"currentColor\"\n stroke-width=\"1.3\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n <p class=\"text-[13px] text-slate-400 font-medium\">No threads yet</p>\n <p class=\"text-[11px] text-slate-350 mt-1\">Start a new conversation</p>\n </div>\n }\n }\n </div>\n </div>\n\n <!-- \u2550\u2550\u2550 SCREEN 2: CHAT \u2550\u2550\u2550 -->\n <div\n class=\"absolute inset-0 flex flex-col transition-transform duration-300 ease-out\"\n [ngClass]=\"screen() === 'chat' ? 'translate-x-0' : 'ltr:translate-x-full rtl:-translate-x-full'\"\n >\n <!-- Chat header -->\n <div class=\"shrink-0 h-12 px-4 flex items-center gap-3 border-b border-slate-200 bg-white\">\n <!-- Back button -->\n <button\n (click)=\"goBack()\"\n class=\"shrink-0 size-8 rounded-md flex items-center justify-center text-slate-400 hover:text-slate-600 hover:bg-slate-100 transition-colors cursor-pointer\"\n >\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n class=\"rtl:transform rtl:rotate-180\"\n >\n <path\n d=\"M10 3L5 8l5 5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n\n <!-- Title -->\n <div class=\"min-w-0 flex-1\">\n <p class=\"text-[13px] font-medium text-slate-700 truncate\">\n {{ chatService.selectedThread()?.title ?? ('translate_new_chat' | translate) }}\n </p>\n </div>\n\n <!-- Debug toggle -->\n <button\n (click)=\"toggleDebugMode()\"\n class=\"shrink-0 text-[11px] font-medium px-2.5 py-1 rounded-md transition-colors cursor-pointer\"\n [ngClass]=\"{\n 'bg-amber-50 text-amber-600 hover:bg-amber-100': debugMode(),\n 'bg-slate-100 text-slate-400 hover:bg-slate-200': !debugMode(),\n }\"\n >\n {{ debugMode() ? ('translate_debug' | translate) : ('translate_normal' | translate) }}\n </button>\n </div>\n\n <!-- Messages loading skeleton -->\n @if (chatService.isLoadingMessages() && !chatService.isAgentWorking()) {\n <div class=\"flex-1 overflow-hidden p-4 flex flex-col gap-2\">\n <!-- User message skeleton -->\n <div class=\"w-full flex flex-col items-start\">\n <div\n class=\"max-w-4/5 p-4 rounded-2xl rounded-bl-none border border-slate-200 bg-slate-50 flex flex-col gap-2\"\n >\n <div class=\"skeleton h-3 w-52\"></div>\n <div class=\"skeleton h-3 w-36\"></div>\n </div>\n </div>\n <!-- Assistant message skeleton -->\n <div class=\"w-full flex flex-col items-end\">\n <div\n class=\"max-w-4/5 p-4 rounded-2xl rounded-br-none border border-slate-200 bg-slate-100 flex flex-col gap-2\"\n >\n <div class=\"skeleton h-3 w-64\"></div>\n <div class=\"skeleton h-3 w-72\"></div>\n <div class=\"skeleton h-3 w-48\"></div>\n </div>\n </div>\n <!-- User message skeleton -->\n <div class=\"w-full flex flex-col items-start\">\n <div\n class=\"max-w-4/5 p-4 rounded-2xl rounded-bl-none border border-slate-200 bg-slate-50 flex flex-col gap-2\"\n >\n <div class=\"skeleton h-3 w-44\"></div>\n </div>\n </div>\n <!-- Assistant message skeleton -->\n <div class=\"w-full flex flex-col items-end\">\n <div\n class=\"max-w-4/5 p-4 rounded-2xl rounded-br-none border border-slate-200 bg-slate-100 flex flex-col gap-2\"\n >\n <div class=\"skeleton h-3 w-56\"></div>\n <div class=\"skeleton h-3 w-60\"></div>\n </div>\n </div>\n </div>\n } @else {\n <!-- Messages area -->\n <cortex-message-list class=\"flex-1 overflow-hidden\" [debugMode]=\"debugMode()\" />\n }\n\n <!-- Agent working indicator -->\n @if (chatService.isAgentWorking() && !chatService.hasPendingToolCalls()) {\n <div class=\"shrink-0 px-4 py-1.5 flex items-center gap-1.5\">\n <div class=\"flex items-center gap-[3px]\">\n <span class=\"cortex-working-dot block size-[5px] rounded-full bg-slate-400\"></span>\n <span class=\"cortex-working-dot block size-[5px] rounded-full bg-slate-400\"></span>\n <span class=\"cortex-working-dot block size-[5px] rounded-full bg-slate-400\"></span>\n </div>\n <span class=\"text-[11px] text-slate-400 font-medium\">{{\n 'translate_thinking' | translate\n }}</span>\n </div>\n }\n\n <!-- Chat input (hidden during pending tool calls) -->\n @if (!chatService.hasPendingToolCalls()) {\n <div class=\"shrink-0 p-3 bg-white border-t border-slate-200\">\n <div\n class=\"flex items-end gap-2 rounded-xl border border-slate-200 p-1.5 transition-colors\"\n [ngClass]=\"{\n 'bg-slate-100': chatService.isAgentWorking(),\n 'bg-slate-50 focus-within:border-slate-300 focus-within:bg-white':\n !chatService.isAgentWorking(),\n }\"\n >\n <textarea\n (keydown)=\"onKeydown($event)\"\n [(ngModel)]=\"text\"\n [placeholder]=\"\n chatService.isAgentWorking() ? '' : ('translate_type_a_message' | translate)\n \"\n [disabled]=\"chatService.isAgentWorking()\"\n rows=\"1\"\n class=\"flex-1 bg-transparent resize-none border-0 outline-none text-[13px] placeholder:text-slate-350 py-1.5 px-2 min-h-[28px] max-h-[120px] leading-snug disabled:cursor-not-allowed disabled:border-0\"\n [ngClass]=\"{\n 'text-slate-400': chatService.isAgentWorking(),\n 'text-slate-700': !chatService.isAgentWorking(),\n }\"\n ></textarea>\n @if (chatService.isAgentWorking()) {\n <!-- Stop button -->\n <button\n (click)=\"chatService.abort()\"\n class=\"stop-btn shrink-0 size-8 rounded-lg flex items-center justify-center cursor-pointer relative\"\n >\n <!-- Pulsing ring -->\n <span class=\"stop-btn-ring absolute inset-0 rounded-lg\"></span>\n <!-- Stop icon (rounded square) -->\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" class=\"relative z-10\">\n <rect x=\"1\" y=\"1\" width=\"10\" height=\"10\" rx=\"2.5\" fill=\"currentColor\" />\n </svg>\n </button>\n } @else {\n <!-- Send button -->\n <button\n (click)=\"send()\"\n class=\"shrink-0 size-8 rounded-lg flex items-center justify-center transition-colors\"\n [ngClass]=\"{\n 'bg-slate-200 text-slate-400': !text().trim(),\n 'bg-slate-800 text-white hover:bg-slate-700 cursor-pointer': text().trim(),\n }\"\n >\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n class=\"rtl:transform rtl:rotate-180\"\n >\n <path\n d=\"M3 8h10M9 4l4 4-4 4\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n }\n </div>\n </div>\n }\n </div>\n</div>\n", styles: ["@charset \"UTF-8\";@keyframes skeleton-shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.skeleton{background:linear-gradient(90deg,#e2e8f0,#f1f5f9,#e2e8f0 80%);background-size:200% 100%;animation:skeleton-shimmer 1.8s ease-in-out infinite;border-radius:6px}@keyframes cortex-pulse{0%,to{opacity:.4}50%{opacity:1}}@keyframes cortex-dot-bounce{0%,80%,to{transform:translateY(0)}40%{transform:translateY(-3px)}}.cortex-working-dot{animation:cortex-pulse 1.6s ease-in-out infinite,cortex-dot-bounce 1.2s ease-in-out infinite}.cortex-working-dot:nth-child(2){animation-delay:.15s}.cortex-working-dot:nth-child(3){animation-delay:.3s}@keyframes stop-pulse-ring{0%{opacity:.45;transform:scale(1)}50%{opacity:.15;transform:scale(1.12)}to{opacity:.45;transform:scale(1)}}@keyframes stop-fade-in{0%{opacity:0;transform:scale(.7)}to{opacity:1;transform:scale(1)}}.stop-btn{background:#e11d48;color:#fff;animation:stop-fade-in .2s ease-out both;transition:background .15s ease}.stop-btn:hover{background:#be123c}.stop-btn:active{transform:scale(.92)}.stop-btn-ring{background:#e11d48;animation:stop-pulse-ring 2s ease-in-out infinite;pointer-events:none}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: MessageListComponent, selector: "cortex-message-list", inputs: ["debugMode"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1211
+ ], ngImport: i0, template: "<div class=\"cortex-widget__container\">\n <!-- SCREEN 1: THREADS -->\n <div\n class=\"cortex-widget__screen\"\n [class.cortex-widget__screen--active]=\"screen() === 'threads'\"\n [class.cortex-widget__screen--left]=\"screen() !== 'threads'\"\n >\n <!-- Threads header -->\n <div class=\"cortex-widget__threads-header\">\n <div>\n <h2 class=\"cortex-widget__threads-title\">\n {{ 'translate_threads' | translate }}\n </h2>\n <p class=\"cortex-widget__threads-count\">\n {{\n 'translate_n_conversations' | translate: { count: chatService.threads()?.length ?? 0 }\n }}\n </p>\n </div>\n <button (click)=\"newChat()\" class=\"cortex-widget__new-chat-btn\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path\n d=\"M8 3v10M3 8h10\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n />\n </svg>\n {{ 'translate_new' | translate }}\n </button>\n </div>\n\n <!-- Threads list -->\n <div class=\"cortex-widget__threads-list\">\n <!-- Threads loading skeleton -->\n @if (chatService.threads() === undefined) {\n @for (i of [1, 2, 3, 4]; track i) {\n <div class=\"cortex-widget__thread-skeleton\">\n <div class=\"cortex-skeleton cortex-widget__thread-skeleton-icon\"></div>\n <div class=\"cortex-widget__thread-skeleton-lines\">\n <div\n class=\"cortex-skeleton cortex-widget__thread-skeleton-line\"\n [style.width]=\"40 + i * 12 + '%'\"\n ></div>\n </div>\n </div>\n }\n } @else {\n @for (thread of chatService.threads(); track thread.id) {\n <button\n (click)=\"selectThread(thread)\"\n class=\"cortex-widget__thread-item\"\n [class.cortex-widget__thread-item--active]=\"\n thread.id === chatService.selectedThread()?.id\n \"\n >\n <!-- Thread icon -->\n <div\n class=\"cortex-widget__thread-icon\"\n [class.cortex-widget__thread-icon--active]=\"\n thread.id === chatService.selectedThread()?.id\n \"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path\n d=\"M2.5 4.5h11M2.5 8h7M2.5 11.5h9\"\n stroke=\"currentColor\"\n stroke-width=\"1.3\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n\n <!-- Thread info -->\n <div class=\"cortex-widget__thread-info\">\n <p\n class=\"cortex-widget__thread-title\"\n [class.cortex-widget__thread-title--active]=\"\n thread.id === chatService.selectedThread()?.id\n \"\n >\n {{ thread.title ?? ('translate_untitled' | translate) }}\n </p>\n </div>\n\n <!-- Delete button -->\n <button\n (click)=\"$event.stopPropagation(); chatService.deleteThread(thread.id).subscribe()\"\n class=\"cortex-widget__thread-delete\"\n [class.cortex-widget__thread-delete--active]=\"\n thread.id === chatService.selectedThread()?.id\n \"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path\n d=\"M4 4l8 8M12 4l-8 8\"\n stroke=\"currentColor\"\n stroke-width=\"1.3\"\n stroke-linecap=\"round\"\n />\n </svg>\n </button>\n\n <!-- Arrow -->\n <svg\n class=\"cortex-widget__thread-arrow\"\n [class.cortex-widget__thread-arrow--active]=\"\n thread.id === chatService.selectedThread()?.id\n \"\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n >\n <path\n d=\"M6 4l4 4-4 4\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n }\n\n @if (!chatService.threads()?.length) {\n <div class=\"cortex-widget__threads-empty\">\n <div class=\"cortex-widget__threads-empty-icon\">\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n class=\"cortex-widget__threads-empty-svg\"\n >\n <path\n d=\"M2.5 4.5h11M2.5 8h7M2.5 11.5h9\"\n stroke=\"currentColor\"\n stroke-width=\"1.3\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n <p class=\"cortex-widget__threads-empty-title\">No threads yet</p>\n <p class=\"cortex-widget__threads-empty-subtitle\">Start a new conversation</p>\n </div>\n }\n }\n </div>\n </div>\n\n <!-- SCREEN 2: CHAT -->\n <div\n class=\"cortex-widget__screen\"\n [class.cortex-widget__screen--active]=\"screen() === 'chat'\"\n [class.cortex-widget__screen--right]=\"screen() !== 'chat'\"\n >\n <!-- Chat header -->\n <div class=\"cortex-widget__chat-header\">\n <!-- Back button -->\n <button (click)=\"goBack()\" class=\"cortex-widget__back-btn\">\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n class=\"cortex-widget__back-icon\"\n >\n <path\n d=\"M10 3L5 8l5 5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n\n <!-- Title -->\n <div class=\"cortex-widget__chat-title-wrap\">\n <p class=\"cortex-widget__chat-title\">\n {{ chatService.selectedThread()?.title ?? ('translate_new_chat' | translate) }}\n </p>\n </div>\n\n <!-- Debug toggle -->\n <button\n (click)=\"toggleDebugMode()\"\n class=\"cortex-widget__debug-btn\"\n [class.cortex-widget__debug-btn--on]=\"debugMode()\"\n [class.cortex-widget__debug-btn--off]=\"!debugMode()\"\n >\n {{ debugMode() ? ('translate_debug' | translate) : ('translate_normal' | translate) }}\n </button>\n </div>\n\n <!-- Messages loading skeleton -->\n @if (chatService.isLoadingMessages() && !chatService.isAgentWorking()) {\n <div class=\"cortex-widget__messages-skeleton\">\n <!-- User message skeleton -->\n <div class=\"cortex-widget__msg-skel cortex-widget__msg-skel--user\">\n <div class=\"cortex-widget__msg-skel-bubble cortex-widget__msg-skel-bubble--user\">\n <div class=\"cortex-skeleton cortex-widget__msg-skel-line\" style=\"width: 13rem\"></div>\n <div class=\"cortex-skeleton cortex-widget__msg-skel-line\" style=\"width: 9rem\"></div>\n </div>\n </div>\n <!-- Assistant message skeleton -->\n <div class=\"cortex-widget__msg-skel cortex-widget__msg-skel--assistant\">\n <div class=\"cortex-widget__msg-skel-bubble cortex-widget__msg-skel-bubble--assistant\">\n <div class=\"cortex-skeleton cortex-widget__msg-skel-line\" style=\"width: 16rem\"></div>\n <div class=\"cortex-skeleton cortex-widget__msg-skel-line\" style=\"width: 18rem\"></div>\n <div class=\"cortex-skeleton cortex-widget__msg-skel-line\" style=\"width: 12rem\"></div>\n </div>\n </div>\n <!-- User message skeleton -->\n <div class=\"cortex-widget__msg-skel cortex-widget__msg-skel--user\">\n <div class=\"cortex-widget__msg-skel-bubble cortex-widget__msg-skel-bubble--user\">\n <div class=\"cortex-skeleton cortex-widget__msg-skel-line\" style=\"width: 11rem\"></div>\n </div>\n </div>\n <!-- Assistant message skeleton -->\n <div class=\"cortex-widget__msg-skel cortex-widget__msg-skel--assistant\">\n <div class=\"cortex-widget__msg-skel-bubble cortex-widget__msg-skel-bubble--assistant\">\n <div class=\"cortex-skeleton cortex-widget__msg-skel-line\" style=\"width: 14rem\"></div>\n <div class=\"cortex-skeleton cortex-widget__msg-skel-line\" style=\"width: 15rem\"></div>\n </div>\n </div>\n </div>\n } @else {\n <!-- Messages area -->\n <cortex-message-list class=\"cortex-widget__messages\" [debugMode]=\"debugMode()\" />\n }\n\n <!-- Agent working indicator -->\n @if (chatService.isAgentWorking() && !chatService.hasPendingToolCalls()) {\n <div class=\"cortex-widget__working\">\n <div class=\"cortex-widget__working-dots\">\n <span class=\"cortex-working-dot\"></span>\n <span class=\"cortex-working-dot\"></span>\n <span class=\"cortex-working-dot\"></span>\n </div>\n <span class=\"cortex-widget__working-text\">{{ 'translate_thinking' | translate }}</span>\n </div>\n }\n\n <!-- Chat input (hidden during pending tool calls) -->\n @if (!chatService.hasPendingToolCalls()) {\n <div class=\"cortex-widget__input-area\">\n <div\n class=\"cortex-widget__input-box\"\n [class.cortex-widget__input-box--disabled]=\"chatService.isAgentWorking()\"\n [class.cortex-widget__input-box--enabled]=\"!chatService.isAgentWorking()\"\n >\n <textarea\n (keydown)=\"onKeydown($event)\"\n [(ngModel)]=\"text\"\n [placeholder]=\"\n chatService.isAgentWorking() ? '' : ('translate_type_a_message' | translate)\n \"\n [disabled]=\"chatService.isAgentWorking()\"\n rows=\"1\"\n class=\"cortex-widget__textarea\"\n [class.cortex-widget__textarea--disabled]=\"chatService.isAgentWorking()\"\n ></textarea>\n @if (chatService.isAgentWorking()) {\n <!-- Stop button -->\n <button (click)=\"chatService.abort()\" class=\"cortex-stop-btn\">\n <!-- Pulsing ring -->\n <span class=\"cortex-stop-btn__ring\"></span>\n <!-- Stop icon (rounded square) -->\n <svg\n width=\"12\"\n height=\"12\"\n viewBox=\"0 0 12 12\"\n fill=\"none\"\n class=\"cortex-stop-btn__icon\"\n >\n <rect x=\"1\" y=\"1\" width=\"10\" height=\"10\" rx=\"2.5\" fill=\"currentColor\" />\n </svg>\n </button>\n } @else {\n <!-- Send button -->\n <button\n (click)=\"send()\"\n class=\"cortex-widget__send-btn\"\n [class.cortex-widget__send-btn--empty]=\"!text().trim()\"\n [class.cortex-widget__send-btn--ready]=\"!!text().trim()\"\n >\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n class=\"cortex-widget__send-icon\"\n >\n <path\n d=\"M3 8h10M9 4l4 4-4 4\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n }\n </div>\n </div>\n }\n </div>\n</div>\n", styles: [".cortex-widget{display:block}.cortex-widget__container{width:100%;height:100%;position:relative;overflow:hidden}.cortex-widget__screen{position:absolute;inset:0;display:flex;flex-direction:column;transition:transform .3s ease-out}.cortex-widget__screen--active{transform:translate(0)}.cortex-widget__screen--left{transform:translate(-100%)}[dir=rtl] .cortex-widget__screen--left,.cortex-widget__screen--right{transform:translate(100%)}[dir=rtl] .cortex-widget__screen--right{transform:translate(-100%)}.cortex-widget__threads-header{flex-shrink:0;padding:1.25rem 1.25rem 1rem;display:flex;align-items:center;justify-content:space-between}.cortex-widget__threads-title{font-size:.9375rem;font-weight:600;color:#1e293b;letter-spacing:-.01em;margin:0}.cortex-widget__threads-count{font-size:.6875rem;color:#94a3b8;margin-top:.125rem;margin-bottom:0}.cortex-widget__new-chat-btn{display:inline-flex;align-items:center;gap:.375rem;font-size:.75rem;font-weight:500;color:#f1f5f9;background:#1e293b;padding:.375rem .75rem;border-radius:.375rem;border:0;cursor:pointer;transition:background-color .15s}.cortex-widget__new-chat-btn:hover{background:#334155}.cortex-widget__new-chat-btn:active{background:#0f172a}.cortex-widget__threads-list{flex:1;overflow-y:auto;padding:0 .75rem .75rem}.cortex-widget__thread-skeleton{display:flex;align-items:center;gap:.75rem;padding:.75rem;margin-bottom:.125rem}.cortex-widget__thread-skeleton-icon{flex-shrink:0;width:2rem;height:2rem}.cortex-widget__thread-skeleton-lines{flex:1;display:flex;flex-direction:column;gap:.375rem}.cortex-widget__thread-skeleton-line{height:.875rem}@keyframes cortex-skeleton-shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.cortex-skeleton{background:linear-gradient(90deg,#e2e8f0,#f1f5f9,#e2e8f0 80%);background-size:200% 100%;animation:cortex-skeleton-shimmer 1.8s ease-in-out infinite;border-radius:6px}.cortex-widget__thread-item{width:100%;text-align:start;padding:.75rem;border-radius:.5rem;display:flex;align-items:center;gap:.75rem;transition:background-color .15s;cursor:pointer;margin-bottom:.125rem;border:0;background:transparent;color:#475569}.cortex-widget__thread-item:hover{background:#f1f5f9}.cortex-widget__thread-item:active{background:#e8ecf1}.cortex-widget__thread-item--active{background:#e2e8f0;color:#f1f5f9}.cortex-widget__thread-icon{flex-shrink:0;width:2rem;height:2rem;border-radius:.375rem;display:flex;align-items:center;justify-content:center;font-size:.6875rem;font-weight:600;background:#e2e8f0;color:#64748b}[dir=rtl] .cortex-widget__thread-icon{transform:rotate(180deg)}.cortex-widget__thread-icon--active{background:#334155;color:#cbd5e1}.cortex-widget__thread-info{min-width:0;flex:1}.cortex-widget__thread-title{font-size:.8125rem;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.25;margin:0;color:#334155}.cortex-widget__thread-title--active{color:#f1f5f9}.cortex-widget__thread-delete{flex-shrink:0;opacity:0;width:1.75rem;height:1.75rem;border-radius:.375rem;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:opacity .15s;background:transparent;border:0;color:#94a3b8}.cortex-widget__thread-item:hover>.cortex-widget__thread-delete{opacity:1}.cortex-widget__thread-delete:hover{background:#e2e8f0}.cortex-widget__thread-delete--active:hover{background:#475569}.cortex-widget__thread-arrow{flex-shrink:0;transition:color .15s;color:#cbd5e1}[dir=rtl] .cortex-widget__thread-arrow{transform:rotate(180deg)}.cortex-widget__thread-item:hover>.cortex-widget__thread-arrow{color:#94a3b8}.cortex-widget__thread-arrow--active{color:#64748b}.cortex-widget__threads-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:4rem 0;text-align:center}.cortex-widget__threads-empty-icon{width:2.5rem;height:2.5rem;border-radius:.5rem;background:#e2e8f0;display:flex;align-items:center;justify-content:center;margin-bottom:.75rem}.cortex-widget__threads-empty-svg{color:#94a3b8}.cortex-widget__threads-empty-title{font-size:.8125rem;color:#94a3b8;font-weight:500;margin:0}.cortex-widget__threads-empty-subtitle{font-size:.6875rem;color:#a5afbd;margin-top:.25rem;margin-bottom:0}.cortex-widget__chat-header{flex-shrink:0;height:3rem;padding:0 1rem;display:flex;align-items:center;gap:.75rem;border-bottom:1px solid #e2e8f0;background:#fff}.cortex-widget__back-btn{flex-shrink:0;width:2rem;height:2rem;border-radius:.375rem;display:flex;align-items:center;justify-content:center;color:#94a3b8;background:transparent;border:0;cursor:pointer;transition:color .15s,background-color .15s}.cortex-widget__back-btn:hover{color:#475569;background:#f1f5f9}[dir=rtl] .cortex-widget__back-icon{transform:rotate(180deg)}.cortex-widget__chat-title-wrap{min-width:0;flex:1}.cortex-widget__chat-title{font-size:.8125rem;font-weight:500;color:#334155;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin:0}.cortex-widget__debug-btn{flex-shrink:0;font-size:.6875rem;font-weight:500;padding:.25rem .625rem;border-radius:.375rem;border:0;cursor:pointer;transition:background-color .15s}.cortex-widget__debug-btn--on{background:#fef3c7;color:#d97706}.cortex-widget__debug-btn--on:hover{background:#fde68a}.cortex-widget__debug-btn--off{background:#f1f5f9;color:#94a3b8}.cortex-widget__debug-btn--off:hover{background:#e2e8f0}.cortex-widget__messages-skeleton{flex:1;overflow:hidden;padding:1rem;display:flex;flex-direction:column;gap:.5rem}.cortex-widget__msg-skel{width:100%;display:flex;flex-direction:column}.cortex-widget__msg-skel--user{align-items:flex-start}.cortex-widget__msg-skel--assistant{align-items:flex-end}.cortex-widget__msg-skel-bubble{max-width:80%;padding:1rem;border-radius:1rem;border:1px solid #e2e8f0;display:flex;flex-direction:column;gap:.5rem}.cortex-widget__msg-skel-bubble--user{background:#f8fafc;border-bottom-left-radius:0}.cortex-widget__msg-skel-bubble--assistant{background:#f1f5f9;border-bottom-right-radius:0}.cortex-widget__msg-skel-line{height:.75rem}.cortex-widget__messages{flex:1;overflow:hidden}.cortex-widget__working{flex-shrink:0;padding:.375rem 1rem;display:flex;align-items:center;gap:.375rem}.cortex-widget__working-dots{display:flex;align-items:center;gap:3px}@keyframes cortex-pulse{0%,to{opacity:.4}50%{opacity:1}}@keyframes cortex-dot-bounce{0%,80%,to{transform:translateY(0)}40%{transform:translateY(-3px)}}.cortex-working-dot{display:block;width:5px;height:5px;border-radius:9999px;background:#94a3b8;animation:cortex-pulse 1.6s ease-in-out infinite,cortex-dot-bounce 1.2s ease-in-out infinite}.cortex-working-dot:nth-child(2){animation-delay:.15s}.cortex-working-dot:nth-child(3){animation-delay:.3s}.cortex-widget__working-text{font-size:.6875rem;color:#94a3b8;font-weight:500}.cortex-widget__input-area{flex-shrink:0;padding:.75rem;background:#fff;border-top:1px solid #e2e8f0}.cortex-widget__input-box{display:flex;align-items:flex-end;gap:.5rem;border-radius:.75rem;border:1px solid #e2e8f0;padding:.375rem;transition:border-color .15s,background-color .15s}.cortex-widget__input-box--disabled{background:#f1f5f9}.cortex-widget__input-box--enabled{background:#f8fafc}.cortex-widget__input-box--enabled:focus-within{border-color:#cbd5e1;background:#fff}.cortex-widget__textarea{flex:1;background:transparent;resize:none;border:0;outline:none;font-size:.8125rem;padding:.375rem .5rem;min-height:28px;max-height:120px;line-height:1.375;color:#334155}.cortex-widget__textarea::placeholder{color:#a5afbd}.cortex-widget__textarea--disabled{color:#94a3b8;cursor:not-allowed}.cortex-widget__textarea:disabled{cursor:not-allowed;border:0}@keyframes cortex-stop-pulse-ring{0%{opacity:.45;transform:scale(1)}50%{opacity:.15;transform:scale(1.12)}to{opacity:.45;transform:scale(1)}}@keyframes cortex-stop-fade-in{0%{opacity:0;transform:scale(.7)}to{opacity:1;transform:scale(1)}}.cortex-stop-btn{flex-shrink:0;width:2rem;height:2rem;border-radius:.5rem;display:flex;align-items:center;justify-content:center;cursor:pointer;position:relative;background:#e11d48;color:#fff;border:0;animation:cortex-stop-fade-in .2s ease-out both;transition:background .15s ease}.cortex-stop-btn:hover{background:#be123c}.cortex-stop-btn:active{transform:scale(.92)}.cortex-stop-btn__ring{position:absolute;inset:0;border-radius:.5rem;background:#e11d48;animation:cortex-stop-pulse-ring 2s ease-in-out infinite;pointer-events:none}.cortex-stop-btn__icon{position:relative;z-index:1}.cortex-widget__send-btn{flex-shrink:0;width:2rem;height:2rem;border-radius:.5rem;display:flex;align-items:center;justify-content:center;border:0;transition:background-color .15s}.cortex-widget__send-btn--empty{background:#e2e8f0;color:#94a3b8}.cortex-widget__send-btn--ready{background:#1e293b;color:#fff;cursor:pointer}.cortex-widget__send-btn--ready:hover{background:#334155}[dir=rtl] .cortex-widget__send-icon{transform:rotate(180deg)}\n"], dependencies: [{ kind: "component", type: MessageListComponent, selector: "cortex-message-list", inputs: ["debugMode"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1206
1212
  }
1207
1213
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.3", ngImport: i0, type: CortexChatWidgetComponent, decorators: [{
1208
1214
  type: Component,
1209
- args: [{ selector: 'cortex-chat-widget', imports: [NgClass, MessageListComponent, FormsModule, TranslatePipe], providers: [
1215
+ args: [{ selector: 'cortex-chat-widget', imports: [MessageListComponent, FormsModule, TranslatePipe], providers: [
1210
1216
  CortexClientConfigRef,
1211
1217
  {
1212
1218
  provide: CORTEX_CLIENT_CONFIG,
@@ -1218,14 +1224,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.3", ngImpor
1218
1224
  fallbackLang: 'en',
1219
1225
  loader: provideTranslateLoader(CortexClientTranslateLoader),
1220
1226
  }),
1221
- ], changeDetection: ChangeDetectionStrategy.OnPush, host: {
1222
- class: 'block',
1223
- '[style.--cortex-primary-color]': 'theme()?.primaryColor',
1224
- '[style.--cortex-bg-color]': 'theme()?.backgroundColor',
1225
- '[style.--cortex-surface-color]': 'theme()?.surfaceColor',
1226
- '[style.--cortex-text-color]': 'theme()?.textColor',
1227
- '[style.--cortex-border-radius]': 'theme()?.borderRadius',
1228
- }, template: "<div class=\"w-full h-full relative overflow-hidden bg-slate-5\">\n <!-- \u2550\u2550\u2550 SCREEN 1: THREADS \u2550\u2550\u2550 -->\n <div\n class=\"absolute inset-0 flex flex-col transition-transform duration-300 ease-out\"\n [ngClass]=\"\n screen() === 'threads' ? 'translate-x-0' : 'ltr:-translate-x-full rtl:translate-x-full'\n \"\n >\n <!-- Threads header -->\n <div class=\"shrink-0 px-5 pt-5 pb-4 flex items-center justify-between\">\n <div>\n <h2 class=\"text-[15px] font-semibold text-slate-800 tracking-tight\">\n {{ 'translate_threads' | translate }}\n </h2>\n <p class=\"text-[11px] text-slate-400 mt-0.5\">\n {{\n 'translate_n_conversations' | translate: { count: chatService.threads()?.length ?? 0 }\n }}\n </p>\n </div>\n <button\n (click)=\"newChat()\"\n class=\"inline-flex items-center gap-1.5 text-[12px] font-medium text-slate-100 bg-slate-800 px-3 py-1.5 rounded-md hover:bg-slate-700 active:bg-slate-900 transition-colors cursor-pointer\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path\n d=\"M8 3v10M3 8h10\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n />\n </svg>\n {{ 'translate_new' | translate }}\n </button>\n </div>\n\n <!-- Threads list -->\n <div class=\"flex-1 overflow-y-auto px-3 pb-3\">\n <!-- Threads loading skeleton -->\n @if (chatService.threads() === undefined) {\n @for (i of [1, 2, 3, 4]; track i) {\n <div class=\"flex items-center gap-3 px-3 py-3 mb-0.5\">\n <div class=\"skeleton shrink-0 size-8\"></div>\n <div class=\"flex-1 flex flex-col gap-1.5\">\n <div class=\"skeleton h-3.5\" [style.width]=\"40 + i * 12 + '%'\"></div>\n </div>\n </div>\n }\n } @else {\n @for (thread of chatService.threads(); track thread.id) {\n <button\n (click)=\"selectThread(thread)\"\n class=\"group w-full text-start px-3 py-3 rounded-lg flex items-center gap-3 transition-colors cursor-pointer mb-0.5\"\n [ngClass]=\"{\n 'bg-slate-200 text-slate-100': thread.id === chatService.selectedThread()?.id,\n 'text-slate-600 hover:bg-slate-100 active:bg-slate-150':\n thread.id !== chatService.selectedThread()?.id,\n }\"\n >\n <!-- Thread icon -->\n <div\n class=\"shrink-0 size-8 rounded-md flex items-center justify-center text-[11px] font-semibold rtl:transform rtl:rotate-180\"\n [ngClass]=\"{\n 'bg-slate-700 text-slate-300': thread.id === chatService.selectedThread()?.id,\n 'bg-slate-200 text-slate-500': thread.id !== chatService.selectedThread()?.id,\n }\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path\n d=\"M2.5 4.5h11M2.5 8h7M2.5 11.5h9\"\n stroke=\"currentColor\"\n stroke-width=\"1.3\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n\n <!-- Thread info -->\n <div class=\"min-w-0 flex-1\">\n <p\n class=\"text-[13px] font-medium truncate leading-tight\"\n [ngClass]=\"{\n 'text-slate-100': thread.id === chatService.selectedThread()?.id,\n 'text-slate-700': thread.id !== chatService.selectedThread()?.id,\n }\"\n >\n {{ thread.title ?? ('translate_untitled' | translate) }}\n </p>\n </div>\n\n <!-- Delete button -->\n <button\n (click)=\"$event.stopPropagation(); chatService.deleteThread(thread.id).subscribe()\"\n class=\"shrink-0 opacity-0 group-hover:opacity-100 transition-opacity size-7 rounded-md flex items-center justify-center cursor-pointer\"\n [ngClass]=\"{\n 'hover:bg-slate-600 text-slate-400': thread.id === chatService.selectedThread()?.id,\n 'hover:bg-slate-200 text-slate-400': thread.id !== chatService.selectedThread()?.id,\n }\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path\n d=\"M4 4l8 8M12 4l-8 8\"\n stroke=\"currentColor\"\n stroke-width=\"1.3\"\n stroke-linecap=\"round\"\n />\n </svg>\n </button>\n\n <!-- Arrow -->\n <svg\n class=\"shrink-0 transition-colors rtl:transform rtl:rotate-180\"\n [ngClass]=\"{\n 'text-slate-500': thread.id === chatService.selectedThread()?.id,\n 'text-slate-300 group-hover:text-slate-400':\n thread.id !== chatService.selectedThread()?.id,\n }\"\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n >\n <path\n d=\"M6 4l4 4-4 4\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n }\n\n @if (!chatService.threads()?.length) {\n <div class=\"flex flex-col items-center justify-center py-16 text-center\">\n <div class=\"size-10 rounded-lg bg-slate-200 flex items-center justify-center mb-3\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 16 16\" fill=\"none\" class=\"text-slate-400\">\n <path\n d=\"M2.5 4.5h11M2.5 8h7M2.5 11.5h9\"\n stroke=\"currentColor\"\n stroke-width=\"1.3\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n <p class=\"text-[13px] text-slate-400 font-medium\">No threads yet</p>\n <p class=\"text-[11px] text-slate-350 mt-1\">Start a new conversation</p>\n </div>\n }\n }\n </div>\n </div>\n\n <!-- \u2550\u2550\u2550 SCREEN 2: CHAT \u2550\u2550\u2550 -->\n <div\n class=\"absolute inset-0 flex flex-col transition-transform duration-300 ease-out\"\n [ngClass]=\"screen() === 'chat' ? 'translate-x-0' : 'ltr:translate-x-full rtl:-translate-x-full'\"\n >\n <!-- Chat header -->\n <div class=\"shrink-0 h-12 px-4 flex items-center gap-3 border-b border-slate-200 bg-white\">\n <!-- Back button -->\n <button\n (click)=\"goBack()\"\n class=\"shrink-0 size-8 rounded-md flex items-center justify-center text-slate-400 hover:text-slate-600 hover:bg-slate-100 transition-colors cursor-pointer\"\n >\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n class=\"rtl:transform rtl:rotate-180\"\n >\n <path\n d=\"M10 3L5 8l5 5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n\n <!-- Title -->\n <div class=\"min-w-0 flex-1\">\n <p class=\"text-[13px] font-medium text-slate-700 truncate\">\n {{ chatService.selectedThread()?.title ?? ('translate_new_chat' | translate) }}\n </p>\n </div>\n\n <!-- Debug toggle -->\n <button\n (click)=\"toggleDebugMode()\"\n class=\"shrink-0 text-[11px] font-medium px-2.5 py-1 rounded-md transition-colors cursor-pointer\"\n [ngClass]=\"{\n 'bg-amber-50 text-amber-600 hover:bg-amber-100': debugMode(),\n 'bg-slate-100 text-slate-400 hover:bg-slate-200': !debugMode(),\n }\"\n >\n {{ debugMode() ? ('translate_debug' | translate) : ('translate_normal' | translate) }}\n </button>\n </div>\n\n <!-- Messages loading skeleton -->\n @if (chatService.isLoadingMessages() && !chatService.isAgentWorking()) {\n <div class=\"flex-1 overflow-hidden p-4 flex flex-col gap-2\">\n <!-- User message skeleton -->\n <div class=\"w-full flex flex-col items-start\">\n <div\n class=\"max-w-4/5 p-4 rounded-2xl rounded-bl-none border border-slate-200 bg-slate-50 flex flex-col gap-2\"\n >\n <div class=\"skeleton h-3 w-52\"></div>\n <div class=\"skeleton h-3 w-36\"></div>\n </div>\n </div>\n <!-- Assistant message skeleton -->\n <div class=\"w-full flex flex-col items-end\">\n <div\n class=\"max-w-4/5 p-4 rounded-2xl rounded-br-none border border-slate-200 bg-slate-100 flex flex-col gap-2\"\n >\n <div class=\"skeleton h-3 w-64\"></div>\n <div class=\"skeleton h-3 w-72\"></div>\n <div class=\"skeleton h-3 w-48\"></div>\n </div>\n </div>\n <!-- User message skeleton -->\n <div class=\"w-full flex flex-col items-start\">\n <div\n class=\"max-w-4/5 p-4 rounded-2xl rounded-bl-none border border-slate-200 bg-slate-50 flex flex-col gap-2\"\n >\n <div class=\"skeleton h-3 w-44\"></div>\n </div>\n </div>\n <!-- Assistant message skeleton -->\n <div class=\"w-full flex flex-col items-end\">\n <div\n class=\"max-w-4/5 p-4 rounded-2xl rounded-br-none border border-slate-200 bg-slate-100 flex flex-col gap-2\"\n >\n <div class=\"skeleton h-3 w-56\"></div>\n <div class=\"skeleton h-3 w-60\"></div>\n </div>\n </div>\n </div>\n } @else {\n <!-- Messages area -->\n <cortex-message-list class=\"flex-1 overflow-hidden\" [debugMode]=\"debugMode()\" />\n }\n\n <!-- Agent working indicator -->\n @if (chatService.isAgentWorking() && !chatService.hasPendingToolCalls()) {\n <div class=\"shrink-0 px-4 py-1.5 flex items-center gap-1.5\">\n <div class=\"flex items-center gap-[3px]\">\n <span class=\"cortex-working-dot block size-[5px] rounded-full bg-slate-400\"></span>\n <span class=\"cortex-working-dot block size-[5px] rounded-full bg-slate-400\"></span>\n <span class=\"cortex-working-dot block size-[5px] rounded-full bg-slate-400\"></span>\n </div>\n <span class=\"text-[11px] text-slate-400 font-medium\">{{\n 'translate_thinking' | translate\n }}</span>\n </div>\n }\n\n <!-- Chat input (hidden during pending tool calls) -->\n @if (!chatService.hasPendingToolCalls()) {\n <div class=\"shrink-0 p-3 bg-white border-t border-slate-200\">\n <div\n class=\"flex items-end gap-2 rounded-xl border border-slate-200 p-1.5 transition-colors\"\n [ngClass]=\"{\n 'bg-slate-100': chatService.isAgentWorking(),\n 'bg-slate-50 focus-within:border-slate-300 focus-within:bg-white':\n !chatService.isAgentWorking(),\n }\"\n >\n <textarea\n (keydown)=\"onKeydown($event)\"\n [(ngModel)]=\"text\"\n [placeholder]=\"\n chatService.isAgentWorking() ? '' : ('translate_type_a_message' | translate)\n \"\n [disabled]=\"chatService.isAgentWorking()\"\n rows=\"1\"\n class=\"flex-1 bg-transparent resize-none border-0 outline-none text-[13px] placeholder:text-slate-350 py-1.5 px-2 min-h-[28px] max-h-[120px] leading-snug disabled:cursor-not-allowed disabled:border-0\"\n [ngClass]=\"{\n 'text-slate-400': chatService.isAgentWorking(),\n 'text-slate-700': !chatService.isAgentWorking(),\n }\"\n ></textarea>\n @if (chatService.isAgentWorking()) {\n <!-- Stop button -->\n <button\n (click)=\"chatService.abort()\"\n class=\"stop-btn shrink-0 size-8 rounded-lg flex items-center justify-center cursor-pointer relative\"\n >\n <!-- Pulsing ring -->\n <span class=\"stop-btn-ring absolute inset-0 rounded-lg\"></span>\n <!-- Stop icon (rounded square) -->\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\" class=\"relative z-10\">\n <rect x=\"1\" y=\"1\" width=\"10\" height=\"10\" rx=\"2.5\" fill=\"currentColor\" />\n </svg>\n </button>\n } @else {\n <!-- Send button -->\n <button\n (click)=\"send()\"\n class=\"shrink-0 size-8 rounded-lg flex items-center justify-center transition-colors\"\n [ngClass]=\"{\n 'bg-slate-200 text-slate-400': !text().trim(),\n 'bg-slate-800 text-white hover:bg-slate-700 cursor-pointer': text().trim(),\n }\"\n >\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n class=\"rtl:transform rtl:rotate-180\"\n >\n <path\n d=\"M3 8h10M9 4l4 4-4 4\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n }\n </div>\n </div>\n }\n </div>\n</div>\n", styles: ["@charset \"UTF-8\";@keyframes skeleton-shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.skeleton{background:linear-gradient(90deg,#e2e8f0,#f1f5f9,#e2e8f0 80%);background-size:200% 100%;animation:skeleton-shimmer 1.8s ease-in-out infinite;border-radius:6px}@keyframes cortex-pulse{0%,to{opacity:.4}50%{opacity:1}}@keyframes cortex-dot-bounce{0%,80%,to{transform:translateY(0)}40%{transform:translateY(-3px)}}.cortex-working-dot{animation:cortex-pulse 1.6s ease-in-out infinite,cortex-dot-bounce 1.2s ease-in-out infinite}.cortex-working-dot:nth-child(2){animation-delay:.15s}.cortex-working-dot:nth-child(3){animation-delay:.3s}@keyframes stop-pulse-ring{0%{opacity:.45;transform:scale(1)}50%{opacity:.15;transform:scale(1.12)}to{opacity:.45;transform:scale(1)}}@keyframes stop-fade-in{0%{opacity:0;transform:scale(.7)}to{opacity:1;transform:scale(1)}}.stop-btn{background:#e11d48;color:#fff;animation:stop-fade-in .2s ease-out both;transition:background .15s ease}.stop-btn:hover{background:#be123c}.stop-btn:active{transform:scale(.92)}.stop-btn-ring{background:#e11d48;animation:stop-pulse-ring 2s ease-in-out infinite;pointer-events:none}\n"] }]
1227
+ ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: { class: 'cortex-widget' }, template: "<div class=\"cortex-widget__container\">\n <!-- SCREEN 1: THREADS -->\n <div\n class=\"cortex-widget__screen\"\n [class.cortex-widget__screen--active]=\"screen() === 'threads'\"\n [class.cortex-widget__screen--left]=\"screen() !== 'threads'\"\n >\n <!-- Threads header -->\n <div class=\"cortex-widget__threads-header\">\n <div>\n <h2 class=\"cortex-widget__threads-title\">\n {{ 'translate_threads' | translate }}\n </h2>\n <p class=\"cortex-widget__threads-count\">\n {{\n 'translate_n_conversations' | translate: { count: chatService.threads()?.length ?? 0 }\n }}\n </p>\n </div>\n <button (click)=\"newChat()\" class=\"cortex-widget__new-chat-btn\">\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path\n d=\"M8 3v10M3 8h10\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n />\n </svg>\n {{ 'translate_new' | translate }}\n </button>\n </div>\n\n <!-- Threads list -->\n <div class=\"cortex-widget__threads-list\">\n <!-- Threads loading skeleton -->\n @if (chatService.threads() === undefined) {\n @for (i of [1, 2, 3, 4]; track i) {\n <div class=\"cortex-widget__thread-skeleton\">\n <div class=\"cortex-skeleton cortex-widget__thread-skeleton-icon\"></div>\n <div class=\"cortex-widget__thread-skeleton-lines\">\n <div\n class=\"cortex-skeleton cortex-widget__thread-skeleton-line\"\n [style.width]=\"40 + i * 12 + '%'\"\n ></div>\n </div>\n </div>\n }\n } @else {\n @for (thread of chatService.threads(); track thread.id) {\n <button\n (click)=\"selectThread(thread)\"\n class=\"cortex-widget__thread-item\"\n [class.cortex-widget__thread-item--active]=\"\n thread.id === chatService.selectedThread()?.id\n \"\n >\n <!-- Thread icon -->\n <div\n class=\"cortex-widget__thread-icon\"\n [class.cortex-widget__thread-icon--active]=\"\n thread.id === chatService.selectedThread()?.id\n \"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path\n d=\"M2.5 4.5h11M2.5 8h7M2.5 11.5h9\"\n stroke=\"currentColor\"\n stroke-width=\"1.3\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n\n <!-- Thread info -->\n <div class=\"cortex-widget__thread-info\">\n <p\n class=\"cortex-widget__thread-title\"\n [class.cortex-widget__thread-title--active]=\"\n thread.id === chatService.selectedThread()?.id\n \"\n >\n {{ thread.title ?? ('translate_untitled' | translate) }}\n </p>\n </div>\n\n <!-- Delete button -->\n <button\n (click)=\"$event.stopPropagation(); chatService.deleteThread(thread.id).subscribe()\"\n class=\"cortex-widget__thread-delete\"\n [class.cortex-widget__thread-delete--active]=\"\n thread.id === chatService.selectedThread()?.id\n \"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path\n d=\"M4 4l8 8M12 4l-8 8\"\n stroke=\"currentColor\"\n stroke-width=\"1.3\"\n stroke-linecap=\"round\"\n />\n </svg>\n </button>\n\n <!-- Arrow -->\n <svg\n class=\"cortex-widget__thread-arrow\"\n [class.cortex-widget__thread-arrow--active]=\"\n thread.id === chatService.selectedThread()?.id\n \"\n width=\"10\"\n height=\"10\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n >\n <path\n d=\"M6 4l4 4-4 4\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n }\n\n @if (!chatService.threads()?.length) {\n <div class=\"cortex-widget__threads-empty\">\n <div class=\"cortex-widget__threads-empty-icon\">\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n class=\"cortex-widget__threads-empty-svg\"\n >\n <path\n d=\"M2.5 4.5h11M2.5 8h7M2.5 11.5h9\"\n stroke=\"currentColor\"\n stroke-width=\"1.3\"\n stroke-linecap=\"round\"\n />\n </svg>\n </div>\n <p class=\"cortex-widget__threads-empty-title\">No threads yet</p>\n <p class=\"cortex-widget__threads-empty-subtitle\">Start a new conversation</p>\n </div>\n }\n }\n </div>\n </div>\n\n <!-- SCREEN 2: CHAT -->\n <div\n class=\"cortex-widget__screen\"\n [class.cortex-widget__screen--active]=\"screen() === 'chat'\"\n [class.cortex-widget__screen--right]=\"screen() !== 'chat'\"\n >\n <!-- Chat header -->\n <div class=\"cortex-widget__chat-header\">\n <!-- Back button -->\n <button (click)=\"goBack()\" class=\"cortex-widget__back-btn\">\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n class=\"cortex-widget__back-icon\"\n >\n <path\n d=\"M10 3L5 8l5 5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n\n <!-- Title -->\n <div class=\"cortex-widget__chat-title-wrap\">\n <p class=\"cortex-widget__chat-title\">\n {{ chatService.selectedThread()?.title ?? ('translate_new_chat' | translate) }}\n </p>\n </div>\n\n <!-- Debug toggle -->\n <button\n (click)=\"toggleDebugMode()\"\n class=\"cortex-widget__debug-btn\"\n [class.cortex-widget__debug-btn--on]=\"debugMode()\"\n [class.cortex-widget__debug-btn--off]=\"!debugMode()\"\n >\n {{ debugMode() ? ('translate_debug' | translate) : ('translate_normal' | translate) }}\n </button>\n </div>\n\n <!-- Messages loading skeleton -->\n @if (chatService.isLoadingMessages() && !chatService.isAgentWorking()) {\n <div class=\"cortex-widget__messages-skeleton\">\n <!-- User message skeleton -->\n <div class=\"cortex-widget__msg-skel cortex-widget__msg-skel--user\">\n <div class=\"cortex-widget__msg-skel-bubble cortex-widget__msg-skel-bubble--user\">\n <div class=\"cortex-skeleton cortex-widget__msg-skel-line\" style=\"width: 13rem\"></div>\n <div class=\"cortex-skeleton cortex-widget__msg-skel-line\" style=\"width: 9rem\"></div>\n </div>\n </div>\n <!-- Assistant message skeleton -->\n <div class=\"cortex-widget__msg-skel cortex-widget__msg-skel--assistant\">\n <div class=\"cortex-widget__msg-skel-bubble cortex-widget__msg-skel-bubble--assistant\">\n <div class=\"cortex-skeleton cortex-widget__msg-skel-line\" style=\"width: 16rem\"></div>\n <div class=\"cortex-skeleton cortex-widget__msg-skel-line\" style=\"width: 18rem\"></div>\n <div class=\"cortex-skeleton cortex-widget__msg-skel-line\" style=\"width: 12rem\"></div>\n </div>\n </div>\n <!-- User message skeleton -->\n <div class=\"cortex-widget__msg-skel cortex-widget__msg-skel--user\">\n <div class=\"cortex-widget__msg-skel-bubble cortex-widget__msg-skel-bubble--user\">\n <div class=\"cortex-skeleton cortex-widget__msg-skel-line\" style=\"width: 11rem\"></div>\n </div>\n </div>\n <!-- Assistant message skeleton -->\n <div class=\"cortex-widget__msg-skel cortex-widget__msg-skel--assistant\">\n <div class=\"cortex-widget__msg-skel-bubble cortex-widget__msg-skel-bubble--assistant\">\n <div class=\"cortex-skeleton cortex-widget__msg-skel-line\" style=\"width: 14rem\"></div>\n <div class=\"cortex-skeleton cortex-widget__msg-skel-line\" style=\"width: 15rem\"></div>\n </div>\n </div>\n </div>\n } @else {\n <!-- Messages area -->\n <cortex-message-list class=\"cortex-widget__messages\" [debugMode]=\"debugMode()\" />\n }\n\n <!-- Agent working indicator -->\n @if (chatService.isAgentWorking() && !chatService.hasPendingToolCalls()) {\n <div class=\"cortex-widget__working\">\n <div class=\"cortex-widget__working-dots\">\n <span class=\"cortex-working-dot\"></span>\n <span class=\"cortex-working-dot\"></span>\n <span class=\"cortex-working-dot\"></span>\n </div>\n <span class=\"cortex-widget__working-text\">{{ 'translate_thinking' | translate }}</span>\n </div>\n }\n\n <!-- Chat input (hidden during pending tool calls) -->\n @if (!chatService.hasPendingToolCalls()) {\n <div class=\"cortex-widget__input-area\">\n <div\n class=\"cortex-widget__input-box\"\n [class.cortex-widget__input-box--disabled]=\"chatService.isAgentWorking()\"\n [class.cortex-widget__input-box--enabled]=\"!chatService.isAgentWorking()\"\n >\n <textarea\n (keydown)=\"onKeydown($event)\"\n [(ngModel)]=\"text\"\n [placeholder]=\"\n chatService.isAgentWorking() ? '' : ('translate_type_a_message' | translate)\n \"\n [disabled]=\"chatService.isAgentWorking()\"\n rows=\"1\"\n class=\"cortex-widget__textarea\"\n [class.cortex-widget__textarea--disabled]=\"chatService.isAgentWorking()\"\n ></textarea>\n @if (chatService.isAgentWorking()) {\n <!-- Stop button -->\n <button (click)=\"chatService.abort()\" class=\"cortex-stop-btn\">\n <!-- Pulsing ring -->\n <span class=\"cortex-stop-btn__ring\"></span>\n <!-- Stop icon (rounded square) -->\n <svg\n width=\"12\"\n height=\"12\"\n viewBox=\"0 0 12 12\"\n fill=\"none\"\n class=\"cortex-stop-btn__icon\"\n >\n <rect x=\"1\" y=\"1\" width=\"10\" height=\"10\" rx=\"2.5\" fill=\"currentColor\" />\n </svg>\n </button>\n } @else {\n <!-- Send button -->\n <button\n (click)=\"send()\"\n class=\"cortex-widget__send-btn\"\n [class.cortex-widget__send-btn--empty]=\"!text().trim()\"\n [class.cortex-widget__send-btn--ready]=\"!!text().trim()\"\n >\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n class=\"cortex-widget__send-icon\"\n >\n <path\n d=\"M3 8h10M9 4l4 4-4 4\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n }\n </div>\n </div>\n }\n </div>\n</div>\n", styles: [".cortex-widget{display:block}.cortex-widget__container{width:100%;height:100%;position:relative;overflow:hidden}.cortex-widget__screen{position:absolute;inset:0;display:flex;flex-direction:column;transition:transform .3s ease-out}.cortex-widget__screen--active{transform:translate(0)}.cortex-widget__screen--left{transform:translate(-100%)}[dir=rtl] .cortex-widget__screen--left,.cortex-widget__screen--right{transform:translate(100%)}[dir=rtl] .cortex-widget__screen--right{transform:translate(-100%)}.cortex-widget__threads-header{flex-shrink:0;padding:1.25rem 1.25rem 1rem;display:flex;align-items:center;justify-content:space-between}.cortex-widget__threads-title{font-size:.9375rem;font-weight:600;color:#1e293b;letter-spacing:-.01em;margin:0}.cortex-widget__threads-count{font-size:.6875rem;color:#94a3b8;margin-top:.125rem;margin-bottom:0}.cortex-widget__new-chat-btn{display:inline-flex;align-items:center;gap:.375rem;font-size:.75rem;font-weight:500;color:#f1f5f9;background:#1e293b;padding:.375rem .75rem;border-radius:.375rem;border:0;cursor:pointer;transition:background-color .15s}.cortex-widget__new-chat-btn:hover{background:#334155}.cortex-widget__new-chat-btn:active{background:#0f172a}.cortex-widget__threads-list{flex:1;overflow-y:auto;padding:0 .75rem .75rem}.cortex-widget__thread-skeleton{display:flex;align-items:center;gap:.75rem;padding:.75rem;margin-bottom:.125rem}.cortex-widget__thread-skeleton-icon{flex-shrink:0;width:2rem;height:2rem}.cortex-widget__thread-skeleton-lines{flex:1;display:flex;flex-direction:column;gap:.375rem}.cortex-widget__thread-skeleton-line{height:.875rem}@keyframes cortex-skeleton-shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.cortex-skeleton{background:linear-gradient(90deg,#e2e8f0,#f1f5f9,#e2e8f0 80%);background-size:200% 100%;animation:cortex-skeleton-shimmer 1.8s ease-in-out infinite;border-radius:6px}.cortex-widget__thread-item{width:100%;text-align:start;padding:.75rem;border-radius:.5rem;display:flex;align-items:center;gap:.75rem;transition:background-color .15s;cursor:pointer;margin-bottom:.125rem;border:0;background:transparent;color:#475569}.cortex-widget__thread-item:hover{background:#f1f5f9}.cortex-widget__thread-item:active{background:#e8ecf1}.cortex-widget__thread-item--active{background:#e2e8f0;color:#f1f5f9}.cortex-widget__thread-icon{flex-shrink:0;width:2rem;height:2rem;border-radius:.375rem;display:flex;align-items:center;justify-content:center;font-size:.6875rem;font-weight:600;background:#e2e8f0;color:#64748b}[dir=rtl] .cortex-widget__thread-icon{transform:rotate(180deg)}.cortex-widget__thread-icon--active{background:#334155;color:#cbd5e1}.cortex-widget__thread-info{min-width:0;flex:1}.cortex-widget__thread-title{font-size:.8125rem;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.25;margin:0;color:#334155}.cortex-widget__thread-title--active{color:#f1f5f9}.cortex-widget__thread-delete{flex-shrink:0;opacity:0;width:1.75rem;height:1.75rem;border-radius:.375rem;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:opacity .15s;background:transparent;border:0;color:#94a3b8}.cortex-widget__thread-item:hover>.cortex-widget__thread-delete{opacity:1}.cortex-widget__thread-delete:hover{background:#e2e8f0}.cortex-widget__thread-delete--active:hover{background:#475569}.cortex-widget__thread-arrow{flex-shrink:0;transition:color .15s;color:#cbd5e1}[dir=rtl] .cortex-widget__thread-arrow{transform:rotate(180deg)}.cortex-widget__thread-item:hover>.cortex-widget__thread-arrow{color:#94a3b8}.cortex-widget__thread-arrow--active{color:#64748b}.cortex-widget__threads-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:4rem 0;text-align:center}.cortex-widget__threads-empty-icon{width:2.5rem;height:2.5rem;border-radius:.5rem;background:#e2e8f0;display:flex;align-items:center;justify-content:center;margin-bottom:.75rem}.cortex-widget__threads-empty-svg{color:#94a3b8}.cortex-widget__threads-empty-title{font-size:.8125rem;color:#94a3b8;font-weight:500;margin:0}.cortex-widget__threads-empty-subtitle{font-size:.6875rem;color:#a5afbd;margin-top:.25rem;margin-bottom:0}.cortex-widget__chat-header{flex-shrink:0;height:3rem;padding:0 1rem;display:flex;align-items:center;gap:.75rem;border-bottom:1px solid #e2e8f0;background:#fff}.cortex-widget__back-btn{flex-shrink:0;width:2rem;height:2rem;border-radius:.375rem;display:flex;align-items:center;justify-content:center;color:#94a3b8;background:transparent;border:0;cursor:pointer;transition:color .15s,background-color .15s}.cortex-widget__back-btn:hover{color:#475569;background:#f1f5f9}[dir=rtl] .cortex-widget__back-icon{transform:rotate(180deg)}.cortex-widget__chat-title-wrap{min-width:0;flex:1}.cortex-widget__chat-title{font-size:.8125rem;font-weight:500;color:#334155;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin:0}.cortex-widget__debug-btn{flex-shrink:0;font-size:.6875rem;font-weight:500;padding:.25rem .625rem;border-radius:.375rem;border:0;cursor:pointer;transition:background-color .15s}.cortex-widget__debug-btn--on{background:#fef3c7;color:#d97706}.cortex-widget__debug-btn--on:hover{background:#fde68a}.cortex-widget__debug-btn--off{background:#f1f5f9;color:#94a3b8}.cortex-widget__debug-btn--off:hover{background:#e2e8f0}.cortex-widget__messages-skeleton{flex:1;overflow:hidden;padding:1rem;display:flex;flex-direction:column;gap:.5rem}.cortex-widget__msg-skel{width:100%;display:flex;flex-direction:column}.cortex-widget__msg-skel--user{align-items:flex-start}.cortex-widget__msg-skel--assistant{align-items:flex-end}.cortex-widget__msg-skel-bubble{max-width:80%;padding:1rem;border-radius:1rem;border:1px solid #e2e8f0;display:flex;flex-direction:column;gap:.5rem}.cortex-widget__msg-skel-bubble--user{background:#f8fafc;border-bottom-left-radius:0}.cortex-widget__msg-skel-bubble--assistant{background:#f1f5f9;border-bottom-right-radius:0}.cortex-widget__msg-skel-line{height:.75rem}.cortex-widget__messages{flex:1;overflow:hidden}.cortex-widget__working{flex-shrink:0;padding:.375rem 1rem;display:flex;align-items:center;gap:.375rem}.cortex-widget__working-dots{display:flex;align-items:center;gap:3px}@keyframes cortex-pulse{0%,to{opacity:.4}50%{opacity:1}}@keyframes cortex-dot-bounce{0%,80%,to{transform:translateY(0)}40%{transform:translateY(-3px)}}.cortex-working-dot{display:block;width:5px;height:5px;border-radius:9999px;background:#94a3b8;animation:cortex-pulse 1.6s ease-in-out infinite,cortex-dot-bounce 1.2s ease-in-out infinite}.cortex-working-dot:nth-child(2){animation-delay:.15s}.cortex-working-dot:nth-child(3){animation-delay:.3s}.cortex-widget__working-text{font-size:.6875rem;color:#94a3b8;font-weight:500}.cortex-widget__input-area{flex-shrink:0;padding:.75rem;background:#fff;border-top:1px solid #e2e8f0}.cortex-widget__input-box{display:flex;align-items:flex-end;gap:.5rem;border-radius:.75rem;border:1px solid #e2e8f0;padding:.375rem;transition:border-color .15s,background-color .15s}.cortex-widget__input-box--disabled{background:#f1f5f9}.cortex-widget__input-box--enabled{background:#f8fafc}.cortex-widget__input-box--enabled:focus-within{border-color:#cbd5e1;background:#fff}.cortex-widget__textarea{flex:1;background:transparent;resize:none;border:0;outline:none;font-size:.8125rem;padding:.375rem .5rem;min-height:28px;max-height:120px;line-height:1.375;color:#334155}.cortex-widget__textarea::placeholder{color:#a5afbd}.cortex-widget__textarea--disabled{color:#94a3b8;cursor:not-allowed}.cortex-widget__textarea:disabled{cursor:not-allowed;border:0}@keyframes cortex-stop-pulse-ring{0%{opacity:.45;transform:scale(1)}50%{opacity:.15;transform:scale(1.12)}to{opacity:.45;transform:scale(1)}}@keyframes cortex-stop-fade-in{0%{opacity:0;transform:scale(.7)}to{opacity:1;transform:scale(1)}}.cortex-stop-btn{flex-shrink:0;width:2rem;height:2rem;border-radius:.5rem;display:flex;align-items:center;justify-content:center;cursor:pointer;position:relative;background:#e11d48;color:#fff;border:0;animation:cortex-stop-fade-in .2s ease-out both;transition:background .15s ease}.cortex-stop-btn:hover{background:#be123c}.cortex-stop-btn:active{transform:scale(.92)}.cortex-stop-btn__ring{position:absolute;inset:0;border-radius:.5rem;background:#e11d48;animation:cortex-stop-pulse-ring 2s ease-in-out infinite;pointer-events:none}.cortex-stop-btn__icon{position:relative;z-index:1}.cortex-widget__send-btn{flex-shrink:0;width:2rem;height:2rem;border-radius:.5rem;display:flex;align-items:center;justify-content:center;border:0;transition:background-color .15s}.cortex-widget__send-btn--empty{background:#e2e8f0;color:#94a3b8}.cortex-widget__send-btn--ready{background:#1e293b;color:#fff;cursor:pointer}.cortex-widget__send-btn--ready:hover{background:#334155}[dir=rtl] .cortex-widget__send-icon{transform:rotate(180deg)}\n"] }]
1229
1228
  }], propDecorators: { config: [{ type: i0.Input, args: [{ isSignal: true, alias: "config", required: true }] }], text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: false }] }, { type: i0.Output, args: ["textChange"] }] } });
1230
1229
 
1231
1230
  class PrettyJsonPipe {