@tylertech/forge-ai 0.13.0 → 0.13.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.
Files changed (91) hide show
  1. package/custom-elements.json +5083 -3159
  2. package/dist/ai-agent-info/ai-agent-info.d.ts +1 -1
  3. package/dist/ai-agent-info/ai-agent-info.mjs +17 -62
  4. package/dist/ai-agent-info/ai-agent-info.scss.mjs +1 -1
  5. package/dist/ai-assistant-response/ai-assistant-response.scss.mjs +1 -1
  6. package/dist/ai-chat-header/ai-chat-header.d.ts +7 -0
  7. package/dist/ai-chat-header/ai-chat-header.mjs +52 -7
  8. package/dist/ai-chat-header/ai-chat-header.scss.mjs +1 -1
  9. package/dist/ai-chatbot/ai-chatbot-base.d.ts +37 -3
  10. package/dist/ai-chatbot/ai-chatbot-base.mjs +83 -24
  11. package/dist/ai-chatbot/ai-chatbot.d.ts +41 -1
  12. package/dist/ai-chatbot/ai-chatbot.mjs +242 -43
  13. package/dist/ai-chatbot/ai-chatbot.scss.mjs +1 -1
  14. package/dist/ai-chatbot/index.d.ts +1 -1
  15. package/dist/ai-chatbot/types.d.ts +39 -0
  16. package/dist/ai-chatbot/utils.d.ts +5 -0
  17. package/dist/ai-chatbot/utils.mjs +28 -0
  18. package/dist/ai-chatbot-launcher/ai-chatbot-launcher.d.ts +29 -2
  19. package/dist/ai-chatbot-launcher/ai-chatbot-launcher.mjs +156 -10
  20. package/dist/ai-chatbot-launcher/ai-chatbot-launcher.scss.mjs +1 -1
  21. package/dist/ai-confirmation-prompt/ai-confirmation-prompt.d.ts +6 -0
  22. package/dist/ai-confirmation-prompt/ai-confirmation-prompt.mjs +64 -19
  23. package/dist/ai-confirmation-prompt/ai-confirmation-prompt.scss.mjs +1 -1
  24. package/dist/ai-context-chip/ai-context-chip.d.ts +45 -0
  25. package/dist/ai-context-chip/ai-context-chip.mjs +169 -0
  26. package/dist/ai-context-chip/ai-context-chip.scss.mjs +4 -0
  27. package/dist/ai-context-chip/index.d.ts +2 -0
  28. package/dist/ai-context-chip/index.mjs +5 -0
  29. package/dist/ai-context-items/ai-context-items.d.ts +20 -0
  30. package/dist/ai-context-items/ai-context-items.mjs +78 -0
  31. package/dist/ai-context-items/ai-context-items.scss.mjs +4 -0
  32. package/dist/ai-context-items/index.d.ts +1 -0
  33. package/dist/ai-context-items/index.mjs +5 -0
  34. package/dist/ai-conversations-panel/ai-conversations-panel.d.ts +86 -0
  35. package/dist/ai-conversations-panel/ai-conversations-panel.mjs +558 -0
  36. package/dist/ai-conversations-panel/ai-conversations-panel.scss.mjs +4 -0
  37. package/dist/ai-conversations-panel/index.d.ts +2 -0
  38. package/dist/ai-conversations-panel/index.mjs +5 -0
  39. package/dist/ai-copy-button/ai-copy-button.d.ts +32 -0
  40. package/dist/ai-copy-button/ai-copy-button.mjs +116 -0
  41. package/dist/ai-copy-button/ai-copy-button.scss.mjs +4 -0
  42. package/dist/ai-copy-button/index.d.ts +1 -0
  43. package/dist/ai-copy-button/index.mjs +5 -0
  44. package/dist/ai-dialog/ai-dialog.scss.mjs +1 -1
  45. package/dist/ai-dropdown-menu/ai-dropdown-menu-item.scss.mjs +1 -1
  46. package/dist/ai-dropdown-menu/ai-dropdown-menu.d.ts +6 -1
  47. package/dist/ai-dropdown-menu/ai-dropdown-menu.mjs +20 -8
  48. package/dist/ai-dropdown-menu/ai-dropdown-menu.scss.mjs +1 -1
  49. package/dist/ai-edit-thread/ai-edit-thread.d.ts +34 -0
  50. package/dist/ai-edit-thread/ai-edit-thread.mjs +130 -0
  51. package/dist/ai-edit-thread/ai-edit-thread.scss.mjs +4 -0
  52. package/dist/ai-edit-thread/index.d.ts +1 -0
  53. package/dist/ai-edit-thread/index.mjs +5 -0
  54. package/dist/ai-message-thread/ai-message-thread.scss.mjs +1 -1
  55. package/dist/ai-modal/ai-modal.d.ts +11 -2
  56. package/dist/ai-modal/ai-modal.mjs +31 -6
  57. package/dist/ai-prompt/ai-prompt.d.ts +7 -1
  58. package/dist/ai-prompt/ai-prompt.mjs +29 -2
  59. package/dist/ai-sidebar/ai-sidebar.d.ts +11 -0
  60. package/dist/ai-sidebar/ai-sidebar.mjs +94 -4
  61. package/dist/ai-sidebar/ai-sidebar.scss.mjs +1 -1
  62. package/dist/ai-sidebar-chat/ai-sidebar-chat.d.ts +1 -0
  63. package/dist/ai-sidebar-chat/ai-sidebar-chat.mjs +5 -0
  64. package/dist/ai-spinner/ai-spinner.d.ts +1 -1
  65. package/dist/ai-spinner/ai-spinner.scss.mjs +1 -1
  66. package/dist/ai-thread-actions-menu/ai-thread-actions-menu.d.ts +44 -0
  67. package/dist/ai-thread-actions-menu/ai-thread-actions-menu.mjs +136 -0
  68. package/dist/ai-thread-actions-menu/ai-thread-actions-menu.scss.mjs +4 -0
  69. package/dist/ai-thread-actions-menu/index.d.ts +1 -0
  70. package/dist/ai-thread-actions-menu/index.mjs +5 -0
  71. package/dist/ai-threads/ai-threads.d.ts +45 -16
  72. package/dist/ai-threads/ai-threads.mjs +256 -98
  73. package/dist/ai-threads/ai-threads.scss.mjs +1 -1
  74. package/dist/ai-threads-search/ai-threads-search.d.ts +81 -0
  75. package/dist/ai-threads-search/ai-threads-search.mjs +414 -0
  76. package/dist/ai-threads-search/ai-threads-search.scss.mjs +4 -0
  77. package/dist/ai-threads-search/index.d.ts +1 -0
  78. package/dist/ai-threads-search/index.mjs +5 -0
  79. package/dist/core/index.d.ts +1 -0
  80. package/dist/core/index.mjs +3 -1
  81. package/dist/core/resize-controller.d.ts +25 -0
  82. package/dist/core/resize-controller.mjs +144 -0
  83. package/dist/index.d.ts +7 -0
  84. package/dist/index.mjs +21 -0
  85. package/dist/utils/delete-thread-controller.d.ts +14 -0
  86. package/dist/utils/delete-thread-controller.mjs +80 -0
  87. package/dist/utils/index.d.ts +3 -0
  88. package/dist/utils/index.mjs +8 -0
  89. package/dist/utils/infinite-scroll-controller.d.ts +33 -0
  90. package/dist/utils/infinite-scroll-controller.mjs +95 -0
  91. package/package.json +15 -14
@@ -0,0 +1,25 @@
1
+ import { ReactiveController, ReactiveControllerHost } from 'lit';
2
+ import { Ref } from 'lit/directives/ref.js';
3
+ export interface ResizeControllerOptions {
4
+ targetElementRef: Ref<HTMLElement>;
5
+ resizeHandleRef: Ref<HTMLElement>;
6
+ minWidth?: number;
7
+ maxWidth?: number;
8
+ defaultWidth?: number;
9
+ keyboardStep?: number;
10
+ onResize?: (width: number) => void;
11
+ onResizeStart?: () => void;
12
+ onResizeEnd?: () => void;
13
+ }
14
+ export declare class ResizeController implements ReactiveController {
15
+ #private;
16
+ constructor(host: ReactiveControllerHost, options: ResizeControllerOptions);
17
+ hostConnected(): void;
18
+ hostDisconnected(): void;
19
+ get isResizing(): boolean;
20
+ get currentWidth(): number;
21
+ setWidth(width: number): void;
22
+ resetWidth(): void;
23
+ handlePointerDown: (event: PointerEvent) => void;
24
+ handleKeyDown: (event: KeyboardEvent) => void;
25
+ }
@@ -0,0 +1,144 @@
1
+ const RESIZE_SCREEN_BUFFER = 100;
2
+ class ResizeController {
3
+ constructor(host, options) {
4
+ this.#isResizing = false;
5
+ this.#currentWidth = 0;
6
+ this.#dragStartX = 0;
7
+ this.#dragStartWidth = 0;
8
+ this.handlePointerDown = (event) => {
9
+ if (event.button !== 0) {
10
+ return;
11
+ }
12
+ event.preventDefault();
13
+ const resizeHandle = this.#options.resizeHandleRef.value;
14
+ if (!resizeHandle) {
15
+ return;
16
+ }
17
+ resizeHandle.setPointerCapture(event.pointerId);
18
+ this.#dragStartX = event.clientX;
19
+ this.#dragStartWidth = this.#currentWidth;
20
+ this.#isResizing = true;
21
+ this.#host.requestUpdate();
22
+ this.#options.onResizeStart();
23
+ this.#addResizeListeners();
24
+ };
25
+ this.handleKeyDown = (event) => {
26
+ const step = this.#options.keyboardStep;
27
+ let delta = 0;
28
+ switch (event.key) {
29
+ case "ArrowLeft":
30
+ delta = step;
31
+ break;
32
+ case "ArrowRight":
33
+ delta = -step;
34
+ break;
35
+ default:
36
+ return;
37
+ }
38
+ event.preventDefault();
39
+ const newWidth = this.#currentWidth + delta;
40
+ this.#currentWidth = this.#constrainWidth(newWidth);
41
+ this.#applyWidth();
42
+ };
43
+ this.#handlePointerMove = (event) => {
44
+ if (!this.#isResizing) {
45
+ return;
46
+ }
47
+ event.preventDefault();
48
+ const deltaX = this.#dragStartX - event.clientX;
49
+ const newWidth = this.#dragStartWidth + deltaX;
50
+ this.#currentWidth = this.#constrainWidth(newWidth);
51
+ this.#applyWidth();
52
+ };
53
+ this.#handlePointerUp = (event) => {
54
+ if (!this.#isResizing) {
55
+ return;
56
+ }
57
+ event.preventDefault();
58
+ const resizeHandle = this.#options.resizeHandleRef.value;
59
+ if (resizeHandle) {
60
+ resizeHandle.releasePointerCapture(event.pointerId);
61
+ }
62
+ this.#isResizing = false;
63
+ this.#host.requestUpdate();
64
+ this.#options.onResizeEnd();
65
+ this.#removeResizeListeners();
66
+ };
67
+ this.#host = host;
68
+ this.#options = {
69
+ minWidth: 360,
70
+ maxWidth: 800,
71
+ defaultWidth: 420,
72
+ keyboardStep: 20,
73
+ onResize: () => {
74
+ },
75
+ onResizeStart: () => {
76
+ },
77
+ onResizeEnd: () => {
78
+ },
79
+ ...options
80
+ };
81
+ this.#currentWidth = this.#options.defaultWidth;
82
+ host.addController(this);
83
+ }
84
+ #host;
85
+ #options;
86
+ #isResizing;
87
+ #currentWidth;
88
+ #dragStartX;
89
+ #dragStartWidth;
90
+ #resizeAbortController;
91
+ hostConnected() {
92
+ }
93
+ hostDisconnected() {
94
+ this.#cleanup();
95
+ }
96
+ get isResizing() {
97
+ return this.#isResizing;
98
+ }
99
+ get currentWidth() {
100
+ return this.#currentWidth;
101
+ }
102
+ setWidth(width) {
103
+ this.#currentWidth = this.#constrainWidth(width);
104
+ this.#applyWidth();
105
+ }
106
+ resetWidth() {
107
+ this.setWidth(this.#options.defaultWidth);
108
+ }
109
+ #handlePointerMove;
110
+ #handlePointerUp;
111
+ #constrainWidth(width) {
112
+ const availableWidth = window.innerWidth - RESIZE_SCREEN_BUFFER;
113
+ const effectiveMaxWidth = Math.min(this.#options.maxWidth, availableWidth);
114
+ return Math.max(this.#options.minWidth, Math.min(width, effectiveMaxWidth));
115
+ }
116
+ #applyWidth() {
117
+ this.#options.onResize(this.#currentWidth);
118
+ this.#host.requestUpdate();
119
+ }
120
+ #addResizeListeners() {
121
+ if (!this.#resizeAbortController) {
122
+ this.#resizeAbortController = new AbortController();
123
+ document.addEventListener("pointermove", this.#handlePointerMove, {
124
+ signal: this.#resizeAbortController.signal
125
+ });
126
+ document.addEventListener("pointerup", this.#handlePointerUp, {
127
+ signal: this.#resizeAbortController.signal
128
+ });
129
+ }
130
+ }
131
+ #removeResizeListeners() {
132
+ if (this.#resizeAbortController) {
133
+ this.#resizeAbortController.abort();
134
+ this.#resizeAbortController = void 0;
135
+ }
136
+ }
137
+ #cleanup() {
138
+ this.#removeResizeListeners();
139
+ this.#isResizing = false;
140
+ }
141
+ }
142
+ export {
143
+ ResizeController
144
+ };
package/dist/index.d.ts CHANGED
@@ -12,10 +12,15 @@ export * from './ai-chat-header';
12
12
  export * from './ai-chat-interface';
13
13
  export * from './ai-chatbot';
14
14
  export * from './ai-chatbot-launcher';
15
+ export * from './ai-copy-button';
15
16
  export * from './ai-confirmation-prompt';
17
+ export * from './ai-conversations-panel';
18
+ export * from './ai-context-chip';
19
+ export * from './ai-context-items';
16
20
  export * from './ai-dialog';
17
21
  export * from './ai-disclaimer';
18
22
  export * from './ai-dropdown-menu';
23
+ export * from './ai-edit-thread';
19
24
  export * from './ai-embedded-chat';
20
25
  export * from './ai-empty-state';
21
26
  export * from './ai-error-message';
@@ -37,5 +42,7 @@ export * from './ai-slash-command-menu';
37
42
  export * from './ai-suggestions';
38
43
  export * from './ai-thinking-indicator';
39
44
  export * from './ai-threads';
45
+ export * from './ai-threads-search';
46
+ export * from './ai-thread-actions-menu';
40
47
  export * from './ai-user-message';
41
48
  export * from './ai-voice-input';
package/dist/index.mjs CHANGED
@@ -28,13 +28,18 @@ import { FileUploadManager } from "./ai-chatbot/file-upload-manager.mjs";
28
28
  import { MarkdownStreamController } from "./ai-chatbot/markdown-stream-controller.mjs";
29
29
  import { generateId, renderMarkdown } from "./ai-chatbot/utils.mjs";
30
30
  import { AiChatbotLauncherComponent, AiChatbotLauncherComponentTagName } from "./ai-chatbot-launcher/ai-chatbot-launcher.mjs";
31
+ import { AiCopyButtonComponent, AiCopyButtonComponentTagName } from "./ai-copy-button/ai-copy-button.mjs";
31
32
  import { AiConfirmationPromptComponent, AiConfirmationPromptComponentTagName } from "./ai-confirmation-prompt/ai-confirmation-prompt.mjs";
33
+ import { AiConversationsPanelComponent, AiConversationsPanelComponentTagName } from "./ai-conversations-panel/ai-conversations-panel.mjs";
34
+ import { AiContextChipComponent, AiContextChipComponentTagName } from "./ai-context-chip/ai-context-chip.mjs";
35
+ import { AiContextItemsComponent, AiContextItemsComponentTagName } from "./ai-context-items/ai-context-items.mjs";
32
36
  import { AiDialogComponent, AiDialogComponentTagName } from "./ai-dialog/ai-dialog.mjs";
33
37
  import { AiDisclaimerComponent, AiDisclaimerComponentTagName } from "./ai-disclaimer/ai-disclaimer.mjs";
34
38
  import { ForgeAiDropdownMenuComponent } from "./ai-dropdown-menu/ai-dropdown-menu.mjs";
35
39
  import { ForgeAiDropdownMenuItemComponent } from "./ai-dropdown-menu/ai-dropdown-menu-item.mjs";
36
40
  import { ForgeAiDropdownMenuItemGroupComponent } from "./ai-dropdown-menu/ai-dropdown-menu-item-group.mjs";
37
41
  import { ForgeAiDropdownMenuSeparatorComponent } from "./ai-dropdown-menu/ai-dropdown-menu-separator.mjs";
42
+ import { AiEditThreadComponent, AiEditThreadComponentTagName } from "./ai-edit-thread/ai-edit-thread.mjs";
38
43
  import { AiEmbeddedChatComponent, AiEmbeddedChatComponentTagName } from "./ai-embedded-chat/ai-embedded-chat.mjs";
39
44
  import { AiEmptyStateComponent, AiEmptyStateComponentTagName } from "./ai-empty-state/ai-empty-state.mjs";
40
45
  import { AiErrorMessageComponent, AiErrorMessageComponentTagName } from "./ai-error-message/ai-error-message.mjs";
@@ -57,6 +62,8 @@ import { AiSlashCommandMenuComponent, AiSlashCommandMenuComponentTagName } from
57
62
  import { AiSuggestionsComponent, AiSuggestionsComponentTagName } from "./ai-suggestions/ai-suggestions.mjs";
58
63
  import { AiThinkingIndicatorComponent, AiThinkingIndicatorComponentTagName } from "./ai-thinking-indicator/ai-thinking-indicator.mjs";
59
64
  import { AiThreadsComponent, AiThreadsComponentTagName } from "./ai-threads/ai-threads.mjs";
65
+ import { AiThreadsSearchComponent, AiThreadsSearchComponentTagName } from "./ai-threads-search/ai-threads-search.mjs";
66
+ import { AiThreadActionsMenuComponent, AiThreadActionsMenuComponentTagName } from "./ai-thread-actions-menu/ai-thread-actions-menu.mjs";
60
67
  import { AiUserMessageComponent, AiUserMessageComponentTagName } from "./ai-user-message/ai-user-message.mjs";
61
68
  import { AiVoiceInputComponent, AiVoiceInputComponentTagName } from "./ai-voice-input/ai-voice-input.mjs";
62
69
  export {
@@ -90,10 +97,20 @@ export {
90
97
  AiChatbotToolCallComponentTagName,
91
98
  AiConfirmationPromptComponent,
92
99
  AiConfirmationPromptComponentTagName,
100
+ AiContextChipComponent,
101
+ AiContextChipComponentTagName,
102
+ AiContextItemsComponent,
103
+ AiContextItemsComponentTagName,
104
+ AiConversationsPanelComponent,
105
+ AiConversationsPanelComponentTagName,
106
+ AiCopyButtonComponent,
107
+ AiCopyButtonComponentTagName,
93
108
  AiDialogComponent,
94
109
  AiDialogComponentTagName,
95
110
  AiDisclaimerComponent,
96
111
  AiDisclaimerComponentTagName,
112
+ AiEditThreadComponent,
113
+ AiEditThreadComponentTagName,
97
114
  AiEmbeddedChatComponent,
98
115
  AiEmbeddedChatComponentTagName,
99
116
  AiEmptyStateComponent,
@@ -134,8 +151,12 @@ export {
134
151
  AiSuggestionsComponentTagName,
135
152
  AiThinkingIndicatorComponent,
136
153
  AiThinkingIndicatorComponentTagName,
154
+ AiThreadActionsMenuComponent,
155
+ AiThreadActionsMenuComponentTagName,
137
156
  AiThreadsComponent,
138
157
  AiThreadsComponentTagName,
158
+ AiThreadsSearchComponent,
159
+ AiThreadsSearchComponentTagName,
139
160
  AiToolCallIndicatorComponent,
140
161
  AiToolCallIndicatorComponentTagName,
141
162
  AiUserMessageComponent,
@@ -0,0 +1,14 @@
1
+ import { ReactiveController, ReactiveControllerHost, nothing, TemplateResult } from 'lit';
2
+ import { Thread } from '../ai-threads';
3
+ export interface DeleteThreadControllerCallbacks {
4
+ onConfirm: (thread: Thread) => void;
5
+ }
6
+ export declare class DeleteThreadController implements ReactiveController {
7
+ #private;
8
+ constructor(host: ReactiveControllerHost, callbacks: DeleteThreadControllerCallbacks);
9
+ hostConnected(): void;
10
+ hostDisconnected(): void;
11
+ show(thread: Thread): Promise<void>;
12
+ close(): void;
13
+ get template(): TemplateResult | typeof nothing;
14
+ }
@@ -0,0 +1,80 @@
1
+ import { nothing, html } from "lit";
2
+ import { createRef, ref } from "lit/directives/ref.js";
3
+ class DeleteThreadController {
4
+ #host;
5
+ #callbacks;
6
+ #confirmingThread = null;
7
+ #modalRef = createRef();
8
+ constructor(host, callbacks) {
9
+ this.#host = host;
10
+ this.#callbacks = callbacks;
11
+ host.addController(this);
12
+ }
13
+ hostConnected() {
14
+ }
15
+ hostDisconnected() {
16
+ }
17
+ async show(thread) {
18
+ this.#confirmingThread = thread;
19
+ this.#host.requestUpdate();
20
+ await this.#host.updateComplete;
21
+ this.#modalRef.value?.show();
22
+ }
23
+ close() {
24
+ this.#modalRef.value?.close();
25
+ this.#confirmingThread = null;
26
+ this.#host.requestUpdate();
27
+ }
28
+ #handleConfirm = () => {
29
+ if (!this.#confirmingThread) {
30
+ return;
31
+ }
32
+ this.#callbacks.onConfirm(this.#confirmingThread);
33
+ this.close();
34
+ };
35
+ #handleDeny = () => {
36
+ this.close();
37
+ };
38
+ #handleModalClose = (evt) => {
39
+ evt.stopPropagation();
40
+ this.#confirmingThread = null;
41
+ this.#host.requestUpdate();
42
+ };
43
+ get template() {
44
+ if (!this.#confirmingThread) {
45
+ return nothing;
46
+ }
47
+ return html`
48
+ <forge-ai-modal
49
+ ${ref(this.#modalRef)}
50
+ .open=${true}
51
+ size-strategy="fixed"
52
+ @forge-ai-modal-close=${this.#handleModalClose}>
53
+ <div class="delete-confirmation" role="alertdialog" aria-labelledby="delete-confirm-title">
54
+ <h2 id="delete-confirm-title" class="delete-confirmation__title">Delete chat</h2>
55
+ <div class="delete-confirmation__text">
56
+ Are you sure you want to delete "${this.#confirmingThread.title}"? This action cannot be undone.
57
+ </div>
58
+ <div class="delete-confirmation__actions">
59
+ <button
60
+ class="forge-button forge-button--outlined"
61
+ @click=${this.#handleDeny}
62
+ autofocus
63
+ aria-label="Cancel deletion">
64
+ Cancel
65
+ </button>
66
+ <button
67
+ class="forge-button forge-button--filled"
68
+ @click=${this.#handleConfirm}
69
+ aria-label="Confirm deletion">
70
+ Delete
71
+ </button>
72
+ </div>
73
+ </div>
74
+ </forge-ai-modal>
75
+ `;
76
+ }
77
+ }
78
+ export {
79
+ DeleteThreadController
80
+ };
@@ -0,0 +1,3 @@
1
+ export { DeleteThreadController } from './delete-thread-controller';
2
+ export { InfiniteScrollController } from './infinite-scroll-controller';
3
+ export { KeyboardNavigationController } from './keyboard-navigation-controller';
@@ -0,0 +1,8 @@
1
+ import { DeleteThreadController } from "./delete-thread-controller.mjs";
2
+ import { InfiniteScrollController } from "./infinite-scroll-controller.mjs";
3
+ import { KeyboardNavigationController } from "./keyboard-navigation-controller.mjs";
4
+ export {
5
+ DeleteThreadController,
6
+ InfiniteScrollController,
7
+ KeyboardNavigationController
8
+ };
@@ -0,0 +1,33 @@
1
+ import { ReactiveController, ReactiveControllerHost } from 'lit';
2
+ export interface InfiniteScrollControllerConfig {
3
+ /** Callback when load-more conditions met */
4
+ onLoadMore: () => void;
5
+ /** Distance from bottom to trigger load (default: 100px) */
6
+ scrollThreshold?: number;
7
+ /** Debounce delay for scroll events (default: 150ms) */
8
+ debounceMs?: number;
9
+ }
10
+ /**
11
+ * Reactive controller for infinite scroll functionality.
12
+ * Manages scroll detection, debouncing, and pagination state.
13
+ */
14
+ export declare class InfiniteScrollController implements ReactiveController {
15
+ #private;
16
+ constructor(host: ReactiveControllerHost, config: InfiniteScrollControllerConfig);
17
+ hostConnected(): void;
18
+ hostDisconnected(): void;
19
+ /** Attach scroll listener to container element */
20
+ attach(container: HTMLElement): void;
21
+ /** Remove scroll listener from current container */
22
+ detach(): void;
23
+ /** Reset pagination state */
24
+ reset(): void;
25
+ /** Set loading state (call when starting async load) */
26
+ setLoadingState(isLoading: boolean): void;
27
+ /** Set whether more results available (false = end of list) */
28
+ setHasMore(hasMore: boolean): void;
29
+ /** Current loading state */
30
+ get isLoadingMore(): boolean;
31
+ /** Whether more results available */
32
+ get hasMoreResults(): boolean;
33
+ }
@@ -0,0 +1,95 @@
1
+ class InfiniteScrollController {
2
+ #host;
3
+ #config;
4
+ #container = null;
5
+ #scrollDebounceTimeout;
6
+ #isLoadingMore = false;
7
+ #hasMoreResults = true;
8
+ #lastLoadScrollTop = -1;
9
+ constructor(host, config) {
10
+ this.#host = host;
11
+ this.#config = {
12
+ onLoadMore: config.onLoadMore,
13
+ scrollThreshold: config.scrollThreshold ?? 100,
14
+ debounceMs: config.debounceMs ?? 150
15
+ };
16
+ host.addController(this);
17
+ }
18
+ hostConnected() {
19
+ }
20
+ hostDisconnected() {
21
+ this.detach();
22
+ clearTimeout(this.#scrollDebounceTimeout);
23
+ }
24
+ /** Attach scroll listener to container element */
25
+ attach(container) {
26
+ if (this.#container === container) {
27
+ return;
28
+ }
29
+ this.detach();
30
+ this.#container = container;
31
+ this.#container.addEventListener("scroll", this.#handleScroll);
32
+ }
33
+ /** Remove scroll listener from current container */
34
+ detach() {
35
+ if (this.#container) {
36
+ this.#container.removeEventListener("scroll", this.#handleScroll);
37
+ this.#container = null;
38
+ }
39
+ }
40
+ /** Reset pagination state */
41
+ reset() {
42
+ this.#isLoadingMore = false;
43
+ this.#hasMoreResults = true;
44
+ this.#lastLoadScrollTop = -1;
45
+ clearTimeout(this.#scrollDebounceTimeout);
46
+ this.#host.requestUpdate();
47
+ }
48
+ /** Set loading state (call when starting async load) */
49
+ setLoadingState(isLoading) {
50
+ this.#isLoadingMore = isLoading;
51
+ if (!isLoading) {
52
+ clearTimeout(this.#scrollDebounceTimeout);
53
+ }
54
+ this.#host.requestUpdate();
55
+ }
56
+ /** Set whether more results available (false = end of list) */
57
+ setHasMore(hasMore) {
58
+ this.#hasMoreResults = hasMore;
59
+ this.#host.requestUpdate();
60
+ }
61
+ /** Current loading state */
62
+ get isLoadingMore() {
63
+ return this.#isLoadingMore;
64
+ }
65
+ /** Whether more results available */
66
+ get hasMoreResults() {
67
+ return this.#hasMoreResults;
68
+ }
69
+ #handleScroll = () => {
70
+ clearTimeout(this.#scrollDebounceTimeout);
71
+ this.#scrollDebounceTimeout = window.setTimeout(() => {
72
+ this.#checkScrollPosition();
73
+ }, this.#config.debounceMs);
74
+ };
75
+ #checkScrollPosition() {
76
+ if (!this.#hasMoreResults || this.#isLoadingMore || !this.#container) {
77
+ return;
78
+ }
79
+ const { scrollTop, scrollHeight, clientHeight } = this.#container;
80
+ const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
81
+ if (distanceFromBottom < this.#config.scrollThreshold) {
82
+ if (this.#lastLoadScrollTop === scrollTop) {
83
+ return;
84
+ }
85
+ this.#lastLoadScrollTop = scrollTop;
86
+ this.#isLoadingMore = true;
87
+ clearTimeout(this.#scrollDebounceTimeout);
88
+ this.#host.requestUpdate();
89
+ this.#config.onLoadMore();
90
+ }
91
+ }
92
+ }
93
+ export {
94
+ InfiniteScrollController
95
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tylertech/forge-ai",
3
- "version": "0.13.0",
3
+ "version": "0.13.2",
4
4
  "description": "A library of Tyler Forge™ AI chat interface web components.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Tyler Technologies, Inc.",
@@ -39,13 +39,13 @@
39
39
  "url": "git+https://github.com/tyler-technologies-oss/forge-ai.git"
40
40
  },
41
41
  "dependencies": {
42
- "@ag-ui/client": "^0.0.53",
43
- "@ag-ui/core": "^0.0.53",
42
+ "@ag-ui/client": "^0.0.57",
43
+ "@ag-ui/core": "^0.0.57",
44
44
  "@floating-ui/dom": "^1.7.6",
45
45
  "@lit/context": "^1.1.6",
46
- "dompurify": "^3.4.2",
47
- "lit": "3.3.2",
48
- "marked": "^18.0.3",
46
+ "dompurify": "^3.4.11",
47
+ "lit": "3.3.3",
48
+ "marked": "^18.0.5",
49
49
  "remend": "^1.3.0",
50
50
  "uuid": "^14.0.0"
51
51
  },
@@ -58,19 +58,20 @@
58
58
  "@storybook/addon-links": "9.1.20",
59
59
  "@storybook/addon-themes": "9.1.20",
60
60
  "@storybook/web-components-vite": "9.1.20",
61
- "@tylertech/forge": "3.14.1",
61
+ "@tylertech-eslint/eslint-plugin": "3.0.0",
62
+ "@tylertech/forge": "3.14.4",
62
63
  "@tylertech/stylelint-rules": "5.1.2",
63
- "@tylertech/tyler-icons": "2.1.0",
64
+ "@tylertech/tyler-icons": "2.2.0",
64
65
  "@types/cssbeautify": "^0.3.5",
65
66
  "@types/dompurify": "^3.2.0",
66
67
  "@types/mocha": "10.0.10",
67
- "@types/node": "25.6.0",
68
- "@types/react": "^18.3.28",
69
- "@types/sinon": "21.0.0",
68
+ "@types/node": "25.6.2",
69
+ "@types/react": "^18.3.31",
70
+ "@types/sinon": "21.0.1",
70
71
  "@ungap/structured-clone": "^1.3.1",
71
72
  "@vueless/storybook-dark-mode": "9.0.10",
72
73
  "@wc-toolkit/jsdoc-tags": "^1.2.0",
73
- "@web/dev-server-esbuild": "1.0.4",
74
+ "@web/dev-server-esbuild": "1.0.5",
74
75
  "@web/dev-server-rollup": "0.6.4",
75
76
  "@web/test-runner": "0.20.2",
76
77
  "@web/test-runner-commands": "0.9.0",
@@ -90,12 +91,12 @@
90
91
  "remark-gfm": "4.0.1",
91
92
  "rimraf": "6.1.3",
92
93
  "sass": "1.93.3",
93
- "sinon": "21.0.3",
94
+ "sinon": "21.1.2",
94
95
  "storybook": "9.1.20",
95
96
  "stylelint": "16.26.1",
96
97
  "stylelint-config-standard": "39.0.1",
97
98
  "typescript": "~5.9.3",
98
- "vite": "7.3.2",
99
+ "vite": "7.3.5",
99
100
  "vite-plugin-ejs": "^1.7.0",
100
101
  "vite-plugin-full-reload": "^1.2.0",
101
102
  "vite-plugin-dts": "4.5.4",