@meshmakers/shared-ui 3.4.1490 → 3.5.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.
Files changed (47) hide show
  1. package/README.md +18 -0
  2. package/assets/octobot/lg/octo_blink.webp +0 -0
  3. package/assets/octobot/lg/octo_blink_still.png +0 -0
  4. package/assets/octobot/lg/octo_idle.webp +0 -0
  5. package/assets/octobot/lg/octo_idle_still.png +0 -0
  6. package/assets/octobot/lg/octo_look.webp +0 -0
  7. package/assets/octobot/lg/octo_look_still.png +0 -0
  8. package/assets/octobot/lg/octo_swim.webp +0 -0
  9. package/assets/octobot/lg/octo_swim_still.png +0 -0
  10. package/assets/octobot/lg/octo_thinking.webp +0 -0
  11. package/assets/octobot/lg/octo_thinking_still.png +0 -0
  12. package/assets/octobot/lg/octo_wave.webp +0 -0
  13. package/assets/octobot/lg/octo_wave_still.png +0 -0
  14. package/assets/octobot/md/octo_blink.webp +0 -0
  15. package/assets/octobot/md/octo_blink_still.png +0 -0
  16. package/assets/octobot/md/octo_idle.webp +0 -0
  17. package/assets/octobot/md/octo_idle_still.png +0 -0
  18. package/assets/octobot/md/octo_look.webp +0 -0
  19. package/assets/octobot/md/octo_look_still.png +0 -0
  20. package/assets/octobot/md/octo_swim.webp +0 -0
  21. package/assets/octobot/md/octo_swim_still.png +0 -0
  22. package/assets/octobot/md/octo_thinking.webp +0 -0
  23. package/assets/octobot/md/octo_thinking_still.png +0 -0
  24. package/assets/octobot/md/octo_wave.webp +0 -0
  25. package/assets/octobot/md/octo_wave_still.png +0 -0
  26. package/assets/octobot/sm/octo_blink.webp +0 -0
  27. package/assets/octobot/sm/octo_blink_still.png +0 -0
  28. package/assets/octobot/sm/octo_idle.webp +0 -0
  29. package/assets/octobot/sm/octo_idle_still.png +0 -0
  30. package/assets/octobot/sm/octo_look.webp +0 -0
  31. package/assets/octobot/sm/octo_look_still.png +0 -0
  32. package/assets/octobot/sm/octo_swim.webp +0 -0
  33. package/assets/octobot/sm/octo_swim_still.png +0 -0
  34. package/assets/octobot/sm/octo_thinking.webp +0 -0
  35. package/assets/octobot/sm/octo_thinking_still.png +0 -0
  36. package/assets/octobot/sm/octo_wave.webp +0 -0
  37. package/assets/octobot/sm/octo_wave_still.png +0 -0
  38. package/fesm2022/meshmakers-shared-ui-assistant-core.mjs +2 -0
  39. package/fesm2022/meshmakers-shared-ui-assistant-core.mjs.map +1 -1
  40. package/fesm2022/meshmakers-shared-ui-assistant.mjs +28 -5
  41. package/fesm2022/meshmakers-shared-ui-assistant.mjs.map +1 -1
  42. package/fesm2022/meshmakers-shared-ui.mjs +402 -21
  43. package/fesm2022/meshmakers-shared-ui.mjs.map +1 -1
  44. package/package.json +1 -1
  45. package/types/meshmakers-shared-ui-assistant-core.d.ts +4 -0
  46. package/types/meshmakers-shared-ui-assistant.d.ts +11 -0
  47. package/types/meshmakers-shared-ui.d.ts +270 -6
@@ -6,6 +6,7 @@ import { Marked } from 'marked';
6
6
  import { SVGIconComponent } from '@progress/kendo-angular-icons';
7
7
  import { xIcon, paperclipIcon, plusIcon, clockArrowRotateIcon, trashIcon } from '@progress/kendo-svg-icons';
8
8
  import { NgComponentOutlet } from '@angular/common';
9
+ import { OctoBotComponent } from '@meshmakers/shared-ui';
9
10
 
10
11
  /** Protocols an assistant link may use; everything else (javascript:, data:, vbscript: …) renders as text. */
11
12
  const SAFE_PROTOCOLS = new Set(['http:', 'https:', 'mailto:']);
@@ -283,6 +284,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
283
284
  * A user turn sent through a slash command's `expand` (AB#5621) shows the expanded prompt with the
284
285
  * typed command (`slashCommand`) as a small label above it.
285
286
  *
287
+ * While an assistant turn streams it shows the OctoBot `thinking` with "Thinking…" (AB#3444);
288
+ * once the reply is complete the newest assistant turn keeps the OctoBot as a still frame. The
289
+ * figure is decorative — the text carries the meaning — and appears at most once in the thread.
290
+ *
286
291
  * The list is an `aria-live` log; it is `aria-busy` while a turn streams so a
287
292
  * screen reader announces finished messages instead of every token. Nothing
288
293
  * inside it carries its own live role, so each addition is announced once.
@@ -297,6 +302,17 @@ class AssistantThreadComponent {
297
302
  ...(ngDevMode ? [{ debugName: "messages" }] : /* istanbul ignore next */ []));
298
303
  m = assistantMessages(this.messages);
299
304
  toolCallComponent = inject(ASSISTANT_TOOL_CALL_COMPONENT);
305
+ /** Id of the newest assistant turn: the only one that shows the resting OctoBot. */
306
+ lastAssistantId = computed(() => {
307
+ const items = this.items();
308
+ for (let i = items.length - 1; i >= 0; i--) {
309
+ if (items[i].kind === 'assistant') {
310
+ return items[i].id;
311
+ }
312
+ }
313
+ return null;
314
+ }, /* @ts-ignore */
315
+ ...(ngDevMode ? [{ debugName: "lastAssistantId" }] : /* istanbul ignore next */ []));
300
316
  streaming = computed(() => this.items().some(item => item.kind === 'assistant' && item.streaming), /* @ts-ignore */
301
317
  ...(ngDevMode ? [{ debugName: "streaming" }] : /* istanbul ignore next */ []));
302
318
  constructor() {
@@ -310,11 +326,11 @@ class AssistantThreadComponent {
310
326
  });
311
327
  }
312
328
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AssistantThreadComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
313
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: AssistantThreadComponent, isStandalone: true, selector: "mm-assistant-thread", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { proposalDecision: "proposalDecision" }, ngImport: i0, template: "<ol class=\"thread\" role=\"log\" aria-live=\"polite\" aria-relevant=\"additions text\" [attr.aria-label]=\"m().conversationLabel\"\n [attr.aria-busy]=\"streaming()\">\n @for (item of items(); track item.id) {\n @switch (item.kind) {\n @case ('user') {\n <li class=\"msg user\"><span class=\"sr-only\">{{ m().youPrefix }}</span>@if (item.slashCommand) {<span class=\"slash-command\"><span class=\"sr-only\">{{ m().slashCommandPrefix }}</span>{{ item.slashCommand }}</span>}{{ item.text }}@if (item.attachments?.length) {<ul class=\"attachments\" [attr.aria-label]=\"m().attachmentsLabel\">@for (file of item.attachments; track $index) {<li class=\"attachment\">{{ file.name }}</li>}</ul>}</li>\n }\n @case ('assistant') {\n <li class=\"msg bot\">\n <span class=\"sr-only\">{{ m().assistantPrefix }}</span>\n <div class=\"md\" [innerHTML]=\"item.markdown | assistantMarkdown\"></div>\n @if (item.streaming) {\n <span class=\"streaming\" aria-hidden=\"true\">\u25CF</span>\n }\n </li>\n }\n @case ('tool-call') {\n <li class=\"msg tool\"><ng-container *ngComponentOutlet=\"toolCallComponent; inputs: { call: item.call }\"></ng-container></li>\n }\n @case ('proposal') {\n <li class=\"msg\">\n <mm-assistant-proposal-card\n [messages]=\"messages()\"\n [proposal]=\"item.proposal\"\n [decision]=\"item.decision\"\n (run)=\"proposalDecision.emit({ kind: 'run', proposal: $event })\"\n (edit)=\"proposalDecision.emit({ kind: 'edit', proposal: $event })\"\n (discard)=\"proposalDecision.emit({ kind: 'discard', proposal: $event })\">\n </mm-assistant-proposal-card>\n </li>\n }\n @case ('error') {\n <li class=\"msg error\"><span class=\"sr-only\">{{ m().errorPrefix }}</span>{{ item.text }}</li>\n }\n }\n }\n</ol>\n", styles: [":host{display:block;overflow:auto}.thread{display:grid;align-content:start;gap:var(--theme-space-3);margin:0;padding:14px;list-style:none}.msg{display:grid;gap:6px;min-width:0;overflow-wrap:anywhere}.user{justify-self:end;max-width:85%;padding:8px 12px;border-radius:var(--theme-radius-md);background:var(--theme-bg-overlay);white-space:pre-wrap}.slash-command{color:var(--theme-text-muted);font-family:var(--theme-font-mono, monospace);font-size:.71875rem;overflow-wrap:anywhere}.attachments{display:flex;flex-wrap:wrap;gap:4px;margin:4px 0 0;padding:0;list-style:none;white-space:normal}.attachment{padding:0 6px;border:1px solid var(--theme-border-default);border-radius:var(--theme-radius-xs);color:var(--theme-text-secondary);font-size:.75rem}.bot{padding-left:12px;border-left:2px solid var(--theme-ai)}.bot ::ng-deep p{margin:0 0 var(--theme-space-2)}.bot ::ng-deep a{color:var(--theme-text-accent)}.streaming{color:var(--theme-ai);font-size:.5rem;animation:assistant-pulse 1s ease-in-out infinite}@keyframes assistant-pulse{50%{opacity:.3}}@media(prefers-reduced-motion:reduce){.streaming{animation:none}}.error{color:var(--theme-status-error)}mm-ai-tool-call{--mm-ai-tool-bg: var(--theme-bg-app);--mm-ai-tool-border: var(--theme-border-default);--mm-ai-tool-text: var(--theme-text-primary);--mm-ai-tool-muted: var(--theme-text-secondary);--mm-ai-tool-success: var(--theme-status-success);--mm-ai-tool-failed: var(--theme-status-error);--mm-ai-tool-rejected: var(--theme-status-warning);--mm-ai-tool-pending: var(--theme-status-neutral);--mm-ai-tool-code-bg: var(--theme-bg-sunken)}.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: AssistantProposalCardComponent, selector: "mm-assistant-proposal-card", inputs: ["proposal", "decision", "messages"], outputs: ["run", "edit", "discard"] }, { kind: "pipe", type: AssistantMarkdownPipe, name: "assistantMarkdown" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
329
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: AssistantThreadComponent, isStandalone: true, selector: "mm-assistant-thread", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { proposalDecision: "proposalDecision" }, ngImport: i0, template: "<ol class=\"thread\" role=\"log\" aria-live=\"polite\" aria-relevant=\"additions text\" [attr.aria-label]=\"m().conversationLabel\"\n [attr.aria-busy]=\"streaming()\">\n @for (item of items(); track item.id) {\n @switch (item.kind) {\n @case ('user') {\n <li class=\"msg user\"><span class=\"sr-only\">{{ m().youPrefix }}</span>@if (item.slashCommand) {<span class=\"slash-command\"><span class=\"sr-only\">{{ m().slashCommandPrefix }}</span>{{ item.slashCommand }}</span>}{{ item.text }}@if (item.attachments?.length) {<ul class=\"attachments\" [attr.aria-label]=\"m().attachmentsLabel\">@for (file of item.attachments; track $index) {<li class=\"attachment\">{{ file.name }}</li>}</ul>}</li>\n }\n @case ('assistant') {\n <li class=\"msg bot\">\n <span class=\"sr-only\">{{ m().assistantPrefix }}</span>\n <div class=\"md\" [innerHTML]=\"item.markdown | assistantMarkdown\"></div>\n @if (item.streaming) {\n <span class=\"streaming\" data-testid=\"assistant-thinking\">\n <mm-octobot animation=\"thinking\" size=\"sm\" />\n <span class=\"streaming-text\">{{ m().thinking }}</span>\n </span>\n } @else if (item.id === lastAssistantId()) {\n <mm-octobot class=\"settled\" animation=\"thinking\" size=\"sm\" still data-testid=\"assistant-settled\" />\n }\n </li>\n }\n @case ('tool-call') {\n <li class=\"msg tool\"><ng-container *ngComponentOutlet=\"toolCallComponent; inputs: { call: item.call }\"></ng-container></li>\n }\n @case ('proposal') {\n <li class=\"msg\">\n <mm-assistant-proposal-card\n [messages]=\"messages()\"\n [proposal]=\"item.proposal\"\n [decision]=\"item.decision\"\n (run)=\"proposalDecision.emit({ kind: 'run', proposal: $event })\"\n (edit)=\"proposalDecision.emit({ kind: 'edit', proposal: $event })\"\n (discard)=\"proposalDecision.emit({ kind: 'discard', proposal: $event })\">\n </mm-assistant-proposal-card>\n </li>\n }\n @case ('error') {\n <li class=\"msg error\"><span class=\"sr-only\">{{ m().errorPrefix }}</span>{{ item.text }}</li>\n }\n }\n }\n</ol>\n", styles: [":host{display:block;overflow:auto}.thread{display:grid;align-content:start;gap:var(--theme-space-3);margin:0;padding:14px;list-style:none}.msg{display:grid;gap:6px;min-width:0;overflow-wrap:anywhere}.user{justify-self:end;max-width:85%;padding:8px 12px;border-radius:var(--theme-radius-md);background:var(--theme-bg-overlay);white-space:pre-wrap}.slash-command{color:var(--theme-text-muted);font-family:var(--theme-font-mono, monospace);font-size:.71875rem;overflow-wrap:anywhere}.attachments{display:flex;flex-wrap:wrap;gap:4px;margin:4px 0 0;padding:0;list-style:none;white-space:normal}.attachment{padding:0 6px;border:1px solid var(--theme-border-default);border-radius:var(--theme-radius-xs);color:var(--theme-text-secondary);font-size:.75rem}.bot{padding-left:12px;border-left:2px solid var(--theme-ai)}.bot ::ng-deep p{margin:0 0 var(--theme-space-2)}.bot ::ng-deep a{color:var(--theme-text-accent)}.streaming{display:inline-flex;align-items:center;gap:var(--theme-space-2);color:var(--theme-text-secondary);font-size:.8125rem}.settled{opacity:.85}.error{color:var(--theme-status-error)}mm-ai-tool-call{--mm-ai-tool-bg: var(--theme-bg-app);--mm-ai-tool-border: var(--theme-border-default);--mm-ai-tool-text: var(--theme-text-primary);--mm-ai-tool-muted: var(--theme-text-secondary);--mm-ai-tool-success: var(--theme-status-success);--mm-ai-tool-failed: var(--theme-status-error);--mm-ai-tool-rejected: var(--theme-status-warning);--mm-ai-tool-pending: var(--theme-status-neutral);--mm-ai-tool-code-bg: var(--theme-bg-sunken)}.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }, { kind: "component", type: AssistantProposalCardComponent, selector: "mm-assistant-proposal-card", inputs: ["proposal", "decision", "messages"], outputs: ["run", "edit", "discard"] }, { kind: "component", type: OctoBotComponent, selector: "mm-octobot", inputs: ["animation", "size", "label", "loading", "still", "playOnce"] }, { kind: "pipe", type: AssistantMarkdownPipe, name: "assistantMarkdown" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
314
330
  }
315
331
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AssistantThreadComponent, decorators: [{
316
332
  type: Component,
317
- args: [{ selector: 'mm-assistant-thread', imports: [NgComponentOutlet, AssistantMarkdownPipe, AssistantProposalCardComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ol class=\"thread\" role=\"log\" aria-live=\"polite\" aria-relevant=\"additions text\" [attr.aria-label]=\"m().conversationLabel\"\n [attr.aria-busy]=\"streaming()\">\n @for (item of items(); track item.id) {\n @switch (item.kind) {\n @case ('user') {\n <li class=\"msg user\"><span class=\"sr-only\">{{ m().youPrefix }}</span>@if (item.slashCommand) {<span class=\"slash-command\"><span class=\"sr-only\">{{ m().slashCommandPrefix }}</span>{{ item.slashCommand }}</span>}{{ item.text }}@if (item.attachments?.length) {<ul class=\"attachments\" [attr.aria-label]=\"m().attachmentsLabel\">@for (file of item.attachments; track $index) {<li class=\"attachment\">{{ file.name }}</li>}</ul>}</li>\n }\n @case ('assistant') {\n <li class=\"msg bot\">\n <span class=\"sr-only\">{{ m().assistantPrefix }}</span>\n <div class=\"md\" [innerHTML]=\"item.markdown | assistantMarkdown\"></div>\n @if (item.streaming) {\n <span class=\"streaming\" aria-hidden=\"true\">\u25CF</span>\n }\n </li>\n }\n @case ('tool-call') {\n <li class=\"msg tool\"><ng-container *ngComponentOutlet=\"toolCallComponent; inputs: { call: item.call }\"></ng-container></li>\n }\n @case ('proposal') {\n <li class=\"msg\">\n <mm-assistant-proposal-card\n [messages]=\"messages()\"\n [proposal]=\"item.proposal\"\n [decision]=\"item.decision\"\n (run)=\"proposalDecision.emit({ kind: 'run', proposal: $event })\"\n (edit)=\"proposalDecision.emit({ kind: 'edit', proposal: $event })\"\n (discard)=\"proposalDecision.emit({ kind: 'discard', proposal: $event })\">\n </mm-assistant-proposal-card>\n </li>\n }\n @case ('error') {\n <li class=\"msg error\"><span class=\"sr-only\">{{ m().errorPrefix }}</span>{{ item.text }}</li>\n }\n }\n }\n</ol>\n", styles: [":host{display:block;overflow:auto}.thread{display:grid;align-content:start;gap:var(--theme-space-3);margin:0;padding:14px;list-style:none}.msg{display:grid;gap:6px;min-width:0;overflow-wrap:anywhere}.user{justify-self:end;max-width:85%;padding:8px 12px;border-radius:var(--theme-radius-md);background:var(--theme-bg-overlay);white-space:pre-wrap}.slash-command{color:var(--theme-text-muted);font-family:var(--theme-font-mono, monospace);font-size:.71875rem;overflow-wrap:anywhere}.attachments{display:flex;flex-wrap:wrap;gap:4px;margin:4px 0 0;padding:0;list-style:none;white-space:normal}.attachment{padding:0 6px;border:1px solid var(--theme-border-default);border-radius:var(--theme-radius-xs);color:var(--theme-text-secondary);font-size:.75rem}.bot{padding-left:12px;border-left:2px solid var(--theme-ai)}.bot ::ng-deep p{margin:0 0 var(--theme-space-2)}.bot ::ng-deep a{color:var(--theme-text-accent)}.streaming{color:var(--theme-ai);font-size:.5rem;animation:assistant-pulse 1s ease-in-out infinite}@keyframes assistant-pulse{50%{opacity:.3}}@media(prefers-reduced-motion:reduce){.streaming{animation:none}}.error{color:var(--theme-status-error)}mm-ai-tool-call{--mm-ai-tool-bg: var(--theme-bg-app);--mm-ai-tool-border: var(--theme-border-default);--mm-ai-tool-text: var(--theme-text-primary);--mm-ai-tool-muted: var(--theme-text-secondary);--mm-ai-tool-success: var(--theme-status-success);--mm-ai-tool-failed: var(--theme-status-error);--mm-ai-tool-rejected: var(--theme-status-warning);--mm-ai-tool-pending: var(--theme-status-neutral);--mm-ai-tool-code-bg: var(--theme-bg-sunken)}.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}\n"] }]
333
+ args: [{ selector: 'mm-assistant-thread', imports: [NgComponentOutlet, AssistantMarkdownPipe, AssistantProposalCardComponent, OctoBotComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<ol class=\"thread\" role=\"log\" aria-live=\"polite\" aria-relevant=\"additions text\" [attr.aria-label]=\"m().conversationLabel\"\n [attr.aria-busy]=\"streaming()\">\n @for (item of items(); track item.id) {\n @switch (item.kind) {\n @case ('user') {\n <li class=\"msg user\"><span class=\"sr-only\">{{ m().youPrefix }}</span>@if (item.slashCommand) {<span class=\"slash-command\"><span class=\"sr-only\">{{ m().slashCommandPrefix }}</span>{{ item.slashCommand }}</span>}{{ item.text }}@if (item.attachments?.length) {<ul class=\"attachments\" [attr.aria-label]=\"m().attachmentsLabel\">@for (file of item.attachments; track $index) {<li class=\"attachment\">{{ file.name }}</li>}</ul>}</li>\n }\n @case ('assistant') {\n <li class=\"msg bot\">\n <span class=\"sr-only\">{{ m().assistantPrefix }}</span>\n <div class=\"md\" [innerHTML]=\"item.markdown | assistantMarkdown\"></div>\n @if (item.streaming) {\n <span class=\"streaming\" data-testid=\"assistant-thinking\">\n <mm-octobot animation=\"thinking\" size=\"sm\" />\n <span class=\"streaming-text\">{{ m().thinking }}</span>\n </span>\n } @else if (item.id === lastAssistantId()) {\n <mm-octobot class=\"settled\" animation=\"thinking\" size=\"sm\" still data-testid=\"assistant-settled\" />\n }\n </li>\n }\n @case ('tool-call') {\n <li class=\"msg tool\"><ng-container *ngComponentOutlet=\"toolCallComponent; inputs: { call: item.call }\"></ng-container></li>\n }\n @case ('proposal') {\n <li class=\"msg\">\n <mm-assistant-proposal-card\n [messages]=\"messages()\"\n [proposal]=\"item.proposal\"\n [decision]=\"item.decision\"\n (run)=\"proposalDecision.emit({ kind: 'run', proposal: $event })\"\n (edit)=\"proposalDecision.emit({ kind: 'edit', proposal: $event })\"\n (discard)=\"proposalDecision.emit({ kind: 'discard', proposal: $event })\">\n </mm-assistant-proposal-card>\n </li>\n }\n @case ('error') {\n <li class=\"msg error\"><span class=\"sr-only\">{{ m().errorPrefix }}</span>{{ item.text }}</li>\n }\n }\n }\n</ol>\n", styles: [":host{display:block;overflow:auto}.thread{display:grid;align-content:start;gap:var(--theme-space-3);margin:0;padding:14px;list-style:none}.msg{display:grid;gap:6px;min-width:0;overflow-wrap:anywhere}.user{justify-self:end;max-width:85%;padding:8px 12px;border-radius:var(--theme-radius-md);background:var(--theme-bg-overlay);white-space:pre-wrap}.slash-command{color:var(--theme-text-muted);font-family:var(--theme-font-mono, monospace);font-size:.71875rem;overflow-wrap:anywhere}.attachments{display:flex;flex-wrap:wrap;gap:4px;margin:4px 0 0;padding:0;list-style:none;white-space:normal}.attachment{padding:0 6px;border:1px solid var(--theme-border-default);border-radius:var(--theme-radius-xs);color:var(--theme-text-secondary);font-size:.75rem}.bot{padding-left:12px;border-left:2px solid var(--theme-ai)}.bot ::ng-deep p{margin:0 0 var(--theme-space-2)}.bot ::ng-deep a{color:var(--theme-text-accent)}.streaming{display:inline-flex;align-items:center;gap:var(--theme-space-2);color:var(--theme-text-secondary);font-size:.8125rem}.settled{opacity:.85}.error{color:var(--theme-status-error)}mm-ai-tool-call{--mm-ai-tool-bg: var(--theme-bg-app);--mm-ai-tool-border: var(--theme-border-default);--mm-ai-tool-text: var(--theme-text-primary);--mm-ai-tool-muted: var(--theme-text-secondary);--mm-ai-tool-success: var(--theme-status-success);--mm-ai-tool-failed: var(--theme-status-error);--mm-ai-tool-rejected: var(--theme-status-warning);--mm-ai-tool-pending: var(--theme-status-neutral);--mm-ai-tool-code-bg: var(--theme-bg-sunken)}.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}\n"] }]
318
334
  }], ctorParameters: () => [], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], proposalDecision: [{ type: i0.Output, args: ["proposalDecision"] }], messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: false }] }] } });
319
335
 
320
336
  /** Width of the panel before anyone resized it (CSS: 400 px, 340 px ≤ 1180 px), used until measured. */
@@ -478,6 +494,13 @@ class AssistantPanelComponent {
478
494
  /** Session being opened or deleted (its buttons are disabled meanwhile). */
479
495
  busySession = signal(null, /* @ts-ignore */
480
496
  ...(ngDevMode ? [{ debugName: "busySession" }] : /* istanbul ignore next */ []));
497
+ /**
498
+ * Greeting of an empty chat (AB#3444): the OctoBot waves once, then rests on its still frame.
499
+ * Not shown while the assistant is unavailable (the panel says so instead).
500
+ */
501
+ showGreeting = computed(() => this.view() === 'chat' && this.assistant.thread().length === 0 && this.connected()
502
+ && this.assistant.transportStatus() !== 'error', /* @ts-ignore */
503
+ ...(ngDevMode ? [{ debugName: "showGreeting" }] : /* istanbul ignore next */ []));
481
504
  /** Starter questions on an empty thread (transports with `starterQuestions` only). */
482
505
  showStarters = computed(() => this.view() === 'chat' && this.assistant.thread().length === 0 && this.assistant.starterQuestions().length > 0, /* @ts-ignore */
483
506
  ...(ngDevMode ? [{ debugName: "showStarters" }] : /* istanbul ignore next */ []));
@@ -710,11 +733,11 @@ class AssistantPanelComponent {
710
733
  this.assistant.decideProposal(decision.kind, decision.proposal);
711
734
  }
712
735
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AssistantPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
713
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: AssistantPanelComponent, isStandalone: true, selector: "mm-assistant-panel", inputs: { messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, resizable: { classPropertyName: "resizable", publicName: "resizable", isSignal: true, isRequired: false, transformFunction: null }, minWidth: { classPropertyName: "minWidth", publicName: "minWidth", isSignal: true, isRequired: false, transformFunction: null }, maxWidth: { classPropertyName: "maxWidth", publicName: "maxWidth", isSignal: true, isRequired: false, transformFunction: null }, storageKey: { classPropertyName: "storageKey", publicName: "storageKey", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "aria-labelledby": "assistant-panel-title", "id": "assistant-panel" }, listeners: { "keydown.escape": "onEscape($event)", "keydown.tab": "onTab($event)", "keydown.shift.tab": "onTab($event)" }, properties: { "attr.role": "isPhone() ? \"dialog\" : \"complementary\"", "attr.aria-modal": "isPhone() ? \"true\" : null", "class.mm-assistant-overlay": "overlay()", "class.mm-assistant-resizing": "resizing()", "attr.data-mode": "effectiveMode()", "style.width.px": "isPhone() ? null : appliedWidth()" } }, viewQueries: [{ propertyName: "composer", first: true, predicate: AssistantComposerComponent, descendants: true, isSignal: true }], ngImport: i0, template: "@if (canResize()) {\n <div class=\"resize-handle\" role=\"separator\" tabindex=\"0\" aria-orientation=\"vertical\" aria-controls=\"assistant-panel\"\n [attr.aria-label]=\"m().resizePanel\" [title]=\"m().resizePanel\"\n [attr.aria-valuenow]=\"currentWidth()\" [attr.aria-valuemin]=\"effectiveMin()\" [attr.aria-valuemax]=\"effectiveMax()\"\n (pointerdown)=\"startResize($event)\" (keydown)=\"onResizeKey($event)\" (dblclick)=\"resetWidth()\"></div>\n}\n\n<header class=\"head\">\n <span class=\"sparkle\" aria-hidden=\"true\">\u2726</span>\n <h2 class=\"title\" id=\"assistant-panel-title\">{{ m().title }}</h2>\n @if (connected()) {\n <span class=\"chip-neutral\" [title]=\"m().readsWithYourPermissions\">{{ m().readsWithYourPermissions }}</span>\n }\n @if (assistant.sessionsSupported) {\n <div class=\"head-actions\">\n <button type=\"button\" class=\"icon-btn new-chat\" [attr.aria-label]=\"m().newChat\" [title]=\"m().newChat\" (click)=\"newChat()\">\n <kendo-svg-icon [icon]=\"plusIcon\"></kendo-svg-icon>\n </button>\n <button type=\"button\" class=\"icon-btn sessions-toggle\" aria-controls=\"assistant-sessions\"\n [attr.aria-label]=\"m().sessions\" [title]=\"m().sessions\"\n [attr.aria-expanded]=\"view() === 'sessions'\"\n (click)=\"view() === 'sessions' ? backToChat() : showSessions()\">\n <kendo-svg-icon [icon]=\"historyIcon\"></kendo-svg-icon>\n </button>\n </div>\n }\n <button type=\"button\" class=\"icon-btn close\" [attr.aria-label]=\"m().closeAssistant\" [title]=\"m().closeAssistant\" (click)=\"assistant.close()\">\n <kendo-svg-icon [icon]=\"xIcon\"></kendo-svg-icon>\n </button>\n</header>\n\n@if (assistant.contextChips().length) {\n <ul class=\"ctx\" [attr.aria-label]=\"m().contextLabel\">\n @for (chip of assistant.contextChips(); track chip.id; let i = $index) {\n <li class=\"ctx-chip\" [attr.data-kind]=\"chip.kind\" [title]=\"chip.value\">\n <span class=\"ctx-label\">{{ chip.label }}</span>\n <button type=\"button\" [attr.aria-label]=\"format(m().removeContext, { label: chip.label })\" [title]=\"format(m().removeContext, { label: chip.label })\" (click)=\"removeChip(chip.id, i)\"><kendo-svg-icon [icon]=\"xIcon\" size=\"small\"></kendo-svg-icon></button>\n </li>\n }\n </ul>\n}\n\n@if (view() === 'sessions') {\n <section class=\"sessions\" id=\"assistant-sessions\" [attr.aria-label]=\"m().sessionsLabel\">\n <button type=\"button\" class=\"text-btn back\" (click)=\"backToChat()\">\u2190 {{ m().backToChat }}</button>\n <ul class=\"session-list\">\n @for (session of assistant.sessions(); track session.id) {\n <li class=\"session-row\" [attr.data-session-id]=\"session.id\"\n [class.active]=\"session.id === assistant.activeSessionId()\">\n @if (confirmingDelete() === session.id) {\n <div class=\"session-confirm\" role=\"group\" [attr.aria-label]=\"format(m().deleteSessionConfirm, { title: session.title })\">\n <span class=\"confirm-text\">{{ format(m().deleteSessionConfirm, { title: session.title }) }}</span>\n <button type=\"button\" class=\"text-btn danger confirm-delete\" [disabled]=\"busySession() === session.id\"\n (click)=\"confirmDelete(session)\">{{ m().delete }}</button>\n <button type=\"button\" class=\"text-btn cancel-delete\" (click)=\"cancelDelete()\">{{ m().cancel }}</button>\n </div>\n } @else {\n <button type=\"button\" class=\"session-open\" [title]=\"session.title\"\n [attr.aria-current]=\"session.id === assistant.activeSessionId() ? 'true' : null\"\n [disabled]=\"busySession() === session.id\"\n (click)=\"openSession(session)\">\n <span class=\"session-title\">{{ session.title }}</span>\n @if (sessionDate(session); as date) {\n <span class=\"session-date\">{{ date }}</span>\n }\n </button>\n @if (assistant.sessionDeleteSupported) {\n <button type=\"button\" class=\"icon-btn session-delete\"\n [attr.aria-label]=\"format(m().deleteSession, { title: session.title })\"\n [title]=\"format(m().deleteSession, { title: session.title })\"\n [disabled]=\"busySession() === session.id\"\n (click)=\"askDelete(session)\">\n <kendo-svg-icon [icon]=\"trashIcon\" size=\"small\"></kendo-svg-icon>\n </button>\n }\n }\n </li>\n } @empty {\n <li class=\"session-empty\">{{ m().noSessions }}</li>\n }\n </ul>\n </section>\n} @else {\n @if (showStarters()) {\n <section class=\"starters\" aria-labelledby=\"assistant-starters-title\">\n <h3 class=\"starters-title\" id=\"assistant-starters-title\">{{ m().startersTitle }}</h3>\n <ul class=\"starter-list\">\n @for (question of assistant.starterQuestions(); track $index) {\n <li><button type=\"button\" class=\"starter\" [disabled]=\"!assistant.canSend()\" (click)=\"sendStarter(question)\">{{ question }}</button></li>\n }\n </ul>\n </section>\n }\n\n <mm-assistant-thread class=\"thread\" [items]=\"assistant.thread()\" [messages]=\"messages()\" (proposalDecision)=\"onDecision($event)\"></mm-assistant-thread>\n\n @if (!connected() || assistant.transportStatus() === 'error') {\n <div class=\"unavailable\" role=\"status\">\n <span class=\"sparkle\" aria-hidden=\"true\">\u2726</span>\n <p>{{ assistant.transportMessage() ?? m().notAvailable }}</p>\n </div>\n }\n\n <mm-assistant-composer\n [messages]=\"messages()\"\n [text]=\"assistant.draft()\"\n (textChange)=\"assistant.setDraft($event)\"\n [attachments]=\"assistant.attachments\"\n [files]=\"assistant.draftFiles()\"\n (filesChange)=\"assistant.setDraftFiles($event)\"\n [canSend]=\"assistant.canSend()\"\n [sending]=\"assistant.sending()\"\n (send)=\"assistant.send($event)\"\n (stop)=\"assistant.stop()\">\n </mm-assistant-composer>\n}\n", styles: [":host{position:relative;display:flex;flex-direction:column;width:400px;min-height:0;border-left:1px solid var(--theme-border-subtle);background:var(--theme-bg-surface);color:var(--theme-text-primary)}.head{display:flex;align-items:center;gap:var(--theme-space-2);padding:10px 14px;border-bottom:1px solid var(--theme-border-subtle)}.title{flex:none;margin:0;white-space:nowrap;font-family:var(--theme-font-display);font-size:.8125rem;font-weight:600}.sparkle{color:var(--theme-ai)}.chip-neutral{min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;margin-left:6px;padding:0 7px;border-radius:var(--theme-radius-xs);background:var(--theme-status-neutral-subtle);color:var(--theme-text-secondary);font-size:.71875rem}.icon-btn{flex:none;display:grid;place-items:center;width:28px;height:28px;border:0;border-radius:var(--theme-radius-sm);background:none;color:var(--theme-text-secondary);cursor:pointer}.icon-btn:hover{background:var(--theme-bg-hover)}.icon-btn[aria-expanded=true]{background:var(--theme-accent-subtle);color:var(--theme-text-primary)}.icon-btn:focus-visible{outline:none;box-shadow:var(--theme-focus-ring)}.close{margin-left:auto}.ctx{display:flex;flex-wrap:wrap;gap:6px;margin:0;padding:8px 14px;list-style:none;border-bottom:1px solid var(--theme-border-subtle)}.ctx-chip{display:inline-flex;align-items:center;gap:6px;max-width:100%;height:22px;padding:0 4px 0 8px;border:1px solid var(--theme-border-default);border-radius:var(--theme-radius-xs);font-size:.75rem;color:var(--theme-text-secondary)}.ctx-chip button{display:inline-grid;flex:none;place-items:center;border:0;padding:0 2px;background:none;color:var(--theme-text-muted);cursor:pointer}.ctx-chip button:hover{color:var(--theme-text-primary)}.ctx-chip button:focus-visible{outline:none;box-shadow:var(--theme-focus-ring)}.ctx-label{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.thread{flex:1 1 auto;min-height:0}.unavailable{display:flex;align-items:center;gap:var(--theme-space-2);margin:0 14px var(--theme-space-2);padding:8px 10px;border:1px dashed var(--theme-border-default);border-radius:var(--theme-radius-sm);color:var(--theme-text-secondary)}.unavailable p{margin:0}@media(max-width:1180px){:host{width:340px}}@media(max-width:640px){:host{position:fixed;inset:48px 0 0;z-index:30;width:auto;border-left:0}}.head-actions{display:flex;flex:none;gap:2px;margin-left:auto}.head-actions+.close{margin-left:0}.text-btn{height:26px;padding:0 8px;border:0;border-radius:var(--theme-radius-sm);background:none;color:var(--theme-text-secondary);font:inherit;font-size:.75rem;cursor:pointer}.text-btn:hover:not(:disabled){background:var(--theme-bg-hover);color:var(--theme-text-primary)}.text-btn[aria-expanded=true]{background:var(--theme-accent-subtle);color:var(--theme-text-primary)}.text-btn:disabled{color:var(--theme-text-muted);cursor:default}.text-btn:focus-visible{outline:none;box-shadow:var(--theme-focus-ring)}.text-btn.danger{color:var(--theme-status-error)}.sessions{display:flex;flex:1 1 auto;flex-direction:column;gap:var(--theme-space-2);min-height:0;padding:10px 14px;overflow:auto}.sessions .back{align-self:flex-start}.session-list{display:grid;gap:2px;margin:0;padding:0;list-style:none}.session-row{display:flex;align-items:center;gap:4px;border-radius:var(--theme-radius-sm)}.session-row.active{background:var(--theme-accent-subtle)}.session-open{display:grid;flex:1 1 auto;min-width:0;padding:6px 8px;border:0;border-radius:var(--theme-radius-sm);background:none;color:var(--theme-text-primary);font:inherit;text-align:left;cursor:pointer}.session-open:hover:not(:disabled){background:var(--theme-bg-hover)}.session-open:focus-visible{outline:none;box-shadow:var(--theme-focus-ring)}.session-title{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.session-date{color:var(--theme-text-muted);font-size:.71875rem}.session-confirm{display:flex;flex:1 1 auto;flex-wrap:wrap;align-items:center;gap:4px;padding:4px 8px}.confirm-text{flex:1 1 100%;overflow-wrap:anywhere}.session-empty{padding:6px 8px;color:var(--theme-text-secondary)}.starters{display:grid;gap:var(--theme-space-2);padding:14px 14px 0}.starters-title{margin:0;color:var(--theme-text-secondary);font-size:.75rem;font-weight:600}.starter-list{display:grid;gap:6px;margin:0;padding:0;list-style:none}.starter{width:100%;padding:6px 10px;border:1px solid var(--theme-border-default);border-radius:var(--theme-radius-sm);background:none;color:var(--theme-text-primary);font:inherit;text-align:left;cursor:pointer}.starter:hover:not(:disabled){border-color:var(--theme-ai);background:var(--theme-bg-hover)}.starter:disabled{color:var(--theme-text-muted);cursor:default}.starter:focus-visible{outline:none;box-shadow:var(--theme-focus-ring)}.resize-handle{position:absolute;top:0;bottom:0;left:-3px;z-index:1;width:6px;cursor:col-resize;touch-action:none}.resize-handle:after{content:\"\";position:absolute;top:0;bottom:0;left:2px;width:2px;background:transparent;transition:background-color .15s}.resize-handle:hover:after,.resize-handle:focus-visible:after{background:var(--theme-accent)}.resize-handle:focus-visible{outline:none}:host(.mm-assistant-resizing){-webkit-user-select:none;user-select:none;cursor:col-resize}:host(.mm-assistant-resizing) .resize-handle:after{background:var(--theme-accent)}@media(prefers-reduced-motion:reduce){.resize-handle:after{transition:none}}:host(.mm-assistant-overlay){position:fixed;top:var(--mm-assistant-overlay-top, 48px);right:0;bottom:0;z-index:var(--mm-assistant-overlay-z-index, 20);box-shadow:var(--theme-shadow-panel)}\n"], dependencies: [{ kind: "component", type: AssistantThreadComponent, selector: "mm-assistant-thread", inputs: ["items", "messages"], outputs: ["proposalDecision"] }, { kind: "component", type: AssistantComposerComponent, selector: "mm-assistant-composer", inputs: ["text", "canSend", "sending", "attachments", "files", "messages"], outputs: ["textChange", "filesChange", "send", "stop"] }, { kind: "component", type: SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon", "variant"], exportAs: ["kendoSVGIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
736
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.5", type: AssistantPanelComponent, isStandalone: true, selector: "mm-assistant-panel", inputs: { messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, resizable: { classPropertyName: "resizable", publicName: "resizable", isSignal: true, isRequired: false, transformFunction: null }, minWidth: { classPropertyName: "minWidth", publicName: "minWidth", isSignal: true, isRequired: false, transformFunction: null }, maxWidth: { classPropertyName: "maxWidth", publicName: "maxWidth", isSignal: true, isRequired: false, transformFunction: null }, storageKey: { classPropertyName: "storageKey", publicName: "storageKey", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "aria-labelledby": "assistant-panel-title", "id": "assistant-panel" }, listeners: { "keydown.escape": "onEscape($event)", "keydown.tab": "onTab($event)", "keydown.shift.tab": "onTab($event)" }, properties: { "attr.role": "isPhone() ? \"dialog\" : \"complementary\"", "attr.aria-modal": "isPhone() ? \"true\" : null", "class.mm-assistant-overlay": "overlay()", "class.mm-assistant-resizing": "resizing()", "attr.data-mode": "effectiveMode()", "style.width.px": "isPhone() ? null : appliedWidth()" } }, viewQueries: [{ propertyName: "composer", first: true, predicate: AssistantComposerComponent, descendants: true, isSignal: true }], ngImport: i0, template: "@if (canResize()) {\n <div class=\"resize-handle\" role=\"separator\" tabindex=\"0\" aria-orientation=\"vertical\" aria-controls=\"assistant-panel\"\n [attr.aria-label]=\"m().resizePanel\" [title]=\"m().resizePanel\"\n [attr.aria-valuenow]=\"currentWidth()\" [attr.aria-valuemin]=\"effectiveMin()\" [attr.aria-valuemax]=\"effectiveMax()\"\n (pointerdown)=\"startResize($event)\" (keydown)=\"onResizeKey($event)\" (dblclick)=\"resetWidth()\"></div>\n}\n\n<header class=\"head\">\n <span class=\"sparkle\" aria-hidden=\"true\">\u2726</span>\n <h2 class=\"title\" id=\"assistant-panel-title\">{{ m().title }}</h2>\n @if (connected()) {\n <span class=\"chip-neutral\" [title]=\"m().readsWithYourPermissions\">{{ m().readsWithYourPermissions }}</span>\n }\n @if (assistant.sessionsSupported) {\n <div class=\"head-actions\">\n <button type=\"button\" class=\"icon-btn new-chat\" [attr.aria-label]=\"m().newChat\" [title]=\"m().newChat\" (click)=\"newChat()\">\n <kendo-svg-icon [icon]=\"plusIcon\"></kendo-svg-icon>\n </button>\n <button type=\"button\" class=\"icon-btn sessions-toggle\" aria-controls=\"assistant-sessions\"\n [attr.aria-label]=\"m().sessions\" [title]=\"m().sessions\"\n [attr.aria-expanded]=\"view() === 'sessions'\"\n (click)=\"view() === 'sessions' ? backToChat() : showSessions()\">\n <kendo-svg-icon [icon]=\"historyIcon\"></kendo-svg-icon>\n </button>\n </div>\n }\n <button type=\"button\" class=\"icon-btn close\" [attr.aria-label]=\"m().closeAssistant\" [title]=\"m().closeAssistant\" (click)=\"assistant.close()\">\n <kendo-svg-icon [icon]=\"xIcon\"></kendo-svg-icon>\n </button>\n</header>\n\n@if (assistant.contextChips().length) {\n <ul class=\"ctx\" [attr.aria-label]=\"m().contextLabel\">\n @for (chip of assistant.contextChips(); track chip.id; let i = $index) {\n <li class=\"ctx-chip\" [attr.data-kind]=\"chip.kind\" [title]=\"chip.value\">\n <span class=\"ctx-label\">{{ chip.label }}</span>\n <button type=\"button\" [attr.aria-label]=\"format(m().removeContext, { label: chip.label })\" [title]=\"format(m().removeContext, { label: chip.label })\" (click)=\"removeChip(chip.id, i)\"><kendo-svg-icon [icon]=\"xIcon\" size=\"small\"></kendo-svg-icon></button>\n </li>\n }\n </ul>\n}\n\n@if (view() === 'sessions') {\n <section class=\"sessions\" id=\"assistant-sessions\" [attr.aria-label]=\"m().sessionsLabel\">\n <button type=\"button\" class=\"text-btn back\" (click)=\"backToChat()\">\u2190 {{ m().backToChat }}</button>\n <ul class=\"session-list\">\n @for (session of assistant.sessions(); track session.id) {\n <li class=\"session-row\" [attr.data-session-id]=\"session.id\"\n [class.active]=\"session.id === assistant.activeSessionId()\">\n @if (confirmingDelete() === session.id) {\n <div class=\"session-confirm\" role=\"group\" [attr.aria-label]=\"format(m().deleteSessionConfirm, { title: session.title })\">\n <span class=\"confirm-text\">{{ format(m().deleteSessionConfirm, { title: session.title }) }}</span>\n <button type=\"button\" class=\"text-btn danger confirm-delete\" [disabled]=\"busySession() === session.id\"\n (click)=\"confirmDelete(session)\">{{ m().delete }}</button>\n <button type=\"button\" class=\"text-btn cancel-delete\" (click)=\"cancelDelete()\">{{ m().cancel }}</button>\n </div>\n } @else {\n <button type=\"button\" class=\"session-open\" [title]=\"session.title\"\n [attr.aria-current]=\"session.id === assistant.activeSessionId() ? 'true' : null\"\n [disabled]=\"busySession() === session.id\"\n (click)=\"openSession(session)\">\n <span class=\"session-title\">{{ session.title }}</span>\n @if (sessionDate(session); as date) {\n <span class=\"session-date\">{{ date }}</span>\n }\n </button>\n @if (assistant.sessionDeleteSupported) {\n <button type=\"button\" class=\"icon-btn session-delete\"\n [attr.aria-label]=\"format(m().deleteSession, { title: session.title })\"\n [title]=\"format(m().deleteSession, { title: session.title })\"\n [disabled]=\"busySession() === session.id\"\n (click)=\"askDelete(session)\">\n <kendo-svg-icon [icon]=\"trashIcon\" size=\"small\"></kendo-svg-icon>\n </button>\n }\n }\n </li>\n } @empty {\n <li class=\"session-empty\">{{ m().noSessions }}</li>\n }\n </ul>\n </section>\n} @else {\n @if (showGreeting()) {\n <div class=\"greeting\" data-testid=\"assistant-greeting\">\n <mm-octobot animation=\"wave\" size=\"md\" playOnce />\n <p class=\"greeting-text\">{{ m().greeting }}</p>\n </div>\n }\n @if (showStarters()) {\n <section class=\"starters\" aria-labelledby=\"assistant-starters-title\">\n <h3 class=\"starters-title\" id=\"assistant-starters-title\">{{ m().startersTitle }}</h3>\n <ul class=\"starter-list\">\n @for (question of assistant.starterQuestions(); track $index) {\n <li><button type=\"button\" class=\"starter\" [disabled]=\"!assistant.canSend()\" (click)=\"sendStarter(question)\">{{ question }}</button></li>\n }\n </ul>\n </section>\n }\n\n <mm-assistant-thread class=\"thread\" [items]=\"assistant.thread()\" [messages]=\"messages()\" (proposalDecision)=\"onDecision($event)\"></mm-assistant-thread>\n\n @if (!connected() || assistant.transportStatus() === 'error') {\n <div class=\"unavailable\" role=\"status\">\n <span class=\"sparkle\" aria-hidden=\"true\">\u2726</span>\n <p>{{ assistant.transportMessage() ?? m().notAvailable }}</p>\n </div>\n }\n\n <mm-assistant-composer\n [messages]=\"messages()\"\n [text]=\"assistant.draft()\"\n (textChange)=\"assistant.setDraft($event)\"\n [attachments]=\"assistant.attachments\"\n [files]=\"assistant.draftFiles()\"\n (filesChange)=\"assistant.setDraftFiles($event)\"\n [canSend]=\"assistant.canSend()\"\n [sending]=\"assistant.sending()\"\n (send)=\"assistant.send($event)\"\n (stop)=\"assistant.stop()\">\n </mm-assistant-composer>\n}\n", styles: [":host{position:relative;display:flex;flex-direction:column;width:400px;min-height:0;border-left:1px solid var(--theme-border-subtle);background:var(--theme-bg-surface);color:var(--theme-text-primary)}.head{display:flex;align-items:center;gap:var(--theme-space-2);padding:10px 14px;border-bottom:1px solid var(--theme-border-subtle)}.title{flex:none;margin:0;white-space:nowrap;font-family:var(--theme-font-display);font-size:.8125rem;font-weight:600}.sparkle{color:var(--theme-ai)}.chip-neutral{min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;margin-left:6px;padding:0 7px;border-radius:var(--theme-radius-xs);background:var(--theme-status-neutral-subtle);color:var(--theme-text-secondary);font-size:.71875rem}.icon-btn{flex:none;display:grid;place-items:center;width:28px;height:28px;border:0;border-radius:var(--theme-radius-sm);background:none;color:var(--theme-text-secondary);cursor:pointer}.icon-btn:hover{background:var(--theme-bg-hover)}.icon-btn[aria-expanded=true]{background:var(--theme-accent-subtle);color:var(--theme-text-primary)}.icon-btn:focus-visible{outline:none;box-shadow:var(--theme-focus-ring)}.close{margin-left:auto}.ctx{display:flex;flex-wrap:wrap;gap:6px;margin:0;padding:8px 14px;list-style:none;border-bottom:1px solid var(--theme-border-subtle)}.ctx-chip{display:inline-flex;align-items:center;gap:6px;max-width:100%;height:22px;padding:0 4px 0 8px;border:1px solid var(--theme-border-default);border-radius:var(--theme-radius-xs);font-size:.75rem;color:var(--theme-text-secondary)}.ctx-chip button{display:inline-grid;flex:none;place-items:center;border:0;padding:0 2px;background:none;color:var(--theme-text-muted);cursor:pointer}.ctx-chip button:hover{color:var(--theme-text-primary)}.ctx-chip button:focus-visible{outline:none;box-shadow:var(--theme-focus-ring)}.ctx-label{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.thread{flex:1 1 auto;min-height:0}.unavailable{display:flex;align-items:center;gap:var(--theme-space-2);margin:0 14px var(--theme-space-2);padding:8px 10px;border:1px dashed var(--theme-border-default);border-radius:var(--theme-radius-sm);color:var(--theme-text-secondary)}.unavailable p{margin:0}@media(max-width:1180px){:host{width:340px}}@media(max-width:640px){:host{position:fixed;inset:48px 0 0;z-index:30;width:auto;border-left:0}}.head-actions{display:flex;flex:none;gap:2px;margin-left:auto}.head-actions+.close{margin-left:0}.text-btn{height:26px;padding:0 8px;border:0;border-radius:var(--theme-radius-sm);background:none;color:var(--theme-text-secondary);font:inherit;font-size:.75rem;cursor:pointer}.text-btn:hover:not(:disabled){background:var(--theme-bg-hover);color:var(--theme-text-primary)}.text-btn[aria-expanded=true]{background:var(--theme-accent-subtle);color:var(--theme-text-primary)}.text-btn:disabled{color:var(--theme-text-muted);cursor:default}.text-btn:focus-visible{outline:none;box-shadow:var(--theme-focus-ring)}.text-btn.danger{color:var(--theme-status-error)}.sessions{display:flex;flex:1 1 auto;flex-direction:column;gap:var(--theme-space-2);min-height:0;padding:10px 14px;overflow:auto}.sessions .back{align-self:flex-start}.session-list{display:grid;gap:2px;margin:0;padding:0;list-style:none}.session-row{display:flex;align-items:center;gap:4px;border-radius:var(--theme-radius-sm)}.session-row.active{background:var(--theme-accent-subtle)}.session-open{display:grid;flex:1 1 auto;min-width:0;padding:6px 8px;border:0;border-radius:var(--theme-radius-sm);background:none;color:var(--theme-text-primary);font:inherit;text-align:left;cursor:pointer}.session-open:hover:not(:disabled){background:var(--theme-bg-hover)}.session-open:focus-visible{outline:none;box-shadow:var(--theme-focus-ring)}.session-title{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.session-date{color:var(--theme-text-muted);font-size:.71875rem}.session-confirm{display:flex;flex:1 1 auto;flex-wrap:wrap;align-items:center;gap:4px;padding:4px 8px}.confirm-text{flex:1 1 100%;overflow-wrap:anywhere}.session-empty{padding:6px 8px;color:var(--theme-text-secondary)}.greeting{display:grid;justify-items:center;gap:var(--theme-space-2);padding:20px 14px 0;text-align:center}.greeting-text{margin:0;color:var(--theme-text-primary);font-weight:600}.starters{display:grid;gap:var(--theme-space-2);padding:14px 14px 0}.starters-title{margin:0;color:var(--theme-text-secondary);font-size:.75rem;font-weight:600}.starter-list{display:grid;gap:6px;margin:0;padding:0;list-style:none}.starter{width:100%;padding:6px 10px;border:1px solid var(--theme-border-default);border-radius:var(--theme-radius-sm);background:none;color:var(--theme-text-primary);font:inherit;text-align:left;cursor:pointer}.starter:hover:not(:disabled){border-color:var(--theme-ai);background:var(--theme-bg-hover)}.starter:disabled{color:var(--theme-text-muted);cursor:default}.starter:focus-visible{outline:none;box-shadow:var(--theme-focus-ring)}.resize-handle{--mm-resize-line: transparent;--mm-resize-grip: var(--theme-border-default, var(--theme-border-subtle, rgba(128, 128, 128, .5)));position:absolute;top:0;bottom:0;left:-4px;z-index:1;width:8px;cursor:col-resize;touch-action:none}.resize-handle:after{content:\"\";position:absolute;top:0;bottom:0;left:3px;width:2px;background:var(--mm-resize-line);transition:background-color .15s}.resize-handle:before{content:\"\";position:absolute;top:50%;left:2px;z-index:1;width:4px;height:32px;margin-top:-16px;border-radius:2px;background:var(--mm-resize-grip);transition:background-color .15s}.resize-handle:hover,.resize-handle:focus-visible{--mm-resize-line: var(--theme-accent);--mm-resize-grip: var(--theme-accent)}.resize-handle:focus-visible{outline:none}:host(.mm-assistant-resizing){-webkit-user-select:none;user-select:none;cursor:col-resize}:host(.mm-assistant-resizing) .resize-handle{--mm-resize-line: var(--theme-accent);--mm-resize-grip: var(--theme-accent)}@media(prefers-reduced-motion:reduce){.resize-handle:after,.resize-handle:before{transition:none}}:host(.mm-assistant-overlay){position:fixed;top:var(--mm-assistant-overlay-top, 48px);right:0;bottom:0;z-index:var(--mm-assistant-overlay-z-index, 20);box-shadow:var(--theme-shadow-panel)}\n"], dependencies: [{ kind: "component", type: AssistantThreadComponent, selector: "mm-assistant-thread", inputs: ["items", "messages"], outputs: ["proposalDecision"] }, { kind: "component", type: AssistantComposerComponent, selector: "mm-assistant-composer", inputs: ["text", "canSend", "sending", "attachments", "files", "messages"], outputs: ["textChange", "filesChange", "send", "stop"] }, { kind: "component", type: SVGIconComponent, selector: "kendo-svg-icon, kendo-svgicon", inputs: ["icon", "variant"], exportAs: ["kendoSVGIcon"] }, { kind: "component", type: OctoBotComponent, selector: "mm-octobot", inputs: ["animation", "size", "label", "loading", "still", "playOnce"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
714
737
  }
715
738
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImport: i0, type: AssistantPanelComponent, decorators: [{
716
739
  type: Component,
717
- args: [{ selector: 'mm-assistant-panel', imports: [AssistantThreadComponent, AssistantComposerComponent, SVGIconComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
740
+ args: [{ selector: 'mm-assistant-panel', imports: [AssistantThreadComponent, AssistantComposerComponent, SVGIconComponent, OctoBotComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: {
718
741
  '[attr.role]': 'isPhone() ? "dialog" : "complementary"',
719
742
  '[attr.aria-modal]': 'isPhone() ? "true" : null',
720
743
  'aria-labelledby': 'assistant-panel-title',
@@ -726,7 +749,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.5", ngImpor
726
749
  '(keydown.escape)': 'onEscape($event)',
727
750
  '(keydown.tab)': 'onTab($event)',
728
751
  '(keydown.shift.tab)': 'onTab($event)'
729
- }, template: "@if (canResize()) {\n <div class=\"resize-handle\" role=\"separator\" tabindex=\"0\" aria-orientation=\"vertical\" aria-controls=\"assistant-panel\"\n [attr.aria-label]=\"m().resizePanel\" [title]=\"m().resizePanel\"\n [attr.aria-valuenow]=\"currentWidth()\" [attr.aria-valuemin]=\"effectiveMin()\" [attr.aria-valuemax]=\"effectiveMax()\"\n (pointerdown)=\"startResize($event)\" (keydown)=\"onResizeKey($event)\" (dblclick)=\"resetWidth()\"></div>\n}\n\n<header class=\"head\">\n <span class=\"sparkle\" aria-hidden=\"true\">\u2726</span>\n <h2 class=\"title\" id=\"assistant-panel-title\">{{ m().title }}</h2>\n @if (connected()) {\n <span class=\"chip-neutral\" [title]=\"m().readsWithYourPermissions\">{{ m().readsWithYourPermissions }}</span>\n }\n @if (assistant.sessionsSupported) {\n <div class=\"head-actions\">\n <button type=\"button\" class=\"icon-btn new-chat\" [attr.aria-label]=\"m().newChat\" [title]=\"m().newChat\" (click)=\"newChat()\">\n <kendo-svg-icon [icon]=\"plusIcon\"></kendo-svg-icon>\n </button>\n <button type=\"button\" class=\"icon-btn sessions-toggle\" aria-controls=\"assistant-sessions\"\n [attr.aria-label]=\"m().sessions\" [title]=\"m().sessions\"\n [attr.aria-expanded]=\"view() === 'sessions'\"\n (click)=\"view() === 'sessions' ? backToChat() : showSessions()\">\n <kendo-svg-icon [icon]=\"historyIcon\"></kendo-svg-icon>\n </button>\n </div>\n }\n <button type=\"button\" class=\"icon-btn close\" [attr.aria-label]=\"m().closeAssistant\" [title]=\"m().closeAssistant\" (click)=\"assistant.close()\">\n <kendo-svg-icon [icon]=\"xIcon\"></kendo-svg-icon>\n </button>\n</header>\n\n@if (assistant.contextChips().length) {\n <ul class=\"ctx\" [attr.aria-label]=\"m().contextLabel\">\n @for (chip of assistant.contextChips(); track chip.id; let i = $index) {\n <li class=\"ctx-chip\" [attr.data-kind]=\"chip.kind\" [title]=\"chip.value\">\n <span class=\"ctx-label\">{{ chip.label }}</span>\n <button type=\"button\" [attr.aria-label]=\"format(m().removeContext, { label: chip.label })\" [title]=\"format(m().removeContext, { label: chip.label })\" (click)=\"removeChip(chip.id, i)\"><kendo-svg-icon [icon]=\"xIcon\" size=\"small\"></kendo-svg-icon></button>\n </li>\n }\n </ul>\n}\n\n@if (view() === 'sessions') {\n <section class=\"sessions\" id=\"assistant-sessions\" [attr.aria-label]=\"m().sessionsLabel\">\n <button type=\"button\" class=\"text-btn back\" (click)=\"backToChat()\">\u2190 {{ m().backToChat }}</button>\n <ul class=\"session-list\">\n @for (session of assistant.sessions(); track session.id) {\n <li class=\"session-row\" [attr.data-session-id]=\"session.id\"\n [class.active]=\"session.id === assistant.activeSessionId()\">\n @if (confirmingDelete() === session.id) {\n <div class=\"session-confirm\" role=\"group\" [attr.aria-label]=\"format(m().deleteSessionConfirm, { title: session.title })\">\n <span class=\"confirm-text\">{{ format(m().deleteSessionConfirm, { title: session.title }) }}</span>\n <button type=\"button\" class=\"text-btn danger confirm-delete\" [disabled]=\"busySession() === session.id\"\n (click)=\"confirmDelete(session)\">{{ m().delete }}</button>\n <button type=\"button\" class=\"text-btn cancel-delete\" (click)=\"cancelDelete()\">{{ m().cancel }}</button>\n </div>\n } @else {\n <button type=\"button\" class=\"session-open\" [title]=\"session.title\"\n [attr.aria-current]=\"session.id === assistant.activeSessionId() ? 'true' : null\"\n [disabled]=\"busySession() === session.id\"\n (click)=\"openSession(session)\">\n <span class=\"session-title\">{{ session.title }}</span>\n @if (sessionDate(session); as date) {\n <span class=\"session-date\">{{ date }}</span>\n }\n </button>\n @if (assistant.sessionDeleteSupported) {\n <button type=\"button\" class=\"icon-btn session-delete\"\n [attr.aria-label]=\"format(m().deleteSession, { title: session.title })\"\n [title]=\"format(m().deleteSession, { title: session.title })\"\n [disabled]=\"busySession() === session.id\"\n (click)=\"askDelete(session)\">\n <kendo-svg-icon [icon]=\"trashIcon\" size=\"small\"></kendo-svg-icon>\n </button>\n }\n }\n </li>\n } @empty {\n <li class=\"session-empty\">{{ m().noSessions }}</li>\n }\n </ul>\n </section>\n} @else {\n @if (showStarters()) {\n <section class=\"starters\" aria-labelledby=\"assistant-starters-title\">\n <h3 class=\"starters-title\" id=\"assistant-starters-title\">{{ m().startersTitle }}</h3>\n <ul class=\"starter-list\">\n @for (question of assistant.starterQuestions(); track $index) {\n <li><button type=\"button\" class=\"starter\" [disabled]=\"!assistant.canSend()\" (click)=\"sendStarter(question)\">{{ question }}</button></li>\n }\n </ul>\n </section>\n }\n\n <mm-assistant-thread class=\"thread\" [items]=\"assistant.thread()\" [messages]=\"messages()\" (proposalDecision)=\"onDecision($event)\"></mm-assistant-thread>\n\n @if (!connected() || assistant.transportStatus() === 'error') {\n <div class=\"unavailable\" role=\"status\">\n <span class=\"sparkle\" aria-hidden=\"true\">\u2726</span>\n <p>{{ assistant.transportMessage() ?? m().notAvailable }}</p>\n </div>\n }\n\n <mm-assistant-composer\n [messages]=\"messages()\"\n [text]=\"assistant.draft()\"\n (textChange)=\"assistant.setDraft($event)\"\n [attachments]=\"assistant.attachments\"\n [files]=\"assistant.draftFiles()\"\n (filesChange)=\"assistant.setDraftFiles($event)\"\n [canSend]=\"assistant.canSend()\"\n [sending]=\"assistant.sending()\"\n (send)=\"assistant.send($event)\"\n (stop)=\"assistant.stop()\">\n </mm-assistant-composer>\n}\n", styles: [":host{position:relative;display:flex;flex-direction:column;width:400px;min-height:0;border-left:1px solid var(--theme-border-subtle);background:var(--theme-bg-surface);color:var(--theme-text-primary)}.head{display:flex;align-items:center;gap:var(--theme-space-2);padding:10px 14px;border-bottom:1px solid var(--theme-border-subtle)}.title{flex:none;margin:0;white-space:nowrap;font-family:var(--theme-font-display);font-size:.8125rem;font-weight:600}.sparkle{color:var(--theme-ai)}.chip-neutral{min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;margin-left:6px;padding:0 7px;border-radius:var(--theme-radius-xs);background:var(--theme-status-neutral-subtle);color:var(--theme-text-secondary);font-size:.71875rem}.icon-btn{flex:none;display:grid;place-items:center;width:28px;height:28px;border:0;border-radius:var(--theme-radius-sm);background:none;color:var(--theme-text-secondary);cursor:pointer}.icon-btn:hover{background:var(--theme-bg-hover)}.icon-btn[aria-expanded=true]{background:var(--theme-accent-subtle);color:var(--theme-text-primary)}.icon-btn:focus-visible{outline:none;box-shadow:var(--theme-focus-ring)}.close{margin-left:auto}.ctx{display:flex;flex-wrap:wrap;gap:6px;margin:0;padding:8px 14px;list-style:none;border-bottom:1px solid var(--theme-border-subtle)}.ctx-chip{display:inline-flex;align-items:center;gap:6px;max-width:100%;height:22px;padding:0 4px 0 8px;border:1px solid var(--theme-border-default);border-radius:var(--theme-radius-xs);font-size:.75rem;color:var(--theme-text-secondary)}.ctx-chip button{display:inline-grid;flex:none;place-items:center;border:0;padding:0 2px;background:none;color:var(--theme-text-muted);cursor:pointer}.ctx-chip button:hover{color:var(--theme-text-primary)}.ctx-chip button:focus-visible{outline:none;box-shadow:var(--theme-focus-ring)}.ctx-label{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.thread{flex:1 1 auto;min-height:0}.unavailable{display:flex;align-items:center;gap:var(--theme-space-2);margin:0 14px var(--theme-space-2);padding:8px 10px;border:1px dashed var(--theme-border-default);border-radius:var(--theme-radius-sm);color:var(--theme-text-secondary)}.unavailable p{margin:0}@media(max-width:1180px){:host{width:340px}}@media(max-width:640px){:host{position:fixed;inset:48px 0 0;z-index:30;width:auto;border-left:0}}.head-actions{display:flex;flex:none;gap:2px;margin-left:auto}.head-actions+.close{margin-left:0}.text-btn{height:26px;padding:0 8px;border:0;border-radius:var(--theme-radius-sm);background:none;color:var(--theme-text-secondary);font:inherit;font-size:.75rem;cursor:pointer}.text-btn:hover:not(:disabled){background:var(--theme-bg-hover);color:var(--theme-text-primary)}.text-btn[aria-expanded=true]{background:var(--theme-accent-subtle);color:var(--theme-text-primary)}.text-btn:disabled{color:var(--theme-text-muted);cursor:default}.text-btn:focus-visible{outline:none;box-shadow:var(--theme-focus-ring)}.text-btn.danger{color:var(--theme-status-error)}.sessions{display:flex;flex:1 1 auto;flex-direction:column;gap:var(--theme-space-2);min-height:0;padding:10px 14px;overflow:auto}.sessions .back{align-self:flex-start}.session-list{display:grid;gap:2px;margin:0;padding:0;list-style:none}.session-row{display:flex;align-items:center;gap:4px;border-radius:var(--theme-radius-sm)}.session-row.active{background:var(--theme-accent-subtle)}.session-open{display:grid;flex:1 1 auto;min-width:0;padding:6px 8px;border:0;border-radius:var(--theme-radius-sm);background:none;color:var(--theme-text-primary);font:inherit;text-align:left;cursor:pointer}.session-open:hover:not(:disabled){background:var(--theme-bg-hover)}.session-open:focus-visible{outline:none;box-shadow:var(--theme-focus-ring)}.session-title{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.session-date{color:var(--theme-text-muted);font-size:.71875rem}.session-confirm{display:flex;flex:1 1 auto;flex-wrap:wrap;align-items:center;gap:4px;padding:4px 8px}.confirm-text{flex:1 1 100%;overflow-wrap:anywhere}.session-empty{padding:6px 8px;color:var(--theme-text-secondary)}.starters{display:grid;gap:var(--theme-space-2);padding:14px 14px 0}.starters-title{margin:0;color:var(--theme-text-secondary);font-size:.75rem;font-weight:600}.starter-list{display:grid;gap:6px;margin:0;padding:0;list-style:none}.starter{width:100%;padding:6px 10px;border:1px solid var(--theme-border-default);border-radius:var(--theme-radius-sm);background:none;color:var(--theme-text-primary);font:inherit;text-align:left;cursor:pointer}.starter:hover:not(:disabled){border-color:var(--theme-ai);background:var(--theme-bg-hover)}.starter:disabled{color:var(--theme-text-muted);cursor:default}.starter:focus-visible{outline:none;box-shadow:var(--theme-focus-ring)}.resize-handle{position:absolute;top:0;bottom:0;left:-3px;z-index:1;width:6px;cursor:col-resize;touch-action:none}.resize-handle:after{content:\"\";position:absolute;top:0;bottom:0;left:2px;width:2px;background:transparent;transition:background-color .15s}.resize-handle:hover:after,.resize-handle:focus-visible:after{background:var(--theme-accent)}.resize-handle:focus-visible{outline:none}:host(.mm-assistant-resizing){-webkit-user-select:none;user-select:none;cursor:col-resize}:host(.mm-assistant-resizing) .resize-handle:after{background:var(--theme-accent)}@media(prefers-reduced-motion:reduce){.resize-handle:after{transition:none}}:host(.mm-assistant-overlay){position:fixed;top:var(--mm-assistant-overlay-top, 48px);right:0;bottom:0;z-index:var(--mm-assistant-overlay-z-index, 20);box-shadow:var(--theme-shadow-panel)}\n"] }]
752
+ }, template: "@if (canResize()) {\n <div class=\"resize-handle\" role=\"separator\" tabindex=\"0\" aria-orientation=\"vertical\" aria-controls=\"assistant-panel\"\n [attr.aria-label]=\"m().resizePanel\" [title]=\"m().resizePanel\"\n [attr.aria-valuenow]=\"currentWidth()\" [attr.aria-valuemin]=\"effectiveMin()\" [attr.aria-valuemax]=\"effectiveMax()\"\n (pointerdown)=\"startResize($event)\" (keydown)=\"onResizeKey($event)\" (dblclick)=\"resetWidth()\"></div>\n}\n\n<header class=\"head\">\n <span class=\"sparkle\" aria-hidden=\"true\">\u2726</span>\n <h2 class=\"title\" id=\"assistant-panel-title\">{{ m().title }}</h2>\n @if (connected()) {\n <span class=\"chip-neutral\" [title]=\"m().readsWithYourPermissions\">{{ m().readsWithYourPermissions }}</span>\n }\n @if (assistant.sessionsSupported) {\n <div class=\"head-actions\">\n <button type=\"button\" class=\"icon-btn new-chat\" [attr.aria-label]=\"m().newChat\" [title]=\"m().newChat\" (click)=\"newChat()\">\n <kendo-svg-icon [icon]=\"plusIcon\"></kendo-svg-icon>\n </button>\n <button type=\"button\" class=\"icon-btn sessions-toggle\" aria-controls=\"assistant-sessions\"\n [attr.aria-label]=\"m().sessions\" [title]=\"m().sessions\"\n [attr.aria-expanded]=\"view() === 'sessions'\"\n (click)=\"view() === 'sessions' ? backToChat() : showSessions()\">\n <kendo-svg-icon [icon]=\"historyIcon\"></kendo-svg-icon>\n </button>\n </div>\n }\n <button type=\"button\" class=\"icon-btn close\" [attr.aria-label]=\"m().closeAssistant\" [title]=\"m().closeAssistant\" (click)=\"assistant.close()\">\n <kendo-svg-icon [icon]=\"xIcon\"></kendo-svg-icon>\n </button>\n</header>\n\n@if (assistant.contextChips().length) {\n <ul class=\"ctx\" [attr.aria-label]=\"m().contextLabel\">\n @for (chip of assistant.contextChips(); track chip.id; let i = $index) {\n <li class=\"ctx-chip\" [attr.data-kind]=\"chip.kind\" [title]=\"chip.value\">\n <span class=\"ctx-label\">{{ chip.label }}</span>\n <button type=\"button\" [attr.aria-label]=\"format(m().removeContext, { label: chip.label })\" [title]=\"format(m().removeContext, { label: chip.label })\" (click)=\"removeChip(chip.id, i)\"><kendo-svg-icon [icon]=\"xIcon\" size=\"small\"></kendo-svg-icon></button>\n </li>\n }\n </ul>\n}\n\n@if (view() === 'sessions') {\n <section class=\"sessions\" id=\"assistant-sessions\" [attr.aria-label]=\"m().sessionsLabel\">\n <button type=\"button\" class=\"text-btn back\" (click)=\"backToChat()\">\u2190 {{ m().backToChat }}</button>\n <ul class=\"session-list\">\n @for (session of assistant.sessions(); track session.id) {\n <li class=\"session-row\" [attr.data-session-id]=\"session.id\"\n [class.active]=\"session.id === assistant.activeSessionId()\">\n @if (confirmingDelete() === session.id) {\n <div class=\"session-confirm\" role=\"group\" [attr.aria-label]=\"format(m().deleteSessionConfirm, { title: session.title })\">\n <span class=\"confirm-text\">{{ format(m().deleteSessionConfirm, { title: session.title }) }}</span>\n <button type=\"button\" class=\"text-btn danger confirm-delete\" [disabled]=\"busySession() === session.id\"\n (click)=\"confirmDelete(session)\">{{ m().delete }}</button>\n <button type=\"button\" class=\"text-btn cancel-delete\" (click)=\"cancelDelete()\">{{ m().cancel }}</button>\n </div>\n } @else {\n <button type=\"button\" class=\"session-open\" [title]=\"session.title\"\n [attr.aria-current]=\"session.id === assistant.activeSessionId() ? 'true' : null\"\n [disabled]=\"busySession() === session.id\"\n (click)=\"openSession(session)\">\n <span class=\"session-title\">{{ session.title }}</span>\n @if (sessionDate(session); as date) {\n <span class=\"session-date\">{{ date }}</span>\n }\n </button>\n @if (assistant.sessionDeleteSupported) {\n <button type=\"button\" class=\"icon-btn session-delete\"\n [attr.aria-label]=\"format(m().deleteSession, { title: session.title })\"\n [title]=\"format(m().deleteSession, { title: session.title })\"\n [disabled]=\"busySession() === session.id\"\n (click)=\"askDelete(session)\">\n <kendo-svg-icon [icon]=\"trashIcon\" size=\"small\"></kendo-svg-icon>\n </button>\n }\n }\n </li>\n } @empty {\n <li class=\"session-empty\">{{ m().noSessions }}</li>\n }\n </ul>\n </section>\n} @else {\n @if (showGreeting()) {\n <div class=\"greeting\" data-testid=\"assistant-greeting\">\n <mm-octobot animation=\"wave\" size=\"md\" playOnce />\n <p class=\"greeting-text\">{{ m().greeting }}</p>\n </div>\n }\n @if (showStarters()) {\n <section class=\"starters\" aria-labelledby=\"assistant-starters-title\">\n <h3 class=\"starters-title\" id=\"assistant-starters-title\">{{ m().startersTitle }}</h3>\n <ul class=\"starter-list\">\n @for (question of assistant.starterQuestions(); track $index) {\n <li><button type=\"button\" class=\"starter\" [disabled]=\"!assistant.canSend()\" (click)=\"sendStarter(question)\">{{ question }}</button></li>\n }\n </ul>\n </section>\n }\n\n <mm-assistant-thread class=\"thread\" [items]=\"assistant.thread()\" [messages]=\"messages()\" (proposalDecision)=\"onDecision($event)\"></mm-assistant-thread>\n\n @if (!connected() || assistant.transportStatus() === 'error') {\n <div class=\"unavailable\" role=\"status\">\n <span class=\"sparkle\" aria-hidden=\"true\">\u2726</span>\n <p>{{ assistant.transportMessage() ?? m().notAvailable }}</p>\n </div>\n }\n\n <mm-assistant-composer\n [messages]=\"messages()\"\n [text]=\"assistant.draft()\"\n (textChange)=\"assistant.setDraft($event)\"\n [attachments]=\"assistant.attachments\"\n [files]=\"assistant.draftFiles()\"\n (filesChange)=\"assistant.setDraftFiles($event)\"\n [canSend]=\"assistant.canSend()\"\n [sending]=\"assistant.sending()\"\n (send)=\"assistant.send($event)\"\n (stop)=\"assistant.stop()\">\n </mm-assistant-composer>\n}\n", styles: [":host{position:relative;display:flex;flex-direction:column;width:400px;min-height:0;border-left:1px solid var(--theme-border-subtle);background:var(--theme-bg-surface);color:var(--theme-text-primary)}.head{display:flex;align-items:center;gap:var(--theme-space-2);padding:10px 14px;border-bottom:1px solid var(--theme-border-subtle)}.title{flex:none;margin:0;white-space:nowrap;font-family:var(--theme-font-display);font-size:.8125rem;font-weight:600}.sparkle{color:var(--theme-ai)}.chip-neutral{min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;margin-left:6px;padding:0 7px;border-radius:var(--theme-radius-xs);background:var(--theme-status-neutral-subtle);color:var(--theme-text-secondary);font-size:.71875rem}.icon-btn{flex:none;display:grid;place-items:center;width:28px;height:28px;border:0;border-radius:var(--theme-radius-sm);background:none;color:var(--theme-text-secondary);cursor:pointer}.icon-btn:hover{background:var(--theme-bg-hover)}.icon-btn[aria-expanded=true]{background:var(--theme-accent-subtle);color:var(--theme-text-primary)}.icon-btn:focus-visible{outline:none;box-shadow:var(--theme-focus-ring)}.close{margin-left:auto}.ctx{display:flex;flex-wrap:wrap;gap:6px;margin:0;padding:8px 14px;list-style:none;border-bottom:1px solid var(--theme-border-subtle)}.ctx-chip{display:inline-flex;align-items:center;gap:6px;max-width:100%;height:22px;padding:0 4px 0 8px;border:1px solid var(--theme-border-default);border-radius:var(--theme-radius-xs);font-size:.75rem;color:var(--theme-text-secondary)}.ctx-chip button{display:inline-grid;flex:none;place-items:center;border:0;padding:0 2px;background:none;color:var(--theme-text-muted);cursor:pointer}.ctx-chip button:hover{color:var(--theme-text-primary)}.ctx-chip button:focus-visible{outline:none;box-shadow:var(--theme-focus-ring)}.ctx-label{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.thread{flex:1 1 auto;min-height:0}.unavailable{display:flex;align-items:center;gap:var(--theme-space-2);margin:0 14px var(--theme-space-2);padding:8px 10px;border:1px dashed var(--theme-border-default);border-radius:var(--theme-radius-sm);color:var(--theme-text-secondary)}.unavailable p{margin:0}@media(max-width:1180px){:host{width:340px}}@media(max-width:640px){:host{position:fixed;inset:48px 0 0;z-index:30;width:auto;border-left:0}}.head-actions{display:flex;flex:none;gap:2px;margin-left:auto}.head-actions+.close{margin-left:0}.text-btn{height:26px;padding:0 8px;border:0;border-radius:var(--theme-radius-sm);background:none;color:var(--theme-text-secondary);font:inherit;font-size:.75rem;cursor:pointer}.text-btn:hover:not(:disabled){background:var(--theme-bg-hover);color:var(--theme-text-primary)}.text-btn[aria-expanded=true]{background:var(--theme-accent-subtle);color:var(--theme-text-primary)}.text-btn:disabled{color:var(--theme-text-muted);cursor:default}.text-btn:focus-visible{outline:none;box-shadow:var(--theme-focus-ring)}.text-btn.danger{color:var(--theme-status-error)}.sessions{display:flex;flex:1 1 auto;flex-direction:column;gap:var(--theme-space-2);min-height:0;padding:10px 14px;overflow:auto}.sessions .back{align-self:flex-start}.session-list{display:grid;gap:2px;margin:0;padding:0;list-style:none}.session-row{display:flex;align-items:center;gap:4px;border-radius:var(--theme-radius-sm)}.session-row.active{background:var(--theme-accent-subtle)}.session-open{display:grid;flex:1 1 auto;min-width:0;padding:6px 8px;border:0;border-radius:var(--theme-radius-sm);background:none;color:var(--theme-text-primary);font:inherit;text-align:left;cursor:pointer}.session-open:hover:not(:disabled){background:var(--theme-bg-hover)}.session-open:focus-visible{outline:none;box-shadow:var(--theme-focus-ring)}.session-title{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.session-date{color:var(--theme-text-muted);font-size:.71875rem}.session-confirm{display:flex;flex:1 1 auto;flex-wrap:wrap;align-items:center;gap:4px;padding:4px 8px}.confirm-text{flex:1 1 100%;overflow-wrap:anywhere}.session-empty{padding:6px 8px;color:var(--theme-text-secondary)}.greeting{display:grid;justify-items:center;gap:var(--theme-space-2);padding:20px 14px 0;text-align:center}.greeting-text{margin:0;color:var(--theme-text-primary);font-weight:600}.starters{display:grid;gap:var(--theme-space-2);padding:14px 14px 0}.starters-title{margin:0;color:var(--theme-text-secondary);font-size:.75rem;font-weight:600}.starter-list{display:grid;gap:6px;margin:0;padding:0;list-style:none}.starter{width:100%;padding:6px 10px;border:1px solid var(--theme-border-default);border-radius:var(--theme-radius-sm);background:none;color:var(--theme-text-primary);font:inherit;text-align:left;cursor:pointer}.starter:hover:not(:disabled){border-color:var(--theme-ai);background:var(--theme-bg-hover)}.starter:disabled{color:var(--theme-text-muted);cursor:default}.starter:focus-visible{outline:none;box-shadow:var(--theme-focus-ring)}.resize-handle{--mm-resize-line: transparent;--mm-resize-grip: var(--theme-border-default, var(--theme-border-subtle, rgba(128, 128, 128, .5)));position:absolute;top:0;bottom:0;left:-4px;z-index:1;width:8px;cursor:col-resize;touch-action:none}.resize-handle:after{content:\"\";position:absolute;top:0;bottom:0;left:3px;width:2px;background:var(--mm-resize-line);transition:background-color .15s}.resize-handle:before{content:\"\";position:absolute;top:50%;left:2px;z-index:1;width:4px;height:32px;margin-top:-16px;border-radius:2px;background:var(--mm-resize-grip);transition:background-color .15s}.resize-handle:hover,.resize-handle:focus-visible{--mm-resize-line: var(--theme-accent);--mm-resize-grip: var(--theme-accent)}.resize-handle:focus-visible{outline:none}:host(.mm-assistant-resizing){-webkit-user-select:none;user-select:none;cursor:col-resize}:host(.mm-assistant-resizing) .resize-handle{--mm-resize-line: var(--theme-accent);--mm-resize-grip: var(--theme-accent)}@media(prefers-reduced-motion:reduce){.resize-handle:after,.resize-handle:before{transition:none}}:host(.mm-assistant-overlay){position:fixed;top:var(--mm-assistant-overlay-top, 48px);right:0;bottom:0;z-index:var(--mm-assistant-overlay-z-index, 20);box-shadow:var(--theme-shadow-panel)}\n"] }]
730
753
  }], ctorParameters: () => [], propDecorators: { composer: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AssistantComposerComponent), { isSignal: true }] }], messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], resizable: [{ type: i0.Input, args: [{ isSignal: true, alias: "resizable", required: false }] }], minWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "minWidth", required: false }] }], maxWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxWidth", required: false }] }], storageKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "storageKey", required: false }] }] } });
731
754
 
732
755
  /*