@kodaris/krubble-app-components 1.0.84 → 1.0.86
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 +33 -42
- package/dist/chatbot.d.ts.map +1 -1
- package/dist/chatbot.js +107 -100
- package/dist/chatbot.js.map +1 -1
- package/dist/krubble-app.bundled.js +107 -100
- package/dist/krubble-app.bundled.js.map +1 -1
- package/dist/krubble-app.bundled.min.js +16 -38
- package/dist/krubble-app.bundled.min.js.map +1 -1
- package/dist/krubble-app.umd.js +107 -100
- package/dist/krubble-app.umd.js.map +1 -1
- package/dist/krubble-app.umd.min.js +13 -35
- package/dist/krubble-app.umd.min.js.map +1 -1
- package/package.json +1 -1
package/dist/krubble-app.umd.js
CHANGED
|
@@ -5642,14 +5642,6 @@
|
|
|
5642
5642
|
* from the Kodaris Bedrock conversation endpoints. Set this to override.
|
|
5643
5643
|
*/
|
|
5644
5644
|
this.send = null;
|
|
5645
|
-
/**
|
|
5646
|
-
* Callback function called when the user clears the conversation.
|
|
5647
|
-
* Must return a Promise — messages are only cleared if it resolves.
|
|
5648
|
-
*
|
|
5649
|
-
* Optional — when not set, the chatbot uses its built-in default that clears
|
|
5650
|
-
* the Kodaris Bedrock conversation state. Set this to override.
|
|
5651
|
-
*/
|
|
5652
|
-
this.clear = null;
|
|
5653
5645
|
/**
|
|
5654
5646
|
* Callback function called on init to load existing conversation messages.
|
|
5655
5647
|
* Must return a Promise that resolves with an array of KRChatbotMessage.
|
|
@@ -5692,6 +5684,20 @@
|
|
|
5692
5684
|
* full page or panel region.
|
|
5693
5685
|
*/
|
|
5694
5686
|
this.variant = 'floating';
|
|
5687
|
+
/**
|
|
5688
|
+
* The conversation this chatbot's turns target (backend `conversationID`).
|
|
5689
|
+
*
|
|
5690
|
+
* - Set → every turn is sent with this `conversationID`, so the chat continues
|
|
5691
|
+
* that specific conversation.
|
|
5692
|
+
* - Unset → the first turn is sent with `createNew: true` (a fresh
|
|
5693
|
+
* conversation); the component then resolves the new conversation's id (via
|
|
5694
|
+
* `/conversation/last`) and pins it here so the rest of the session stays one
|
|
5695
|
+
* conversation instead of spawning a new one per message.
|
|
5696
|
+
*
|
|
5697
|
+
* Only used by the built-in `send` default; a custom `send` owns its own
|
|
5698
|
+
* targeting.
|
|
5699
|
+
*/
|
|
5700
|
+
this.conversationId = null;
|
|
5695
5701
|
// --- @state (internal) ---
|
|
5696
5702
|
this._inputValue = '';
|
|
5697
5703
|
this._isStreaming = false;
|
|
@@ -5730,7 +5736,7 @@
|
|
|
5730
5736
|
/** True while the user is dragging the debug drawer's resize handle. */
|
|
5731
5737
|
this._isDebugResizing = false;
|
|
5732
5738
|
/** Images staged in the composer, before the message is sent. */
|
|
5733
|
-
this.
|
|
5739
|
+
this._pendingConversationFiles = [];
|
|
5734
5740
|
/** True while a file is being dragged over the panel (shows the drop overlay). */
|
|
5735
5741
|
this._isDraggingFile = false;
|
|
5736
5742
|
/** Whether the `/` skill menu is open above the composer. */
|
|
@@ -5900,7 +5906,7 @@
|
|
|
5900
5906
|
document.removeEventListener('mousemove', this._handleMouseMove);
|
|
5901
5907
|
document.removeEventListener('mouseup', this._handleMouseUp);
|
|
5902
5908
|
// Release any object URLs still held by staged attachments.
|
|
5903
|
-
this.
|
|
5909
|
+
this._pendingConversationFiles.forEach((p) => URL.revokeObjectURL(p.previewUrl));
|
|
5904
5910
|
}
|
|
5905
5911
|
updated(changed) {
|
|
5906
5912
|
if (changed.has('messages')) {
|
|
@@ -6233,7 +6239,7 @@
|
|
|
6233
6239
|
this._skillMenuOpened = false;
|
|
6234
6240
|
this._send({
|
|
6235
6241
|
message: '/test-context-warning',
|
|
6236
|
-
|
|
6242
|
+
conversationFiles: [],
|
|
6237
6243
|
});
|
|
6238
6244
|
return;
|
|
6239
6245
|
}
|
|
@@ -6241,7 +6247,7 @@
|
|
|
6241
6247
|
this._skillMenuOpened = false;
|
|
6242
6248
|
this._send({
|
|
6243
6249
|
message: '/test-context-limit',
|
|
6244
|
-
|
|
6250
|
+
conversationFiles: [],
|
|
6245
6251
|
});
|
|
6246
6252
|
return;
|
|
6247
6253
|
}
|
|
@@ -6558,6 +6564,10 @@
|
|
|
6558
6564
|
}
|
|
6559
6565
|
// --- Attachments ---
|
|
6560
6566
|
_openFilePicker() {
|
|
6567
|
+
// Close the skill/attachment menu — opening the OS file picker is the end of
|
|
6568
|
+
// the menu interaction, and the picker is a separate window so the menu would
|
|
6569
|
+
// otherwise linger open behind it (and after a file is chosen).
|
|
6570
|
+
this._skillMenuOpened = false;
|
|
6561
6571
|
this._fileInputEl?.click();
|
|
6562
6572
|
}
|
|
6563
6573
|
_handleFileInputChange(e) {
|
|
@@ -6639,49 +6649,49 @@
|
|
|
6639
6649
|
name: file.name,
|
|
6640
6650
|
previewUrl: URL.createObjectURL(file),
|
|
6641
6651
|
};
|
|
6642
|
-
this.
|
|
6652
|
+
this._pendingConversationFiles = [...this._pendingConversationFiles, pending];
|
|
6643
6653
|
});
|
|
6644
6654
|
}
|
|
6645
6655
|
_removePending(localId) {
|
|
6646
|
-
const target = this.
|
|
6656
|
+
const target = this._pendingConversationFiles.find((p) => p.localId === localId);
|
|
6647
6657
|
if (target) {
|
|
6648
6658
|
URL.revokeObjectURL(target.previewUrl);
|
|
6649
6659
|
}
|
|
6650
|
-
this.
|
|
6660
|
+
this._pendingConversationFiles = this._pendingConversationFiles.filter((p) => p.localId !== localId);
|
|
6651
6661
|
}
|
|
6652
6662
|
_clearPending() {
|
|
6653
|
-
this.
|
|
6654
|
-
this.
|
|
6663
|
+
this._pendingConversationFiles.forEach((p) => URL.revokeObjectURL(p.previewUrl));
|
|
6664
|
+
this._pendingConversationFiles = [];
|
|
6655
6665
|
}
|
|
6656
6666
|
/** Open the shared full-screen previewer for a message image. */
|
|
6657
6667
|
_openImage(img) {
|
|
6658
6668
|
krubbleComponents.FilePreview.open({
|
|
6659
|
-
src: `/api/system/
|
|
6669
|
+
src: `/api/system/conversationFile/${img.conversationFileID}/view`,
|
|
6660
6670
|
name: img.niceName || 'image',
|
|
6661
6671
|
});
|
|
6662
6672
|
}
|
|
6663
6673
|
/** Whether the send button is active: needs text or at least one image. */
|
|
6664
6674
|
_canSend() {
|
|
6665
|
-
return Boolean(this._inputValue.trim()) || this.
|
|
6675
|
+
return Boolean(this._inputValue.trim()) || this._pendingConversationFiles.length > 0;
|
|
6666
6676
|
}
|
|
6667
6677
|
_handleSubmit() {
|
|
6668
6678
|
if (this._isStreaming) {
|
|
6669
6679
|
return;
|
|
6670
6680
|
}
|
|
6671
6681
|
const messageText = this._inputValue.trim();
|
|
6672
|
-
const pending = this.
|
|
6682
|
+
const pending = this._pendingConversationFiles;
|
|
6673
6683
|
// Allow sending an image with no text, but require at least one of them.
|
|
6674
6684
|
if (!messageText && !pending.length) {
|
|
6675
6685
|
return;
|
|
6676
6686
|
}
|
|
6677
6687
|
// The raw files sent to the backend. Sent images are shown once the
|
|
6678
|
-
// conversation reloads and they come back as stored
|
|
6688
|
+
// conversation reloads and they come back as stored conversation files; the
|
|
6679
6689
|
// just-sent message itself carries text only.
|
|
6680
|
-
const
|
|
6690
|
+
const conversationFiles = pending.map((p) => p.file);
|
|
6681
6691
|
const submitEvent = new CustomEvent('message-submit', {
|
|
6682
6692
|
detail: {
|
|
6683
6693
|
message: messageText,
|
|
6684
|
-
|
|
6694
|
+
conversationFiles,
|
|
6685
6695
|
},
|
|
6686
6696
|
bubbles: true,
|
|
6687
6697
|
composed: true,
|
|
@@ -6693,7 +6703,7 @@
|
|
|
6693
6703
|
}
|
|
6694
6704
|
this._send({
|
|
6695
6705
|
message: messageText,
|
|
6696
|
-
|
|
6706
|
+
conversationFiles
|
|
6697
6707
|
});
|
|
6698
6708
|
}
|
|
6699
6709
|
/**
|
|
@@ -6713,8 +6723,8 @@
|
|
|
6713
6723
|
this._error = '';
|
|
6714
6724
|
this._userHasScrolledUp = false;
|
|
6715
6725
|
// Clear the composer and release its preview object URLs.
|
|
6716
|
-
this.
|
|
6717
|
-
this.
|
|
6726
|
+
this._pendingConversationFiles.forEach((p) => URL.revokeObjectURL(p.previewUrl));
|
|
6727
|
+
this._pendingConversationFiles = [];
|
|
6718
6728
|
// Reset textarea height
|
|
6719
6729
|
if (this._inputEl) {
|
|
6720
6730
|
this._inputEl.style.height = 'auto';
|
|
@@ -6750,30 +6760,74 @@
|
|
|
6750
6760
|
// the consumer hasn't provided their own `send`.
|
|
6751
6761
|
if (!this.send) {
|
|
6752
6762
|
this.send = (params) => {
|
|
6753
|
-
//
|
|
6754
|
-
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
|
|
6763
|
+
// Images are upload-first: each file is stored as a conversation file
|
|
6764
|
+
// (type LIBRARY — the format lives in mimeType), then the resulting
|
|
6765
|
+
// conversationFileIDs are attached to the message via `fileIds` on the
|
|
6766
|
+
// normal JSON stream call. There is no multipart chat endpoint. With no
|
|
6767
|
+
// images the upload resolves to an empty list immediately; a failed
|
|
6768
|
+
// upload is dropped so an undefined id never reaches `fileIds`.
|
|
6769
|
+
let uploaded = Promise.resolve([]);
|
|
6770
|
+
if (params.conversationFiles.length) {
|
|
6771
|
+
uploaded = Promise.all(params.conversationFiles.map((file) => {
|
|
6772
|
+
const form = new FormData();
|
|
6773
|
+
form.append('file', file);
|
|
6774
|
+
form.append('type', 'LIBRARY');
|
|
6775
|
+
form.append('niceName', file.name);
|
|
6776
|
+
return krubbleHttp.Http.fetch({
|
|
6777
|
+
url: '/api/system/conversationFile/upload',
|
|
6778
|
+
method: 'POST',
|
|
6779
|
+
body: form,
|
|
6780
|
+
})
|
|
6781
|
+
.then((r) => r.json())
|
|
6782
|
+
.then((res) => res?.data?.conversationFileID);
|
|
6783
|
+
})).then((ids) => ids.filter((id) => typeof id === 'number'));
|
|
6784
|
+
}
|
|
6785
|
+
return uploaded.then((fileIds) => {
|
|
6786
|
+
// Conversation targeting: continue `conversationId` when set, else
|
|
6787
|
+
// start a fresh conversation (createNew) for this turn. The pin below
|
|
6788
|
+
// means createNew only fires on the very first untargeted turn.
|
|
6789
|
+
const body = {
|
|
6790
|
+
userText: params.message,
|
|
6791
|
+
fileIds,
|
|
6792
|
+
};
|
|
6793
|
+
if (this.conversationId != null) {
|
|
6794
|
+
body.conversationID = this.conversationId;
|
|
6795
|
+
}
|
|
6796
|
+
else {
|
|
6797
|
+
body.createNew = true;
|
|
6798
|
+
}
|
|
6758
6799
|
return krubbleHttp.Http.fetch({
|
|
6759
|
-
url: '/api/system/integration/aws/bedrock/stream/
|
|
6800
|
+
url: '/api/system/integration/aws/bedrock/stream/invokeModelForConversationWithState',
|
|
6760
6801
|
method: 'POST',
|
|
6761
6802
|
headers: { Accept: 'text/event-stream' },
|
|
6762
|
-
body
|
|
6803
|
+
body,
|
|
6804
|
+
}).then((response) => {
|
|
6805
|
+
// Pin the session to the conversation this turn used. The stream
|
|
6806
|
+
// doesn't echo the resolved id, so when we let the server create one
|
|
6807
|
+
// (no conversationId set) we resolve it via `/last` and keep it, so
|
|
6808
|
+
// subsequent turns continue this conversation instead of each sending
|
|
6809
|
+
// createNew and spawning a new one. Best-effort, fire-and-forget: a
|
|
6810
|
+
// failure leaves it unpinned and the next turn resolves server-side.
|
|
6811
|
+
if (this.conversationId == null) {
|
|
6812
|
+
krubbleHttp.Http.fetch({ url: '/api/system/conversation/last' })
|
|
6813
|
+
.then((r) => r.json())
|
|
6814
|
+
.then((res) => {
|
|
6815
|
+
const id = res?.data?.conversationID;
|
|
6816
|
+
if (typeof id === 'number') {
|
|
6817
|
+
this.conversationId = id;
|
|
6818
|
+
}
|
|
6819
|
+
})
|
|
6820
|
+
.catch(() => { });
|
|
6821
|
+
}
|
|
6822
|
+
return response;
|
|
6763
6823
|
});
|
|
6764
|
-
}
|
|
6765
|
-
return krubbleHttp.Http.fetch({
|
|
6766
|
-
url: '/api/system/integration/aws/bedrock/stream/invokeModelForConversationWithState',
|
|
6767
|
-
method: 'POST',
|
|
6768
|
-
headers: { Accept: 'text/event-stream' },
|
|
6769
|
-
body: { userText: params.message },
|
|
6770
6824
|
});
|
|
6771
6825
|
};
|
|
6772
6826
|
}
|
|
6773
6827
|
this.send({
|
|
6774
6828
|
message: params.message,
|
|
6775
6829
|
messages: this.messages.slice(0, -1),
|
|
6776
|
-
|
|
6830
|
+
conversationFiles: params.conversationFiles,
|
|
6777
6831
|
}).then((response) => {
|
|
6778
6832
|
if (!response.ok) {
|
|
6779
6833
|
this._handleStreamError('Request failed: ' + response.status);
|
|
@@ -6800,35 +6854,6 @@
|
|
|
6800
6854
|
}
|
|
6801
6855
|
this._isStreaming = false;
|
|
6802
6856
|
}
|
|
6803
|
-
_handleClear() {
|
|
6804
|
-
// Default to clearing the Kodaris Bedrock conversation state when the consumer
|
|
6805
|
-
// hasn't provided their own `clear`.
|
|
6806
|
-
if (!this.clear) {
|
|
6807
|
-
this.clear = () => krubbleHttp.Http.fetch({
|
|
6808
|
-
url: '/api/system/integration/aws/bedrock/clearConversationWithState',
|
|
6809
|
-
method: 'POST',
|
|
6810
|
-
});
|
|
6811
|
-
}
|
|
6812
|
-
this.clear().then(() => {
|
|
6813
|
-
if (this._reader) {
|
|
6814
|
-
this._reader.cancel();
|
|
6815
|
-
this._reader = null;
|
|
6816
|
-
}
|
|
6817
|
-
this.messages = [];
|
|
6818
|
-
this._isStreaming = false;
|
|
6819
|
-
this._error = '';
|
|
6820
|
-
this._inputValue = '';
|
|
6821
|
-
this._userHasScrolledUp = false;
|
|
6822
|
-
// Reset conversation-scoped context + telemetry state for the new convo.
|
|
6823
|
-
this._contextWarning = '';
|
|
6824
|
-
this._contextLimitReached = false;
|
|
6825
|
-
this._contextLimitMessage = '';
|
|
6826
|
-
this._telemetry = null;
|
|
6827
|
-
this._iterationLog = [];
|
|
6828
|
-
this._cacheCheckpoints = {};
|
|
6829
|
-
this._saveState();
|
|
6830
|
-
});
|
|
6831
|
-
}
|
|
6832
6857
|
_handleMessagesScroll(e) {
|
|
6833
6858
|
const container = e.target;
|
|
6834
6859
|
const distanceFromBottom = container.scrollHeight - container.scrollTop - container.clientHeight;
|
|
@@ -7438,28 +7463,6 @@
|
|
|
7438
7463
|
</svg>
|
|
7439
7464
|
</button>
|
|
7440
7465
|
` : A}
|
|
7441
|
-
${this.messages.length > 0 ? b `
|
|
7442
|
-
<button
|
|
7443
|
-
class="header-action"
|
|
7444
|
-
@click=${this._handleClear}
|
|
7445
|
-
title="Clear conversation"
|
|
7446
|
-
>
|
|
7447
|
-
<svg
|
|
7448
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
7449
|
-
fill="none"
|
|
7450
|
-
viewBox="0 0 24 24"
|
|
7451
|
-
stroke-width="1.5"
|
|
7452
|
-
stroke="currentColor"
|
|
7453
|
-
class="header-action-icon"
|
|
7454
|
-
>
|
|
7455
|
-
<path
|
|
7456
|
-
stroke-linecap="round"
|
|
7457
|
-
stroke-linejoin="round"
|
|
7458
|
-
d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
|
|
7459
|
-
/>
|
|
7460
|
-
</svg>
|
|
7461
|
-
</button>
|
|
7462
|
-
` : A}
|
|
7463
7466
|
${this.variant === 'inline' ? A : b `
|
|
7464
7467
|
<button
|
|
7465
7468
|
class="header-action"
|
|
@@ -7504,9 +7507,9 @@
|
|
|
7504
7507
|
<div class="reasoning-content">${msg.reasoning}</div>
|
|
7505
7508
|
</details>
|
|
7506
7509
|
` : A}
|
|
7507
|
-
${msg.
|
|
7510
|
+
${msg.conversationFiles && msg.conversationFiles.length ? b `
|
|
7508
7511
|
<div class="attachments">
|
|
7509
|
-
${msg.
|
|
7512
|
+
${msg.conversationFiles.map((img) => b `
|
|
7510
7513
|
<button
|
|
7511
7514
|
class="attachment"
|
|
7512
7515
|
type="button"
|
|
@@ -7514,7 +7517,7 @@
|
|
|
7514
7517
|
@click=${() => this._openImage(img)}
|
|
7515
7518
|
>
|
|
7516
7519
|
<img
|
|
7517
|
-
src="/api/system/
|
|
7520
|
+
src="/api/system/conversationFile/${img.conversationFileID}/view"
|
|
7518
7521
|
alt=${img.niceName || ''}
|
|
7519
7522
|
/>
|
|
7520
7523
|
</button>
|
|
@@ -7632,9 +7635,9 @@
|
|
|
7632
7635
|
` : A}
|
|
7633
7636
|
|
|
7634
7637
|
<div class="footer">
|
|
7635
|
-
${this.
|
|
7638
|
+
${this._pendingConversationFiles.length ? b `
|
|
7636
7639
|
<div class="composer-attachments">
|
|
7637
|
-
${c(this.
|
|
7640
|
+
${c(this._pendingConversationFiles, (p) => p.localId, (p) => b `
|
|
7638
7641
|
<div class="chip">
|
|
7639
7642
|
<img src=${p.previewUrl} alt=${p.name} />
|
|
7640
7643
|
<button
|
|
@@ -8924,9 +8927,6 @@
|
|
|
8924
8927
|
__decorate([
|
|
8925
8928
|
n$1({ attribute: false })
|
|
8926
8929
|
], exports.Chatbot.prototype, "send", void 0);
|
|
8927
|
-
__decorate([
|
|
8928
|
-
n$1({ attribute: false })
|
|
8929
|
-
], exports.Chatbot.prototype, "clear", void 0);
|
|
8930
8930
|
__decorate([
|
|
8931
8931
|
n$1({ attribute: false })
|
|
8932
8932
|
], exports.Chatbot.prototype, "load", void 0);
|
|
@@ -8958,6 +8958,13 @@
|
|
|
8958
8958
|
reflect: true
|
|
8959
8959
|
})
|
|
8960
8960
|
], exports.Chatbot.prototype, "variant", void 0);
|
|
8961
|
+
__decorate([
|
|
8962
|
+
n$1({
|
|
8963
|
+
type: Number,
|
|
8964
|
+
attribute: 'conversation-id',
|
|
8965
|
+
reflect: true
|
|
8966
|
+
})
|
|
8967
|
+
], exports.Chatbot.prototype, "conversationId", void 0);
|
|
8961
8968
|
__decorate([
|
|
8962
8969
|
r()
|
|
8963
8970
|
], exports.Chatbot.prototype, "_inputValue", void 0);
|
|
@@ -8996,7 +9003,7 @@
|
|
|
8996
9003
|
], exports.Chatbot.prototype, "_isDebugResizing", void 0);
|
|
8997
9004
|
__decorate([
|
|
8998
9005
|
r()
|
|
8999
|
-
], exports.Chatbot.prototype, "
|
|
9006
|
+
], exports.Chatbot.prototype, "_pendingConversationFiles", void 0);
|
|
9000
9007
|
__decorate([
|
|
9001
9008
|
r()
|
|
9002
9009
|
], exports.Chatbot.prototype, "_isDraggingFile", void 0);
|