@tylertech/forge-ai 0.13.1 → 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 +3409 -1497
  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 +6 -0
  7. package/dist/ai-chat-header/ai-chat-header.mjs +51 -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 +40 -1
  12. package/dist/ai-chatbot/ai-chatbot.mjs +242 -44
  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 +26 -0
  19. package/dist/ai-chatbot-launcher/ai-chatbot-launcher.mjs +155 -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 +14 -2
  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 +14 -14
@@ -0,0 +1,136 @@
1
+ import { unsafeCSS, LitElement, html } from "lit";
2
+ import { property, customElement } from "lit/decorators.js";
3
+ import { when } from "lit/directives/when.js";
4
+ import "../ai-dropdown-menu/ai-dropdown-menu.mjs";
5
+ import "../ai-dropdown-menu/ai-dropdown-menu-item.mjs";
6
+ import styles from "./ai-thread-actions-menu.scss.mjs";
7
+ var __defProp = Object.defineProperty;
8
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
9
+ var __typeError = (msg) => {
10
+ throw TypeError(msg);
11
+ };
12
+ var __decorateClass = (decorators, target, key, kind) => {
13
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
14
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
15
+ if (decorator = decorators[i])
16
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
17
+ if (kind && result) __defProp(target, key, result);
18
+ return result;
19
+ };
20
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
21
+ 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);
22
+ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
23
+ var _AiThreadActionsMenuComponent_instances, handleMenuOpen_fn, handleMenuClose_fn, handleMenuKeyDown_fn, handleMenuSelect_fn, handleRenameClick_fn, handleDeleteClick_fn;
24
+ const AiThreadActionsMenuComponentTagName = "forge-ai-thread-actions-menu";
25
+ let AiThreadActionsMenuComponent = class extends LitElement {
26
+ constructor() {
27
+ super(...arguments);
28
+ __privateAdd(this, _AiThreadActionsMenuComponent_instances);
29
+ this.showRename = false;
30
+ this.showDelete = false;
31
+ }
32
+ render() {
33
+ return html`
34
+ <forge-ai-dropdown-menu
35
+ variant="icon-button-squared"
36
+ density="small"
37
+ selection-mode="none"
38
+ popover-placement="bottom-start"
39
+ @keydown=${__privateMethod(this, _AiThreadActionsMenuComponent_instances, handleMenuKeyDown_fn)}
40
+ @forge-ai-dropdown-menu-open=${__privateMethod(this, _AiThreadActionsMenuComponent_instances, handleMenuOpen_fn)}
41
+ @forge-ai-dropdown-menu-close=${__privateMethod(this, _AiThreadActionsMenuComponent_instances, handleMenuClose_fn)}
42
+ @click=${__privateMethod(this, _AiThreadActionsMenuComponent_instances, handleMenuSelect_fn)}>
43
+ <slot name="trigger-icon" slot="trigger-content">
44
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="forge-icon">
45
+ <path
46
+ d="M12 16a2 2 0 0 1 2 2 2 2 0 0 1-2 2 2 2 0 0 1-2-2 2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2 2 2 0 0 1-2 2 2 2 0 0 1-2-2 2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2 2 2 0 0 1-2 2 2 2 0 0 1-2-2 2 2 0 0 1 2-2" />
47
+ </svg>
48
+ </slot>
49
+
50
+ ${when(
51
+ this.showRename,
52
+ () => html`
53
+ <forge-ai-dropdown-menu-item value="rename" @click=${__privateMethod(this, _AiThreadActionsMenuComponent_instances, handleRenameClick_fn)}>
54
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" slot="start" class="forge-icon">
55
+ <path
56
+ d="m15 16-4 4h10v-4zm-2.94-8.81L3 16.25V20h3.75l9.06-9.06zm6.65.85c.39-.39.39-1.04 0-1.41l-2.34-2.34a1 1 0 0 0-1.41 0l-1.83 1.83 3.75 3.75z" />
57
+ </svg>
58
+ <span>Rename</span>
59
+ </forge-ai-dropdown-menu-item>
60
+ `
61
+ )}
62
+ ${when(
63
+ this.showDelete,
64
+ () => html`
65
+ <forge-ai-dropdown-menu-item value="delete" @click=${__privateMethod(this, _AiThreadActionsMenuComponent_instances, handleDeleteClick_fn)}>
66
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" slot="start" class="forge-icon">
67
+ <path d="M19 4h-3.5l-1-1h-5l-1 1H5v2h14M6 19a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V7H6z" />
68
+ </svg>
69
+ <span>Delete</span>
70
+ </forge-ai-dropdown-menu-item>
71
+ `
72
+ )}
73
+ </forge-ai-dropdown-menu>
74
+ `;
75
+ }
76
+ };
77
+ _AiThreadActionsMenuComponent_instances = /* @__PURE__ */ new WeakSet();
78
+ handleMenuOpen_fn = function() {
79
+ const event = new CustomEvent("forge-ai-thread-actions-menu-open", {
80
+ detail: { id: this.thread.id },
81
+ bubbles: true,
82
+ composed: true
83
+ });
84
+ this.dispatchEvent(event);
85
+ };
86
+ handleMenuClose_fn = function() {
87
+ const event = new CustomEvent("forge-ai-thread-actions-menu-close", {
88
+ bubbles: true,
89
+ composed: true
90
+ });
91
+ this.dispatchEvent(event);
92
+ };
93
+ handleMenuKeyDown_fn = function(evt) {
94
+ if (evt.key === "Escape") {
95
+ evt.stopPropagation();
96
+ }
97
+ };
98
+ handleMenuSelect_fn = function(evt) {
99
+ evt.stopPropagation();
100
+ };
101
+ handleRenameClick_fn = function() {
102
+ const event = new CustomEvent("forge-ai-thread-actions-menu-rename", {
103
+ detail: { id: this.thread.id },
104
+ bubbles: true,
105
+ composed: true
106
+ });
107
+ this.dispatchEvent(event);
108
+ };
109
+ handleDeleteClick_fn = function() {
110
+ const event = new CustomEvent(
111
+ "forge-ai-thread-actions-menu-delete-click",
112
+ {
113
+ detail: { id: this.thread.id },
114
+ bubbles: true,
115
+ composed: true
116
+ }
117
+ );
118
+ this.dispatchEvent(event);
119
+ };
120
+ AiThreadActionsMenuComponent.styles = unsafeCSS(styles);
121
+ __decorateClass([
122
+ property({ type: Object })
123
+ ], AiThreadActionsMenuComponent.prototype, "thread", 2);
124
+ __decorateClass([
125
+ property({ type: Boolean, attribute: "show-rename" })
126
+ ], AiThreadActionsMenuComponent.prototype, "showRename", 2);
127
+ __decorateClass([
128
+ property({ type: Boolean, attribute: "show-delete" })
129
+ ], AiThreadActionsMenuComponent.prototype, "showDelete", 2);
130
+ AiThreadActionsMenuComponent = __decorateClass([
131
+ customElement(AiThreadActionsMenuComponentTagName)
132
+ ], AiThreadActionsMenuComponent);
133
+ export {
134
+ AiThreadActionsMenuComponent,
135
+ AiThreadActionsMenuComponentTagName
136
+ };
@@ -0,0 +1,4 @@
1
+ const styles = "/**\n * @license\n * Copyright Tyler Technologies, Inc.\n * License: Apache-2.0\n */\n.forge-icon {\n --_icon-color:var(--forge-icon-color, currentColor);\n --_icon-size:var(--forge-icon-size, 1em);\n --_icon-width:var(--forge-icon-width, var(--_icon-size));\n --_icon-height:var(--forge-icon-height, var(--_icon-size));\n --_icon-font-size:var(--forge-icon-font-size, calc(var(--forge-typography-font-size, 1rem) * 1.5));\n}\n\nimg.forge-icon, svg.forge-icon {\n display: inline-block;\n box-sizing: content-box;\n contain: content;\n font-size: var(--_icon-font-size);\n display: inline-block;\n}\n\nimg.forge-icon {\n width: var(--_icon-width);\n height: var(--_icon-height);\n}\n\nsvg {\n width: var(--_icon-width);\n height: var(--_icon-height);\n display: block;\n fill: var(--_icon-color);\n stroke: var(--_icon-color);\n stroke-width: 0;\n}\n\n:host {\n display: flex;\n}\n\nforge-ai-dropdown-menu {\n display: flex;\n}\n\n.forge-icon {\n color: var(--forge-theme-text-medium, rgba(0, 0, 0, 0.6));\n}";
2
+ export {
3
+ styles as default
4
+ };
@@ -0,0 +1 @@
1
+ export * from './ai-thread-actions-menu';
@@ -0,0 +1,5 @@
1
+ import { AiThreadActionsMenuComponent, AiThreadActionsMenuComponentTagName } from "./ai-thread-actions-menu.mjs";
2
+ export {
3
+ AiThreadActionsMenuComponent,
4
+ AiThreadActionsMenuComponentTagName
5
+ };
@@ -6,48 +6,77 @@ declare global {
6
6
  interface HTMLElementEventMap {
7
7
  'forge-ai-threads-select': CustomEvent<ForgeAiThreadsSelectEventData>;
8
8
  'forge-ai-threads-new-chat': CustomEvent;
9
- 'forge-ai-threads-clear-history': CustomEvent;
9
+ 'forge-ai-threads-search-chats': CustomEvent;
10
+ 'forge-ai-threads-load-more': CustomEvent<ForgeAiThreadsLoadMoreEventData>;
11
+ 'forge-ai-threads-rename': CustomEvent<ForgeAiThreadsRenameEventData>;
12
+ 'forge-ai-threads-delete': CustomEvent<ForgeAiThreadsDeleteEventData>;
10
13
  }
11
14
  }
12
15
  export interface Thread {
13
16
  id: string;
14
17
  title: string;
15
- time: string;
16
- date: Date;
18
+ createdAt: string;
19
+ updatedAt?: string;
20
+ messageCount?: number;
17
21
  }
18
22
  export interface ForgeAiThreadsSelectEventData {
19
23
  id: string;
20
24
  title: string;
21
25
  }
26
+ export interface ForgeAiThreadsLoadMoreEventData {
27
+ appendResults: (results: Thread[]) => void;
28
+ }
29
+ export interface ForgeAiThreadsRenameEventData {
30
+ id: string;
31
+ oldTitle: string;
32
+ newTitle: string;
33
+ onSuccess: () => void;
34
+ onError: (error?: string) => void;
35
+ }
36
+ export interface ForgeAiThreadsDeleteEventData {
37
+ id: string;
38
+ thread: Thread;
39
+ onSuccess: () => void;
40
+ onError: (error?: string) => void;
41
+ }
22
42
  export declare const AiThreadsComponentTagName: keyof HTMLElementTagNameMap;
23
43
  /**
24
44
  * @tag forge-ai-threads
25
45
  *
26
- * @slot - Default slot for chatbot component
46
+ * @slot - Default slot for chatbot launcher component
27
47
  *
28
48
  * @event {CustomEvent<ForgeAiThreadsSelectEventData>} forge-ai-threads-select - Fired when a thread is selected.
29
49
  * @event {CustomEvent} forge-ai-threads-new-chat - Fired when the new chat button is clicked.
30
- * @event {CustomEvent} forge-ai-threads-clear-history - Fired when the clear history button is clicked.
50
+ * @event {CustomEvent} forge-ai-threads-search-chats - Fired when the search chats button is clicked.
51
+ * @event {CustomEvent<ForgeAiThreadsLoadMoreEventData>} forge-ai-threads-load-more - Fired when scrolling near bottom. Call appendResults([]) to signal end.
52
+ * @event {CustomEvent<ForgeAiThreadsRenameEventData>} forge-ai-threads-rename - Fired when thread renamed. Cancelable - if prevented, call onSuccess() to commit or onError() to revert.
53
+ * @event {CustomEvent<ForgeAiThreadsDeleteEventData>} forge-ai-threads-delete - Fired when thread delete confirmed. Cancelable - if prevented, call onSuccess() to commit deletion or onError() to revert.
31
54
  *
32
- * @description A form factor component that positions a slotted chatbot alongside a thread navigation drawer.
33
- * Manages thread list and selection while delegating chat functionality to the slotted chatbot.
34
- * All chatbot events bubble through unchanged.
55
+ * @description A form factor component that positions a slotted chatbot launcher alongside a thread navigation drawer.
56
+ * Manages thread list and selection while delegating chat functionality to the slotted launcher.
57
+ * All launcher events bubble through unchanged.
35
58
  */
36
59
  export declare class AiThreadsComponent extends LitElement {
37
60
  #private;
38
61
  static styles: CSSResult;
39
62
  /** Array of threads to display in the navigation list */
40
63
  threads: Thread[];
41
- /** Current filter value for searching threads */
42
- private _filterValue;
64
+ /**
65
+ * Total number of threads available. When set to a positive number and fewer threads
66
+ * are loaded than the total, infinite scroll is enabled. Leave at 0 (default) to disable
67
+ * infinite scroll entirely. Useful when all data is loaded upfront.
68
+ */
69
+ totalChats: number;
70
+ showThreadRename: boolean;
71
+ showThreadDelete: boolean;
72
+ private _threadListContainer;
43
73
  /** Currently selected thread ID */
44
74
  private _selectedThreadId;
45
- private _slottedChatbots;
46
- private _handleFilterInput;
75
+ private _editingThreadId;
76
+ private _hiddenThreadIds;
77
+ private _openMenuThreadId;
78
+ firstUpdated(): void;
79
+ updated(changedProperties: Map<string, unknown>): void;
47
80
  private _handleThreadSelect;
48
- private _handleNewChatClick;
49
- private _handleChatbotExpand;
50
- private _handleChatbotMinimize;
51
- private _handleClearHistoryClick;
52
81
  render(): TemplateResult;
53
82
  }
@@ -1,5 +1,13 @@
1
- import { unsafeCSS, LitElement, html } from "lit";
2
- import { property, state, queryAssignedElements, customElement } from "lit/decorators.js";
1
+ import { unsafeCSS, LitElement, html, nothing } from "lit";
2
+ import { property, query, state, customElement } from "lit/decorators.js";
3
+ import { when } from "lit/directives/when.js";
4
+ import { DeleteThreadController } from "../utils/delete-thread-controller.mjs";
5
+ import { InfiniteScrollController } from "../utils/infinite-scroll-controller.mjs";
6
+ import "../ai-icon/ai-icon.mjs";
7
+ import "../ai-spinner/ai-spinner.mjs";
8
+ import "../ai-thread-actions-menu/ai-thread-actions-menu.mjs";
9
+ import "../ai-edit-thread/ai-edit-thread.mjs";
10
+ import "../ai-modal/ai-modal.mjs";
3
11
  import styles from "./ai-threads.scss.mjs";
4
12
  var __defProp = Object.defineProperty;
5
13
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -17,39 +25,91 @@ var __decorateClass = (decorators, target, key, kind) => {
17
25
  var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
18
26
  var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
19
27
  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);
20
- var _AiThreadsComponent_instances, chatbot_get, filteredThreads_get, _searchField, threadList_get, threadActions_get, threadListContainer_get, newChatButton_get, drawer_get, _slotContent;
28
+ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
29
+ var _deleteThreadController, _infiniteScrollController, _AiThreadsComponent_instances, shouldEnablePagination_get, handleNewChatClick_fn, handleSearchChatsClick_fn, handleLoadMore_fn, handleMenuRename_fn, handleMenuOpen_fn, handleMenuClose_fn, handleMenuDeleteClick_fn, handleEditSave_fn, handleEditCancel_fn, threadList_get, _chatActionsList, _chatsLabel, loadingMoreIndicator_get, threadListContainer_get, drawer_get, _slotContent;
21
30
  const AiThreadsComponentTagName = "forge-ai-threads";
22
31
  let AiThreadsComponent = class extends LitElement {
23
32
  constructor() {
24
33
  super(...arguments);
25
34
  __privateAdd(this, _AiThreadsComponent_instances);
26
35
  this.threads = [];
27
- this._filterValue = "";
36
+ this.totalChats = 0;
37
+ this.showThreadRename = false;
38
+ this.showThreadDelete = false;
28
39
  this._selectedThreadId = null;
29
- __privateAdd(this, _searchField, html`
30
- <div class="forge-field">
31
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="forge-icon">
32
- <path fill="none" d="M0 0h24v24H0z" />
33
- <path
34
- 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" />
35
- </svg>
36
- <input
37
- type="text"
38
- id="search-threads"
39
- placeholder="Search threads..."
40
- .value=${this._filterValue}
41
- @input=${this._handleFilterInput} />
42
- </div>
43
- `);
44
- __privateAdd(this, _slotContent, html`
45
- <slot
46
- @forge-ai-chatbot-expand=${this._handleChatbotExpand}
47
- @forge-ai-chatbot-minimize=${this._handleChatbotMinimize}>
48
- </slot>
40
+ this._editingThreadId = null;
41
+ this._hiddenThreadIds = /* @__PURE__ */ new Set();
42
+ this._openMenuThreadId = null;
43
+ __privateAdd(this, _deleteThreadController, new DeleteThreadController(this, {
44
+ onConfirm: (thread) => {
45
+ const onSuccess = () => {
46
+ this._hiddenThreadIds.add(thread.id);
47
+ this.requestUpdate();
48
+ };
49
+ const onError = (error) => {
50
+ this._hiddenThreadIds.delete(thread.id);
51
+ this.requestUpdate();
52
+ if (error) {
53
+ console.error("Delete failed:", error);
54
+ }
55
+ };
56
+ const event = new CustomEvent("forge-ai-threads-delete", {
57
+ detail: { id: thread.id, thread, onSuccess, onError },
58
+ bubbles: true,
59
+ composed: true,
60
+ cancelable: true
61
+ });
62
+ const dispatched = this.dispatchEvent(event);
63
+ if (dispatched) {
64
+ this._hiddenThreadIds.add(thread.id);
65
+ this.requestUpdate();
66
+ }
67
+ }
68
+ }));
69
+ __privateAdd(this, _infiniteScrollController, new InfiniteScrollController(this, {
70
+ onLoadMore: () => __privateMethod(this, _AiThreadsComponent_instances, handleLoadMore_fn).call(this)
71
+ }));
72
+ __privateAdd(this, _chatActionsList, html`
73
+ <ul class="forge-list forge-list--dense forge-list--navlist chat-actions-list" role="list">
74
+ <li class="forge-list-item" role="listitem">
75
+ <button @click=${__privateMethod(this, _AiThreadsComponent_instances, handleNewChatClick_fn)}>
76
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="forge-icon">
77
+ <path
78
+ d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2m0 14H5.2L4 17.2V4h16zm-4-7v2h-3v3h-2v-3H8V9h3V6h2v3z" />
79
+ </svg>
80
+ <span>New chat</span>
81
+ </button>
82
+ </li>
83
+ <li class="forge-list-item" role="listitem">
84
+ <button @click=${__privateMethod(this, _AiThreadsComponent_instances, handleSearchChatsClick_fn)}>
85
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="forge-icon">
86
+ <path
87
+ d="M9 6v2H2V6zm0 5v2H2v-2zm9 5v2H2v-2zm1.31-4.5c.44-.68.69-1.5.69-2.39 0-2.5-2-4.5-4.5-4.5s-4.5 2-4.5 4.5 2 4.5 4.5 4.5c.87 0 1.69-.25 2.38-.68L21 16l1.39-1.39zm-3.81.11c-1.38 0-2.5-1.11-2.5-2.5s1.12-2.5 2.5-2.5a2.5 2.5 0 0 1 0 5" />
88
+ </svg>
89
+ <span>All chats</span>
90
+ </button>
91
+ </li>
92
+ </ul>
49
93
  `);
94
+ __privateAdd(this, _chatsLabel, html`<div class="chats-label">Recent chats</div>`);
95
+ __privateAdd(this, _slotContent, html`<slot></slot>`);
96
+ }
97
+ firstUpdated() {
98
+ if (__privateGet(this, _AiThreadsComponent_instances, shouldEnablePagination_get) && this._threadListContainer) {
99
+ __privateGet(this, _infiniteScrollController).attach(this._threadListContainer);
100
+ }
50
101
  }
51
- _handleFilterInput(event) {
52
- this._filterValue = event.target.value;
102
+ updated(changedProperties) {
103
+ if (changedProperties.has("threads") || changedProperties.has("totalChats")) {
104
+ __privateGet(this, _infiniteScrollController).reset();
105
+ if (__privateGet(this, _AiThreadsComponent_instances, shouldEnablePagination_get)) {
106
+ if (this._threadListContainer) {
107
+ __privateGet(this, _infiniteScrollController).attach(this._threadListContainer);
108
+ }
109
+ } else {
110
+ __privateGet(this, _infiniteScrollController).detach();
111
+ }
112
+ }
53
113
  }
54
114
  _handleThreadSelect(thread) {
55
115
  this._selectedThreadId = thread.id;
@@ -61,102 +121,185 @@ let AiThreadsComponent = class extends LitElement {
61
121
  });
62
122
  this.dispatchEvent(event);
63
123
  }
64
- _handleNewChatClick() {
65
- const event = new CustomEvent("forge-ai-threads-new-chat", {
66
- bubbles: true,
67
- composed: true,
68
- cancelable: true
69
- });
70
- this.dispatchEvent(event);
71
- }
72
- _handleChatbotExpand() {
73
- if (__privateGet(this, _AiThreadsComponent_instances, chatbot_get)) {
74
- __privateGet(this, _AiThreadsComponent_instances, chatbot_get).expanded = !__privateGet(this, _AiThreadsComponent_instances, chatbot_get).expanded;
75
- }
76
- }
77
- _handleChatbotMinimize() {
78
- if (__privateGet(this, _AiThreadsComponent_instances, chatbot_get)) {
79
- __privateGet(this, _AiThreadsComponent_instances, chatbot_get).expanded = false;
80
- }
81
- }
82
- _handleClearHistoryClick() {
83
- const event = new CustomEvent("forge-ai-threads-clear-history", {
84
- bubbles: true,
85
- composed: true,
86
- cancelable: true
87
- });
88
- this.dispatchEvent(event);
89
- }
90
124
  render() {
91
125
  return html`
92
126
  <div class="threads-container">
93
- <div class="left">${__privateGet(this, _AiThreadsComponent_instances, drawer_get)}</div>
94
- <div class="right">${__privateGet(this, _slotContent)}</div>
127
+ <div class="drawer">${__privateGet(this, _AiThreadsComponent_instances, drawer_get)}</div>
128
+ <div class="body">${__privateGet(this, _slotContent)}</div>
95
129
  </div>
130
+ ${__privateGet(this, _deleteThreadController).template}
96
131
  `;
97
132
  }
98
133
  };
134
+ _deleteThreadController = /* @__PURE__ */ new WeakMap();
135
+ _infiniteScrollController = /* @__PURE__ */ new WeakMap();
99
136
  _AiThreadsComponent_instances = /* @__PURE__ */ new WeakSet();
100
- chatbot_get = function() {
101
- return this._slottedChatbots[0];
137
+ shouldEnablePagination_get = function() {
138
+ if (this.totalChats <= 0) {
139
+ return false;
140
+ }
141
+ const displayedCount = this.threads.filter((t) => !this._hiddenThreadIds.has(t.id)).length;
142
+ return displayedCount < this.totalChats;
102
143
  };
103
- filteredThreads_get = function() {
104
- if (!this._filterValue.trim()) {
105
- return this.threads;
144
+ handleNewChatClick_fn = function() {
145
+ const event = new CustomEvent("forge-ai-threads-new-chat", {
146
+ bubbles: true,
147
+ composed: true,
148
+ cancelable: true
149
+ });
150
+ this.dispatchEvent(event);
151
+ };
152
+ handleSearchChatsClick_fn = function() {
153
+ const event = new CustomEvent("forge-ai-threads-search-chats", {
154
+ bubbles: true,
155
+ composed: true,
156
+ cancelable: true
157
+ });
158
+ this.dispatchEvent(event);
159
+ };
160
+ handleLoadMore_fn = function() {
161
+ const appendResults = (results) => {
162
+ if (results.length === 0) {
163
+ __privateGet(this, _infiniteScrollController).setHasMore(false);
164
+ } else {
165
+ this.threads = [...this.threads, ...results];
166
+ }
167
+ __privateGet(this, _infiniteScrollController).setLoadingState(false);
168
+ };
169
+ const event = new CustomEvent("forge-ai-threads-load-more", {
170
+ detail: { appendResults },
171
+ bubbles: true,
172
+ composed: true
173
+ });
174
+ this.dispatchEvent(event);
175
+ };
176
+ handleMenuRename_fn = function(e) {
177
+ this._editingThreadId = e.detail.id;
178
+ this._openMenuThreadId = null;
179
+ };
180
+ handleMenuOpen_fn = function(e) {
181
+ this._openMenuThreadId = e.detail.id;
182
+ };
183
+ handleMenuClose_fn = function() {
184
+ this._openMenuThreadId = null;
185
+ };
186
+ handleMenuDeleteClick_fn = function(e) {
187
+ const { id } = e.detail;
188
+ const displayedThreads = this.threads.filter((t) => !this._hiddenThreadIds.has(t.id));
189
+ const thread = displayedThreads.find((t) => t.id === id);
190
+ if (!thread) {
191
+ return;
192
+ }
193
+ __privateGet(this, _deleteThreadController).show(thread);
194
+ };
195
+ handleEditSave_fn = function(e) {
196
+ const { id, oldTitle, newTitle } = e.detail;
197
+ const displayedThreads = this.threads.filter((t) => !this._hiddenThreadIds.has(t.id));
198
+ const targetThread = displayedThreads.find((t) => t.id === id);
199
+ if (!targetThread) {
200
+ return;
106
201
  }
107
- return this.threads.filter((thread) => thread.title.toLowerCase().includes(this._filterValue.toLowerCase()));
202
+ const onSuccess = () => {
203
+ targetThread.title = newTitle;
204
+ this.requestUpdate();
205
+ };
206
+ const onError = (error) => {
207
+ targetThread.title = oldTitle;
208
+ this.requestUpdate();
209
+ if (error) {
210
+ console.error("Rename failed:", error);
211
+ }
212
+ };
213
+ const event = new CustomEvent("forge-ai-threads-rename", {
214
+ detail: { id, oldTitle, newTitle, onSuccess, onError },
215
+ bubbles: true,
216
+ composed: true,
217
+ cancelable: true
218
+ });
219
+ const dispatched = this.dispatchEvent(event);
220
+ if (dispatched) {
221
+ targetThread.title = newTitle;
222
+ this.requestUpdate();
223
+ }
224
+ this._editingThreadId = null;
225
+ };
226
+ handleEditCancel_fn = function() {
227
+ this._editingThreadId = null;
108
228
  };
109
- _searchField = /* @__PURE__ */ new WeakMap();
110
229
  threadList_get = function() {
230
+ const displayedThreads = this.threads.filter((t) => !this._hiddenThreadIds.has(t.id));
111
231
  return html`
112
- <ul class="forge-list forge-list--navlist">
113
- ${__privateGet(this, _AiThreadsComponent_instances, filteredThreads_get).map((thread) => {
232
+ <ul class="forge-list forge-list--dense forge-list--navlist" role="list">
233
+ ${displayedThreads.map((thread) => {
114
234
  const isSelected = this._selectedThreadId === thread.id;
235
+ const isEditing = this._editingThreadId === thread.id;
236
+ const hasOpenMenu = this._openMenuThreadId === thread.id;
115
237
  return html`
116
- <li class="forge-list-item ${isSelected ? "forge-list-item--selected" : ""}">
117
- <button @click=${() => this._handleThreadSelect(thread)}>
118
- <span>${thread.title}</span>
119
- <div class="list-item-second-line">
120
- <span>${thread.date.toLocaleDateString()}</span>
121
- <span>${thread.time}</span>
122
- </div>
123
- </button>
238
+ <li
239
+ class="forge-list-item ${isSelected ? "forge-list-item--selected" : ""} ${hasOpenMenu ? "forge-list-item--menu-open" : ""}"
240
+ role="listitem">
241
+ ${when(
242
+ isEditing,
243
+ () => html`
244
+ <forge-ai-edit-thread
245
+ .thread=${thread}
246
+ .value=${thread.title}
247
+ @forge-ai-edit-thread-save=${__privateMethod(this, _AiThreadsComponent_instances, handleEditSave_fn)}
248
+ @forge-ai-edit-thread-cancel=${__privateMethod(this, _AiThreadsComponent_instances, handleEditCancel_fn)}>
249
+ </forge-ai-edit-thread>
250
+ `,
251
+ () => html`
252
+ <button @click=${() => this._handleThreadSelect(thread)} aria-selected=${isSelected}>
253
+ <span>${thread.title}</span>
254
+ </button>
255
+ ${when(
256
+ this.showThreadRename || this.showThreadDelete,
257
+ () => html`
258
+ <div class="thread-item-actions">
259
+ <forge-ai-thread-actions-menu
260
+ .thread=${thread}
261
+ ?show-rename=${this.showThreadRename}
262
+ ?show-delete=${this.showThreadDelete}
263
+ @forge-ai-thread-actions-menu-rename=${__privateMethod(this, _AiThreadsComponent_instances, handleMenuRename_fn)}
264
+ @forge-ai-thread-actions-menu-delete-click=${__privateMethod(this, _AiThreadsComponent_instances, handleMenuDeleteClick_fn)}
265
+ @forge-ai-thread-actions-menu-open=${__privateMethod(this, _AiThreadsComponent_instances, handleMenuOpen_fn)}
266
+ @forge-ai-thread-actions-menu-close=${__privateMethod(this, _AiThreadsComponent_instances, handleMenuClose_fn)}>
267
+ </forge-ai-thread-actions-menu>
268
+ </div>
269
+ `
270
+ )}
271
+ `
272
+ )}
124
273
  </li>
125
274
  `;
126
275
  })}
127
276
  </ul>
128
277
  `;
129
278
  };
130
- threadActions_get = function() {
279
+ _chatActionsList = /* @__PURE__ */ new WeakMap();
280
+ _chatsLabel = /* @__PURE__ */ new WeakMap();
281
+ loadingMoreIndicator_get = function() {
282
+ if (!__privateGet(this, _infiniteScrollController).isLoadingMore) {
283
+ return nothing;
284
+ }
131
285
  return html`
132
- <button class="forge-button" @click=${this._handleClearHistoryClick}>
133
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="forge-icon">
134
- <path d="M19 4h-3.5l-1-1h-5l-1 1H5v2h14M6 19a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V7H6z" />
135
- </svg>
136
- Clear history
137
- </button>
286
+ <div class="loading-more-indicator">
287
+ <forge-ai-spinner size="small"></forge-ai-spinner>
288
+ </div>
138
289
  `;
139
290
  };
140
291
  threadListContainer_get = function() {
141
- return html` <div class="thread-list-container">${__privateGet(this, _AiThreadsComponent_instances, threadList_get)} ${__privateGet(this, _AiThreadsComponent_instances, threadActions_get)}</div> `;
142
- };
143
- newChatButton_get = function() {
144
- return html`
145
- <button class="forge-button forge-button--tonal" @click=${this._handleNewChatClick}>
146
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="forge-icon">
147
- <path fill="none" d="M0 0h24v24H0z" />
148
- <path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6z" />
149
- </svg>
150
- New chat
151
- </button>
152
- `;
292
+ return html` <div class="thread-list-container">${__privateGet(this, _AiThreadsComponent_instances, threadList_get)} ${__privateGet(this, _AiThreadsComponent_instances, loadingMoreIndicator_get)}</div> `;
153
293
  };
154
294
  drawer_get = function() {
295
+ const displayedThreads = this.threads.filter((t) => !this._hiddenThreadIds.has(t.id));
296
+ const hasThreads = displayedThreads.length > 0;
155
297
  return html`
156
- <aside class="forge-drawer">
157
- ${__privateGet(this, _searchField)} ${__privateGet(this, _AiThreadsComponent_instances, threadListContainer_get)}
158
- <hr class="forge-divider" />
159
- ${__privateGet(this, _AiThreadsComponent_instances, newChatButton_get)}
298
+ <aside class="forge-drawer" role="complementary" aria-label="Thread navigation">
299
+ <slot name="header"></slot>
300
+ ${__privateGet(this, _chatActionsList)}
301
+ <slot name="actions"></slot>
302
+ ${when(hasThreads, () => html`${__privateGet(this, _chatsLabel)} ${__privateGet(this, _AiThreadsComponent_instances, threadListContainer_get)}`)}
160
303
  </aside>
161
304
  `;
162
305
  };
@@ -166,14 +309,29 @@ __decorateClass([
166
309
  property({ type: Array })
167
310
  ], AiThreadsComponent.prototype, "threads", 2);
168
311
  __decorateClass([
169
- state()
170
- ], AiThreadsComponent.prototype, "_filterValue", 2);
312
+ property({ type: Number, attribute: "total-chats" })
313
+ ], AiThreadsComponent.prototype, "totalChats", 2);
314
+ __decorateClass([
315
+ property({ type: Boolean, attribute: "show-thread-rename" })
316
+ ], AiThreadsComponent.prototype, "showThreadRename", 2);
317
+ __decorateClass([
318
+ property({ type: Boolean, attribute: "show-thread-delete" })
319
+ ], AiThreadsComponent.prototype, "showThreadDelete", 2);
320
+ __decorateClass([
321
+ query(".thread-list-container")
322
+ ], AiThreadsComponent.prototype, "_threadListContainer", 2);
171
323
  __decorateClass([
172
324
  state()
173
325
  ], AiThreadsComponent.prototype, "_selectedThreadId", 2);
174
326
  __decorateClass([
175
- queryAssignedElements({ selector: "forge-ai-chatbot" })
176
- ], AiThreadsComponent.prototype, "_slottedChatbots", 2);
327
+ state()
328
+ ], AiThreadsComponent.prototype, "_editingThreadId", 2);
329
+ __decorateClass([
330
+ state()
331
+ ], AiThreadsComponent.prototype, "_hiddenThreadIds", 2);
332
+ __decorateClass([
333
+ state()
334
+ ], AiThreadsComponent.prototype, "_openMenuThreadId", 2);
177
335
  AiThreadsComponent = __decorateClass([
178
336
  customElement(AiThreadsComponentTagName)
179
337
  ], AiThreadsComponent);