@kodaris/krubble-app-components 1.0.87 → 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.
- package/dist/chatbot.d.ts.map +1 -1
- package/dist/chatbot.js +79 -40
- package/dist/chatbot.js.map +1 -1
- package/dist/krubble-app.bundled.js +79 -40
- package/dist/krubble-app.bundled.js.map +1 -1
- package/dist/krubble-app.bundled.min.js +39 -31
- package/dist/krubble-app.bundled.min.js.map +1 -1
- package/dist/krubble-app.umd.js +79 -40
- package/dist/krubble-app.umd.js.map +1 -1
- package/dist/krubble-app.umd.min.js +36 -28
- package/dist/krubble-app.umd.min.js.map +1 -1
- package/package.json +1 -1
package/dist/krubble-app.umd.js
CHANGED
|
@@ -5862,31 +5862,54 @@
|
|
|
5862
5862
|
// --- Lifecycle ---
|
|
5863
5863
|
connectedCallback() {
|
|
5864
5864
|
super.connectedCallback();
|
|
5865
|
-
// Default to loading a
|
|
5866
|
-
//
|
|
5867
|
-
// conversation
|
|
5868
|
-
//
|
|
5869
|
-
//
|
|
5870
|
-
//
|
|
5865
|
+
// Default to loading a conversation's messages when the consumer hasn't
|
|
5866
|
+
// provided their own `load`. With `conversationId` set we rebuild that
|
|
5867
|
+
// conversation; with none set we resume the caller's most recent one via
|
|
5868
|
+
// `/conversation/last` (KRHttp rejects its 404 when they have none yet →
|
|
5869
|
+
// stays null → empty chat) and pin it so this session continues it. Rows
|
|
5870
|
+
// already match KRChatbotMessage ({ type, content }); we only parse each row's
|
|
5871
|
+
// `conversationFileIDs` (a JSON array string of ids) into `conversationFiles`.
|
|
5871
5872
|
if (!this.load) {
|
|
5872
5873
|
this.load = () => {
|
|
5874
|
+
let resolve = Promise.resolve();
|
|
5873
5875
|
if (this.conversationId == null) {
|
|
5874
|
-
|
|
5876
|
+
resolve = krubbleHttp.Http.fetch({ url: '/api/system/conversation/last' })
|
|
5877
|
+
.then((r) => r.json())
|
|
5878
|
+
.then((res) => {
|
|
5879
|
+
this.conversationId = res?.data?.conversationID ?? null;
|
|
5880
|
+
})
|
|
5881
|
+
.catch(() => { });
|
|
5875
5882
|
}
|
|
5876
|
-
return
|
|
5877
|
-
|
|
5883
|
+
return resolve
|
|
5884
|
+
.then(() => {
|
|
5885
|
+
if (this.conversationId == null) {
|
|
5886
|
+
return { data: [] };
|
|
5887
|
+
}
|
|
5888
|
+
return krubbleHttp.Http.fetch({ url: `/api/system/conversation/${this.conversationId}/messages` }).then((r) => r.json());
|
|
5878
5889
|
})
|
|
5879
|
-
.then((
|
|
5880
|
-
|
|
5890
|
+
.then((res) => Promise.all((res?.data || []).map((msg) => {
|
|
5891
|
+
// Resolve this message's `conversationFileIDs` (a JSON array string of
|
|
5892
|
+
// ids) into full file objects — each fetch response IS the
|
|
5893
|
+
// KRChatbotConversationFile (id + niceName + mimeType), and niceName
|
|
5894
|
+
// drives the previewer's file-type detection. A failed fetch drops
|
|
5895
|
+
// that file rather than rendering a broken thumbnail.
|
|
5896
|
+
let ids = [];
|
|
5881
5897
|
try {
|
|
5882
|
-
|
|
5883
|
-
if (ids.length) {
|
|
5884
|
-
msg.conversationFiles = ids.map((id) => ({ conversationFileID: id }));
|
|
5885
|
-
}
|
|
5898
|
+
ids = JSON.parse(msg.conversationFileIDs || '[]');
|
|
5886
5899
|
}
|
|
5887
5900
|
catch { /* no/invalid attachments — leave empty */ }
|
|
5888
|
-
|
|
5889
|
-
|
|
5901
|
+
if (!ids.length) {
|
|
5902
|
+
return Promise.resolve(msg);
|
|
5903
|
+
}
|
|
5904
|
+
return Promise.all(ids.map((id) => krubbleHttp.Http.fetch({ url: `/api/system/conversationFile/${id}` })
|
|
5905
|
+
.then((r) => r.json())
|
|
5906
|
+
.then((meta) => meta?.data)
|
|
5907
|
+
.catch(() => null)))
|
|
5908
|
+
.then((files) => {
|
|
5909
|
+
msg.conversationFiles = files.filter((f) => f != null);
|
|
5910
|
+
return msg;
|
|
5911
|
+
});
|
|
5912
|
+
})));
|
|
5890
5913
|
};
|
|
5891
5914
|
}
|
|
5892
5915
|
this.load().then((messages) => {
|
|
@@ -6681,10 +6704,18 @@
|
|
|
6681
6704
|
}
|
|
6682
6705
|
/** Open the shared full-screen previewer for a message image. */
|
|
6683
6706
|
_openImage(img) {
|
|
6684
|
-
|
|
6685
|
-
|
|
6707
|
+
// Prefix the configured backend base URL — a raw fetch of a relative path
|
|
6708
|
+
// would hit the page origin (e.g. the dev server), not the backend.
|
|
6709
|
+
const preview = krubbleComponents.FilePreview.open({
|
|
6710
|
+
src: `${krubbleHttp.Http.getBaseUrl()}/api/system/conversationFile/${img.conversationFileID}/view`,
|
|
6686
6711
|
name: img.niceName || 'image',
|
|
6687
6712
|
});
|
|
6713
|
+
// The previewer's download button only fires a `download` event — the
|
|
6714
|
+
// consumer performs the actual download. Hit the view endpoint with
|
|
6715
|
+
// `?download=true` (Content-Disposition: attachment) in a new tab.
|
|
6716
|
+
preview.addEventListener('download', () => {
|
|
6717
|
+
window.open(`${krubbleHttp.Http.getBaseUrl()}/api/system/conversationFile/${img.conversationFileID}/view?download=true`, '_blank');
|
|
6718
|
+
});
|
|
6688
6719
|
}
|
|
6689
6720
|
/** Whether the send button is active: needs text or at least one image. */
|
|
6690
6721
|
_canSend() {
|
|
@@ -7533,7 +7564,7 @@
|
|
|
7533
7564
|
@click=${() => this._openImage(img)}
|
|
7534
7565
|
>
|
|
7535
7566
|
<img
|
|
7536
|
-
src="/api/system/conversationFile/${img.conversationFileID}/view"
|
|
7567
|
+
src="${krubbleHttp.Http.getBaseUrl()}/api/system/conversationFile/${img.conversationFileID}/view"
|
|
7537
7568
|
alt=${img.niceName || ''}
|
|
7538
7569
|
/>
|
|
7539
7570
|
</button>
|
|
@@ -7557,7 +7588,7 @@
|
|
|
7557
7588
|
${msg.content ? b `
|
|
7558
7589
|
<button
|
|
7559
7590
|
class="message-copy"
|
|
7560
|
-
title
|
|
7591
|
+
title=${this._copiedIndex === index ? 'Copied' : 'Copy'}
|
|
7561
7592
|
@click=${() => this._handleCopyMessage(msg.content, index)}
|
|
7562
7593
|
>
|
|
7563
7594
|
${this._copiedIndex === index ? b `
|
|
@@ -7571,7 +7602,6 @@
|
|
|
7571
7602
|
>
|
|
7572
7603
|
<path d="M20 6 9 17l-5-5" />
|
|
7573
7604
|
</svg>
|
|
7574
|
-
Copied
|
|
7575
7605
|
` : b `
|
|
7576
7606
|
<svg
|
|
7577
7607
|
viewBox="0 0 24 24"
|
|
@@ -7590,7 +7620,6 @@
|
|
|
7590
7620
|
/>
|
|
7591
7621
|
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
|
|
7592
7622
|
</svg>
|
|
7593
|
-
Copy
|
|
7594
7623
|
`}
|
|
7595
7624
|
</button>
|
|
7596
7625
|
` : A}
|
|
@@ -7957,7 +7986,7 @@
|
|
|
7957
7986
|
flex: 1;
|
|
7958
7987
|
display: flex;
|
|
7959
7988
|
flex-direction: column;
|
|
7960
|
-
gap:
|
|
7989
|
+
gap: 36px;
|
|
7961
7990
|
padding: 32px;
|
|
7962
7991
|
width: 100%;
|
|
7963
7992
|
max-width: 768px;
|
|
@@ -7986,6 +8015,17 @@
|
|
|
7986
8015
|
flex-direction: column;
|
|
7987
8016
|
max-width: 100%;
|
|
7988
8017
|
animation: chatbot-msg-in 0.3s ease-out;
|
|
8018
|
+
/* Reserve room for the copy button INSIDE the message box (so the button is
|
|
8019
|
+
part of the message's hover region — no dead zone to cross), then cancel
|
|
8020
|
+
that space for layout so message spacing is unchanged. */
|
|
8021
|
+
padding-bottom: 30px;
|
|
8022
|
+
margin-bottom: -30px;
|
|
8023
|
+
}
|
|
8024
|
+
|
|
8025
|
+
/* The reserved padding overhangs into the next message; lift the hovered one
|
|
8026
|
+
so its copy button isn't covered by (or stealing hover from) the sibling. */
|
|
8027
|
+
.message:hover {
|
|
8028
|
+
z-index: 1;
|
|
7989
8029
|
}
|
|
7990
8030
|
|
|
7991
8031
|
@keyframes chatbot-msg-in {
|
|
@@ -8034,43 +8074,41 @@
|
|
|
8034
8074
|
color: #b45309;
|
|
8035
8075
|
}
|
|
8036
8076
|
|
|
8037
|
-
/* Per-message copy button.
|
|
8038
|
-
|
|
8039
|
-
|
|
8077
|
+
/* Per-message copy button. Sits in the message's reserved bottom padding —
|
|
8078
|
+
inside the hover box, so moving the cursor down onto it never leaves the
|
|
8079
|
+
message. Height is reserved (via the message padding) so spacing doesn't
|
|
8080
|
+
jump; only opacity toggles on hover. */
|
|
8040
8081
|
.message-copy {
|
|
8041
|
-
|
|
8042
|
-
|
|
8043
|
-
|
|
8082
|
+
position: absolute;
|
|
8083
|
+
bottom: 0;
|
|
8084
|
+
left: 0;
|
|
8044
8085
|
display: inline-flex;
|
|
8045
8086
|
align-items: center;
|
|
8046
|
-
|
|
8047
|
-
|
|
8048
|
-
|
|
8087
|
+
justify-content: center;
|
|
8088
|
+
width: 26px;
|
|
8089
|
+
height: 26px;
|
|
8090
|
+
padding: 0;
|
|
8049
8091
|
background: none;
|
|
8050
8092
|
border: none;
|
|
8051
8093
|
border-radius: 6px;
|
|
8052
|
-
|
|
8053
|
-
font-size: 12px;
|
|
8054
|
-
color: #64668b;
|
|
8094
|
+
color: #000000;
|
|
8055
8095
|
cursor: pointer;
|
|
8056
8096
|
opacity: 0;
|
|
8057
|
-
pointer-events: none;
|
|
8058
8097
|
transition: opacity 0.15s, background-color 0.15s, color 0.15s;
|
|
8059
8098
|
}
|
|
8060
8099
|
|
|
8061
8100
|
.message--user .message-copy {
|
|
8062
|
-
|
|
8101
|
+
left: auto;
|
|
8102
|
+
right: 0;
|
|
8063
8103
|
}
|
|
8064
8104
|
|
|
8065
8105
|
.message:hover .message-copy,
|
|
8066
8106
|
.message-copy:focus-visible {
|
|
8067
8107
|
opacity: 1;
|
|
8068
|
-
pointer-events: auto;
|
|
8069
8108
|
}
|
|
8070
8109
|
|
|
8071
8110
|
.message-copy:hover {
|
|
8072
8111
|
background: rgba(0, 0, 0, 0.06);
|
|
8073
|
-
color: #1a1a2e;
|
|
8074
8112
|
}
|
|
8075
8113
|
|
|
8076
8114
|
.message-copy svg {
|
|
@@ -8644,6 +8682,7 @@
|
|
|
8644
8682
|
|
|
8645
8683
|
.message--user .attachments {
|
|
8646
8684
|
justify-content: flex-end;
|
|
8685
|
+
margin-bottom: 24px;
|
|
8647
8686
|
}
|
|
8648
8687
|
|
|
8649
8688
|
.attachment {
|