@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.
- package/dist/chatbot.d.ts.map +1 -1
- package/dist/chatbot.js +87 -32
- package/dist/chatbot.js.map +1 -1
- package/dist/krubble-app.bundled.js +87 -32
- package/dist/krubble-app.bundled.js.map +1 -1
- package/dist/krubble-app.bundled.min.js +41 -33
- package/dist/krubble-app.bundled.min.js.map +1 -1
- package/dist/krubble-app.umd.js +87 -32
- 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,16 +5862,55 @@
|
|
|
5862
5862
|
// --- Lifecycle ---
|
|
5863
5863
|
connectedCallback() {
|
|
5864
5864
|
super.connectedCallback();
|
|
5865
|
-
// Default to loading
|
|
5866
|
-
//
|
|
5867
|
-
//
|
|
5868
|
-
// they
|
|
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`.
|
|
5869
5872
|
if (!this.load) {
|
|
5870
|
-
this.load = () =>
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5873
|
+
this.load = () => {
|
|
5874
|
+
let resolve = Promise.resolve();
|
|
5875
|
+
if (this.conversationId == null) {
|
|
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(() => { });
|
|
5882
|
+
}
|
|
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());
|
|
5889
|
+
})
|
|
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 = [];
|
|
5897
|
+
try {
|
|
5898
|
+
ids = JSON.parse(msg.conversationFileIDs || '[]');
|
|
5899
|
+
}
|
|
5900
|
+
catch { /* no/invalid attachments — leave empty */ }
|
|
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
|
+
})));
|
|
5913
|
+
};
|
|
5875
5914
|
}
|
|
5876
5915
|
this.load().then((messages) => {
|
|
5877
5916
|
if (messages && messages.length) {
|
|
@@ -6665,10 +6704,18 @@
|
|
|
6665
6704
|
}
|
|
6666
6705
|
/** Open the shared full-screen previewer for a message image. */
|
|
6667
6706
|
_openImage(img) {
|
|
6668
|
-
|
|
6669
|
-
|
|
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`,
|
|
6670
6711
|
name: img.niceName || 'image',
|
|
6671
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
|
+
});
|
|
6672
6719
|
}
|
|
6673
6720
|
/** Whether the send button is active: needs text or at least one image. */
|
|
6674
6721
|
_canSend() {
|
|
@@ -7517,7 +7564,7 @@
|
|
|
7517
7564
|
@click=${() => this._openImage(img)}
|
|
7518
7565
|
>
|
|
7519
7566
|
<img
|
|
7520
|
-
src="/api/system/conversationFile/${img.conversationFileID}/view"
|
|
7567
|
+
src="${krubbleHttp.Http.getBaseUrl()}/api/system/conversationFile/${img.conversationFileID}/view"
|
|
7521
7568
|
alt=${img.niceName || ''}
|
|
7522
7569
|
/>
|
|
7523
7570
|
</button>
|
|
@@ -7541,7 +7588,7 @@
|
|
|
7541
7588
|
${msg.content ? b `
|
|
7542
7589
|
<button
|
|
7543
7590
|
class="message-copy"
|
|
7544
|
-
title
|
|
7591
|
+
title=${this._copiedIndex === index ? 'Copied' : 'Copy'}
|
|
7545
7592
|
@click=${() => this._handleCopyMessage(msg.content, index)}
|
|
7546
7593
|
>
|
|
7547
7594
|
${this._copiedIndex === index ? b `
|
|
@@ -7555,7 +7602,6 @@
|
|
|
7555
7602
|
>
|
|
7556
7603
|
<path d="M20 6 9 17l-5-5" />
|
|
7557
7604
|
</svg>
|
|
7558
|
-
Copied
|
|
7559
7605
|
` : b `
|
|
7560
7606
|
<svg
|
|
7561
7607
|
viewBox="0 0 24 24"
|
|
@@ -7574,7 +7620,6 @@
|
|
|
7574
7620
|
/>
|
|
7575
7621
|
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
|
|
7576
7622
|
</svg>
|
|
7577
|
-
Copy
|
|
7578
7623
|
`}
|
|
7579
7624
|
</button>
|
|
7580
7625
|
` : A}
|
|
@@ -7941,7 +7986,7 @@
|
|
|
7941
7986
|
flex: 1;
|
|
7942
7987
|
display: flex;
|
|
7943
7988
|
flex-direction: column;
|
|
7944
|
-
gap:
|
|
7989
|
+
gap: 36px;
|
|
7945
7990
|
padding: 32px;
|
|
7946
7991
|
width: 100%;
|
|
7947
7992
|
max-width: 768px;
|
|
@@ -7970,6 +8015,17 @@
|
|
|
7970
8015
|
flex-direction: column;
|
|
7971
8016
|
max-width: 100%;
|
|
7972
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;
|
|
7973
8029
|
}
|
|
7974
8030
|
|
|
7975
8031
|
@keyframes chatbot-msg-in {
|
|
@@ -8018,43 +8074,41 @@
|
|
|
8018
8074
|
color: #b45309;
|
|
8019
8075
|
}
|
|
8020
8076
|
|
|
8021
|
-
/* Per-message copy button.
|
|
8022
|
-
|
|
8023
|
-
|
|
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. */
|
|
8024
8081
|
.message-copy {
|
|
8025
|
-
|
|
8026
|
-
|
|
8027
|
-
|
|
8082
|
+
position: absolute;
|
|
8083
|
+
bottom: 0;
|
|
8084
|
+
left: 0;
|
|
8028
8085
|
display: inline-flex;
|
|
8029
8086
|
align-items: center;
|
|
8030
|
-
|
|
8031
|
-
|
|
8032
|
-
|
|
8087
|
+
justify-content: center;
|
|
8088
|
+
width: 26px;
|
|
8089
|
+
height: 26px;
|
|
8090
|
+
padding: 0;
|
|
8033
8091
|
background: none;
|
|
8034
8092
|
border: none;
|
|
8035
8093
|
border-radius: 6px;
|
|
8036
|
-
|
|
8037
|
-
font-size: 12px;
|
|
8038
|
-
color: #64668b;
|
|
8094
|
+
color: #000000;
|
|
8039
8095
|
cursor: pointer;
|
|
8040
8096
|
opacity: 0;
|
|
8041
|
-
pointer-events: none;
|
|
8042
8097
|
transition: opacity 0.15s, background-color 0.15s, color 0.15s;
|
|
8043
8098
|
}
|
|
8044
8099
|
|
|
8045
8100
|
.message--user .message-copy {
|
|
8046
|
-
|
|
8101
|
+
left: auto;
|
|
8102
|
+
right: 0;
|
|
8047
8103
|
}
|
|
8048
8104
|
|
|
8049
8105
|
.message:hover .message-copy,
|
|
8050
8106
|
.message-copy:focus-visible {
|
|
8051
8107
|
opacity: 1;
|
|
8052
|
-
pointer-events: auto;
|
|
8053
8108
|
}
|
|
8054
8109
|
|
|
8055
8110
|
.message-copy:hover {
|
|
8056
8111
|
background: rgba(0, 0, 0, 0.06);
|
|
8057
|
-
color: #1a1a2e;
|
|
8058
8112
|
}
|
|
8059
8113
|
|
|
8060
8114
|
.message-copy svg {
|
|
@@ -8628,6 +8682,7 @@
|
|
|
8628
8682
|
|
|
8629
8683
|
.message--user .attachments {
|
|
8630
8684
|
justify-content: flex-end;
|
|
8685
|
+
margin-bottom: 24px;
|
|
8631
8686
|
}
|
|
8632
8687
|
|
|
8633
8688
|
.attachment {
|