@tylertech/forge-ai 0.15.0 → 0.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- const styles = '/**\n * @license\n * Copyright Tyler Technologies, Inc.\n * License: Apache-2.0\n */\n.forge-drawer {\n --_drawer-background:var(--forge-drawer-background, var(--forge-theme-surface, #ffffff));\n --_drawer-border-color:var(--forge-drawer-border-color, var(--forge-theme-outline, #e0e0e0));\n --_drawer-width:var(--forge-drawer-width, 256px);\n --_drawer-border-width:var(--forge-drawer-border-width, var(--forge-border-thin, 1px));\n --_drawer-transition-duration:var(--forge-drawer-transition-duration, var(--forge-animation-duration-medium1, 250ms));\n --_drawer-transition-easing:var(--forge-drawer-transition-easing, var(--forge-animation-easing-standard, cubic-bezier(0.2, 0, 0, 1)));\n --_drawer-transition-duration-close:var(--forge-drawer-transition-duration-close, var(--forge-animation-duration-short4, 200ms));\n}\n\n.forge-drawer {\n width: var(--_drawer-width);\n height: 100%;\n box-sizing: border-box;\n overflow: hidden;\n display: grid;\n grid-template-columns: 1fr;\n grid-template-rows: auto 1fr auto;\n background-color: var(--_drawer-background);\n border-color: var(--_drawer-border-color);\n transition-property: transform;\n transition-duration: var(--_drawer-transition-duration);\n transition-timing-function: var(--_drawer-transition-easing);\n border-right-width: var(--_drawer-border-width);\n border-right-style: solid;\n}\n\n.forge-drawer--closing {\n transform: translateX(-100%);\n left: 0;\n z-index: var(--forge-z-index-surface, 1);\n position: absolute;\n top: 0;\n right: 0;\n transition-duration: var(--_drawer-transition-duration-close);\n}\n\n.forge-drawer--closed {\n transform: translateX(-100%);\n left: 0;\n width: 0;\n border: none;\n}\n\n.forge-drawer--right {\n border-left-width: var(--_drawer-border-width);\n border-left-style: solid;\n border-right: none;\n}\n\n.forge-drawer--right.forge-drawer--closing {\n transform: translateX(100%);\n right: 0;\n left: auto;\n z-index: var(--forge-z-index-surface, 1);\n position: absolute;\n top: 0;\n right: 0;\n transition-duration: var(--_drawer-transition-duration-close);\n}\n\n.forge-drawer--right.forge-drawer--closed {\n transform: translateX(100%);\n right: 0;\n left: auto;\n width: 0;\n border: none;\n}\n\n.forge-drawer--mini {\n width: var(--_mini-drawer-width);\n z-index: var(--forge-z-index-surface, 1);\n transition: width var(--_mini-drawer-transition-duration) var(--_mini-drawer-transition-easing);\n}\n\n:host {\n display: block;\n width: auto;\n height: 100%;\n overflow: hidden;\n}\n\n.animation-container {\n --forge-drawer-width: 420px;\n width: var(--forge-drawer-width);\n height: 100%;\n position: relative;\n transition: width 250ms cubic-bezier(0.2, 0, 0, 1);\n overflow: hidden;\n}\n.animation-container--closed {\n width: 0;\n}\n.animation-container--closing {\n width: 0;\n}\n.animation-container--open {\n width: var(--forge-drawer-width);\n}\n.animation-container--resizing {\n transition: none;\n}\n\n.forge-drawer {\n display: flex;\n flex-direction: column;\n overflow: hidden;\n width: var(--forge-drawer-width);\n height: 100%;\n position: absolute;\n top: 0;\n right: 0;\n}\n.forge-drawer--resizing {\n user-select: none;\n transition: none;\n}\n\n.resize-handle {\n position: absolute;\n top: 0;\n left: 0;\n width: 8px;\n height: 100%;\n cursor: ew-resize;\n z-index: 10;\n}\n.resize-handle::before {\n content: "";\n position: absolute;\n left: 0;\n top: 0;\n bottom: 0;\n width: 1px;\n background-color: var(--forge-theme-outline, #e0e0e0);\n opacity: 0;\n transition: all var(--forge-animation-duration-short4, 200ms) var(--forge-animation-easing-standard, cubic-bezier(0.2, 0, 0, 1));\n}\n.resize-handle:hover::before {\n opacity: 0.4;\n width: 2px;\n background-color: var(--forge-theme-primary, #3f51b5);\n}\n.resize-handle:focus-visible {\n outline: 2px solid var(--forge-theme-primary, #3f51b5);\n outline-offset: -2px;\n}\n.resize-handle:focus-visible::before {\n opacity: 0.6;\n width: 2px;\n background-color: var(--forge-theme-primary, #3f51b5);\n}\n.resize-handle--active::before {\n opacity: 0.8;\n width: 3px;\n background-color: var(--forge-theme-primary, #3f51b5);\n}\n\n:host([resizable=false]) .resize-handle {\n display: none;\n}';
1
+ const styles = '/**\n * @license\n * Copyright Tyler Technologies, Inc.\n * License: Apache-2.0\n */\n.forge-drawer {\n --_drawer-background:var(--forge-drawer-background, var(--forge-theme-surface, #ffffff));\n --_drawer-border-color:var(--forge-drawer-border-color, var(--forge-theme-outline, #e0e0e0));\n --_drawer-width:var(--forge-drawer-width, 256px);\n --_drawer-border-width:var(--forge-drawer-border-width, var(--forge-border-thin, 1px));\n --_drawer-transition-duration:var(--forge-drawer-transition-duration, var(--forge-animation-duration-medium1, 250ms));\n --_drawer-transition-easing:var(--forge-drawer-transition-easing, var(--forge-animation-easing-standard, cubic-bezier(0.2, 0, 0, 1)));\n --_drawer-transition-duration-close:var(--forge-drawer-transition-duration-close, var(--forge-animation-duration-short4, 200ms));\n}\n\n.forge-drawer {\n width: var(--_drawer-width);\n height: 100%;\n box-sizing: border-box;\n overflow: hidden;\n display: grid;\n grid-template-columns: 1fr;\n grid-template-rows: auto 1fr auto;\n background-color: var(--_drawer-background);\n border-color: var(--_drawer-border-color);\n transition-property: transform;\n transition-duration: var(--_drawer-transition-duration);\n transition-timing-function: var(--_drawer-transition-easing);\n border-right-width: var(--_drawer-border-width);\n border-right-style: solid;\n}\n\n.forge-drawer--closing {\n transform: translateX(-100%);\n left: 0;\n z-index: var(--forge-z-index-surface, 1);\n position: absolute;\n top: 0;\n right: 0;\n transition-duration: var(--_drawer-transition-duration-close);\n}\n\n.forge-drawer--closed {\n transform: translateX(-100%);\n left: 0;\n width: 0;\n border: none;\n}\n\n.forge-drawer--right {\n border-left-width: var(--_drawer-border-width);\n border-left-style: solid;\n border-right: none;\n}\n\n.forge-drawer--right.forge-drawer--closing {\n transform: translateX(100%);\n right: 0;\n left: auto;\n z-index: var(--forge-z-index-surface, 1);\n position: absolute;\n top: 0;\n right: 0;\n transition-duration: var(--_drawer-transition-duration-close);\n}\n\n.forge-drawer--right.forge-drawer--closed {\n transform: translateX(100%);\n right: 0;\n left: auto;\n width: 0;\n border: none;\n}\n\n.forge-drawer--mini {\n width: var(--_mini-drawer-width);\n z-index: var(--forge-z-index-surface, 1);\n transition: width var(--_mini-drawer-transition-duration) var(--_mini-drawer-transition-easing);\n}\n\n:host {\n display: block;\n width: auto;\n height: 100%;\n overflow: hidden;\n}\n\n.animation-container {\n --forge-drawer-width: 420px;\n width: var(--forge-drawer-width);\n height: 100%;\n position: relative;\n transition: width 250ms cubic-bezier(0.2, 0, 0, 1);\n overflow: hidden;\n}\n.animation-container--closed {\n width: 0;\n}\n.animation-container--closing {\n width: 0;\n}\n.animation-container--open {\n width: var(--forge-drawer-width);\n}\n.animation-container--resizing {\n transition: none;\n}\n\n.forge-drawer {\n display: flex;\n flex-direction: column;\n overflow: hidden;\n width: var(--forge-drawer-width);\n height: 100%;\n position: absolute;\n top: 0;\n right: 0;\n}\n.forge-drawer--resizing {\n user-select: none;\n transition: none;\n}\n\n.resize-handle {\n position: absolute;\n top: 0;\n left: 0;\n width: 8px;\n height: 100%;\n cursor: ew-resize;\n z-index: 10;\n}\n.resize-handle::before {\n content: "";\n position: absolute;\n left: 0;\n top: 0;\n bottom: 0;\n width: 1px;\n background-color: var(--forge-theme-outline, #e0e0e0);\n opacity: 0;\n transition: all var(--forge-animation-duration-short4, 200ms) var(--forge-animation-easing-standard, cubic-bezier(0.2, 0, 0, 1));\n}\n.resize-handle__grip {\n position: absolute;\n top: 50%;\n left: 0;\n translate: 0 -50%;\n display: flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n width: 6px;\n height: 32px;\n color: var(--forge-theme-text-low, rgba(0, 0, 0, 0.38));\n background-color: var(--forge-theme-surface, #ffffff);\n border: 1px solid var(--forge-theme-outline, #e0e0e0);\n border-inline-start: none;\n border-start-end-radius: calc(var(--forge-shape-medium, 4px) * var(--forge-shape-factor, 1));\n border-end-end-radius: calc(var(--forge-shape-medium, 4px) * var(--forge-shape-factor, 1));\n pointer-events: none;\n transition: color var(--forge-animation-duration-short4, 200ms) var(--forge-animation-easing-standard, cubic-bezier(0.2, 0, 0, 1)), border-color var(--forge-animation-duration-short4, 200ms) var(--forge-animation-easing-standard, cubic-bezier(0.2, 0, 0, 1));\n}\n.resize-handle__grip svg {\n flex-shrink: 0;\n width: 12px;\n height: 12px;\n fill: currentColor;\n}\n.resize-handle:hover::before {\n opacity: 0.4;\n width: 2px;\n background-color: var(--forge-theme-primary, #3f51b5);\n}\n.resize-handle:focus-visible {\n outline: 2px solid var(--forge-theme-primary, #3f51b5);\n outline-offset: -2px;\n}\n.resize-handle:focus-visible::before {\n opacity: 0.6;\n width: 2px;\n background-color: var(--forge-theme-primary, #3f51b5);\n}\n.resize-handle--active::before {\n opacity: 0.8;\n width: 3px;\n background-color: var(--forge-theme-primary, #3f51b5);\n}\n.resize-handle:hover .resize-handle__grip, .resize-handle:focus-visible .resize-handle__grip, .resize-handle--active .resize-handle__grip {\n color: var(--forge-theme-primary, #3f51b5);\n border-color: var(--forge-theme-primary, #3f51b5);\n}';
2
2
  export {
3
3
  styles as default
4
4
  };
@@ -1 +1,2 @@
1
1
  export { AiSidebarComponent, AiSidebarComponentTagName } from './ai-sidebar';
2
+ export type { ForgeAiSidebarResizeEventData } from './ai-sidebar';
@@ -1,4 +1,5 @@
1
1
  import { LitElement, TemplateResult, CSSResult } from 'lit';
2
+ import { FeatureToggle } from '../ai-chatbot';
2
3
  declare global {
3
4
  interface HTMLElementTagNameMap {
4
5
  'forge-ai-sidebar-chat': AiSidebarChatComponent;
@@ -8,8 +9,12 @@ declare global {
8
9
  'forge-ai-sidebar-chat-close': CustomEvent<void>;
9
10
  'forge-ai-sidebar-chat-expand': CustomEvent<void>;
10
11
  'forge-ai-sidebar-chat-collapse': CustomEvent<void>;
12
+ 'forge-ai-sidebar-chat-resize': CustomEvent<ForgeAiSidebarChatResizeEventData>;
11
13
  }
12
14
  }
15
+ export interface ForgeAiSidebarChatResizeEventData {
16
+ width: number;
17
+ }
13
18
  export declare const AiSidebarChatComponentTagName: keyof HTMLElementTagNameMap;
14
19
  /**
15
20
  * @tag forge-ai-sidebar-chat
@@ -20,6 +25,7 @@ export declare const AiSidebarChatComponentTagName: keyof HTMLElementTagNameMap;
20
25
  * @fires forge-ai-sidebar-chat-close - Fired when the sidebar chat is closed
21
26
  * @fires forge-ai-sidebar-chat-expand - Fired when the sidebar chat is expanded to modal
22
27
  * @fires forge-ai-sidebar-chat-collapse - Fired when the sidebar chat is collapsed from modal
28
+ * @fires forge-ai-sidebar-chat-resize - Fired when the sidebar width is resized
23
29
  *
24
30
  * @description A form factor component that positions a slotted chatbot in a sidebar or modal.
25
31
  * Manages positioning and expand/collapse state while delegating chat functionality to the slotted chatbot.
@@ -31,7 +37,14 @@ export declare class AiSidebarChatComponent extends LitElement {
31
37
  static styles: CSSResult;
32
38
  open: boolean;
33
39
  expanded: boolean;
34
- resizable: boolean;
40
+ /**
41
+ * Enables sidebar resizing. Set to `'off'` to disable.
42
+ */
43
+ resizable: FeatureToggle;
44
+ /**
45
+ * The current width of the sidebar in pixels. Retained across expand/collapse.
46
+ */
47
+ width?: number;
35
48
  render(): TemplateResult;
36
49
  show(): void;
37
50
  close(): void;
@@ -1,6 +1,6 @@
1
1
  import { unsafeCSS, LitElement, html } from "lit";
2
2
  import { property, customElement } from "lit/decorators.js";
3
- import { createRef, ref } from "lit/directives/ref.js";
3
+ import { ifDefined } from "lit/directives/if-defined.js";
4
4
  import { when } from "lit/directives/when.js";
5
5
  import "../ai-sidebar/ai-sidebar.mjs";
6
6
  import "../ai-modal/ai-modal.mjs";
@@ -22,7 +22,7 @@ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot
22
22
  var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
23
23
  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);
24
24
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
25
- var _sidebarRef, _modalRef, _slotContent, _AiSidebarChatComponent_instances, handleSidebarOpen_fn, handleSidebarClose_fn, handleFullscreenChange_fn, handleChatbotExpand_fn, handleChatbotMinimize_fn, handleModalClose_fn, dispatchEvent_fn;
25
+ var _slotContent, _AiSidebarChatComponent_instances, handleSidebarOpen_fn, handleSidebarClose_fn, handleSidebarResize_fn, handleFullscreenChange_fn, handleChatbotExpand_fn, handleChatbotMinimize_fn, handleModalClose_fn, dispatchEvent_fn;
26
26
  const AiSidebarChatComponentTagName = "forge-ai-sidebar-chat";
27
27
  let AiSidebarChatComponent = class extends LitElement {
28
28
  constructor() {
@@ -30,9 +30,7 @@ let AiSidebarChatComponent = class extends LitElement {
30
30
  __privateAdd(this, _AiSidebarChatComponent_instances);
31
31
  this.open = false;
32
32
  this.expanded = false;
33
- this.resizable = true;
34
- __privateAdd(this, _sidebarRef, createRef());
35
- __privateAdd(this, _modalRef, createRef());
33
+ this.resizable = "on";
36
34
  __privateAdd(this, _slotContent, html`
37
35
  <slot
38
36
  @forge-ai-chatbot-expand=${__privateMethod(this, _AiSidebarChatComponent_instances, handleChatbotExpand_fn)}
@@ -45,7 +43,6 @@ let AiSidebarChatComponent = class extends LitElement {
45
43
  this.expanded,
46
44
  () => html`
47
45
  <forge-ai-modal
48
- ${ref(__privateGet(this, _modalRef))}
49
46
  ?open=${this.open && this.expanded}
50
47
  @forge-ai-modal-fullscreen-change=${__privateMethod(this, _AiSidebarChatComponent_instances, handleFullscreenChange_fn)}
51
48
  @forge-ai-modal-close=${__privateMethod(this, _AiSidebarChatComponent_instances, handleModalClose_fn)}>
@@ -54,11 +51,12 @@ let AiSidebarChatComponent = class extends LitElement {
54
51
  `,
55
52
  () => html`
56
53
  <forge-ai-sidebar
57
- ${ref(__privateGet(this, _sidebarRef))}
58
54
  ?open=${this.open && !this.expanded}
59
- ?resizable=${this.resizable}
55
+ resizable=${this.resizable}
56
+ width=${ifDefined(this.width)}
60
57
  @forge-ai-sidebar-open=${__privateMethod(this, _AiSidebarChatComponent_instances, handleSidebarOpen_fn)}
61
- @forge-ai-sidebar-close=${__privateMethod(this, _AiSidebarChatComponent_instances, handleSidebarClose_fn)}>
58
+ @forge-ai-sidebar-close=${__privateMethod(this, _AiSidebarChatComponent_instances, handleSidebarClose_fn)}
59
+ @forge-ai-sidebar-resize=${__privateMethod(this, _AiSidebarChatComponent_instances, handleSidebarResize_fn)}>
62
60
  ${__privateGet(this, _slotContent)}
63
61
  </forge-ai-sidebar>
64
62
  `
@@ -97,8 +95,6 @@ let AiSidebarChatComponent = class extends LitElement {
97
95
  }
98
96
  }
99
97
  };
100
- _sidebarRef = /* @__PURE__ */ new WeakMap();
101
- _modalRef = /* @__PURE__ */ new WeakMap();
102
98
  _slotContent = /* @__PURE__ */ new WeakMap();
103
99
  _AiSidebarChatComponent_instances = /* @__PURE__ */ new WeakSet();
104
100
  handleSidebarOpen_fn = function() {
@@ -109,6 +105,10 @@ handleSidebarClose_fn = function() {
109
105
  this.open = false;
110
106
  __privateMethod(this, _AiSidebarChatComponent_instances, dispatchEvent_fn).call(this, "forge-ai-sidebar-chat-close");
111
107
  };
108
+ handleSidebarResize_fn = function(event) {
109
+ this.width = event.detail.width;
110
+ __privateMethod(this, _AiSidebarChatComponent_instances, dispatchEvent_fn).call(this, "forge-ai-sidebar-chat-resize", { width: this.width });
111
+ };
112
112
  handleFullscreenChange_fn = function(event) {
113
113
  const { isFullscreen } = event.detail;
114
114
  if (isFullscreen && !this.expanded) {
@@ -133,10 +133,11 @@ handleChatbotMinimize_fn = function() {
133
133
  handleModalClose_fn = function() {
134
134
  this.collapse();
135
135
  };
136
- dispatchEvent_fn = function(type) {
136
+ dispatchEvent_fn = function(type, detail) {
137
137
  const event = new CustomEvent(type, {
138
138
  bubbles: true,
139
- composed: true
139
+ composed: true,
140
+ detail
140
141
  });
141
142
  this.dispatchEvent(event);
142
143
  };
@@ -148,8 +149,11 @@ __decorateClass([
148
149
  property({ type: Boolean })
149
150
  ], AiSidebarChatComponent.prototype, "expanded", 2);
150
151
  __decorateClass([
151
- property({ type: Boolean })
152
+ property()
152
153
  ], AiSidebarChatComponent.prototype, "resizable", 2);
154
+ __decorateClass([
155
+ property({ type: Number })
156
+ ], AiSidebarChatComponent.prototype, "width", 2);
153
157
  AiSidebarChatComponent = __decorateClass([
154
158
  customElement(AiSidebarChatComponentTagName)
155
159
  ], AiSidebarChatComponent);
@@ -51,7 +51,7 @@ export declare const AiThreadsSearchComponentTagName: keyof HTMLElementTagNameMa
51
51
  * @slot header-actions - Slot for a persistent action pinned to the top-right of the header, alongside the "New chat" button (e.g. a "View all" button).
52
52
  *
53
53
  * @event {CustomEvent<ForgeAiThreadsSearchQueryEventData>} forge-ai-threads-search-query - Fired when search query changes (debounced).
54
- * @event {CustomEvent<ForgeAiThreadsSearchLoadMoreEventData>} forge-ai-threads-search-load-more - Fired when user scrolls near bottom for pagination.
54
+ * @event {CustomEvent<ForgeAiThreadsSearchLoadMoreEventData>} forge-ai-threads-search-load-more - Fired when the user scrolls near the bottom or clicks "Load more" for pagination.
55
55
  * @event {CustomEvent<ForgeAiThreadsSearchSelectEventData>} forge-ai-threads-search-select - Fired when a thread is selected.
56
56
  * @event {CustomEvent} forge-ai-threads-search-new-chat - Fired when new chat button clicked.
57
57
  * @event {CustomEvent<ForgeAiThreadsSearchRenameEventData>} forge-ai-threads-search-rename - Fired when thread renamed. Cancelable - if prevented, call onSuccess() to commit or onError() to revert.
@@ -109,6 +109,7 @@ export declare class AiThreadsSearchComponent extends LitElement {
109
109
  */
110
110
  selectedThreadId: string | null;
111
111
  private _resultsContainer;
112
+ private _searchInput?;
112
113
  private _searchQuery;
113
114
  private _isSearching;
114
115
  private _searchResults;
@@ -125,5 +126,16 @@ export declare class AiThreadsSearchComponent extends LitElement {
125
126
  * built-in one). Call once the host's own confirmation has been accepted.
126
127
  */
127
128
  confirmThreadDelete(thread: Thread): void;
129
+ /**
130
+ * Discards the current query and its results so the list shows every loaded chat again.
131
+ * Dispatches no search event and moves no focus, so a host may call it on a surface that is
132
+ * currently hidden (e.g. a popover that stays mounted between opens).
133
+ */
134
+ resetSearch(): void;
135
+ /**
136
+ * Moves focus into the component: the search input when shown, otherwise the first chat in
137
+ * the list, otherwise the first header button.
138
+ */
139
+ focus(): void;
128
140
  render(): TemplateResult;
129
141
  }
@@ -26,8 +26,9 @@ var __decorateClass = (decorators, target, key, kind) => {
26
26
  var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
27
27
  var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
28
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);
29
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value);
29
30
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
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;
31
+ var _focusRowIndexAfterLoad, _deleteThreadController, _infiniteScrollController, _AiThreadsSearchComponent_instances, shouldEnablePagination_get, handleSearchInput_fn, performSearch_fn, handleLoadMoreClick_fn, focusRow_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;
31
32
  const AiThreadsSearchComponentTagName = "forge-ai-threads-search";
32
33
  let AiThreadsSearchComponent = class extends LitElement {
33
34
  constructor() {
@@ -49,6 +50,7 @@ let AiThreadsSearchComponent = class extends LitElement {
49
50
  this._editingThreadId = null;
50
51
  this._hiddenThreadIds = /* @__PURE__ */ new Set();
51
52
  this._openMenuThreadId = null;
53
+ __privateAdd(this, _focusRowIndexAfterLoad, null);
52
54
  __privateAdd(this, _deleteThreadController, new DeleteThreadController(this, {
53
55
  onConfirm: (thread) => __privateMethod(this, _AiThreadsSearchComponent_instances, confirmDelete_fn).call(this, thread)
54
56
  }));
@@ -85,6 +87,27 @@ let AiThreadsSearchComponent = class extends LitElement {
85
87
  confirmThreadDelete(thread) {
86
88
  __privateMethod(this, _AiThreadsSearchComponent_instances, confirmDelete_fn).call(this, thread);
87
89
  }
90
+ /**
91
+ * Discards the current query and its results so the list shows every loaded chat again.
92
+ * Dispatches no search event and moves no focus, so a host may call it on a surface that is
93
+ * currently hidden (e.g. a popover that stays mounted between opens).
94
+ */
95
+ resetSearch() {
96
+ clearTimeout(this._searchTimeout);
97
+ this._searchQuery = "";
98
+ this._searchResults = [];
99
+ this._isSearching = false;
100
+ __privateSet(this, _focusRowIndexAfterLoad, null);
101
+ __privateGet(this, _infiniteScrollController).reset();
102
+ }
103
+ /**
104
+ * Moves focus into the component: the search input when shown, otherwise the first chat in
105
+ * the list, otherwise the first header button.
106
+ */
107
+ focus() {
108
+ const target = this._searchInput ?? this.shadowRoot?.querySelector(".forge-list-item > button") ?? this.shadowRoot?.querySelector("button");
109
+ target?.focus();
110
+ }
88
111
  render() {
89
112
  return html`
90
113
  <div class="threads-search-container">
@@ -95,6 +118,7 @@ let AiThreadsSearchComponent = class extends LitElement {
95
118
  `;
96
119
  }
97
120
  };
121
+ _focusRowIndexAfterLoad = /* @__PURE__ */ new WeakMap();
98
122
  _deleteThreadController = /* @__PURE__ */ new WeakMap();
99
123
  _infiniteScrollController = /* @__PURE__ */ new WeakMap();
100
124
  _AiThreadsSearchComponent_instances = /* @__PURE__ */ new WeakSet();
@@ -115,6 +139,7 @@ handleSearchInput_fn = function(e) {
115
139
  };
116
140
  performSearch_fn = function() {
117
141
  const searchQuery = this._searchQuery.trim();
142
+ __privateSet(this, _focusRowIndexAfterLoad, null);
118
143
  __privateGet(this, _infiniteScrollController).reset();
119
144
  const searchEvent = new CustomEvent("forge-ai-threads-search-query", {
120
145
  detail: {
@@ -138,11 +163,25 @@ performSearch_fn = function() {
138
163
  __privateGet(this, _infiniteScrollController).setHasMore(false);
139
164
  }
140
165
  };
166
+ handleLoadMoreClick_fn = function() {
167
+ if (__privateGet(this, _infiniteScrollController).isLoadingMore) {
168
+ return;
169
+ }
170
+ __privateSet(this, _focusRowIndexAfterLoad, __privateGet(this, _AiThreadsSearchComponent_instances, displayedThreads_get).length);
171
+ __privateGet(this, _infiniteScrollController).setLoadingState(true);
172
+ __privateMethod(this, _AiThreadsSearchComponent_instances, loadMore_fn).call(this);
173
+ };
174
+ focusRow_fn = function(index) {
175
+ const rows = this.shadowRoot?.querySelectorAll(".forge-list-item > button") ?? [];
176
+ rows[index]?.focus();
177
+ };
141
178
  loadMore_fn = function() {
142
179
  const loadMoreEvent = new CustomEvent("forge-ai-threads-search-load-more", {
143
180
  detail: {
144
181
  query: this._searchQuery,
145
182
  appendResults: (results) => {
183
+ const focusRowIndex = __privateGet(this, _focusRowIndexAfterLoad);
184
+ __privateSet(this, _focusRowIndexAfterLoad, null);
146
185
  if (results.length === 0) {
147
186
  __privateGet(this, _infiniteScrollController).setHasMore(false);
148
187
  } else {
@@ -151,6 +190,9 @@ loadMore_fn = function() {
151
190
  } else {
152
191
  this.threads = [...this.threads, ...results];
153
192
  }
193
+ if (focusRowIndex !== null) {
194
+ void this.updateComplete.then(() => __privateMethod(this, _AiThreadsSearchComponent_instances, focusRow_fn).call(this, focusRowIndex));
195
+ }
154
196
  }
155
197
  __privateGet(this, _infiniteScrollController).setLoadingState(false);
156
198
  }
@@ -183,6 +225,10 @@ handleBackClick_fn = function() {
183
225
  this.dispatchEvent(event);
184
226
  };
185
227
  handleRetryClick_fn = function() {
228
+ if (this._searchQuery.trim()) {
229
+ __privateMethod(this, _AiThreadsSearchComponent_instances, performSearch_fn).call(this);
230
+ return;
231
+ }
186
232
  const event = new CustomEvent("forge-ai-threads-search-retry", {
187
233
  bubbles: true,
188
234
  composed: true
@@ -277,10 +323,8 @@ handleEditCancel_fn = function() {
277
323
  this._editingThreadId = null;
278
324
  };
279
325
  handleClearSearch_fn = function() {
280
- this._searchQuery = "";
281
- this._searchResults = [];
282
- this._isSearching = false;
283
- __privateGet(this, _infiniteScrollController).reset();
326
+ this.resetSearch();
327
+ this._searchInput?.focus();
284
328
  };
285
329
  displayedThreads_get = function() {
286
330
  const threads = this._searchQuery ? this._searchResults : this.threads;
@@ -345,7 +389,11 @@ searchField_get = function() {
345
389
  ${when(
346
390
  this._searchQuery.length,
347
391
  () => html`
348
- <button class="forge-icon-button forge-icon-button--small" type="button" @click=${__privateMethod(this, _AiThreadsSearchComponent_instances, handleClearSearch_fn)}>
392
+ <button
393
+ class="forge-icon-button forge-icon-button--small"
394
+ type="button"
395
+ aria-label="Clear search"
396
+ @click=${__privateMethod(this, _AiThreadsSearchComponent_instances, handleClearSearch_fn)}>
349
397
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="forge-icon" aria-hidden="true">
350
398
  <path
351
399
  d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" />
@@ -452,14 +500,26 @@ listFooter_get = function() {
452
500
  if (__privateGet(this, _AiThreadsSearchComponent_instances, errorPlacement_get) === "list-footer") {
453
501
  return __privateGet(this, _AiThreadsSearchComponent_instances, compactError_get);
454
502
  }
455
- if (!__privateGet(this, _infiniteScrollController).isLoadingMore) {
456
- return nothing;
503
+ if (__privateGet(this, _infiniteScrollController).isLoadingMore) {
504
+ return html`
505
+ <div class="loading-more-indicator">
506
+ <forge-ai-spinner></forge-ai-spinner>
507
+ </div>
508
+ `;
457
509
  }
458
- return html`
459
- <div class="loading-more-indicator">
460
- <forge-ai-spinner></forge-ai-spinner>
461
- </div>
462
- `;
510
+ if (__privateGet(this, _AiThreadsSearchComponent_instances, shouldEnablePagination_get) && __privateGet(this, _infiniteScrollController).hasMoreResults) {
511
+ return html`
512
+ <div class="loading-more-indicator">
513
+ <button
514
+ type="button"
515
+ class="forge-button forge-button--dense load-more-button"
516
+ @click=${__privateMethod(this, _AiThreadsSearchComponent_instances, handleLoadMoreClick_fn)}>
517
+ Load more
518
+ </button>
519
+ </div>
520
+ `;
521
+ }
522
+ return nothing;
463
523
  };
464
524
  resultsList_get = function() {
465
525
  const displayThreads = __privateGet(this, _AiThreadsSearchComponent_instances, displayedThreads_get);
@@ -512,6 +572,9 @@ __decorateClass([
512
572
  __decorateClass([
513
573
  query(".results-container")
514
574
  ], AiThreadsSearchComponent.prototype, "_resultsContainer", 2);
575
+ __decorateClass([
576
+ query("#threads-search-input")
577
+ ], AiThreadsSearchComponent.prototype, "_searchInput", 2);
515
578
  __decorateClass([
516
579
  state()
517
580
  ], AiThreadsSearchComponent.prototype, "_searchQuery", 2);