@tylertech/forge-ai 0.14.0-dev.1 → 0.15.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 (46) hide show
  1. package/custom-elements.json +5272 -5030
  2. package/dist/ai-agent-selector/ai-agent-selector.mjs +1 -1
  3. package/dist/ai-chat-header/ai-chat-header.d.ts +5 -0
  4. package/dist/ai-chat-header/ai-chat-header.mjs +9 -3
  5. package/dist/ai-chat-header/ai-chat-header.scss.mjs +1 -1
  6. package/dist/ai-chatbot/agent-adapter.d.ts +1 -0
  7. package/dist/ai-chatbot/ai-chatbot-base.mjs +0 -1
  8. package/dist/ai-chatbot/ai-chatbot.d.ts +5 -1
  9. package/dist/ai-chatbot/ai-chatbot.mjs +18 -5
  10. package/dist/ai-chatbot/index.d.ts +1 -0
  11. package/dist/ai-chatbot/index.mjs +4 -1
  12. package/dist/ai-chatbot/message-state-controller.d.ts +8 -11
  13. package/dist/ai-chatbot/message-state-controller.mjs +7 -129
  14. package/dist/ai-chatbot/request-serialization.d.ts +33 -0
  15. package/dist/ai-chatbot/request-serialization.mjs +31 -0
  16. package/dist/ai-chatbot/types.d.ts +0 -73
  17. package/dist/ai-chatbot-launcher/ai-chatbot-launcher.d.ts +14 -2
  18. package/dist/ai-chatbot-launcher/ai-chatbot-launcher.mjs +47 -11
  19. package/dist/ai-chatbot-launcher/ai-chatbot-launcher.scss.mjs +1 -1
  20. package/dist/ai-conversations-panel/ai-conversations-panel.d.ts +10 -0
  21. package/dist/ai-conversations-panel/ai-conversations-panel.mjs +68 -7
  22. package/dist/ai-conversations-panel/ai-conversations-panel.scss.mjs +1 -1
  23. package/dist/ai-dialog/ai-dialog.mjs +1 -1
  24. package/dist/ai-dropdown-menu/ai-dropdown-menu.mjs +1 -1
  25. package/dist/ai-dropdown-menu/index.d.ts +1 -1
  26. package/dist/ai-edit-thread/ai-edit-thread.d.ts +5 -0
  27. package/dist/ai-edit-thread/ai-edit-thread.mjs +16 -9
  28. package/dist/ai-error-message/ai-error-message.d.ts +7 -1
  29. package/dist/ai-error-message/ai-error-message.mjs +40 -5
  30. package/dist/ai-error-message/ai-error-message.scss.mjs +1 -1
  31. package/dist/ai-error-message/index.d.ts +1 -0
  32. package/dist/ai-fab/ai-fab.d.ts +2 -0
  33. package/dist/ai-fab/ai-fab.mjs +5 -1
  34. package/dist/ai-icon-button/ai-icon-button.mjs +4 -1
  35. package/dist/ai-message-thread/ai-message-thread.mjs +1 -1
  36. package/dist/ai-sidebar/ai-sidebar.mjs +2 -2
  37. package/dist/ai-thread-actions-menu/ai-thread-actions-menu.d.ts +5 -0
  38. package/dist/ai-thread-actions-menu/ai-thread-actions-menu.mjs +6 -2
  39. package/dist/ai-threads-search/ai-threads-search.d.ts +13 -1
  40. package/dist/ai-threads-search/ai-threads-search.mjs +87 -30
  41. package/dist/ai-threads-search/ai-threads-search.scss.mjs +1 -1
  42. package/dist/ai-tool-call-indicator/ai-tool-call-indicator.scss.mjs +1 -1
  43. package/dist/ai-voice-input/ai-voice-input.mjs +2 -2
  44. package/dist/core/chatbot-core-controller.mjs +6 -8
  45. package/dist/index.mjs +4 -1
  46. package/package.json +1 -1
@@ -1,16 +1,32 @@
1
1
  import { unsafeCSS, LitElement, html } from "lit";
2
- import { customElement } from "lit/decorators.js";
2
+ import { property, queryAssignedNodes, customElement } from "lit/decorators.js";
3
+ import { classMap } from "lit/directives/class-map.js";
3
4
  import styles from "./ai-error-message.scss.mjs";
5
+ var __defProp = Object.defineProperty;
4
6
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
+ var __typeError = (msg) => {
8
+ throw TypeError(msg);
9
+ };
5
10
  var __decorateClass = (decorators, target, key, kind) => {
6
11
  var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
7
12
  for (var i = decorators.length - 1, decorator; i >= 0; i--)
8
13
  if (decorator = decorators[i])
9
- result = decorator(result) || result;
14
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
15
+ if (kind && result) __defProp(target, key, result);
10
16
  return result;
11
17
  };
18
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
19
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
20
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
21
+ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
22
+ var _AiErrorMessageComponent_instances, handleTitleSlotChange_fn, title_get;
12
23
  const AiErrorMessageComponentTagName = "forge-ai-error-message";
13
24
  let AiErrorMessageComponent = class extends LitElement {
25
+ constructor() {
26
+ super(...arguments);
27
+ __privateAdd(this, _AiErrorMessageComponent_instances);
28
+ this.density = "medium";
29
+ }
14
30
  render() {
15
31
  return html`
16
32
  <div class="forge-inline-message">
@@ -25,9 +41,7 @@ let AiErrorMessageComponent = class extends LitElement {
25
41
  <path fill="none" d="M0 0h24v24H0z" />
26
42
  <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m1 15h-2v-2h2zm0-4h-2V7h2z" />
27
43
  </svg>
28
- <div class="forge-inline-message__title">
29
- <slot name="title"></slot>
30
- </div>
44
+ ${__privateGet(this, _AiErrorMessageComponent_instances, title_get)}
31
45
  <div class="forge-inline-message__content">
32
46
  <slot></slot>
33
47
  </div>
@@ -35,7 +49,28 @@ let AiErrorMessageComponent = class extends LitElement {
35
49
  `;
36
50
  }
37
51
  };
52
+ _AiErrorMessageComponent_instances = /* @__PURE__ */ new WeakSet();
53
+ handleTitleSlotChange_fn = function() {
54
+ this.requestUpdate();
55
+ };
56
+ title_get = function() {
57
+ const classes = classMap({
58
+ "forge-inline-message__title": true,
59
+ "forge-inline-message__title--empty": this._titleNodes.length === 0
60
+ });
61
+ return html`
62
+ <div class=${classes}>
63
+ <slot name="title" @slotchange=${__privateMethod(this, _AiErrorMessageComponent_instances, handleTitleSlotChange_fn)}></slot>
64
+ </div>
65
+ `;
66
+ };
38
67
  AiErrorMessageComponent.styles = unsafeCSS(styles);
68
+ __decorateClass([
69
+ property({ type: String, reflect: true })
70
+ ], AiErrorMessageComponent.prototype, "density", 2);
71
+ __decorateClass([
72
+ queryAssignedNodes({ slot: "title", flatten: true })
73
+ ], AiErrorMessageComponent.prototype, "_titleNodes", 2);
39
74
  AiErrorMessageComponent = __decorateClass([
40
75
  customElement(AiErrorMessageComponentTagName)
41
76
  ], AiErrorMessageComponent);
@@ -1,4 +1,4 @@
1
- const styles = '/* prettier-ignore */\n/**\n * @license\n * Copyright Tyler Technologies, Inc.\n * License: Apache-2.0\n */\n.forge-inline-message {\n --_inline-message-background:var(--forge-inline-message-background, var(--forge-theme-info-container-low, #e3edf7));\n --_inline-message-color:var(--forge-inline-message-color, var(--forge-theme-text-high, rgba(0, 0, 0, 0.87)));\n --_inline-message-shape:var(--forge-inline-message-shape, calc(var(--forge-shape-medium, 4px) * var(--forge-shape-factor, 1)));\n --_inline-message-padding:var(--forge-inline-message-padding, var(--forge-spacing-small, 12px));\n --_inline-message-padding-inline:var(--forge-inline-message-padding-inline, var(--_inline-message-padding));\n --_inline-message-padding-block:var(--forge-inline-message-padding-block, var(--_inline-message-padding));\n --_inline-message-border-width:var(--forge-inline-message-border-width, var(--forge-border-thin, 1px));\n --_inline-message-border-style:var(--forge-inline-message-border-style, none);\n --_inline-message-border-color:var(--forge-inline-message-border-color, var(--forge-theme-info-container, #c7daf0));\n --_inline-message-gap:var(--forge-inline-message-gap, var(--forge-spacing-small, 12px));\n --_inline-message-icon-gap:var(--forge-inline-message-icon-gap, var(--_inline-message-gap));\n --_inline-message-content-gap:var(--forge-inline-message-content-gap, var(--_inline-message-gap));\n --_inline-message-icon-color:var(--forge-inline-message-icon-color, var(--forge-theme-on-info-container-low, #0b3768));\n}\n\n.forge-inline-message {\n display: block;\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-family: var(--forge-typography-body1-font-family, var(--forge-typography-font-family, "Roboto", sans-serif));\n font-size: var(--forge-typography-body1-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-font-size-scale, 0.875)));\n font-weight: var(--forge-typography-body1-font-weight, 400);\n line-height: var(--forge-typography-body1-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-line-height-scale, 1.125)));\n letter-spacing: var(--forge-typography-body1-letter-spacing, 0.0357142857em);\n text-transform: var(--forge-typography-body1-text-transform, inherit);\n text-decoration: var(--forge-typography-body1-text-decoration, inherit);\n background: var(--_inline-message-background);\n color: var(--_inline-message-color);\n border-radius: var(--_inline-message-shape);\n border-width: var(--_inline-message-border-width);\n border-style: var(--_inline-message-border-style);\n border-color: var(--_inline-message-border-color);\n display: flex;\n align-items: center;\n gap: var(--_inline-message-icon-gap);\n box-sizing: border-box;\n padding-inline: var(--_inline-message-padding-inline);\n padding-block: var(--_inline-message-padding-block);\n display: grid;\n grid-template-columns: auto 1fr;\n grid-template-rows: auto 1fr;\n}\n\n.forge-inline-message:not(:has(.forge-inline-message__icon)) {\n grid-template-columns: 1fr;\n}\n\n.forge-inline-message__title {\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-family: var(--forge-typography-heading1-font-family, var(--forge-typography-font-family, "Roboto", sans-serif));\n font-size: var(--forge-typography-heading1-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-heading1-font-size-scale, 0.875)));\n font-weight: var(--forge-typography-heading1-font-weight, 500);\n line-height: var(--forge-typography-heading1-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-heading1-line-height-scale, 1.125)));\n letter-spacing: var(--forge-typography-heading1-letter-spacing, 0.0178571429em);\n text-transform: var(--forge-typography-heading1-text-transform, inherit);\n text-decoration: var(--forge-typography-heading1-text-decoration, inherit);\n}\n\n.forge-inline-message__icon {\n color: var(--_inline-message-icon-color);\n align-self: start;\n grid-row: 1/span 2;\n}\n\n:host {\n display: block;\n}\n\n.forge-inline-message {\n --forge-inline-message-background: var(--forge-theme-error-container-low, #f6e0e4);\n --forge-inline-message-color: var(--forge-theme-on-error-container-low, #5f0011);\n --forge-inline-message-icon-color: var(--forge-theme-on-error-container-low, #5f0011);\n --forge-inline-message-border-color: var(--forge-theme-error-container, #ecc2c9);\n --forge-inline-message-border-style: solid;\n}\n.forge-inline-message__title {\n font-weight: bold;\n}\n.forge-inline-message__content {\n overflow-wrap: anywhere;\n}\n\n::slotted(*) {\n margin-block: 0;\n overflow-wrap: anywhere;\n}';
1
+ const styles = '/* prettier-ignore */\n/**\n * @license\n * Copyright Tyler Technologies, Inc.\n * License: Apache-2.0\n */\n.forge-inline-message {\n --_inline-message-background:var(--forge-inline-message-background, var(--forge-theme-info-container-low, #e3edf7));\n --_inline-message-color:var(--forge-inline-message-color, var(--forge-theme-text-high, rgba(0, 0, 0, 0.87)));\n --_inline-message-shape:var(--forge-inline-message-shape, calc(var(--forge-shape-medium, 4px) * var(--forge-shape-factor, 1)));\n --_inline-message-padding:var(--forge-inline-message-padding, var(--forge-spacing-small, 12px));\n --_inline-message-padding-inline:var(--forge-inline-message-padding-inline, var(--_inline-message-padding));\n --_inline-message-padding-block:var(--forge-inline-message-padding-block, var(--_inline-message-padding));\n --_inline-message-border-width:var(--forge-inline-message-border-width, var(--forge-border-thin, 1px));\n --_inline-message-border-style:var(--forge-inline-message-border-style, none);\n --_inline-message-border-color:var(--forge-inline-message-border-color, var(--forge-theme-info-container, #c7daf0));\n --_inline-message-gap:var(--forge-inline-message-gap, var(--forge-spacing-small, 12px));\n --_inline-message-icon-gap:var(--forge-inline-message-icon-gap, var(--_inline-message-gap));\n --_inline-message-content-gap:var(--forge-inline-message-content-gap, var(--_inline-message-gap));\n --_inline-message-icon-color:var(--forge-inline-message-icon-color, var(--forge-theme-on-info-container-low, #0b3768));\n}\n\n.forge-inline-message {\n display: block;\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-family: var(--forge-typography-body1-font-family, var(--forge-typography-font-family, "Roboto", sans-serif));\n font-size: var(--forge-typography-body1-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-font-size-scale, 0.875)));\n font-weight: var(--forge-typography-body1-font-weight, 400);\n line-height: var(--forge-typography-body1-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-line-height-scale, 1.125)));\n letter-spacing: var(--forge-typography-body1-letter-spacing, 0.0357142857em);\n text-transform: var(--forge-typography-body1-text-transform, inherit);\n text-decoration: var(--forge-typography-body1-text-decoration, inherit);\n background: var(--_inline-message-background);\n color: var(--_inline-message-color);\n border-radius: var(--_inline-message-shape);\n border-width: var(--_inline-message-border-width);\n border-style: var(--_inline-message-border-style);\n border-color: var(--_inline-message-border-color);\n display: flex;\n align-items: center;\n gap: var(--_inline-message-icon-gap);\n box-sizing: border-box;\n padding-inline: var(--_inline-message-padding-inline);\n padding-block: var(--_inline-message-padding-block);\n display: grid;\n grid-template-columns: auto 1fr;\n grid-template-rows: auto 1fr;\n}\n\n.forge-inline-message:not(:has(.forge-inline-message__icon)) {\n grid-template-columns: 1fr;\n}\n\n.forge-inline-message__title {\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-family: var(--forge-typography-heading1-font-family, var(--forge-typography-font-family, "Roboto", sans-serif));\n font-size: var(--forge-typography-heading1-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-heading1-font-size-scale, 0.875)));\n font-weight: var(--forge-typography-heading1-font-weight, 500);\n line-height: var(--forge-typography-heading1-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-heading1-line-height-scale, 1.125)));\n letter-spacing: var(--forge-typography-heading1-letter-spacing, 0.0178571429em);\n text-transform: var(--forge-typography-heading1-text-transform, inherit);\n text-decoration: var(--forge-typography-heading1-text-decoration, inherit);\n}\n\n.forge-inline-message__icon {\n color: var(--_inline-message-icon-color);\n align-self: start;\n grid-row: 1/span 2;\n}\n\n:host {\n display: block;\n}\n\n.forge-inline-message {\n --forge-inline-message-background: var(--forge-theme-error-container-low, #f6e0e4);\n --forge-inline-message-color: var(--forge-theme-on-error-container-low, #5f0011);\n --forge-inline-message-icon-color: var(--forge-theme-on-error-container-low, #5f0011);\n --forge-inline-message-border-color: var(--forge-theme-error-container, #ecc2c9);\n --forge-inline-message-border-style: solid;\n}\n.forge-inline-message__title {\n font-weight: bold;\n}\n.forge-inline-message__title--empty {\n display: none;\n}\n.forge-inline-message__content {\n overflow-wrap: anywhere;\n}\n.forge-inline-message:has(.forge-inline-message__title--empty) {\n grid-template-rows: auto;\n}\n.forge-inline-message:has(.forge-inline-message__title--empty) .forge-inline-message__icon {\n grid-row: auto;\n align-self: center;\n}\n\n:host([density=small]) .forge-inline-message {\n --forge-inline-message-padding: var(--forge-spacing-xsmall, 8px);\n --forge-inline-message-gap: var(--forge-spacing-xsmall, 8px);\n --forge-inline-message-content-gap: var(--forge-spacing-xxsmall, 4px);\n -moz-osx-font-smoothing: grayscale;\n -webkit-font-smoothing: antialiased;\n font-family: var(--forge-typography-body2-font-family, var(--forge-typography-font-family, "Roboto", sans-serif));\n font-size: var(--forge-typography-body2-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-font-size-scale, 1)));\n font-weight: var(--forge-typography-body2-font-weight, 400);\n line-height: var(--forge-typography-body2-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-line-height-scale, 1.375)));\n letter-spacing: var(--forge-typography-body2-letter-spacing, 0.015625em);\n text-transform: var(--forge-typography-body2-text-transform, inherit);\n text-decoration: var(--forge-typography-body2-text-decoration, inherit);\n}\n:host([density=small]) .forge-inline-message__icon {\n height: 20px;\n width: 20px;\n}\n\n::slotted(*) {\n margin-block: 0;\n overflow-wrap: anywhere;\n}';
2
2
  export {
3
3
  styles as default
4
4
  };
@@ -1 +1,2 @@
1
1
  export { AiErrorMessageComponent, AiErrorMessageComponentTagName } from './ai-error-message.js';
2
+ export type { ErrorMessageDensity } from './ai-error-message.js';
@@ -22,6 +22,8 @@ export declare class AiFabComponent extends LitElement {
22
22
  disabled: boolean;
23
23
  /** Whether the button is extended */
24
24
  extended: boolean;
25
+ /** Accessible label for the button, announced by screen readers. */
26
+ label: string;
25
27
  constructor();
26
28
  willUpdate(changedProperties: PropertyValues<this>): void;
27
29
  render(): TemplateResult;
@@ -30,6 +30,7 @@ let AiFabComponent = class extends LitElement {
30
30
  __privateAdd(this, _AiFabComponent_instances);
31
31
  this.disabled = false;
32
32
  this.extended = false;
33
+ this.label = "Launch chatbot";
33
34
  __privateAdd(this, _internals);
34
35
  __privateSet(this, _internals, this.attachInternals());
35
36
  __privateMethod(this, _AiFabComponent_instances, setCssState_fn).call(this);
@@ -43,7 +44,7 @@ let AiFabComponent = class extends LitElement {
43
44
  return html`
44
45
  <forge-ai-gradient-container variant=${this.disabled ? "disabled" : "high"}>
45
46
  <button
46
- aria-label="Floating Action Button Demo"
47
+ aria-label=${this.label}
47
48
  class="forge-fab ai-fab ${this.extended ? "forge-fab--small forge-fab--extended" : ""}"
48
49
  .disabled=${this.disabled}>
49
50
  <slot name="icon">
@@ -68,6 +69,9 @@ __decorateClass([
68
69
  __decorateClass([
69
70
  property({ type: Boolean, reflect: true })
70
71
  ], AiFabComponent.prototype, "extended", 2);
72
+ __decorateClass([
73
+ property({ type: String })
74
+ ], AiFabComponent.prototype, "label", 2);
71
75
  AiFabComponent = __decorateClass([
72
76
  customElement(AiFabComponentTagName)
73
77
  ], AiFabComponent);
@@ -39,7 +39,10 @@ let AiIconButtonComponent = class extends LitElement {
39
39
  }
40
40
  render() {
41
41
  return html`
42
- <button aria-label="AI Icon Button" class="forge-icon-button ai-icon-button" .disabled=${this.disabled}>
42
+ <button
43
+ aria-label="AI Icon Button"
44
+ class="forge-icon-button forge-icon-button--medium ai-icon-button"
45
+ .disabled=${this.disabled}>
43
46
  <forge-ai-icon></forge-ai-icon>
44
47
  </button>
45
48
  `;
@@ -87,9 +87,9 @@ let AiMessageThreadComponent = class extends LitElement {
87
87
  render() {
88
88
  return html`
89
89
  <div class="message-thread" @scroll=${this.autoScroll ? __privateGet(this, _handleScroll) : void 0}>
90
+ ${__privateGet(this, _AiMessageThreadComponent_instances, scrollToBottomButton_get)}
90
91
  <div class="message-thread-content">${__privateGet(this, _AiMessageThreadComponent_instances, emptyState_get)} ${__privateGet(this, _AiMessageThreadComponent_instances, messages_get)} ${__privateGet(this, _AiMessageThreadComponent_instances, thinkingIndicator_get)}</div>
91
92
  </div>
92
- ${__privateGet(this, _AiMessageThreadComponent_instances, scrollToBottomButton_get)}
93
93
  `;
94
94
  }
95
95
  };
@@ -85,9 +85,9 @@ let AiSidebarComponent = class extends LitElement {
85
85
  <div
86
86
  ${ref(__privateGet(this, _resizeHandleRef))}
87
87
  class=${classMap(handleClasses)}
88
- role="separator"
88
+ role="slider"
89
89
  aria-orientation="vertical"
90
- aria-label="Resize sidebar"
90
+ aria-label="Resize chatbot panel, use left and right arrow keys"
91
91
  aria-valuemin="${MIN_WIDTH}"
92
92
  aria-valuemax="${MAX_WIDTH}"
93
93
  aria-valuenow=${this._width}
@@ -1,5 +1,6 @@
1
1
  import { LitElement, TemplateResult, CSSResult } from 'lit';
2
2
  import { Thread } from '../ai-threads';
3
+ import { DropdownMenuDensity } from '../ai-dropdown-menu';
3
4
  declare global {
4
5
  interface HTMLElementTagNameMap {
5
6
  'forge-ai-thread-actions-menu': AiThreadActionsMenuComponent;
@@ -40,5 +41,9 @@ export declare class AiThreadActionsMenuComponent extends LitElement {
40
41
  thread: Thread;
41
42
  showRename: boolean;
42
43
  showDelete: boolean;
44
+ /**
45
+ * The density of the trigger button
46
+ */
47
+ density: DropdownMenuDensity;
43
48
  render(): TemplateResult;
44
49
  }
@@ -28,15 +28,16 @@ let AiThreadActionsMenuComponent = class extends LitElement {
28
28
  __privateAdd(this, _AiThreadActionsMenuComponent_instances);
29
29
  this.showRename = false;
30
30
  this.showDelete = false;
31
+ this.density = "small";
31
32
  }
32
33
  render() {
33
34
  return html`
34
35
  <forge-ai-dropdown-menu
35
36
  variant="icon-button-squared"
36
- density="small"
37
+ density=${this.density}
37
38
  selection-mode="none"
38
39
  popover-placement="bottom-start"
39
- trigger-label="Thread options"
40
+ trigger-label="More actions for ${this.thread.title}"
40
41
  @keydown=${__privateMethod(this, _AiThreadActionsMenuComponent_instances, handleMenuKeyDown_fn)}
41
42
  @forge-ai-dropdown-menu-open=${__privateMethod(this, _AiThreadActionsMenuComponent_instances, handleMenuOpen_fn)}
42
43
  @forge-ai-dropdown-menu-close=${__privateMethod(this, _AiThreadActionsMenuComponent_instances, handleMenuClose_fn)}
@@ -139,6 +140,9 @@ __decorateClass([
139
140
  __decorateClass([
140
141
  property({ type: Boolean, attribute: "show-delete" })
141
142
  ], AiThreadActionsMenuComponent.prototype, "showDelete", 2);
143
+ __decorateClass([
144
+ property()
145
+ ], AiThreadActionsMenuComponent.prototype, "density", 2);
142
146
  AiThreadActionsMenuComponent = __decorateClass([
143
147
  customElement(AiThreadActionsMenuComponentTagName)
144
148
  ], AiThreadActionsMenuComponent);
@@ -13,6 +13,7 @@ declare global {
13
13
  'forge-ai-threads-search-delete': CustomEvent<ForgeAiThreadsSearchDeleteEventData>;
14
14
  'forge-ai-threads-search-delete-confirm': CustomEvent<ForgeAiThreadsSearchDeleteConfirmEventData>;
15
15
  'forge-ai-threads-search-back': CustomEvent<void>;
16
+ 'forge-ai-threads-search-retry': CustomEvent<void>;
16
17
  }
17
18
  }
18
19
  export interface ForgeAiThreadsSearchQueryEventData {
@@ -57,6 +58,7 @@ export declare const AiThreadsSearchComponentTagName: keyof HTMLElementTagNameMa
57
58
  * @event {CustomEvent<ForgeAiThreadsSearchDeleteEventData>} forge-ai-threads-search-delete - Fired when thread delete confirmed. Cancelable - if prevented, call onSuccess() to commit deletion or onError() to revert.
58
59
  * @event {CustomEvent<ForgeAiThreadsSearchDeleteConfirmEventData>} forge-ai-threads-search-delete-confirm - Fired before showing the built-in delete confirmation. Cancelable - if prevented, this component shows no confirmation UI; the host must show its own and call confirmThreadDelete() once accepted.
59
60
  * @event {CustomEvent<void>} forge-ai-threads-search-back - Fired when the back button (shown via showBackButton) is clicked.
61
+ * @event {CustomEvent<void>} forge-ai-threads-search-retry - Fired when the retry button in the error state is clicked. The host should re-request the threads and clear errorMessage once the load succeeds.
60
62
  *
61
63
  * @description A standalone search component for conversations/threads. Can be slotted into forge-ai-threads
62
64
  * or used independently. Supports both local and external/async search patterns via event callbacks.
@@ -88,8 +90,18 @@ export declare class AiThreadsSearchComponent extends LitElement {
88
90
  * infinite scroll entirely. Useful when all data is loaded upfront.
89
91
  */
90
92
  totalChats: number;
93
+ /**
94
+ * Placeholder text for the search input. The input's aria-label is "Search" - avoid reusing
95
+ * that word here so screen readers don't announce the same word twice.
96
+ */
91
97
  placeholder: string;
92
- emptyMessage: string;
98
+ /**
99
+ * Message describing a failed thread load. When no threads are loaded, an error banner with a
100
+ * retry button replaces the empty state. When threads are already on screen the list stays visible
101
+ * and the message shows as a compact single line with a retry - at the bottom of the list if a page
102
+ * was in flight, otherwise above it. The host owns this value and should clear it once a load succeeds.
103
+ */
104
+ errorMessage?: string;
93
105
  showThreadRename: boolean;
94
106
  showThreadDelete: boolean;
95
107
  /**
@@ -1,4 +1,4 @@
1
- import { unsafeCSS, LitElement, html } from "lit";
1
+ import { unsafeCSS, LitElement, html, nothing } from "lit";
2
2
  import { property, query, state, customElement } from "lit/decorators.js";
3
3
  import { when } from "lit/directives/when.js";
4
4
  import { formatRelativeTime } from "../utils.mjs";
@@ -6,6 +6,7 @@ import { DeleteThreadController } from "../utils/delete-thread-controller.mjs";
6
6
  import { InfiniteScrollController } from "../utils/infinite-scroll-controller.mjs";
7
7
  import "../ai-thread-actions-menu/ai-thread-actions-menu.mjs";
8
8
  import "../ai-edit-thread/ai-edit-thread.mjs";
9
+ import "../ai-error-message/ai-error-message.mjs";
9
10
  import "../ai-spinner/ai-spinner.mjs";
10
11
  import "../ai-modal/ai-modal.mjs";
11
12
  import styles from "./ai-threads-search.scss.mjs";
@@ -26,7 +27,7 @@ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot
26
27
  var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
27
28
  var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
28
29
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
29
- var _deleteThreadController, _infiniteScrollController, _AiThreadsSearchComponent_instances, shouldEnablePagination_get, handleSearchInput_fn, performSearch_fn, loadMore_fn, handleThreadSelect_fn, handleNewChatClick_fn, handleBackClick_fn, handleMenuRename_fn, handleMenuOpen_fn, handleMenuClose_fn, handleMenuDeleteClick_fn, confirmDelete_fn, handleEditSave_fn, handleEditCancel_fn, handleClearSearch_fn, displayedThreads_get, header_get, searchField_get, renderThreadItem_fn, resultsList_get;
30
+ var _deleteThreadController, _infiniteScrollController, _AiThreadsSearchComponent_instances, shouldEnablePagination_get, handleSearchInput_fn, performSearch_fn, loadMore_fn, handleThreadSelect_fn, handleNewChatClick_fn, handleBackClick_fn, handleRetryClick_fn, handleMenuRename_fn, handleMenuOpen_fn, handleMenuClose_fn, handleMenuDeleteClick_fn, confirmDelete_fn, handleEditSave_fn, handleEditCancel_fn, handleClearSearch_fn, displayedThreads_get, header_get, searchField_get, renderThreadItem_fn, errorState_get, compactError_get, errorPlacement_get, emptyState_get, listFooter_get, resultsList_get;
30
31
  const AiThreadsSearchComponentTagName = "forge-ai-threads-search";
31
32
  let AiThreadsSearchComponent = class extends LitElement {
32
33
  constructor() {
@@ -38,8 +39,7 @@ let AiThreadsSearchComponent = class extends LitElement {
38
39
  this.showBackButton = false;
39
40
  this.showSearch = true;
40
41
  this.totalChats = 0;
41
- this.placeholder = "Search chats...";
42
- this.emptyMessage = "No chats found";
42
+ this.placeholder = "Find a chat";
43
43
  this.showThreadRename = false;
44
44
  this.showThreadDelete = false;
45
45
  this.selectedThreadId = null;
@@ -88,7 +88,8 @@ let AiThreadsSearchComponent = class extends LitElement {
88
88
  render() {
89
89
  return html`
90
90
  <div class="threads-search-container">
91
- ${__privateGet(this, _AiThreadsSearchComponent_instances, header_get)} ${when(this.showSearch, () => __privateGet(this, _AiThreadsSearchComponent_instances, searchField_get))} ${__privateGet(this, _AiThreadsSearchComponent_instances, resultsList_get)}
91
+ ${__privateGet(this, _AiThreadsSearchComponent_instances, header_get)} ${when(this.showSearch, () => __privateGet(this, _AiThreadsSearchComponent_instances, searchField_get))}
92
+ ${when(__privateGet(this, _AiThreadsSearchComponent_instances, errorPlacement_get) === "above-list", () => __privateGet(this, _AiThreadsSearchComponent_instances, compactError_get))} ${__privateGet(this, _AiThreadsSearchComponent_instances, resultsList_get)}
92
93
  </div>
93
94
  ${__privateGet(this, _deleteThreadController).template}
94
95
  `;
@@ -181,6 +182,13 @@ handleBackClick_fn = function() {
181
182
  });
182
183
  this.dispatchEvent(event);
183
184
  };
185
+ handleRetryClick_fn = function() {
186
+ const event = new CustomEvent("forge-ai-threads-search-retry", {
187
+ bubbles: true,
188
+ composed: true
189
+ });
190
+ this.dispatchEvent(event);
191
+ };
184
192
  handleMenuRename_fn = function(e) {
185
193
  this._editingThreadId = e.detail.id;
186
194
  this._openMenuThreadId = null;
@@ -326,10 +334,12 @@ searchField_get = function() {
326
334
  d="M15.5 14h-.79l-.28-.27A6.47 6.47 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14" />
327
335
  </svg>
328
336
  <input
337
+ id="threads-search-input"
329
338
  type="text"
330
339
  autocomplete="off"
331
- .value=${this._searchQuery}
340
+ aria-label="Search"
332
341
  placeholder=${this.placeholder}
342
+ .value=${this._searchQuery}
333
343
  @input=${__privateMethod(this, _AiThreadsSearchComponent_instances, handleSearchInput_fn)} />
334
344
  ${when(this._isSearching, () => html`<forge-ai-spinner size="extra-small"></forge-ai-spinner>`)}
335
345
  ${when(
@@ -365,7 +375,10 @@ renderThreadItem_fn = function(thread) {
365
375
  </forge-ai-edit-thread>
366
376
  `,
367
377
  () => html`
368
- <button type="button" aria-selected=${isSelected} @click=${() => __privateMethod(this, _AiThreadsSearchComponent_instances, handleThreadSelect_fn).call(this, thread)}>
378
+ <button
379
+ type="button"
380
+ aria-current=${isSelected ? "true" : nothing}
381
+ @click=${() => __privateMethod(this, _AiThreadsSearchComponent_instances, handleThreadSelect_fn).call(this, thread)}>
369
382
  ${thread.title}
370
383
  </button>
371
384
  <span class="thread-time">${formatRelativeTime(thread.createdAt)}</span>
@@ -390,31 +403,75 @@ renderThreadItem_fn = function(thread) {
390
403
  </li>
391
404
  `;
392
405
  };
406
+ errorState_get = function() {
407
+ return html`
408
+ <div class="error-state">
409
+ <forge-ai-error-message density="small">
410
+ <span slot="title">Error</span>
411
+ <p class="error-state__message">${this.errorMessage}</p>
412
+ <button
413
+ class="forge-button forge-button--outlined forge-button--dense error-state__retry"
414
+ type="button"
415
+ @click=${__privateMethod(this, _AiThreadsSearchComponent_instances, handleRetryClick_fn)}>
416
+ Retry
417
+ </button>
418
+ </forge-ai-error-message>
419
+ </div>
420
+ `;
421
+ };
422
+ compactError_get = function() {
423
+ return html`
424
+ <forge-ai-error-message class="compact-error" density="small">
425
+ <div class="compact-error__body">
426
+ <span>${this.errorMessage}</span>
427
+ <button
428
+ class="forge-button forge-button--outlined forge-button--dense compact-error__retry"
429
+ type="button"
430
+ @click=${__privateMethod(this, _AiThreadsSearchComponent_instances, handleRetryClick_fn)}>
431
+ Retry
432
+ </button>
433
+ </div>
434
+ </forge-ai-error-message>
435
+ `;
436
+ };
437
+ errorPlacement_get = function() {
438
+ if (!this.errorMessage || __privateGet(this, _AiThreadsSearchComponent_instances, displayedThreads_get).length === 0) {
439
+ return "none";
440
+ }
441
+ return __privateGet(this, _infiniteScrollController).isLoadingMore ? "list-footer" : "above-list";
442
+ };
443
+ emptyState_get = function() {
444
+ const message = this._searchQuery.trim() ? "No chats found" : "No chats yet";
445
+ return html`
446
+ <div class="empty-state">
447
+ <p>${message}</p>
448
+ </div>
449
+ `;
450
+ };
451
+ listFooter_get = function() {
452
+ if (__privateGet(this, _AiThreadsSearchComponent_instances, errorPlacement_get) === "list-footer") {
453
+ return __privateGet(this, _AiThreadsSearchComponent_instances, compactError_get);
454
+ }
455
+ if (!__privateGet(this, _infiniteScrollController).isLoadingMore) {
456
+ return nothing;
457
+ }
458
+ return html`
459
+ <div class="loading-more-indicator">
460
+ <forge-ai-spinner></forge-ai-spinner>
461
+ </div>
462
+ `;
463
+ };
393
464
  resultsList_get = function() {
394
465
  const displayThreads = __privateGet(this, _AiThreadsSearchComponent_instances, displayedThreads_get);
466
+ if (displayThreads.length === 0) {
467
+ return html`<div class="results-container">${this.errorMessage ? __privateGet(this, _AiThreadsSearchComponent_instances, errorState_get) : __privateGet(this, _AiThreadsSearchComponent_instances, emptyState_get)}</div>`;
468
+ }
395
469
  return html`
396
470
  <div class="results-container">
397
- ${when(
398
- displayThreads.length === 0,
399
- () => html`
400
- <div class="empty-state">
401
- <p>${this.emptyMessage}</p>
402
- </div>
403
- `,
404
- () => html`
405
- <ul class="forge-list forge-list--dense forge-list--navlist" role="list">
406
- ${displayThreads.map((thread) => __privateMethod(this, _AiThreadsSearchComponent_instances, renderThreadItem_fn).call(this, thread))}
407
- </ul>
408
- ${when(
409
- __privateGet(this, _infiniteScrollController).isLoadingMore,
410
- () => html`
411
- <div class="loading-more-indicator">
412
- <forge-ai-spinner></forge-ai-spinner>
413
- </div>
414
- `
415
- )}
416
- `
417
- )}
471
+ <ul class="forge-list forge-list--dense forge-list--navlist" role="list">
472
+ ${displayThreads.map((thread) => __privateMethod(this, _AiThreadsSearchComponent_instances, renderThreadItem_fn).call(this, thread))}
473
+ </ul>
474
+ ${__privateGet(this, _AiThreadsSearchComponent_instances, listFooter_get)}
418
475
  </div>
419
476
  `;
420
477
  };
@@ -441,8 +498,8 @@ __decorateClass([
441
498
  property({ type: String })
442
499
  ], AiThreadsSearchComponent.prototype, "placeholder", 2);
443
500
  __decorateClass([
444
- property({ type: String, attribute: "empty-message" })
445
- ], AiThreadsSearchComponent.prototype, "emptyMessage", 2);
501
+ property({ type: String, attribute: "error-message" })
502
+ ], AiThreadsSearchComponent.prototype, "errorMessage", 2);
446
503
  __decorateClass([
447
504
  property({ type: Boolean, attribute: "show-thread-rename" })
448
505
  ], AiThreadsSearchComponent.prototype, "showThreadRename", 2);