@m6d/cortex-client 1.0.2 → 1.1.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.
@@ -53,10 +53,10 @@ class CortexClientWebSocketService {
53
53
  get events() {
54
54
  return this.events$;
55
55
  }
56
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CortexClientWebSocketService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
57
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CortexClientWebSocketService });
56
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: CortexClientWebSocketService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
57
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: CortexClientWebSocketService });
58
58
  }
59
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CortexClientWebSocketService, decorators: [{
59
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: CortexClientWebSocketService, decorators: [{
60
60
  type: Injectable
61
61
  }], ctorParameters: () => [] });
62
62
  function deriveWsUrl(baseUrl) {
@@ -76,12 +76,12 @@ function appendTokenToUrl(wsUrl, headers) {
76
76
  }
77
77
 
78
78
  class CortexChatService {
79
- threads = signal(undefined, ...(ngDevMode ? [{ debugName: "threads" }] : []));
80
- selectedThread = signal(undefined, ...(ngDevMode ? [{ debugName: "selectedThread" }] : []));
81
- chat = signal(undefined, ...(ngDevMode ? [{ debugName: "chat" }] : []));
82
- isLoadingMessages = signal(false, ...(ngDevMode ? [{ debugName: "isLoadingMessages" }] : []));
83
- isAgentWorking = signal(false, ...(ngDevMode ? [{ debugName: "isAgentWorking" }] : []));
84
- hasPendingToolCalls = signal(false, ...(ngDevMode ? [{ debugName: "hasPendingToolCalls" }] : []));
79
+ threads = signal(undefined, ...(ngDevMode ? [{ debugName: "threads" }] : /* istanbul ignore next */ []));
80
+ selectedThread = signal(undefined, ...(ngDevMode ? [{ debugName: "selectedThread" }] : /* istanbul ignore next */ []));
81
+ chat = signal(undefined, ...(ngDevMode ? [{ debugName: "chat" }] : /* istanbul ignore next */ []));
82
+ isLoadingMessages = signal(false, ...(ngDevMode ? [{ debugName: "isLoadingMessages" }] : /* istanbul ignore next */ []));
83
+ isAgentWorking = signal(false, ...(ngDevMode ? [{ debugName: "isAgentWorking" }] : /* istanbul ignore next */ []));
84
+ hasPendingToolCalls = signal(false, ...(ngDevMode ? [{ debugName: "hasPendingToolCalls" }] : /* istanbul ignore next */ []));
85
85
  events = new Subject();
86
86
  config = inject(CORTEX_CLIENT_CONFIG);
87
87
  wsService = inject(CortexClientWebSocketService);
@@ -267,10 +267,10 @@ class CortexChatService {
267
267
  const text = await res.text();
268
268
  return text ? JSON.parse(text) : undefined;
269
269
  }
270
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CortexChatService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
271
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CortexChatService });
270
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: CortexChatService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
271
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: CortexChatService });
272
272
  }
273
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CortexChatService, decorators: [{
273
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: CortexChatService, decorators: [{
274
274
  type: Injectable
275
275
  }], ctorParameters: () => [] });
276
276
  /**
@@ -310,10 +310,10 @@ class MarkedPipe {
310
310
  .replace(/<(t[hd])([\s>])/g, '<$1 style="padding:0.5rem 1rem"$2');
311
311
  return this.sanitizer.bypassSecurityTrustHtml(DOMPurify.sanitize(html));
312
312
  }
313
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: MarkedPipe, deps: [{ token: i1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Pipe });
314
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.0", ngImport: i0, type: MarkedPipe, isStandalone: true, name: "marked" });
313
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MarkedPipe, deps: [{ token: i1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Pipe });
314
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.2", ngImport: i0, type: MarkedPipe, isStandalone: true, name: "marked" });
315
315
  }
316
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: MarkedPipe, decorators: [{
316
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MarkedPipe, decorators: [{
317
317
  type: Pipe,
318
318
  args: [{
319
319
  name: 'marked',
@@ -322,22 +322,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
322
322
  }], ctorParameters: () => [{ type: i1.DomSanitizer }] });
323
323
 
324
324
  class MessageTextPartComponent {
325
- role = input.required(...(ngDevMode ? [{ debugName: "role" }] : []));
326
- textPart = input.required(...(ngDevMode ? [{ debugName: "textPart" }] : []));
327
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: MessageTextPartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
328
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.0", 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 });
325
+ role = input.required(...(ngDevMode ? [{ debugName: "role" }] : /* istanbul ignore next */ []));
326
+ textPart = input.required(...(ngDevMode ? [{ debugName: "textPart" }] : /* istanbul ignore next */ []));
327
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MessageTextPartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
328
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.2", 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 });
329
329
  }
330
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: MessageTextPartComponent, decorators: [{
330
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MessageTextPartComponent, decorators: [{
331
331
  type: Component,
332
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" }]
333
333
  }], propDecorators: { role: [{ type: i0.Input, args: [{ isSignal: true, alias: "role", required: true }] }], textPart: [{ type: i0.Input, args: [{ isSignal: true, alias: "textPart", required: true }] }] } });
334
334
 
335
335
  class MessageReasoningPartComponent {
336
- reasoningPart = input.required(...(ngDevMode ? [{ debugName: "reasoningPart" }] : []));
337
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: MessageReasoningPartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
338
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", 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 });
336
+ reasoningPart = input.required(...(ngDevMode ? [{ debugName: "reasoningPart" }] : /* istanbul ignore next */ []));
337
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MessageReasoningPartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
338
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.2", 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 });
339
339
  }
340
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: MessageReasoningPartComponent, decorators: [{
340
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MessageReasoningPartComponent, decorators: [{
341
341
  type: Component,
342
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" }]
343
343
  }], propDecorators: { reasoningPart: [{ type: i0.Input, args: [{ isSignal: true, alias: "reasoningPart", required: true }] }] } });
@@ -361,17 +361,17 @@ class HighlightPipe {
361
361
  escapeHtml(str) {
362
362
  return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
363
363
  }
364
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: HighlightPipe, deps: [{ token: i1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Pipe });
365
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.0", ngImport: i0, type: HighlightPipe, isStandalone: true, name: "highlight" });
364
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: HighlightPipe, deps: [{ token: i1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Pipe });
365
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.2", ngImport: i0, type: HighlightPipe, isStandalone: true, name: "highlight" });
366
366
  }
367
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: HighlightPipe, decorators: [{
367
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: HighlightPipe, decorators: [{
368
368
  type: Pipe,
369
369
  args: [{ name: 'highlight', standalone: true }]
370
370
  }], ctorParameters: () => [{ type: i1.DomSanitizer }] });
371
371
 
372
372
  class JsonTreeComponent {
373
- data = input.required(...(ngDevMode ? [{ debugName: "data" }] : []));
374
- expandDepth = input(1, ...(ngDevMode ? [{ debugName: "expandDepth" }] : []));
373
+ data = input.required(...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
374
+ expandDepth = input(1, ...(ngDevMode ? [{ debugName: "expandDepth" }] : /* istanbul ignore next */ []));
375
375
  collapsedPaths = new Set();
376
376
  userToggled = new Set();
377
377
  isCollapsed(path, depth) {
@@ -462,10 +462,10 @@ class JsonTreeComponent {
462
462
  }
463
463
  return value;
464
464
  }
465
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: JsonTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
466
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", 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 });
465
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: JsonTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
466
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.2", 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 });
467
467
  }
468
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: JsonTreeComponent, decorators: [{
468
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: JsonTreeComponent, decorators: [{
469
469
  type: Component,
470
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"] }]
471
471
  }], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: true }] }], expandDepth: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandDepth", required: false }] }] } });
@@ -475,7 +475,7 @@ class MessageToolCallPartComponent {
475
475
  code: 'javascript',
476
476
  query: 'cypher',
477
477
  };
478
- toolCallPart = input.required(...(ngDevMode ? [{ debugName: "toolCallPart" }] : []));
478
+ toolCallPart = input.required(...(ngDevMode ? [{ debugName: "toolCallPart" }] : /* istanbul ignore next */ []));
479
479
  codeSnippets = computed(() => {
480
480
  const inp = this.toolCallPart().input;
481
481
  if (!inp || typeof inp !== 'object')
@@ -492,7 +492,7 @@ class MessageToolCallPartComponent {
492
492
  }
493
493
  }
494
494
  return snippets;
495
- }, ...(ngDevMode ? [{ debugName: "codeSnippets" }] : []));
495
+ }, ...(ngDevMode ? [{ debugName: "codeSnippets" }] : /* istanbul ignore next */ []));
496
496
  remainingInput = computed(() => {
497
497
  const inp = this.toolCallPart().input;
498
498
  if (!inp || typeof inp !== 'object')
@@ -508,33 +508,33 @@ class MessageToolCallPartComponent {
508
508
  }
509
509
  }
510
510
  return hasKeys ? remaining : null;
511
- }, ...(ngDevMode ? [{ debugName: "remainingInput" }] : []));
511
+ }, ...(ngDevMode ? [{ debugName: "remainingInput" }] : /* istanbul ignore next */ []));
512
512
  toolName() {
513
513
  const type = this.toolCallPart().type;
514
514
  if (!type)
515
515
  return '';
516
516
  return type.startsWith('tool-') ? type.slice('tool-'.length) : type;
517
517
  }
518
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: MessageToolCallPartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
519
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", 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 });
518
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MessageToolCallPartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
519
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.2", 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 });
520
520
  }
521
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: MessageToolCallPartComponent, decorators: [{
521
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MessageToolCallPartComponent, decorators: [{
522
522
  type: Component,
523
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" }]
524
524
  }], propDecorators: { toolCallPart: [{ type: i0.Input, args: [{ isSignal: true, alias: "toolCallPart", required: true }] }] } });
525
525
 
526
526
  class MessageCaptureFilesPartComponent {
527
- message = input.required(...(ngDevMode ? [{ debugName: "message" }] : []));
528
- toolPart = input.required(...(ngDevMode ? [{ debugName: "toolPart" }] : []));
529
- data = computed(() => this.toolPart().input, ...(ngDevMode ? [{ debugName: "data" }] : []));
530
- isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
527
+ message = input.required(...(ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []));
528
+ toolPart = input.required(...(ngDevMode ? [{ debugName: "toolPart" }] : /* istanbul ignore next */ []));
529
+ data = computed(() => this.toolPart().input, ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
530
+ isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
531
531
  outputData = computed(() => {
532
532
  const raw = this.toolPart().output;
533
533
  if (!raw)
534
534
  return null;
535
535
  const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;
536
536
  return parsed;
537
- }, ...(ngDevMode ? [{ debugName: "outputData" }] : []));
537
+ }, ...(ngDevMode ? [{ debugName: "outputData" }] : /* istanbul ignore next */ []));
538
538
  form = computed(() => {
539
539
  const form = this.fb.group({
540
540
  files: this.fb.array(this.data().files.map((x) => this.fb.group({
@@ -549,7 +549,7 @@ class MessageCaptureFilesPartComponent {
549
549
  form.disable();
550
550
  }
551
551
  return form;
552
- }, ...(ngDevMode ? [{ debugName: "form" }] : []));
552
+ }, ...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
553
553
  fb = inject(FormBuilder);
554
554
  chatService = inject(CortexChatService);
555
555
  config = inject(CORTEX_CLIENT_CONFIG);
@@ -617,19 +617,19 @@ class MessageCaptureFilesPartComponent {
617
617
  },
618
618
  });
619
619
  }
620
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: MessageCaptureFilesPartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
621
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", 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 });
620
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MessageCaptureFilesPartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
621
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.2", 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 });
622
622
  }
623
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: MessageCaptureFilesPartComponent, decorators: [{
623
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MessageCaptureFilesPartComponent, decorators: [{
624
624
  type: Component,
625
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"] }]
626
626
  }], propDecorators: { message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: true }] }], toolPart: [{ type: i0.Input, args: [{ isSignal: true, alias: "toolPart", required: true }] }] } });
627
627
  class FileInputComponent {
628
- allowedMimeTypes = input('*/*', ...(ngDevMode ? [{ debugName: "allowedMimeTypes" }] : []));
629
- value = signal(undefined, ...(ngDevMode ? [{ debugName: "value" }] : []));
630
- isDisabled = signal(false, ...(ngDevMode ? [{ debugName: "isDisabled" }] : []));
628
+ allowedMimeTypes = input('*/*', ...(ngDevMode ? [{ debugName: "allowedMimeTypes" }] : /* istanbul ignore next */ []));
629
+ value = signal(undefined, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
630
+ isDisabled = signal(false, ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
631
631
  onTouch;
632
- isProcessing = signal(false, ...(ngDevMode ? [{ debugName: "isProcessing" }] : []));
632
+ isProcessing = signal(false, ...(ngDevMode ? [{ debugName: "isProcessing" }] : /* istanbul ignore next */ []));
633
633
  onChange;
634
634
  writeValue(value) {
635
635
  this.value.set(value);
@@ -658,8 +658,8 @@ class FileInputComponent {
658
658
  this.value.set(value);
659
659
  this.onChange?.(value);
660
660
  }
661
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: FileInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
662
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.0", type: FileInputComponent, isStandalone: true, selector: "cortex-file-input", inputs: { allowedMimeTypes: { classPropertyName: "allowedMimeTypes", publicName: "allowedMimeTypes", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
661
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: FileInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
662
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.2", type: FileInputComponent, isStandalone: true, selector: "cortex-file-input", inputs: { allowedMimeTypes: { classPropertyName: "allowedMimeTypes", publicName: "allowedMimeTypes", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
663
663
  {
664
664
  provide: NG_VALUE_ACCESSOR,
665
665
  multi: true,
@@ -681,7 +681,7 @@ class FileInputComponent {
681
681
  <input #fileInput (change)="loadFile($event)" type="file" class="hidden" />
682
682
  </div>`, isInline: true, dependencies: [{ kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
683
683
  }
684
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: FileInputComponent, decorators: [{
684
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: FileInputComponent, decorators: [{
685
685
  type: Component,
686
686
  args: [{
687
687
  selector: 'cortex-file-input',
@@ -734,14 +734,14 @@ async function convertFileToBase64(file) {
734
734
 
735
735
  const STATUS_COUNT = 10;
736
736
  class MessageToolCallAnimatedComponent {
737
- message = input.required(...(ngDevMode ? [{ debugName: "message" }] : []));
738
- toolCallPart = input.required(...(ngDevMode ? [{ debugName: "toolCallPart" }] : []));
737
+ message = input.required(...(ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []));
738
+ toolCallPart = input.required(...(ngDevMode ? [{ debugName: "toolCallPart" }] : /* istanbul ignore next */ []));
739
739
  config = inject(CORTEX_CLIENT_CONFIG);
740
740
  customComponent = computed(() => {
741
741
  const toolName = this.toolCallPart().type;
742
742
  const name = toolName?.startsWith('tool-') ? toolName.slice('tool-'.length) : toolName;
743
743
  return name ? (this.config.toolComponents?.[name] ?? null) : null;
744
- }, ...(ngDevMode ? [{ debugName: "customComponent" }] : []));
744
+ }, ...(ngDevMode ? [{ debugName: "customComponent" }] : /* istanbul ignore next */ []));
745
745
  animState = computed(() => {
746
746
  const state = this.toolCallPart().state;
747
747
  switch (state) {
@@ -760,11 +760,11 @@ class MessageToolCallAnimatedComponent {
760
760
  default:
761
761
  return 'processing';
762
762
  }
763
- }, ...(ngDevMode ? [{ debugName: "animState" }] : []));
763
+ }, ...(ngDevMode ? [{ debugName: "animState" }] : /* istanbul ignore next */ []));
764
764
  isActive = computed(() => {
765
765
  const s = this.animState();
766
766
  return s === 'starting' || s === 'processing';
767
- }, ...(ngDevMode ? [{ debugName: "isActive" }] : []));
767
+ }, ...(ngDevMode ? [{ debugName: "isActive" }] : /* istanbul ignore next */ []));
768
768
  displayTitleKey = computed(() => {
769
769
  const state = this.animState();
770
770
  switch (state) {
@@ -780,7 +780,7 @@ class MessageToolCallAnimatedComponent {
780
780
  default:
781
781
  return `translate_tool_status_${this.stableIndex()}`;
782
782
  }
783
- }, ...(ngDevMode ? [{ debugName: "displayTitleKey" }] : []));
783
+ }, ...(ngDevMode ? [{ debugName: "displayTitleKey" }] : /* istanbul ignore next */ []));
784
784
  stableIndex = computed(() => {
785
785
  const id = this.toolCallPart().toolCallId ?? '';
786
786
  let hash = 0;
@@ -788,30 +788,30 @@ class MessageToolCallAnimatedComponent {
788
788
  hash = (hash * 31 + id.charCodeAt(i)) | 0;
789
789
  }
790
790
  return Math.abs(hash) % STATUS_COUNT;
791
- }, ...(ngDevMode ? [{ debugName: "stableIndex" }] : []));
792
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: MessageToolCallAnimatedComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
793
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", 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 });
791
+ }, ...(ngDevMode ? [{ debugName: "stableIndex" }] : /* istanbul ignore next */ []));
792
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MessageToolCallAnimatedComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
793
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.2", 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 });
794
794
  }
795
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: MessageToolCallAnimatedComponent, decorators: [{
795
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MessageToolCallAnimatedComponent, decorators: [{
796
796
  type: Component,
797
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"] }]
798
798
  }], propDecorators: { message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: true }] }], toolCallPart: [{ type: i0.Input, args: [{ isSignal: true, alias: "toolCallPart", required: true }] }] } });
799
799
 
800
800
  class MessageReasoningAnimatedComponent {
801
- reasoningPart = input.required(...(ngDevMode ? [{ debugName: "reasoningPart" }] : []));
802
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: MessageReasoningAnimatedComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
803
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.0", 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 });
801
+ reasoningPart = input.required(...(ngDevMode ? [{ debugName: "reasoningPart" }] : /* istanbul ignore next */ []));
802
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MessageReasoningAnimatedComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
803
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.2", 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 });
804
804
  }
805
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: MessageReasoningAnimatedComponent, decorators: [{
805
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MessageReasoningAnimatedComponent, decorators: [{
806
806
  type: Component,
807
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"] }]
808
808
  }], propDecorators: { reasoningPart: [{ type: i0.Input, args: [{ isSignal: true, alias: "reasoningPart", required: true }] }] } });
809
809
 
810
810
  class MessagePartComponent {
811
- message = input.required(...(ngDevMode ? [{ debugName: "message" }] : []));
812
- part = input.required(...(ngDevMode ? [{ debugName: "part" }] : []));
813
- debugMode = input(false, ...(ngDevMode ? [{ debugName: "debugMode" }] : []));
814
- animate = input(false, ...(ngDevMode ? [{ debugName: "animate" }] : []));
811
+ message = input.required(...(ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []));
812
+ part = input.required(...(ngDevMode ? [{ debugName: "part" }] : /* istanbul ignore next */ []));
813
+ debugMode = input(false, ...(ngDevMode ? [{ debugName: "debugMode" }] : /* istanbul ignore next */ []));
814
+ animate = input(false, ...(ngDevMode ? [{ debugName: "animate" }] : /* istanbul ignore next */ []));
815
815
  config = inject(CORTEX_CLIENT_CONFIG);
816
816
  isStaticToolUIPart = isStaticToolUIPart;
817
817
  customToolComponent = computed(() => {
@@ -820,11 +820,11 @@ class MessagePartComponent {
820
820
  return null;
821
821
  const toolName = p.type?.startsWith('tool-') ? p.type.slice('tool-'.length) : p.type;
822
822
  return toolName ? (this.config.toolComponents?.[toolName] ?? null) : null;
823
- }, ...(ngDevMode ? [{ debugName: "customToolComponent" }] : []));
824
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: MessagePartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
825
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", 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 });
823
+ }, ...(ngDevMode ? [{ debugName: "customToolComponent" }] : /* istanbul ignore next */ []));
824
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MessagePartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
825
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.2", 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 });
826
826
  }
827
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: MessagePartComponent, decorators: [{
827
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MessagePartComponent, decorators: [{
828
828
  type: Component,
829
829
  args: [{ selector: 'cortex-message-part', imports: [
830
830
  MessageTextPartComponent,
@@ -838,11 +838,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
838
838
  }], 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
839
 
840
840
  class MessageComponent {
841
- message = input.required(...(ngDevMode ? [{ debugName: "message" }] : []));
842
- debugMode = input(false, ...(ngDevMode ? [{ debugName: "debugMode" }] : []));
843
- animate = input(false, ...(ngDevMode ? [{ debugName: "animate" }] : []));
844
- visibleParts = computed(() => this.parts().filter((x) => x.type !== 'step-start'), ...(ngDevMode ? [{ debugName: "visibleParts" }] : []));
845
- parts = signal([], ...(ngDevMode ? [{ debugName: "parts" }] : []));
841
+ message = input.required(...(ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []));
842
+ debugMode = input(false, ...(ngDevMode ? [{ debugName: "debugMode" }] : /* istanbul ignore next */ []));
843
+ animate = input(false, ...(ngDevMode ? [{ debugName: "animate" }] : /* istanbul ignore next */ []));
844
+ visibleParts = computed(() => this.parts().filter((x) => x.type !== 'step-start'), ...(ngDevMode ? [{ debugName: "visibleParts" }] : /* istanbul ignore next */ []));
845
+ parts = signal([], ...(ngDevMode ? [{ debugName: "parts" }] : /* istanbul ignore next */ []));
846
846
  constructor() {
847
847
  effect(() => {
848
848
  const parts = this.message().parts;
@@ -873,21 +873,21 @@ class MessageComponent {
873
873
  };
874
874
  });
875
875
  }
876
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: MessageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
877
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", 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 });
876
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MessageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
877
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.2", 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 });
878
878
  }
879
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: MessageComponent, decorators: [{
879
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MessageComponent, decorators: [{
880
880
  type: Component,
881
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"] }]
882
882
  }], 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
883
 
884
884
  class MessageListComponent {
885
885
  chatService;
886
- debugMode = input(false, ...(ngDevMode ? [{ debugName: "debugMode" }] : []));
887
- messagesContainer = viewChild('messagesContainer', ...(ngDevMode ? [{ debugName: "messagesContainer" }] : []));
886
+ debugMode = input(false, ...(ngDevMode ? [{ debugName: "debugMode" }] : /* istanbul ignore next */ []));
887
+ messagesContainer = viewChild('messagesContainer', ...(ngDevMode ? [{ debugName: "messagesContainer" }] : /* istanbul ignore next */ []));
888
888
  shouldScrollToBottom = true;
889
889
  isNearBottom = true;
890
- animateNewParts = signal(false, ...(ngDevMode ? [{ debugName: "animateNewParts" }] : []));
890
+ animateNewParts = signal(false, ...(ngDevMode ? [{ debugName: "animateNewParts" }] : /* istanbul ignore next */ []));
891
891
  constructor(chatService, destroyRef) {
892
892
  this.chatService = chatService;
893
893
  this.scrollToBottom();
@@ -930,10 +930,10 @@ class MessageListComponent {
930
930
  el.scrollTop = el.scrollHeight;
931
931
  }
932
932
  }
933
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", 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.0", 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 });
933
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", 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.2", 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 });
935
935
  }
936
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: MessageListComponent, decorators: [{
936
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: MessageListComponent, decorators: [{
937
937
  type: Component,
938
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" }]
939
939
  }], 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 }] }] } });
@@ -1137,18 +1137,18 @@ class CortexClientTranslateLoader {
1137
1137
 
1138
1138
  class CortexClientConfigRef {
1139
1139
  config;
1140
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CortexClientConfigRef, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1141
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CortexClientConfigRef });
1140
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: CortexClientConfigRef, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1141
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: CortexClientConfigRef });
1142
1142
  }
1143
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CortexClientConfigRef, decorators: [{
1143
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: CortexClientConfigRef, decorators: [{
1144
1144
  type: Injectable
1145
1145
  }] });
1146
1146
  class CortexChatWidgetComponent {
1147
- config = input.required(...(ngDevMode ? [{ debugName: "config" }] : []));
1148
- text = model('', ...(ngDevMode ? [{ debugName: "text" }] : []));
1149
- debugMode = signal(false, ...(ngDevMode ? [{ debugName: "debugMode" }] : []));
1150
- screen = signal('threads', ...(ngDevMode ? [{ debugName: "screen" }] : []));
1151
- theme = signal(undefined, ...(ngDevMode ? [{ debugName: "theme" }] : []));
1147
+ config = input.required(...(ngDevMode ? [{ debugName: "config" }] : /* istanbul ignore next */ []));
1148
+ text = model('', ...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
1149
+ debugMode = signal(false, ...(ngDevMode ? [{ debugName: "debugMode" }] : /* istanbul ignore next */ []));
1150
+ screen = signal('threads', ...(ngDevMode ? [{ debugName: "screen" }] : /* istanbul ignore next */ []));
1151
+ theme = signal(undefined, ...(ngDevMode ? [{ debugName: "theme" }] : /* istanbul ignore next */ []));
1152
1152
  chatService;
1153
1153
  configRef = inject(CortexClientConfigRef);
1154
1154
  injector = inject(Injector);
@@ -1157,10 +1157,7 @@ class CortexChatWidgetComponent {
1157
1157
  this.theme.set(this.config().theme);
1158
1158
  this.chatService = this.injector.get(CortexChatService);
1159
1159
  const translateService = this.injector.get(TranslateService);
1160
- translateService.use(this.config().locale());
1161
- effect(() => {
1162
- translateService.use(this.config().locale());
1163
- }, { injector: this.injector });
1160
+ translateService.use(this.config().locale);
1164
1161
  }
1165
1162
  toggleDebugMode() {
1166
1163
  this.debugMode.update((v) => !v);
@@ -1192,8 +1189,8 @@ class CortexChatWidgetComponent {
1192
1189
  this.chatService.deselectThread();
1193
1190
  this.screen.set('chat');
1194
1191
  }
1195
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CortexChatWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1196
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", 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: [
1192
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: CortexChatWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1193
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.2", 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: [
1197
1194
  CortexClientConfigRef,
1198
1195
  {
1199
1196
  provide: CORTEX_CLIENT_CONFIG,
@@ -1207,7 +1204,7 @@ class CortexChatWidgetComponent {
1207
1204
  }),
1208
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 });
1209
1206
  }
1210
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: CortexChatWidgetComponent, decorators: [{
1207
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: CortexChatWidgetComponent, decorators: [{
1211
1208
  type: Component,
1212
1209
  args: [{ selector: 'cortex-chat-widget', imports: [NgClass, MessageListComponent, FormsModule, TranslatePipe], providers: [
1213
1210
  CortexClientConfigRef,
@@ -1265,10 +1262,10 @@ class PrettyJsonPipe {
1265
1262
  }
1266
1263
  return value;
1267
1264
  }
1268
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: PrettyJsonPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
1269
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.0", ngImport: i0, type: PrettyJsonPipe, isStandalone: true, name: "prettyJson" });
1265
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: PrettyJsonPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
1266
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.2", ngImport: i0, type: PrettyJsonPipe, isStandalone: true, name: "prettyJson" });
1270
1267
  }
1271
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: PrettyJsonPipe, decorators: [{
1268
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.2", ngImport: i0, type: PrettyJsonPipe, decorators: [{
1272
1269
  type: Pipe,
1273
1270
  args: [{ name: 'prettyJson', standalone: true }]
1274
1271
  }] });