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