@kodaris/krubble-app-components 1.0.86 → 1.0.88

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.
@@ -5859,16 +5859,55 @@ let KRChatbot = class KRChatbot extends i$3 {
5859
5859
  // --- Lifecycle ---
5860
5860
  connectedCallback() {
5861
5861
  super.connectedCallback();
5862
- // Default to loading existing messages from the Kodaris Bedrock conversation
5863
- // when the consumer hasn't provided their own `load`. The endpoint returns
5864
- // messages already in KRChatbotMessage shape ({ type, content, images }), so
5865
- // they pass straight through.
5862
+ // Default to loading a conversation's messages when the consumer hasn't
5863
+ // provided their own `load`. With `conversationId` set we rebuild that
5864
+ // conversation; with none set we resume the caller's most recent one via
5865
+ // `/conversation/last` (KRHttp rejects its 404 when they have none yet →
5866
+ // stays null → empty chat) and pin it so this session continues it. Rows
5867
+ // already match KRChatbotMessage ({ type, content }); we only parse each row's
5868
+ // `conversationFileIDs` (a JSON array string of ids) into `conversationFiles`.
5866
5869
  if (!this.load) {
5867
- this.load = () => KRHttp.fetch({
5868
- url: '/api/system/integration/aws/bedrock/getConversationMessages',
5869
- })
5870
- .then((r) => r.json())
5871
- .then((res) => res.data || []);
5870
+ this.load = () => {
5871
+ let resolve = Promise.resolve();
5872
+ if (this.conversationId == null) {
5873
+ resolve = KRHttp.fetch({ url: '/api/system/conversation/last' })
5874
+ .then((r) => r.json())
5875
+ .then((res) => {
5876
+ this.conversationId = res?.data?.conversationID ?? null;
5877
+ })
5878
+ .catch(() => { });
5879
+ }
5880
+ return resolve
5881
+ .then(() => {
5882
+ if (this.conversationId == null) {
5883
+ return { data: [] };
5884
+ }
5885
+ return KRHttp.fetch({ url: `/api/system/conversation/${this.conversationId}/messages` }).then((r) => r.json());
5886
+ })
5887
+ .then((res) => Promise.all((res?.data || []).map((msg) => {
5888
+ // Resolve this message's `conversationFileIDs` (a JSON array string of
5889
+ // ids) into full file objects — each fetch response IS the
5890
+ // KRChatbotConversationFile (id + niceName + mimeType), and niceName
5891
+ // drives the previewer's file-type detection. A failed fetch drops
5892
+ // that file rather than rendering a broken thumbnail.
5893
+ let ids = [];
5894
+ try {
5895
+ ids = JSON.parse(msg.conversationFileIDs || '[]');
5896
+ }
5897
+ catch { /* no/invalid attachments — leave empty */ }
5898
+ if (!ids.length) {
5899
+ return Promise.resolve(msg);
5900
+ }
5901
+ return Promise.all(ids.map((id) => KRHttp.fetch({ url: `/api/system/conversationFile/${id}` })
5902
+ .then((r) => r.json())
5903
+ .then((meta) => meta?.data)
5904
+ .catch(() => null)))
5905
+ .then((files) => {
5906
+ msg.conversationFiles = files.filter((f) => f != null);
5907
+ return msg;
5908
+ });
5909
+ })));
5910
+ };
5872
5911
  }
5873
5912
  this.load().then((messages) => {
5874
5913
  if (messages && messages.length) {
@@ -6662,10 +6701,18 @@ let KRChatbot = class KRChatbot extends i$3 {
6662
6701
  }
6663
6702
  /** Open the shared full-screen previewer for a message image. */
6664
6703
  _openImage(img) {
6665
- KRFilePreview.open({
6666
- src: `/api/system/conversationFile/${img.conversationFileID}/view`,
6704
+ // Prefix the configured backend base URL — a raw fetch of a relative path
6705
+ // would hit the page origin (e.g. the dev server), not the backend.
6706
+ const preview = KRFilePreview.open({
6707
+ src: `${KRHttp.getBaseUrl()}/api/system/conversationFile/${img.conversationFileID}/view`,
6667
6708
  name: img.niceName || 'image',
6668
6709
  });
6710
+ // The previewer's download button only fires a `download` event — the
6711
+ // consumer performs the actual download. Hit the view endpoint with
6712
+ // `?download=true` (Content-Disposition: attachment) in a new tab.
6713
+ preview.addEventListener('download', () => {
6714
+ window.open(`${KRHttp.getBaseUrl()}/api/system/conversationFile/${img.conversationFileID}/view?download=true`, '_blank');
6715
+ });
6669
6716
  }
6670
6717
  /** Whether the send button is active: needs text or at least one image. */
6671
6718
  _canSend() {
@@ -7514,7 +7561,7 @@ let KRChatbot = class KRChatbot extends i$3 {
7514
7561
  @click=${() => this._openImage(img)}
7515
7562
  >
7516
7563
  <img
7517
- src="/api/system/conversationFile/${img.conversationFileID}/view"
7564
+ src="${KRHttp.getBaseUrl()}/api/system/conversationFile/${img.conversationFileID}/view"
7518
7565
  alt=${img.niceName || ''}
7519
7566
  />
7520
7567
  </button>
@@ -7538,7 +7585,7 @@ let KRChatbot = class KRChatbot extends i$3 {
7538
7585
  ${msg.content ? b `
7539
7586
  <button
7540
7587
  class="message-copy"
7541
- title="Copy"
7588
+ title=${this._copiedIndex === index ? 'Copied' : 'Copy'}
7542
7589
  @click=${() => this._handleCopyMessage(msg.content, index)}
7543
7590
  >
7544
7591
  ${this._copiedIndex === index ? b `
@@ -7552,7 +7599,6 @@ let KRChatbot = class KRChatbot extends i$3 {
7552
7599
  >
7553
7600
  <path d="M20 6 9 17l-5-5" />
7554
7601
  </svg>
7555
- Copied
7556
7602
  ` : b `
7557
7603
  <svg
7558
7604
  viewBox="0 0 24 24"
@@ -7571,7 +7617,6 @@ let KRChatbot = class KRChatbot extends i$3 {
7571
7617
  />
7572
7618
  <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
7573
7619
  </svg>
7574
- Copy
7575
7620
  `}
7576
7621
  </button>
7577
7622
  ` : A}
@@ -7938,7 +7983,7 @@ KRChatbot.styles = i$6 `
7938
7983
  flex: 1;
7939
7984
  display: flex;
7940
7985
  flex-direction: column;
7941
- gap: 24px;
7986
+ gap: 36px;
7942
7987
  padding: 32px;
7943
7988
  width: 100%;
7944
7989
  max-width: 768px;
@@ -7967,6 +8012,17 @@ KRChatbot.styles = i$6 `
7967
8012
  flex-direction: column;
7968
8013
  max-width: 100%;
7969
8014
  animation: chatbot-msg-in 0.3s ease-out;
8015
+ /* Reserve room for the copy button INSIDE the message box (so the button is
8016
+ part of the message's hover region — no dead zone to cross), then cancel
8017
+ that space for layout so message spacing is unchanged. */
8018
+ padding-bottom: 30px;
8019
+ margin-bottom: -30px;
8020
+ }
8021
+
8022
+ /* The reserved padding overhangs into the next message; lift the hovered one
8023
+ so its copy button isn't covered by (or stealing hover from) the sibling. */
8024
+ .message:hover {
8025
+ z-index: 1;
7970
8026
  }
7971
8027
 
7972
8028
  @keyframes chatbot-msg-in {
@@ -8015,43 +8071,41 @@ KRChatbot.styles = i$6 `
8015
8071
  color: #b45309;
8016
8072
  }
8017
8073
 
8018
- /* Per-message copy button. Kept in flow with a fixed height that is always
8019
- reserved (via a negative bottom margin that cancels it) so message spacing
8020
- is identical whether or not it's shown; only its opacity toggles on hover. */
8074
+ /* Per-message copy button. Sits in the message's reserved bottom padding —
8075
+ inside the hover box, so moving the cursor down onto it never leaves the
8076
+ message. Height is reserved (via the message padding) so spacing doesn't
8077
+ jump; only opacity toggles on hover. */
8021
8078
  .message-copy {
8022
- align-self: flex-start;
8023
- margin-top: 4px;
8024
- margin-bottom: -27px;
8079
+ position: absolute;
8080
+ bottom: 0;
8081
+ left: 0;
8025
8082
  display: inline-flex;
8026
8083
  align-items: center;
8027
- gap: 4px;
8028
- height: 23px;
8029
- padding: 4px 8px;
8084
+ justify-content: center;
8085
+ width: 26px;
8086
+ height: 26px;
8087
+ padding: 0;
8030
8088
  background: none;
8031
8089
  border: none;
8032
8090
  border-radius: 6px;
8033
- font: inherit;
8034
- font-size: 12px;
8035
- color: #64668b;
8091
+ color: #000000;
8036
8092
  cursor: pointer;
8037
8093
  opacity: 0;
8038
- pointer-events: none;
8039
8094
  transition: opacity 0.15s, background-color 0.15s, color 0.15s;
8040
8095
  }
8041
8096
 
8042
8097
  .message--user .message-copy {
8043
- align-self: flex-end;
8098
+ left: auto;
8099
+ right: 0;
8044
8100
  }
8045
8101
 
8046
8102
  .message:hover .message-copy,
8047
8103
  .message-copy:focus-visible {
8048
8104
  opacity: 1;
8049
- pointer-events: auto;
8050
8105
  }
8051
8106
 
8052
8107
  .message-copy:hover {
8053
8108
  background: rgba(0, 0, 0, 0.06);
8054
- color: #1a1a2e;
8055
8109
  }
8056
8110
 
8057
8111
  .message-copy svg {
@@ -8625,6 +8679,7 @@ KRChatbot.styles = i$6 `
8625
8679
 
8626
8680
  .message--user .attachments {
8627
8681
  justify-content: flex-end;
8682
+ margin-bottom: 24px;
8628
8683
  }
8629
8684
 
8630
8685
  .attachment {