@kodaris/krubble-app-components 1.0.126 → 1.0.127

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.
@@ -3470,10 +3470,9 @@ class KRChatbot extends i$3 {
3470
3470
  }
3471
3471
  }
3472
3472
  // `this.load` is always set by here (defaulted above when the consumer didn't
3473
- // supply one), so capture it for the async chain — TS can't narrow the nullable
3474
- // property across the promise boundary.
3475
- const load = this.load;
3476
- resolveConversationId.then(() => load()).then((messages) => {
3473
+ // supply one); the `!` is because TS can't narrow the nullable property across
3474
+ // the promise boundary.
3475
+ resolveConversationId.then(() => this.load()).then((messages) => {
3477
3476
  if (messages && messages.length) {
3478
3477
  this.messages = messages;
3479
3478
  }
@@ -3742,11 +3741,10 @@ class KRChatbot extends i$3 {
3742
3741
  }
3743
3742
  _saveState() {
3744
3743
  try {
3745
- // `toolDetail` and `history` are transient views, not saved ones — persist
3746
- // only the real preference views so a reload never reopens a stale tool or a
3747
- // stale conversation list.
3744
+ // `toolDetail` is a transient peek, not a saved view — persist only the two
3745
+ // real preference views so a reload never reopens a stale tool.
3748
3746
  let persistedDrawerView = this._drawerView;
3749
- if (persistedDrawerView === 'toolDetail' || persistedDrawerView === 'history') {
3747
+ if (persistedDrawerView === 'toolDetail') {
3750
3748
  persistedDrawerView = 'settings';
3751
3749
  }
3752
3750
  const state = {
@@ -3805,8 +3803,14 @@ class KRChatbot extends i$3 {
3805
3803
  this._showToolInputs = state.showToolInputs === 'true';
3806
3804
  this._showConfirmationInput = state.showConfirmationInput === 'true';
3807
3805
  this._drawerOpen = state.drawerOpen === 'true';
3808
- if (state.drawerView === 'settings' || state.drawerView === 'debug') {
3806
+ if (state.drawerView === 'settings' || state.drawerView === 'debug' || state.drawerView === 'history') {
3809
3807
  this._drawerView = state.drawerView;
3808
+ // History's list isn't persisted (it can go stale); refetch it so a reload
3809
+ // that was on the conversations panel comes back to a populated list rather
3810
+ // than an empty one.
3811
+ if (state.drawerView === 'history') {
3812
+ this._loadConversations();
3813
+ }
3810
3814
  }
3811
3815
  const w = Number(state.drawerWidth);
3812
3816
  if (w >= 300) {
@@ -4950,41 +4954,47 @@ class KRChatbot extends i$3 {
4950
4954
  else {
4951
4955
  this._drawerOpen = true;
4952
4956
  this._drawerView = view;
4953
- // Opening the history view refetches the user's previous conversations
4954
- // (most-recently modified first) so a just-created or renamed conversation
4955
- // shows without a reload. Ownership is enforced server-side (the endpoint
4956
- // scopes to the current administrator), so we only filter to active ones.
4957
+ // Opening the history view refetches the list so a just-created or renamed
4958
+ // conversation shows without a reload.
4957
4959
  if (view === 'history') {
4958
- this._conversationsLoading = true;
4959
- KRHttp.fetch({
4960
- url: '/api/system/conversation/list',
4961
- method: 'POST',
4962
- body: {
4963
- page: 0,
4964
- size: 50,
4965
- sortBy: 'lastModified',
4966
- sortDirection: 'desc',
4967
- filterFields: [{
4968
- name: 'active',
4969
- operation: 'IS',
4970
- value: 'true',
4971
- }],
4972
- },
4973
- })
4974
- .then((r) => r.json())
4975
- .then((res) => {
4976
- this._conversations = res?.data?.content || [];
4977
- })
4978
- .catch(() => {
4979
- this._conversations = [];
4980
- })
4981
- .then(() => {
4982
- this._conversationsLoading = false;
4983
- });
4960
+ this._loadConversations();
4984
4961
  }
4985
4962
  }
4986
4963
  this._saveState();
4987
4964
  }
4965
+ /**
4966
+ * Fetch the user's previous conversations for the history list, most-recently
4967
+ * modified first. Ownership is enforced server-side (the endpoint scopes to the
4968
+ * current administrator), so we only filter to active ones.
4969
+ */
4970
+ _loadConversations() {
4971
+ this._conversationsLoading = true;
4972
+ KRHttp.fetch({
4973
+ url: '/api/system/conversation/list',
4974
+ method: 'POST',
4975
+ body: {
4976
+ page: 0,
4977
+ size: 50,
4978
+ sortBy: 'lastModified',
4979
+ sortDirection: 'desc',
4980
+ filterFields: [{
4981
+ name: 'active',
4982
+ operation: 'IS',
4983
+ value: 'true',
4984
+ }],
4985
+ },
4986
+ })
4987
+ .then((r) => r.json())
4988
+ .then((res) => {
4989
+ this._conversations = res?.data?.content || [];
4990
+ })
4991
+ .catch(() => {
4992
+ this._conversations = [];
4993
+ })
4994
+ .then(() => {
4995
+ this._conversationsLoading = false;
4996
+ });
4997
+ }
4988
4998
  /**
4989
4999
  * Switch the floating widget to a previously-created conversation (clicked in the
4990
5000
  * history list): stop any live turn, reset the transient turn/context state, pin
@@ -5003,7 +5013,22 @@ class KRChatbot extends i$3 {
5003
5013
  // one, so the header falls back to the default label).
5004
5014
  this._conversationTitle = this._conversations.find((c) => c.conversationID === conversationID)?.title || '';
5005
5015
  this._persistConversationId();
5006
- this._resetConversationState();
5016
+ // Clear the transient, per-conversation state so it doesn't bleed into the one
5017
+ // we're switching to — pending confirmations, context banners, errors, the
5018
+ // reconnect counter, and staged composer files (the transcript is loaded below).
5019
+ this._pendingActions = [];
5020
+ this._inlineConfirm = true;
5021
+ this._contextWarning = '';
5022
+ this._contextLimitReached = false;
5023
+ this._contextLimitMessage = '';
5024
+ this._error = '';
5025
+ this._reconnectAttempts = 0;
5026
+ this._pendingConversationFiles.forEach((p) => {
5027
+ if (p.previewUrl) {
5028
+ URL.revokeObjectURL(p.previewUrl);
5029
+ }
5030
+ });
5031
+ this._pendingConversationFiles = [];
5007
5032
  this._drawerOpen = false;
5008
5033
  this._saveState();
5009
5034
  if (this.load) {
@@ -5030,17 +5055,9 @@ class KRChatbot extends i$3 {
5030
5055
  this._conversationTitle = '';
5031
5056
  this._persistConversationId();
5032
5057
  this.messages = [];
5033
- this._resetConversationState();
5034
- this._drawerOpen = false;
5035
- this._saveState();
5036
- }
5037
- /**
5038
- * Clear the transient, per-conversation state that must not bleed across a
5039
- * conversation switch/new — pending confirmations, context banners, errors, the
5040
- * reconnect counter, and staged composer files. The transcript itself is set by
5041
- * the caller (loaded for a switch, emptied for new).
5042
- */
5043
- _resetConversationState() {
5058
+ // Clear the transient, per-conversation state so it doesn't bleed into the new
5059
+ // conversation — pending confirmations, context banners, errors, the reconnect
5060
+ // counter, and staged composer files.
5044
5061
  this._pendingActions = [];
5045
5062
  this._inlineConfirm = true;
5046
5063
  this._contextWarning = '';
@@ -5054,6 +5071,8 @@ class KRChatbot extends i$3 {
5054
5071
  }
5055
5072
  });
5056
5073
  this._pendingConversationFiles = [];
5074
+ this._drawerOpen = false;
5075
+ this._saveState();
5057
5076
  }
5058
5077
  /** Close the drawer. `_drawerView` is left intact so its content stays visible
5059
5078
  * while the drawer slides shut. */
@@ -5936,55 +5955,58 @@ class KRChatbot extends i$3 {
5936
5955
  <div class="history__empty">No previous conversations</div>
5937
5956
  ` : b `
5938
5957
  <div class="history__list">
5939
- ${this._conversations.map((conversation) => b `
5940
- <button
5941
- type="button"
5942
- class=${e({
5943
- 'history__item': true,
5944
- 'history__item--active': conversation.conversationID === this.conversationId,
5958
+ ${this._conversations.map((conversation) => {
5959
+ // Format lastModified for the row: today shows the time, this year
5960
+ // shows month/day, older shows month/day/year; the raw value if it
5961
+ // isn't a parseable date. Empty when the conversation has no date.
5962
+ let dateLabel = '';
5963
+ if (conversation.lastModified) {
5964
+ const date = new Date(conversation.lastModified);
5965
+ const now = new Date();
5966
+ if (isNaN(date.getTime())) {
5967
+ dateLabel = conversation.lastModified;
5968
+ }
5969
+ else if (date.toDateString() === now.toDateString()) {
5970
+ dateLabel = date.toLocaleTimeString('en-US', {
5971
+ hour: 'numeric',
5972
+ minute: '2-digit',
5973
+ });
5974
+ }
5975
+ else if (date.getFullYear() === now.getFullYear()) {
5976
+ dateLabel = date.toLocaleDateString('en-US', {
5977
+ month: 'short',
5978
+ day: 'numeric',
5979
+ });
5980
+ }
5981
+ else {
5982
+ dateLabel = date.toLocaleDateString('en-US', {
5983
+ month: 'short',
5984
+ day: 'numeric',
5985
+ year: 'numeric',
5986
+ });
5987
+ }
5988
+ }
5989
+ return b `
5990
+ <button
5991
+ type="button"
5992
+ class=${e({
5993
+ 'history__item': true,
5994
+ 'history__item--active': conversation.conversationID === this.conversationId,
5995
+ })}
5996
+ @click=${() => this._handleSwitchConversation(conversation.conversationID)}
5997
+ >
5998
+ <span class="history__item-title">${conversation.title || 'Untitled conversation'}</span>
5999
+ ${dateLabel ? b `
6000
+ <span class="history__item-date">${dateLabel}</span>
6001
+ ` : A}
6002
+ </button>
6003
+ `;
5945
6004
  })}
5946
- @click=${() => this._handleSwitchConversation(conversation.conversationID)}
5947
- >
5948
- <span class="history__item-title">${conversation.title || 'Untitled conversation'}</span>
5949
- ${conversation.lastModified ? b `
5950
- <span class="history__item-date">${this._renderConversationDate(conversation.lastModified)}</span>
5951
- ` : A}
5952
- </button>
5953
- `)}
5954
6005
  </div>
5955
6006
  `}
5956
6007
  </div>
5957
6008
  `;
5958
6009
  }
5959
- /**
5960
- * Format a conversation's lastModified for the history list: today shows the
5961
- * time, this year shows month/day, older shows month/day/year. Falls back to the
5962
- * raw value if it isn't a parseable date.
5963
- */
5964
- _renderConversationDate(lastModified) {
5965
- const date = new Date(lastModified);
5966
- if (isNaN(date.getTime())) {
5967
- return lastModified;
5968
- }
5969
- const now = new Date();
5970
- if (date.toDateString() === now.toDateString()) {
5971
- return date.toLocaleTimeString('en-US', {
5972
- hour: 'numeric',
5973
- minute: '2-digit',
5974
- });
5975
- }
5976
- if (date.getFullYear() === now.getFullYear()) {
5977
- return date.toLocaleDateString('en-US', {
5978
- month: 'short',
5979
- day: 'numeric',
5980
- });
5981
- }
5982
- return date.toLocaleDateString('en-US', {
5983
- month: 'short',
5984
- day: 'numeric',
5985
- year: 'numeric',
5986
- });
5987
- }
5988
6010
  /**
5989
6011
  * The side drawer — one sliding panel that holds the Settings, Debug, History,
5990
6012
  * and tool-detail views. The header buttons switch `_drawerView`; the title and
@@ -6015,25 +6037,24 @@ class KRChatbot extends i$3 {
6015
6037
  // In the floating widget the drawer fills the panel over the transcript (a
6016
6038
  // full-panel view with a back button) rather than sliding in beside it — the
6017
6039
  // panel is too narrow to show both. Inline keeps the side-drawer behavior.
6018
- const fullPanel = this.variant === 'floating';
6019
6040
  return b `
6020
6041
  <div
6021
6042
  class=${e({
6022
6043
  'drawer': true,
6023
6044
  'drawer--open': this._drawerOpen,
6024
6045
  'drawer--resizing': this._isDrawerResizing,
6025
- 'drawer--full': fullPanel,
6046
+ 'drawer--full': this.variant === 'floating',
6026
6047
  })}
6027
- style=${o(fullPanel ? {} : { width: `${this._drawerWidth}px` })}
6048
+ style=${o(this.variant === 'floating' ? {} : { width: `${this._drawerWidth}px` })}
6028
6049
  >
6029
- ${fullPanel ? A : b `
6050
+ ${this.variant === 'floating' ? A : b `
6030
6051
  <div
6031
6052
  class="drawer__resize"
6032
6053
  @mousedown=${this._handleDrawerResizeMouseDown}
6033
6054
  ></div>
6034
6055
  `}
6035
6056
  <div class="drawer__header">
6036
- ${fullPanel ? b `
6057
+ ${this.variant === 'floating' ? b `
6037
6058
  <button
6038
6059
  class="header-action"
6039
6060
  @click=${this._handleCloseDrawer}
@@ -6081,7 +6102,7 @@ class KRChatbot extends i$3 {
6081
6102
  New conversation
6082
6103
  </kr-button>
6083
6104
  ` : A}
6084
- ${fullPanel ? A : b `
6105
+ ${this.variant === 'floating' ? A : b `
6085
6106
  <button
6086
6107
  class="header-action"
6087
6108
  @click=${this._handleCloseDrawer}