@kodaris/krubble-app-components 1.0.71 → 1.0.72
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 +65 -0
- package/dist/chatbot.d.ts.map +1 -1
- package/dist/chatbot.js +583 -4
- package/dist/chatbot.js.map +1 -1
- package/dist/krubble-app.bundled.js +581 -4
- package/dist/krubble-app.bundled.js.map +1 -1
- package/dist/krubble-app.bundled.min.js +396 -56
- package/dist/krubble-app.bundled.min.js.map +1 -1
- package/dist/krubble-app.umd.js +581 -4
- package/dist/krubble-app.umd.js.map +1 -1
- package/dist/krubble-app.umd.min.js +431 -91
- package/dist/krubble-app.umd.min.js.map +1 -1
- package/package.json +1 -1
|
@@ -5644,6 +5644,12 @@ let KRChatbot = class KRChatbot extends i$2 {
|
|
|
5644
5644
|
* Input placeholder text.
|
|
5645
5645
|
*/
|
|
5646
5646
|
this.placeholder = 'Type a message...';
|
|
5647
|
+
/**
|
|
5648
|
+
* Skills the user can invoke by typing `/` in the composer. Selecting one
|
|
5649
|
+
* inserts `/name ` into the message. The `/` menu shows the first 15 (filtered
|
|
5650
|
+
* as the user types); "Browse all skills" opens a dialog with the full list.
|
|
5651
|
+
*/
|
|
5652
|
+
this.skills = [];
|
|
5647
5653
|
/**
|
|
5648
5654
|
* Whether the chat panel is open. Only relevant in floating mode.
|
|
5649
5655
|
*/
|
|
@@ -5673,6 +5679,20 @@ let KRChatbot = class KRChatbot extends i$2 {
|
|
|
5673
5679
|
this._pendingInternalFiles = [];
|
|
5674
5680
|
/** True while a file is being dragged over the panel (shows the drop overlay). */
|
|
5675
5681
|
this._isDraggingFile = false;
|
|
5682
|
+
/** Whether the `/` skill menu is open above the composer. */
|
|
5683
|
+
this._skillMenuOpened = false;
|
|
5684
|
+
/** Current `/` query (text after the slash), used to filter the skill menu. */
|
|
5685
|
+
this._skillQuery = '';
|
|
5686
|
+
/** Highlighted item in the skill menu for keyboard navigation (-1 = none). */
|
|
5687
|
+
this._skillHighlighted = 0;
|
|
5688
|
+
/** Index of the message whose copy button just flashed "Copied" (-1 = none). */
|
|
5689
|
+
this._copiedIndex = -1;
|
|
5690
|
+
/** Whether the "Browse all skills" dialog is open. */
|
|
5691
|
+
this._skillBrowseOpened = false;
|
|
5692
|
+
/** Active tab in the browse dialog. */
|
|
5693
|
+
this._skillBrowseSource = 'org';
|
|
5694
|
+
/** Search text in the browse dialog. */
|
|
5695
|
+
this._skillBrowseQuery = '';
|
|
5676
5696
|
this._handleMouseMove = (e) => {
|
|
5677
5697
|
if (this._isDragging) {
|
|
5678
5698
|
let newLeft = this._dragStartLeft + (e.clientX - this._dragStartX);
|
|
@@ -5786,6 +5806,40 @@ let KRChatbot = class KRChatbot extends i$2 {
|
|
|
5786
5806
|
}
|
|
5787
5807
|
}, 200);
|
|
5788
5808
|
}
|
|
5809
|
+
// Position the fixed skill menu above the composer, following the krubble
|
|
5810
|
+
// select-field pattern (fixed element positioned from the trigger's rect).
|
|
5811
|
+
if (this._skillMenuOpened) {
|
|
5812
|
+
this._positionSkillMenu();
|
|
5813
|
+
}
|
|
5814
|
+
// Keep the keyboard-highlighted skill scrolled into view.
|
|
5815
|
+
if (changed.has('_skillHighlighted') && this._skillMenuOpened) {
|
|
5816
|
+
this._skillMenuEl
|
|
5817
|
+
?.querySelector('.skill-menu__item--highlighted')
|
|
5818
|
+
?.scrollIntoView({ block: 'nearest' });
|
|
5819
|
+
}
|
|
5820
|
+
}
|
|
5821
|
+
/** Anchor the fixed skill menu to the composer, opening upward above it. */
|
|
5822
|
+
_positionSkillMenu() {
|
|
5823
|
+
requestAnimationFrame(() => {
|
|
5824
|
+
if (!this._skillMenuEl || !this._inputWrapperEl) {
|
|
5825
|
+
return;
|
|
5826
|
+
}
|
|
5827
|
+
const inputRect = this._inputWrapperEl.getBoundingClientRect();
|
|
5828
|
+
this._skillMenuEl.style.left = inputRect.left + 'px';
|
|
5829
|
+
this._skillMenuEl.style.width = inputRect.width + 'px';
|
|
5830
|
+
// Bound the menu by the chat panel's own top edge so it stays within the
|
|
5831
|
+
// widget. In floating mode that keeps it inside the card; in inline mode the
|
|
5832
|
+
// panel top sits just below the app header/breadcrumb, so it stays clear of
|
|
5833
|
+
// those too. An 8px gap keeps it off both edges.
|
|
5834
|
+
const topLimit = this._panelEl.getBoundingClientRect().top + 8;
|
|
5835
|
+
const spaceAbove = inputRect.top - 8 - topLimit;
|
|
5836
|
+
this._skillMenuEl.style.maxHeight = spaceAbove + 'px';
|
|
5837
|
+
// Measure after the cap is applied so the upward anchor uses the real
|
|
5838
|
+
// height, then clamp the top edge to the panel bound.
|
|
5839
|
+
const menuRect = this._skillMenuEl.getBoundingClientRect();
|
|
5840
|
+
const top = Math.max(topLimit, inputRect.top - menuRect.height - 8);
|
|
5841
|
+
this._skillMenuEl.style.top = top + 'px';
|
|
5842
|
+
});
|
|
5789
5843
|
}
|
|
5790
5844
|
// --- Public methods ---
|
|
5791
5845
|
/**
|
|
@@ -5979,13 +6033,129 @@ let KRChatbot = class KRChatbot extends i$2 {
|
|
|
5979
6033
|
const textarea = e.target;
|
|
5980
6034
|
textarea.style.height = 'auto';
|
|
5981
6035
|
textarea.style.height = Math.min(textarea.scrollHeight, 120) + 'px';
|
|
6036
|
+
// Open the skill menu while the message is a `/` command being typed (a
|
|
6037
|
+
// leading slash with no space yet). The text after the slash filters it.
|
|
6038
|
+
const slashMatch = this._inputValue.match(/^\/(\S*)$/);
|
|
6039
|
+
if (slashMatch && this.skills.length) {
|
|
6040
|
+
this._skillMenuOpened = true;
|
|
6041
|
+
this._skillQuery = slashMatch[1];
|
|
6042
|
+
this._skillHighlighted = 0;
|
|
6043
|
+
}
|
|
6044
|
+
else {
|
|
6045
|
+
this._skillMenuOpened = false;
|
|
6046
|
+
}
|
|
5982
6047
|
}
|
|
5983
6048
|
_handleKeyDown(e) {
|
|
6049
|
+
// When the skill menu is open, arrow keys / Enter / Escape drive it. The two
|
|
6050
|
+
// top actions (Attach Files, Browse All Skills) are index 0 and 1, then the
|
|
6051
|
+
// filtered skills follow — all navigable as one list.
|
|
6052
|
+
if (this._skillMenuOpened) {
|
|
6053
|
+
const count = 2 + this._filteredSkills().length;
|
|
6054
|
+
if (e.key === 'ArrowDown') {
|
|
6055
|
+
e.preventDefault();
|
|
6056
|
+
this._skillHighlighted = (this._skillHighlighted + 1) % count;
|
|
6057
|
+
return;
|
|
6058
|
+
}
|
|
6059
|
+
if (e.key === 'ArrowUp') {
|
|
6060
|
+
e.preventDefault();
|
|
6061
|
+
this._skillHighlighted = (this._skillHighlighted - 1 + count) % count;
|
|
6062
|
+
return;
|
|
6063
|
+
}
|
|
6064
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
6065
|
+
e.preventDefault();
|
|
6066
|
+
this._activateSkillMenuItem(this._skillHighlighted);
|
|
6067
|
+
return;
|
|
6068
|
+
}
|
|
6069
|
+
if (e.key === 'Escape') {
|
|
6070
|
+
e.preventDefault();
|
|
6071
|
+
this._skillMenuOpened = false;
|
|
6072
|
+
return;
|
|
6073
|
+
}
|
|
6074
|
+
}
|
|
5984
6075
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
5985
6076
|
e.preventDefault();
|
|
5986
6077
|
this._handleSubmit();
|
|
5987
6078
|
}
|
|
5988
6079
|
}
|
|
6080
|
+
/** Run the skill-menu item at the given flat index (0/1 = actions, then skills). */
|
|
6081
|
+
_activateSkillMenuItem(index) {
|
|
6082
|
+
if (index === 0) {
|
|
6083
|
+
this._openFilePicker();
|
|
6084
|
+
return;
|
|
6085
|
+
}
|
|
6086
|
+
if (index === 1) {
|
|
6087
|
+
this._handleSkillBrowseOpen();
|
|
6088
|
+
return;
|
|
6089
|
+
}
|
|
6090
|
+
const skill = this._filteredSkills()[index - 2];
|
|
6091
|
+
if (skill) {
|
|
6092
|
+
this._handleSkillSelect(skill);
|
|
6093
|
+
}
|
|
6094
|
+
}
|
|
6095
|
+
// --- Skills ---
|
|
6096
|
+
/** Skills matching the current `/` query, capped at the first 15. */
|
|
6097
|
+
_filteredSkills() {
|
|
6098
|
+
const query = this._skillQuery.toLowerCase();
|
|
6099
|
+
return this.skills
|
|
6100
|
+
.filter((skill) => skill.name.toLowerCase().includes(query) ||
|
|
6101
|
+
skill.label.toLowerCase().includes(query))
|
|
6102
|
+
.slice(0, 15);
|
|
6103
|
+
}
|
|
6104
|
+
/** The `+` button toggles the skill/attachment menu open (showing all skills). */
|
|
6105
|
+
_handlePlusClick() {
|
|
6106
|
+
if (this._skillMenuOpened) {
|
|
6107
|
+
this._skillMenuOpened = false;
|
|
6108
|
+
return;
|
|
6109
|
+
}
|
|
6110
|
+
this._skillQuery = '';
|
|
6111
|
+
this._skillHighlighted = 0;
|
|
6112
|
+
this._skillMenuOpened = true;
|
|
6113
|
+
}
|
|
6114
|
+
/** Insert `/name ` into the composer and focus it, ready for the message. */
|
|
6115
|
+
_handleSkillSelect(skill) {
|
|
6116
|
+
this._inputValue = `/${skill.name} `;
|
|
6117
|
+
this._skillMenuOpened = false;
|
|
6118
|
+
this._skillBrowseOpened = false;
|
|
6119
|
+
this._inputEl?.focus();
|
|
6120
|
+
}
|
|
6121
|
+
/** Copy a message's text to the clipboard and briefly flash "Copied". */
|
|
6122
|
+
_handleCopyMessage(content, index) {
|
|
6123
|
+
navigator.clipboard.writeText(content).then(() => {
|
|
6124
|
+
this._copiedIndex = index;
|
|
6125
|
+
setTimeout(() => {
|
|
6126
|
+
if (this._copiedIndex === index) {
|
|
6127
|
+
this._copiedIndex = -1;
|
|
6128
|
+
}
|
|
6129
|
+
}, 2000);
|
|
6130
|
+
}).catch(() => { });
|
|
6131
|
+
}
|
|
6132
|
+
_handleSkillBrowseOpen() {
|
|
6133
|
+
this._skillMenuOpened = false;
|
|
6134
|
+
this._skillBrowseQuery = '';
|
|
6135
|
+
this._skillBrowseOpened = true;
|
|
6136
|
+
}
|
|
6137
|
+
_handleSkillBrowseClose() {
|
|
6138
|
+
this._skillBrowseOpened = false;
|
|
6139
|
+
}
|
|
6140
|
+
_handleSkillBrowseSearch(e) {
|
|
6141
|
+
this._skillBrowseQuery = e.target.value;
|
|
6142
|
+
}
|
|
6143
|
+
_handleSkillBrowseTabChange(e) {
|
|
6144
|
+
this._skillBrowseSource = e.detail.activeTabId;
|
|
6145
|
+
}
|
|
6146
|
+
/** Skills for the browse dialog's active tab, filtered by its search box. */
|
|
6147
|
+
_browseSkills() {
|
|
6148
|
+
const query = this._skillBrowseQuery.toLowerCase();
|
|
6149
|
+
return this.skills.filter((skill) => {
|
|
6150
|
+
const source = skill.source || 'org';
|
|
6151
|
+
if (source !== this._skillBrowseSource) {
|
|
6152
|
+
return false;
|
|
6153
|
+
}
|
|
6154
|
+
return skill.name.toLowerCase().includes(query) ||
|
|
6155
|
+
skill.label.toLowerCase().includes(query) ||
|
|
6156
|
+
(skill.description || '').toLowerCase().includes(query);
|
|
6157
|
+
});
|
|
6158
|
+
}
|
|
5989
6159
|
// --- Attachments ---
|
|
5990
6160
|
_openFilePicker() {
|
|
5991
6161
|
this._fileInputEl?.click();
|
|
@@ -6343,6 +6513,119 @@ let KRChatbot = class KRChatbot extends i$2 {
|
|
|
6343
6513
|
}
|
|
6344
6514
|
}
|
|
6345
6515
|
// --- Render ---
|
|
6516
|
+
/** The `/` skill menu shown above the composer while typing a slash command. */
|
|
6517
|
+
_renderSkillMenu() {
|
|
6518
|
+
const skills = this._filteredSkills();
|
|
6519
|
+
return b `
|
|
6520
|
+
<div class="skill-menu">
|
|
6521
|
+
<div class="skill-menu__actions">
|
|
6522
|
+
<button
|
|
6523
|
+
class=${e({
|
|
6524
|
+
'skill-menu__action': true,
|
|
6525
|
+
'skill-menu__item--highlighted': this._skillHighlighted === 0,
|
|
6526
|
+
})}
|
|
6527
|
+
@click=${this._openFilePicker}
|
|
6528
|
+
>
|
|
6529
|
+
Attach Files
|
|
6530
|
+
</button>
|
|
6531
|
+
<button
|
|
6532
|
+
class=${e({
|
|
6533
|
+
'skill-menu__action': true,
|
|
6534
|
+
'skill-menu__item--highlighted': this._skillHighlighted === 1,
|
|
6535
|
+
})}
|
|
6536
|
+
@click=${this._handleSkillBrowseOpen}
|
|
6537
|
+
>
|
|
6538
|
+
Browse All Skills
|
|
6539
|
+
</button>
|
|
6540
|
+
</div>
|
|
6541
|
+
${skills.length ? b `
|
|
6542
|
+
<ul class="skill-menu__list">
|
|
6543
|
+
${skills.map((skill, index) => b `
|
|
6544
|
+
<li>
|
|
6545
|
+
<button
|
|
6546
|
+
class=${e({
|
|
6547
|
+
'skill-menu__item': true,
|
|
6548
|
+
'skill-menu__item--highlighted': index + 2 === this._skillHighlighted,
|
|
6549
|
+
})}
|
|
6550
|
+
@click=${() => this._handleSkillSelect(skill)}
|
|
6551
|
+
>
|
|
6552
|
+
<span class="skill-menu__name">${skill.label}</span>
|
|
6553
|
+
${skill.description ? b `
|
|
6554
|
+
<span class="skill-menu__desc">${skill.description}</span>
|
|
6555
|
+
` : A}
|
|
6556
|
+
</button>
|
|
6557
|
+
</li>
|
|
6558
|
+
`)}
|
|
6559
|
+
</ul>
|
|
6560
|
+
` : b `
|
|
6561
|
+
<div class="skill-menu__empty">No matching skills</div>
|
|
6562
|
+
`}
|
|
6563
|
+
</div>
|
|
6564
|
+
`;
|
|
6565
|
+
}
|
|
6566
|
+
/** The "Browse all skills" dialog — a searchable grid of Org / Kodaris cards. */
|
|
6567
|
+
_renderSkillBrowse() {
|
|
6568
|
+
return b `
|
|
6569
|
+
<kr-dialog
|
|
6570
|
+
opened
|
|
6571
|
+
label="Browse Skills"
|
|
6572
|
+
width="720px"
|
|
6573
|
+
@close=${this._handleSkillBrowseClose}
|
|
6574
|
+
>
|
|
6575
|
+
<div class="skill-browse">
|
|
6576
|
+
<kr-text-field
|
|
6577
|
+
placeholder="Search skills..."
|
|
6578
|
+
.value=${this._skillBrowseQuery}
|
|
6579
|
+
@input=${this._handleSkillBrowseSearch}
|
|
6580
|
+
></kr-text-field>
|
|
6581
|
+
<kr-tab-group
|
|
6582
|
+
active-tab-id=${this._skillBrowseSource}
|
|
6583
|
+
@tab-change=${this._handleSkillBrowseTabChange}
|
|
6584
|
+
>
|
|
6585
|
+
<kr-tab id="org" label="Your Org">
|
|
6586
|
+
${this._renderSkillGrid()}
|
|
6587
|
+
</kr-tab>
|
|
6588
|
+
<kr-tab id="kodaris" label="Kodaris">
|
|
6589
|
+
${this._renderSkillGrid()}
|
|
6590
|
+
</kr-tab>
|
|
6591
|
+
</kr-tab-group>
|
|
6592
|
+
</div>
|
|
6593
|
+
</kr-dialog>
|
|
6594
|
+
`;
|
|
6595
|
+
}
|
|
6596
|
+
/** The card grid for the browse dialog's active tab. */
|
|
6597
|
+
_renderSkillGrid() {
|
|
6598
|
+
const skills = this._browseSkills();
|
|
6599
|
+
if (!skills.length) {
|
|
6600
|
+
return b `<div class="skill-browse__empty">No matching skills</div>`;
|
|
6601
|
+
}
|
|
6602
|
+
return b `
|
|
6603
|
+
<div class="skill-browse__grid">
|
|
6604
|
+
${skills.map((skill) => b `
|
|
6605
|
+
<button
|
|
6606
|
+
class="skill-card"
|
|
6607
|
+
@click=${() => this._handleSkillSelect(skill)}
|
|
6608
|
+
>
|
|
6609
|
+
<span class="skill-card__name">${skill.label}</span>
|
|
6610
|
+
${skill.description ? b `
|
|
6611
|
+
<span class="skill-card__desc">${skill.description}</span>
|
|
6612
|
+
` : A}
|
|
6613
|
+
</button>
|
|
6614
|
+
`)}
|
|
6615
|
+
</div>
|
|
6616
|
+
`;
|
|
6617
|
+
}
|
|
6618
|
+
/**
|
|
6619
|
+
* Render a user message, highlighting a leading `/skill-name` token when it
|
|
6620
|
+
* matches a known skill (like the way Claude highlights slash commands).
|
|
6621
|
+
*/
|
|
6622
|
+
_renderUserContent(content) {
|
|
6623
|
+
const match = content.match(/^\/(\S+)(\s[\s\S]*)?$/);
|
|
6624
|
+
if (match && this.skills.some((skill) => skill.name === match[1])) {
|
|
6625
|
+
return b `<span class="message-skill">/${match[1]}</span>${match[2] || ''}`;
|
|
6626
|
+
}
|
|
6627
|
+
return content;
|
|
6628
|
+
}
|
|
6346
6629
|
render() {
|
|
6347
6630
|
return b `
|
|
6348
6631
|
${(this.hideToggle || this.variant === 'inline') ? A : b `
|
|
@@ -6499,7 +6782,7 @@ let KRChatbot = class KRChatbot extends i$2 {
|
|
|
6499
6782
|
<span class="empty-text">What can I help you with?</span>
|
|
6500
6783
|
</div>
|
|
6501
6784
|
` : A}
|
|
6502
|
-
${this.messages.map((msg) => b `
|
|
6785
|
+
${this.messages.map((msg, index) => b `
|
|
6503
6786
|
<div class=${e({
|
|
6504
6787
|
'message': true,
|
|
6505
6788
|
'message--user': msg.type === 'user',
|
|
@@ -6531,7 +6814,7 @@ let KRChatbot = class KRChatbot extends i$2 {
|
|
|
6531
6814
|
${msg.content ? b `
|
|
6532
6815
|
<div class="message-content">
|
|
6533
6816
|
${msg.type === 'user'
|
|
6534
|
-
? msg.content
|
|
6817
|
+
? this._renderUserContent(msg.content)
|
|
6535
6818
|
: o(marked.parse(msg.content, { breaks: true }))}
|
|
6536
6819
|
</div>
|
|
6537
6820
|
` : A}
|
|
@@ -6542,6 +6825,46 @@ let KRChatbot = class KRChatbot extends i$2 {
|
|
|
6542
6825
|
<span></span>
|
|
6543
6826
|
</div>
|
|
6544
6827
|
` : A}
|
|
6828
|
+
${msg.content ? b `
|
|
6829
|
+
<button
|
|
6830
|
+
class="message-copy"
|
|
6831
|
+
title="Copy"
|
|
6832
|
+
@click=${() => this._handleCopyMessage(msg.content, index)}
|
|
6833
|
+
>
|
|
6834
|
+
${this._copiedIndex === index ? b `
|
|
6835
|
+
<svg
|
|
6836
|
+
viewBox="0 0 24 24"
|
|
6837
|
+
fill="none"
|
|
6838
|
+
stroke="currentColor"
|
|
6839
|
+
stroke-width="1.5"
|
|
6840
|
+
stroke-linecap="round"
|
|
6841
|
+
stroke-linejoin="round"
|
|
6842
|
+
>
|
|
6843
|
+
<path d="M20 6 9 17l-5-5" />
|
|
6844
|
+
</svg>
|
|
6845
|
+
Copied
|
|
6846
|
+
` : b `
|
|
6847
|
+
<svg
|
|
6848
|
+
viewBox="0 0 24 24"
|
|
6849
|
+
fill="none"
|
|
6850
|
+
stroke="currentColor"
|
|
6851
|
+
stroke-width="1.5"
|
|
6852
|
+
stroke-linecap="round"
|
|
6853
|
+
stroke-linejoin="round"
|
|
6854
|
+
>
|
|
6855
|
+
<rect
|
|
6856
|
+
x="9"
|
|
6857
|
+
y="9"
|
|
6858
|
+
width="13"
|
|
6859
|
+
height="13"
|
|
6860
|
+
rx="2"
|
|
6861
|
+
/>
|
|
6862
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
|
|
6863
|
+
</svg>
|
|
6864
|
+
Copy
|
|
6865
|
+
`}
|
|
6866
|
+
</button>
|
|
6867
|
+
` : A}
|
|
6545
6868
|
</div>
|
|
6546
6869
|
`)}
|
|
6547
6870
|
</div>
|
|
@@ -6606,11 +6929,11 @@ let KRChatbot = class KRChatbot extends i$2 {
|
|
|
6606
6929
|
hidden
|
|
6607
6930
|
@change=${this._handleFileInputChange}
|
|
6608
6931
|
/>
|
|
6932
|
+
${this._skillMenuOpened ? this._renderSkillMenu() : A}
|
|
6609
6933
|
<div class="input-wrapper">
|
|
6610
6934
|
<button
|
|
6611
6935
|
class="input-plus"
|
|
6612
|
-
|
|
6613
|
-
@click=${this._openFilePicker}
|
|
6936
|
+
@click=${this._handlePlusClick}
|
|
6614
6937
|
>+</button>
|
|
6615
6938
|
<textarea
|
|
6616
6939
|
class="input"
|
|
@@ -6667,6 +6990,7 @@ let KRChatbot = class KRChatbot extends i$2 {
|
|
|
6667
6990
|
`}
|
|
6668
6991
|
</div>
|
|
6669
6992
|
</div>
|
|
6993
|
+
${this._skillBrowseOpened ? this._renderSkillBrowse() : A}
|
|
6670
6994
|
</div>
|
|
6671
6995
|
`;
|
|
6672
6996
|
}
|
|
@@ -6906,6 +7230,7 @@ KRChatbot.styles = i$5 `
|
|
|
6906
7230
|
}
|
|
6907
7231
|
|
|
6908
7232
|
.message {
|
|
7233
|
+
position: relative;
|
|
6909
7234
|
display: flex;
|
|
6910
7235
|
flex-direction: column;
|
|
6911
7236
|
max-width: 100%;
|
|
@@ -6950,6 +7275,58 @@ KRChatbot.styles = i$5 `
|
|
|
6950
7275
|
font-size: 14px;
|
|
6951
7276
|
}
|
|
6952
7277
|
|
|
7278
|
+
/* Highlighted leading /skill token in a sent user message. Monospace + a
|
|
7279
|
+
stronger color makes it read as a distinct command without a pill. */
|
|
7280
|
+
.message-skill {
|
|
7281
|
+
font-family: 'SF Mono', SFMono-Regular, ui-monospace, Menlo, Consolas, monospace;
|
|
7282
|
+
font-weight: 600;
|
|
7283
|
+
color: #b45309;
|
|
7284
|
+
}
|
|
7285
|
+
|
|
7286
|
+
/* Per-message copy button. Kept in flow with a fixed height that is always
|
|
7287
|
+
reserved (via a negative bottom margin that cancels it) so message spacing
|
|
7288
|
+
is identical whether or not it's shown; only its opacity toggles on hover. */
|
|
7289
|
+
.message-copy {
|
|
7290
|
+
align-self: flex-start;
|
|
7291
|
+
margin-top: 4px;
|
|
7292
|
+
margin-bottom: -27px;
|
|
7293
|
+
display: inline-flex;
|
|
7294
|
+
align-items: center;
|
|
7295
|
+
gap: 4px;
|
|
7296
|
+
height: 23px;
|
|
7297
|
+
padding: 4px 8px;
|
|
7298
|
+
background: none;
|
|
7299
|
+
border: none;
|
|
7300
|
+
border-radius: 6px;
|
|
7301
|
+
font: inherit;
|
|
7302
|
+
font-size: 12px;
|
|
7303
|
+
color: #64668b;
|
|
7304
|
+
cursor: pointer;
|
|
7305
|
+
opacity: 0;
|
|
7306
|
+
pointer-events: none;
|
|
7307
|
+
transition: opacity 0.15s, background-color 0.15s, color 0.15s;
|
|
7308
|
+
}
|
|
7309
|
+
|
|
7310
|
+
.message--user .message-copy {
|
|
7311
|
+
align-self: flex-end;
|
|
7312
|
+
}
|
|
7313
|
+
|
|
7314
|
+
.message:hover .message-copy,
|
|
7315
|
+
.message-copy:focus-visible {
|
|
7316
|
+
opacity: 1;
|
|
7317
|
+
pointer-events: auto;
|
|
7318
|
+
}
|
|
7319
|
+
|
|
7320
|
+
.message-copy:hover {
|
|
7321
|
+
background: rgba(0, 0, 0, 0.06);
|
|
7322
|
+
color: #1a1a2e;
|
|
7323
|
+
}
|
|
7324
|
+
|
|
7325
|
+
.message-copy svg {
|
|
7326
|
+
width: 14px;
|
|
7327
|
+
height: 14px;
|
|
7328
|
+
}
|
|
7329
|
+
|
|
6953
7330
|
.message--assistant .message-content {
|
|
6954
7331
|
background: transparent;
|
|
6955
7332
|
color: #000000;
|
|
@@ -7285,6 +7662,176 @@ KRChatbot.styles = i$5 `
|
|
|
7285
7662
|
display: block;
|
|
7286
7663
|
}
|
|
7287
7664
|
|
|
7665
|
+
/* Skill menu (opened by typing '/'). Fixed + JS-positioned above the
|
|
7666
|
+
composer, following the krubble select-field dropdown pattern. */
|
|
7667
|
+
.skill-menu {
|
|
7668
|
+
position: fixed;
|
|
7669
|
+
z-index: 10000;
|
|
7670
|
+
background: white;
|
|
7671
|
+
border: 1px solid #9ba7b6;
|
|
7672
|
+
border-radius: 8px;
|
|
7673
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
|
7674
|
+
overflow: hidden;
|
|
7675
|
+
display: flex;
|
|
7676
|
+
flex-direction: column;
|
|
7677
|
+
/* max-height is set dynamically to the space above the composer. */
|
|
7678
|
+
}
|
|
7679
|
+
|
|
7680
|
+
/* Floating mode is a compact card — use a lighter shadow and softer border. */
|
|
7681
|
+
:host(:not([variant='inline'])) .skill-menu {
|
|
7682
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
7683
|
+
border-color: #b0b0b0;
|
|
7684
|
+
}
|
|
7685
|
+
|
|
7686
|
+
.skill-menu__list {
|
|
7687
|
+
list-style: none;
|
|
7688
|
+
margin: 0;
|
|
7689
|
+
padding: 8px 0;
|
|
7690
|
+
overflow-y: auto;
|
|
7691
|
+
}
|
|
7692
|
+
|
|
7693
|
+
.skill-menu__item {
|
|
7694
|
+
display: flex;
|
|
7695
|
+
flex-direction: column;
|
|
7696
|
+
gap: 2px;
|
|
7697
|
+
width: 100%;
|
|
7698
|
+
padding: 8px 16px;
|
|
7699
|
+
background: none;
|
|
7700
|
+
border: none;
|
|
7701
|
+
text-align: left;
|
|
7702
|
+
cursor: pointer;
|
|
7703
|
+
font: inherit;
|
|
7704
|
+
color: #000000;
|
|
7705
|
+
}
|
|
7706
|
+
|
|
7707
|
+
|
|
7708
|
+
/* Floating mode is a compact card — tighten name/description spacing. */
|
|
7709
|
+
:host(:not([variant='inline'])) .skill-menu__item {
|
|
7710
|
+
gap: 1px;
|
|
7711
|
+
}
|
|
7712
|
+
|
|
7713
|
+
.skill-menu__name {
|
|
7714
|
+
font-weight: 500;
|
|
7715
|
+
}
|
|
7716
|
+
|
|
7717
|
+
/* Floating mode is a compact card — shrink the skill name a touch. */
|
|
7718
|
+
:host(:not([variant='inline'])) .skill-menu__name {
|
|
7719
|
+
font-size: 13px;
|
|
7720
|
+
}
|
|
7721
|
+
|
|
7722
|
+
.skill-menu__desc {
|
|
7723
|
+
font-size: 12px;
|
|
7724
|
+
color: #000000;
|
|
7725
|
+
overflow: hidden;
|
|
7726
|
+
text-overflow: ellipsis;
|
|
7727
|
+
white-space: nowrap;
|
|
7728
|
+
}
|
|
7729
|
+
|
|
7730
|
+
.skill-menu__empty {
|
|
7731
|
+
padding: 16px;
|
|
7732
|
+
text-align: center;
|
|
7733
|
+
color: #000000;
|
|
7734
|
+
}
|
|
7735
|
+
|
|
7736
|
+
.skill-menu__actions {
|
|
7737
|
+
display: flex;
|
|
7738
|
+
flex-direction: column;
|
|
7739
|
+
padding: 8px 0;
|
|
7740
|
+
border-bottom: 1px solid #eee;
|
|
7741
|
+
flex-shrink: 0;
|
|
7742
|
+
}
|
|
7743
|
+
|
|
7744
|
+
.skill-menu__action {
|
|
7745
|
+
width: 100%;
|
|
7746
|
+
padding: 6px 16px;
|
|
7747
|
+
background: transparent;
|
|
7748
|
+
border: none;
|
|
7749
|
+
text-align: left;
|
|
7750
|
+
cursor: pointer;
|
|
7751
|
+
font: inherit;
|
|
7752
|
+
font-size: 13px;
|
|
7753
|
+
color: var(--kr-chatbot-primary);
|
|
7754
|
+
font-weight: 500;
|
|
7755
|
+
}
|
|
7756
|
+
|
|
7757
|
+
/* Highlight (keyboard) and hover (mouse) are independent — either shades the
|
|
7758
|
+
row. Declared after the base rules so it wins the cascade for both the
|
|
7759
|
+
action buttons and the skill items. */
|
|
7760
|
+
.skill-menu__action.skill-menu__item--highlighted,
|
|
7761
|
+
.skill-menu__item.skill-menu__item--highlighted,
|
|
7762
|
+
.skill-menu__action:hover,
|
|
7763
|
+
.skill-menu__item:hover {
|
|
7764
|
+
background: #f3f4f6;
|
|
7765
|
+
}
|
|
7766
|
+
|
|
7767
|
+
/* Browse-all dialog body. Owns the height so the dialog is the same size on
|
|
7768
|
+
every tab; the search is fixed and the card grid flexes/scrolls to fill. */
|
|
7769
|
+
.skill-browse {
|
|
7770
|
+
display: flex;
|
|
7771
|
+
flex-direction: column;
|
|
7772
|
+
height: 70vh;
|
|
7773
|
+
}
|
|
7774
|
+
|
|
7775
|
+
.skill-browse kr-text-field {
|
|
7776
|
+
display: block;
|
|
7777
|
+
margin: 20px 20px 16px;
|
|
7778
|
+
flex-shrink: 0;
|
|
7779
|
+
}
|
|
7780
|
+
|
|
7781
|
+
.skill-browse kr-tab-group {
|
|
7782
|
+
flex: 1;
|
|
7783
|
+
min-height: 0;
|
|
7784
|
+
}
|
|
7785
|
+
|
|
7786
|
+
/* The grid is the scroller and spans the full width, so its scrollbar sits
|
|
7787
|
+
flush against the dialog edge; padding-right insets the cards from it. */
|
|
7788
|
+
.skill-browse__grid {
|
|
7789
|
+
height: 100%;
|
|
7790
|
+
overflow-y: auto;
|
|
7791
|
+
display: grid;
|
|
7792
|
+
grid-template-columns: repeat(3, 1fr);
|
|
7793
|
+
grid-auto-rows: min-content;
|
|
7794
|
+
align-content: start;
|
|
7795
|
+
gap: 12px;
|
|
7796
|
+
padding: 16px 20px 20px;
|
|
7797
|
+
}
|
|
7798
|
+
|
|
7799
|
+
.skill-card {
|
|
7800
|
+
display: flex;
|
|
7801
|
+
flex-direction: column;
|
|
7802
|
+
gap: 6px;
|
|
7803
|
+
padding: 14px;
|
|
7804
|
+
background: white;
|
|
7805
|
+
border: 1px solid #e5e7eb;
|
|
7806
|
+
border-radius: 10px;
|
|
7807
|
+
text-align: left;
|
|
7808
|
+
cursor: pointer;
|
|
7809
|
+
font: inherit;
|
|
7810
|
+
color: #000000;
|
|
7811
|
+
transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
|
7812
|
+
}
|
|
7813
|
+
|
|
7814
|
+
.skill-card:hover {
|
|
7815
|
+
border-color: var(--kr-chatbot-primary);
|
|
7816
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
7817
|
+
}
|
|
7818
|
+
|
|
7819
|
+
.skill-card__name {
|
|
7820
|
+
font-weight: 600;
|
|
7821
|
+
}
|
|
7822
|
+
|
|
7823
|
+
.skill-card__desc {
|
|
7824
|
+
font-size: 12px;
|
|
7825
|
+
color: #000000;
|
|
7826
|
+
}
|
|
7827
|
+
|
|
7828
|
+
.skill-browse__empty {
|
|
7829
|
+
height: 100%;
|
|
7830
|
+
padding: 32px;
|
|
7831
|
+
text-align: center;
|
|
7832
|
+
color: #000000;
|
|
7833
|
+
}
|
|
7834
|
+
|
|
7288
7835
|
/* Composer attachment chips (pre-send) */
|
|
7289
7836
|
.composer-attachments {
|
|
7290
7837
|
display: flex;
|
|
@@ -7384,6 +7931,9 @@ __decorate([
|
|
|
7384
7931
|
__decorate([
|
|
7385
7932
|
n({ type: String })
|
|
7386
7933
|
], KRChatbot.prototype, "placeholder", void 0);
|
|
7934
|
+
__decorate([
|
|
7935
|
+
n({ attribute: false })
|
|
7936
|
+
], KRChatbot.prototype, "skills", void 0);
|
|
7387
7937
|
__decorate([
|
|
7388
7938
|
n({
|
|
7389
7939
|
type: Boolean,
|
|
@@ -7418,6 +7968,27 @@ __decorate([
|
|
|
7418
7968
|
__decorate([
|
|
7419
7969
|
r()
|
|
7420
7970
|
], KRChatbot.prototype, "_isDraggingFile", void 0);
|
|
7971
|
+
__decorate([
|
|
7972
|
+
r()
|
|
7973
|
+
], KRChatbot.prototype, "_skillMenuOpened", void 0);
|
|
7974
|
+
__decorate([
|
|
7975
|
+
r()
|
|
7976
|
+
], KRChatbot.prototype, "_skillQuery", void 0);
|
|
7977
|
+
__decorate([
|
|
7978
|
+
r()
|
|
7979
|
+
], KRChatbot.prototype, "_skillHighlighted", void 0);
|
|
7980
|
+
__decorate([
|
|
7981
|
+
r()
|
|
7982
|
+
], KRChatbot.prototype, "_copiedIndex", void 0);
|
|
7983
|
+
__decorate([
|
|
7984
|
+
r()
|
|
7985
|
+
], KRChatbot.prototype, "_skillBrowseOpened", void 0);
|
|
7986
|
+
__decorate([
|
|
7987
|
+
r()
|
|
7988
|
+
], KRChatbot.prototype, "_skillBrowseSource", void 0);
|
|
7989
|
+
__decorate([
|
|
7990
|
+
r()
|
|
7991
|
+
], KRChatbot.prototype, "_skillBrowseQuery", void 0);
|
|
7421
7992
|
__decorate([
|
|
7422
7993
|
e$3('.panel')
|
|
7423
7994
|
], KRChatbot.prototype, "_panelEl", void 0);
|
|
@@ -7430,6 +8001,12 @@ __decorate([
|
|
|
7430
8001
|
__decorate([
|
|
7431
8002
|
e$3('.file-input')
|
|
7432
8003
|
], KRChatbot.prototype, "_fileInputEl", void 0);
|
|
8004
|
+
__decorate([
|
|
8005
|
+
e$3('.input-wrapper')
|
|
8006
|
+
], KRChatbot.prototype, "_inputWrapperEl", void 0);
|
|
8007
|
+
__decorate([
|
|
8008
|
+
e$3('.skill-menu')
|
|
8009
|
+
], KRChatbot.prototype, "_skillMenuEl", void 0);
|
|
7433
8010
|
KRChatbot = __decorate([
|
|
7434
8011
|
t$2('kr-chatbot')
|
|
7435
8012
|
], KRChatbot);
|