@kodaris/krubble-app-components 1.0.79 → 1.0.81
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 +42 -19
- package/dist/chatbot.d.ts.map +1 -1
- package/dist/chatbot.js +240 -131
- package/dist/chatbot.js.map +1 -1
- package/dist/krubble-app.bundled.js +240 -131
- package/dist/krubble-app.bundled.js.map +1 -1
- package/dist/krubble-app.bundled.min.js +89 -102
- package/dist/krubble-app.bundled.min.js.map +1 -1
- package/dist/krubble-app.umd.js +240 -131
- package/dist/krubble-app.umd.js.map +1 -1
- package/dist/krubble-app.umd.min.js +80 -93
- package/dist/krubble-app.umd.min.js.map +1 -1
- package/package.json +1 -1
package/dist/chatbot.js
CHANGED
|
@@ -85,8 +85,9 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
85
85
|
* responses are dropped (mirrors the kr-combo-box `_requestId` pattern). Org and
|
|
86
86
|
* global searches run concurrently into separate buffers, so each has its own.
|
|
87
87
|
*/
|
|
88
|
-
this.
|
|
88
|
+
this._orgSkillRequestId = 0;
|
|
89
89
|
this._globalSkillRequestId = 0;
|
|
90
|
+
this._invocableSkillRequestId = 0;
|
|
90
91
|
/**
|
|
91
92
|
* Codes of global skills with an in-flight enable/disable request. The `+`
|
|
92
93
|
* button reads this to show a pending state and ignore repeat clicks. Mutated
|
|
@@ -195,24 +196,31 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
195
196
|
this._isDraggingFile = false;
|
|
196
197
|
/** Whether the `/` skill menu is open above the composer. */
|
|
197
198
|
this._skillMenuOpened = false;
|
|
198
|
-
/** Current `/` query (text after the slash), sent to `
|
|
199
|
+
/** Current `/` query (text after the slash), sent to `_searchOrgSkills`. */
|
|
199
200
|
this._skillQuery = '';
|
|
200
201
|
/**
|
|
201
|
-
* Latest org skill search results (this instance's own skills). Backs the
|
|
202
|
-
*
|
|
202
|
+
* Latest org skill search results (this instance's own skills). Backs the
|
|
203
|
+
* browse dialog's "Your Org" tab only — global skills never appear here.
|
|
203
204
|
*/
|
|
204
|
-
this.
|
|
205
|
+
this._orgSkills = [];
|
|
205
206
|
/**
|
|
206
207
|
* Latest shared-Kodaris (global) skill results, for the browse dialog's
|
|
207
|
-
* "Kodaris" tab. Kept separate from `
|
|
208
|
+
* "Kodaris" tab. Kept separate from `_orgSkills` so switching tabs shows
|
|
208
209
|
* fresh results immediately instead of the other tab's stale list — the browse
|
|
209
210
|
* search fills both buffers at once.
|
|
210
211
|
*/
|
|
211
|
-
this.
|
|
212
|
+
this._globalSkills = [];
|
|
213
|
+
/**
|
|
214
|
+
* Latest results for the `/` composer menu: this instance's own org skills PLUS
|
|
215
|
+
* the global skills installed on it (both are invocable by code). A separate
|
|
216
|
+
* buffer from the two browse-tab lists so merging installed globals into the
|
|
217
|
+
* menu never leaks them into the "Your Org" tab.
|
|
218
|
+
*/
|
|
219
|
+
this._invocableSkills = [];
|
|
212
220
|
/**
|
|
213
221
|
* AI framework UI config flags fetched from `aiFrameworkConfig` on connect.
|
|
214
222
|
* `debugTelemetryEnabled` (same server-side gate as TOKEN_USAGE emission) turns
|
|
215
|
-
* on the debug drawer toggle and the `/test-warning` / `/test-limit` composer
|
|
223
|
+
* on the debug drawer toggle and the `/test-context-warning` / `/test-context-limit` composer
|
|
216
224
|
* options — known up front, before any message.
|
|
217
225
|
*/
|
|
218
226
|
this._aiFrameworkConfig = {};
|
|
@@ -223,8 +231,8 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
223
231
|
/** Whether the "Browse all skills" dialog is open. */
|
|
224
232
|
this._skillBrowseOpened = false;
|
|
225
233
|
/**
|
|
226
|
-
* Active tab in the browse dialog. `org` shows `
|
|
227
|
-
* skills); `kodaris` shows `
|
|
234
|
+
* Active tab in the browse dialog. `org` shows `_orgSkills` (this instance's
|
|
235
|
+
* skills); `kodaris` shows `_globalSkills` (the shared global catalog).
|
|
228
236
|
*/
|
|
229
237
|
this._skillBrowseSource = 'org';
|
|
230
238
|
/** Search text in the browse dialog, routed to the active tab's search. */
|
|
@@ -617,13 +625,14 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
617
625
|
textarea.style.height = Math.min(textarea.scrollHeight, 120) + 'px';
|
|
618
626
|
// Open the skill menu while the message is a `/` command being typed (a
|
|
619
627
|
// leading slash with no space yet). The text after the slash is the search
|
|
620
|
-
// query, run server-side via `
|
|
628
|
+
// query, run server-side via `_searchInvocableSkills`.
|
|
629
|
+
// (org skills + installed globals — everything invocable by /code)
|
|
621
630
|
const slashMatch = this._inputValue.match(/^\/(\S*)$/);
|
|
622
631
|
if (slashMatch) {
|
|
623
632
|
this._skillMenuOpened = true;
|
|
624
633
|
this._skillQuery = slashMatch[1];
|
|
625
634
|
this._skillHighlighted = 0;
|
|
626
|
-
this.
|
|
635
|
+
this._searchInvocableSkills(this._skillQuery);
|
|
627
636
|
}
|
|
628
637
|
else {
|
|
629
638
|
this._skillMenuOpened = false;
|
|
@@ -639,7 +648,7 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
639
648
|
if (debugActions) {
|
|
640
649
|
actionCount = 4;
|
|
641
650
|
}
|
|
642
|
-
const count = actionCount + this.
|
|
651
|
+
const count = actionCount + this._invocableSkills.length;
|
|
643
652
|
if (e.key === 'ArrowDown') {
|
|
644
653
|
e.preventDefault();
|
|
645
654
|
this._skillHighlighted = (this._skillHighlighted + 1) % count;
|
|
@@ -684,7 +693,7 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
684
693
|
if (debugActions && index === 2) {
|
|
685
694
|
this._skillMenuOpened = false;
|
|
686
695
|
this._send({
|
|
687
|
-
message: '/test-warning',
|
|
696
|
+
message: '/test-context-warning',
|
|
688
697
|
internalFiles: [],
|
|
689
698
|
});
|
|
690
699
|
return;
|
|
@@ -692,19 +701,19 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
692
701
|
if (debugActions && index === 3) {
|
|
693
702
|
this._skillMenuOpened = false;
|
|
694
703
|
this._send({
|
|
695
|
-
message: '/test-limit',
|
|
704
|
+
message: '/test-context-limit',
|
|
696
705
|
internalFiles: [],
|
|
697
706
|
});
|
|
698
707
|
return;
|
|
699
708
|
}
|
|
700
|
-
const skill = this.
|
|
709
|
+
const skill = this._invocableSkills[index - actionCount];
|
|
701
710
|
if (skill) {
|
|
702
711
|
this._handleSkillSelect(skill);
|
|
703
712
|
}
|
|
704
713
|
}
|
|
705
714
|
// --- Skills ---
|
|
706
715
|
/**
|
|
707
|
-
* Search the Kodaris AI skills index and store the results in `
|
|
716
|
+
* Search the Kodaris AI skills index and store the results in `_orgSkills`.
|
|
708
717
|
* The `/` menu and the browse dialog both call this; results are shared since
|
|
709
718
|
* the two are never open at once. Mirrors the backend's own skill search (BM25
|
|
710
719
|
* over name^3/description^2/instructions, active-only) so the menu ranks skills
|
|
@@ -715,9 +724,9 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
715
724
|
* that arrive out of order. `/aiskills/search` returns a raw OpenSearch response,
|
|
716
725
|
* so we lift each hit's `_source` (already in KRChatbotSkill shape) out.
|
|
717
726
|
*/
|
|
718
|
-
|
|
719
|
-
this.
|
|
720
|
-
const requestId = this.
|
|
727
|
+
_searchOrgSkills(query) {
|
|
728
|
+
this._orgSkillRequestId++;
|
|
729
|
+
const requestId = this._orgSkillRequestId;
|
|
721
730
|
const trimmed = query.trim();
|
|
722
731
|
// An empty query matches everything; otherwise substring-match the query
|
|
723
732
|
// against name/code/description. We use case-insensitive wildcard queries on
|
|
@@ -767,6 +776,11 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
767
776
|
bool: {
|
|
768
777
|
must: [must],
|
|
769
778
|
filter: [{ term: { active: true } }],
|
|
779
|
+
// The "Your Org" tab lists only this instance's own skills. Globally-
|
|
780
|
+
// published skills belong to the "Kodaris" tab (fed by the registry),
|
|
781
|
+
// so exclude them here. Documents without the field aren't matched by
|
|
782
|
+
// the term, so ordinary org skills are kept.
|
|
783
|
+
must_not: [{ term: { globalPublished: true } }],
|
|
770
784
|
},
|
|
771
785
|
},
|
|
772
786
|
},
|
|
@@ -774,17 +788,106 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
774
788
|
.then((r) => r.json())
|
|
775
789
|
.then((res) => {
|
|
776
790
|
// Ignore a stale response superseded by a newer search.
|
|
777
|
-
if (requestId !== this.
|
|
791
|
+
if (requestId !== this._orgSkillRequestId) {
|
|
778
792
|
return;
|
|
779
793
|
}
|
|
780
|
-
this.
|
|
794
|
+
this._orgSkills = (res?.data?.hits?.hits || []).map((hit) => hit._source);
|
|
781
795
|
})
|
|
782
796
|
.catch((error) => {
|
|
783
797
|
console.error('kr-chatbot: skill search failed', error);
|
|
784
798
|
});
|
|
785
799
|
}
|
|
786
800
|
/**
|
|
787
|
-
* Search the
|
|
801
|
+
* Search skills for the `/` composer menu into `_invocableSkills`: this instance's
|
|
802
|
+
* own org skills PLUS the global skills installed on it, since both are
|
|
803
|
+
* invocable by code. They come from two indexes (the local aiskills search and
|
|
804
|
+
* the registry search filtered client-side to enabled), so we fire both and
|
|
805
|
+
* concat — org skills first, then installed globals. Skill codes are unique
|
|
806
|
+
* across the two, so no dedup is needed. Kept out of `_orgSkills` so
|
|
807
|
+
* installed globals never leak into the browse dialog's "Your Org" tab.
|
|
808
|
+
* (Cross-list relevance ranking is a later concern; for now org skills lead.)
|
|
809
|
+
*/
|
|
810
|
+
_searchInvocableSkills(query) {
|
|
811
|
+
this._invocableSkillRequestId++;
|
|
812
|
+
const requestId = this._invocableSkillRequestId;
|
|
813
|
+
// Same wildcard matching as _searchOrgSkills (see there for the rationale).
|
|
814
|
+
let must = { match_all: {} };
|
|
815
|
+
if (query.trim()) {
|
|
816
|
+
must = {
|
|
817
|
+
bool: {
|
|
818
|
+
should: [
|
|
819
|
+
{
|
|
820
|
+
wildcard: {
|
|
821
|
+
'name.raw': {
|
|
822
|
+
value: `*${query.trim().replace(/([*?\\])/g, '\\$1')}*`,
|
|
823
|
+
case_insensitive: true,
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
},
|
|
827
|
+
{
|
|
828
|
+
wildcard: {
|
|
829
|
+
'code.raw': {
|
|
830
|
+
value: `*${query.trim().replace(/([*?\\])/g, '\\$1')}*`,
|
|
831
|
+
case_insensitive: true,
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
},
|
|
835
|
+
{
|
|
836
|
+
wildcard: {
|
|
837
|
+
'description.raw': {
|
|
838
|
+
value: `*${query.trim().replace(/([*?\\])/g, '\\$1')}*`,
|
|
839
|
+
case_insensitive: true,
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
},
|
|
843
|
+
],
|
|
844
|
+
minimum_should_match: 1,
|
|
845
|
+
},
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
// Org skills — this instance's own catalog (globalPublished excluded).
|
|
849
|
+
const orgSearch = KRHttp.fetch({
|
|
850
|
+
url: '/api/system/opensearch/aiskills/search',
|
|
851
|
+
method: 'POST',
|
|
852
|
+
body: {
|
|
853
|
+
size: 100,
|
|
854
|
+
query: {
|
|
855
|
+
bool: {
|
|
856
|
+
must: [must],
|
|
857
|
+
filter: [{ term: { active: true } }],
|
|
858
|
+
must_not: [{ term: { globalPublished: true } }],
|
|
859
|
+
},
|
|
860
|
+
},
|
|
861
|
+
},
|
|
862
|
+
})
|
|
863
|
+
.then((r) => r.json())
|
|
864
|
+
.then((res) => (res?.data?.hits?.hits || []).map((hit) => hit._source))
|
|
865
|
+
.catch((error) => {
|
|
866
|
+
console.error('kr-chatbot: invocable skill search failed', error);
|
|
867
|
+
return [];
|
|
868
|
+
});
|
|
869
|
+
// Installed globals — the registry catalog, kept only where enabled=true.
|
|
870
|
+
const globalSearch = KRHttp.fetch({
|
|
871
|
+
url: '/api/system/opensearch/aiskills/global/search',
|
|
872
|
+
method: 'POST',
|
|
873
|
+
body: { query: query.trim() },
|
|
874
|
+
})
|
|
875
|
+
.then((r) => r.json())
|
|
876
|
+
.then((res) => (res?.data || []).filter((s) => !!s.enabled))
|
|
877
|
+
.catch((error) => {
|
|
878
|
+
console.error('kr-chatbot: installed global skill search failed', error);
|
|
879
|
+
return [];
|
|
880
|
+
});
|
|
881
|
+
Promise.all([orgSearch, globalSearch]).then(([orgSkills, globalSkills]) => {
|
|
882
|
+
// Ignore a stale response superseded by a newer search.
|
|
883
|
+
if (requestId !== this._invocableSkillRequestId) {
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
this._invocableSkills = [...orgSkills, ...globalSkills];
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* Search the shared Kodaris global-skill registry into `_globalSkills`
|
|
788
891
|
* (the browse dialog's "Kodaris" tab). Unlike `/aiskills/search`,
|
|
789
892
|
* `/aiskills/global/search` returns a digested list
|
|
790
893
|
* (`data: [{ code, name, description, enabled }]`) already in `KRChatbotSkill`
|
|
@@ -805,7 +908,7 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
805
908
|
if (requestId !== this._globalSkillRequestId) {
|
|
806
909
|
return;
|
|
807
910
|
}
|
|
808
|
-
this.
|
|
911
|
+
this._globalSkills = (res?.data || []);
|
|
809
912
|
})
|
|
810
913
|
.catch((error) => {
|
|
811
914
|
console.error('kr-chatbot: global skill search failed', error);
|
|
@@ -817,13 +920,16 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
817
920
|
* matching card. Ignores clicks while a request for the same code is pending.
|
|
818
921
|
*/
|
|
819
922
|
_handleGlobalSkillEnable(skill, e) {
|
|
820
|
-
// The card body inserts the skill; the
|
|
923
|
+
// The card body inserts the skill; the install button must not also do that.
|
|
821
924
|
e.stopPropagation();
|
|
822
925
|
if (this._globalSkillPending.has(skill.code)) {
|
|
823
926
|
return;
|
|
824
927
|
}
|
|
928
|
+
// Drive the clicked button's own spinner (KRButton disables itself while
|
|
929
|
+
// loading, so a second click can't fire until the request settles).
|
|
930
|
+
const btn = e.currentTarget;
|
|
825
931
|
this._globalSkillPending.add(skill.code);
|
|
826
|
-
|
|
932
|
+
btn?.showLoading();
|
|
827
933
|
KRHttp.fetch({
|
|
828
934
|
url: `/api/system/opensearch/aiskills/global/${encodeURIComponent(skill.code)}/enabled`,
|
|
829
935
|
method: 'POST',
|
|
@@ -831,16 +937,18 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
831
937
|
})
|
|
832
938
|
.then((r) => r.json())
|
|
833
939
|
.then((res) => {
|
|
834
|
-
// Reflect the server's returned flag on the card.
|
|
940
|
+
// Reflect the server's returned flag on the card. The button label/style
|
|
941
|
+
// re-render from skill.enabled; reset() clears the spinner to idle.
|
|
835
942
|
skill.enabled = res?.data?.enabled;
|
|
943
|
+
btn?.reset();
|
|
836
944
|
this.requestUpdate();
|
|
837
945
|
})
|
|
838
946
|
.catch((error) => {
|
|
839
947
|
console.error('kr-chatbot: global skill enable failed', error);
|
|
948
|
+
btn?.showError('Error');
|
|
840
949
|
})
|
|
841
950
|
.finally(() => {
|
|
842
951
|
this._globalSkillPending.delete(skill.code);
|
|
843
|
-
this.requestUpdate();
|
|
844
952
|
});
|
|
845
953
|
}
|
|
846
954
|
/** The `+` button toggles the skill/attachment menu open (showing all skills). */
|
|
@@ -852,7 +960,7 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
852
960
|
this._skillQuery = '';
|
|
853
961
|
this._skillHighlighted = 0;
|
|
854
962
|
this._skillMenuOpened = true;
|
|
855
|
-
this.
|
|
963
|
+
this._searchInvocableSkills('');
|
|
856
964
|
}
|
|
857
965
|
/** Insert `/code ` into the composer and focus it, ready for the message. */
|
|
858
966
|
_handleSkillSelect(skill) {
|
|
@@ -885,7 +993,7 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
885
993
|
this._skillBrowseQuery = '';
|
|
886
994
|
this._skillBrowseOpened = true;
|
|
887
995
|
// Fill both tabs up front so switching between them is instant, not stale.
|
|
888
|
-
this.
|
|
996
|
+
this._searchOrgSkills('');
|
|
889
997
|
this._searchGlobalSkills('');
|
|
890
998
|
}
|
|
891
999
|
_handleSkillBrowseClose() {
|
|
@@ -894,7 +1002,7 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
894
1002
|
_handleSkillBrowseSearch(e) {
|
|
895
1003
|
this._skillBrowseQuery = e.target.value;
|
|
896
1004
|
// Filter both catalogs so whichever tab the user switches to is already current.
|
|
897
|
-
this.
|
|
1005
|
+
this._searchOrgSkills(this._skillBrowseQuery);
|
|
898
1006
|
this._searchGlobalSkills(this._skillBrowseQuery);
|
|
899
1007
|
}
|
|
900
1008
|
_handleSkillBrowseTabChange(e) {
|
|
@@ -1339,7 +1447,7 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
1339
1447
|
// --- Render ---
|
|
1340
1448
|
/** The `/` skill menu shown above the composer while typing a slash command. */
|
|
1341
1449
|
_renderSkillMenu() {
|
|
1342
|
-
const skills = this.
|
|
1450
|
+
const skills = this._invocableSkills;
|
|
1343
1451
|
// Debug test triggers are extra actions after Attach Files / Browse. The
|
|
1344
1452
|
// flat keyboard index counts actions first, then skills, so the skills'
|
|
1345
1453
|
// index offset is the number of actions shown.
|
|
@@ -1377,7 +1485,7 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
1377
1485
|
})}
|
|
1378
1486
|
@click=${() => this._activateSkillMenuItem(2)}
|
|
1379
1487
|
>
|
|
1380
|
-
Test
|
|
1488
|
+
Test Context Warning
|
|
1381
1489
|
</button>
|
|
1382
1490
|
<button
|
|
1383
1491
|
class=${classMap({
|
|
@@ -1386,7 +1494,7 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
1386
1494
|
})}
|
|
1387
1495
|
@click=${() => this._activateSkillMenuItem(3)}
|
|
1388
1496
|
>
|
|
1389
|
-
Test
|
|
1497
|
+
Test Context Limit
|
|
1390
1498
|
</button>
|
|
1391
1499
|
` : nothing}
|
|
1392
1500
|
</div>
|
|
@@ -1446,69 +1554,71 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
1446
1554
|
/**
|
|
1447
1555
|
* The card grid of skill search results for the browse dialog. Clicking a card
|
|
1448
1556
|
* inserts `/code ` into the composer. In the shared-Kodaris (`global`) tab each
|
|
1449
|
-
* card also gets a
|
|
1450
|
-
*
|
|
1557
|
+
* card also gets a footer row with an Enabled toggle that installs/removes the
|
|
1558
|
+
* skill on this instance.
|
|
1451
1559
|
*/
|
|
1452
1560
|
_renderSkillGrid(global = false) {
|
|
1453
|
-
let skills = this.
|
|
1561
|
+
let skills = this._orgSkills;
|
|
1454
1562
|
if (global) {
|
|
1455
|
-
skills = this.
|
|
1563
|
+
skills = this._globalSkills;
|
|
1456
1564
|
}
|
|
1457
1565
|
if (!skills.length) {
|
|
1458
1566
|
return nothing;
|
|
1459
1567
|
}
|
|
1460
1568
|
return html `
|
|
1461
1569
|
<div class="skill-browse__grid">
|
|
1462
|
-
${skills.map((skill) =>
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1570
|
+
${skills.map((skill) => {
|
|
1571
|
+
// A global skill must be installed before it can be chosen — inserting
|
|
1572
|
+
// /code for a skill not enabled on this instance would just fail. So an
|
|
1573
|
+
// uninstalled global card does nothing on click; only its Install button
|
|
1574
|
+
// is interactive.
|
|
1575
|
+
const selectable = !global || !!skill.enabled;
|
|
1576
|
+
return html `
|
|
1577
|
+
<div
|
|
1578
|
+
class="skill-card"
|
|
1579
|
+
role=${selectable ? 'button' : nothing}
|
|
1580
|
+
tabindex=${selectable ? 0 : nothing}
|
|
1581
|
+
@click=${selectable ? () => this._handleSkillSelect(skill) : nothing}
|
|
1582
|
+
@keydown=${selectable ? (e) => this._handleSkillCardKeydown(e, skill) : nothing}
|
|
1583
|
+
>
|
|
1584
|
+
<span class="skill-card__name">${skill.name}</span>
|
|
1585
|
+
${skill.description ? html `
|
|
1586
|
+
<span class="skill-card__desc">${skill.description}</span>
|
|
1587
|
+
` : nothing}
|
|
1588
|
+
${global ? this._renderSkillToggle(skill) : nothing}
|
|
1589
|
+
</div>
|
|
1590
|
+
`;
|
|
1591
|
+
})}
|
|
1477
1592
|
</div>
|
|
1478
1593
|
`;
|
|
1479
1594
|
}
|
|
1595
|
+
/** Keep a click on the card footer from bubbling to the card's insert handler. */
|
|
1596
|
+
_stopFooterClick(e) {
|
|
1597
|
+
e.stopPropagation();
|
|
1598
|
+
}
|
|
1480
1599
|
/**
|
|
1481
|
-
* The install affordance on a global skill card: a
|
|
1482
|
-
*
|
|
1483
|
-
* while the enable/disable request is in flight
|
|
1600
|
+
* The install affordance on a global skill card: a footer row with an
|
|
1601
|
+
* install/remove button. The button owns its own spinner via KRButton's
|
|
1602
|
+
* showLoading()/showError() while the enable/disable request is in flight, so
|
|
1603
|
+
* the async call has clear feedback. Clicks are stopped from bubbling so the
|
|
1604
|
+
* button never also inserts the skill (the card body does that).
|
|
1484
1605
|
*/
|
|
1485
|
-
|
|
1606
|
+
_renderSkillToggle(skill) {
|
|
1607
|
+
const on = !!skill.enabled;
|
|
1486
1608
|
return html `
|
|
1487
|
-
<
|
|
1488
|
-
class
|
|
1489
|
-
|
|
1490
|
-
'skill-card__install--installed': !!skill.enabled,
|
|
1491
|
-
})}
|
|
1492
|
-
?disabled=${this._globalSkillPending.has(skill.code)}
|
|
1493
|
-
title=${!!skill.enabled ? 'Installed — click to remove' : 'Install this skill'}
|
|
1494
|
-
aria-label=${!!skill.enabled ? 'Remove skill' : 'Install skill'}
|
|
1495
|
-
@click=${(e) => this._handleGlobalSkillEnable(skill, e)}
|
|
1609
|
+
<div
|
|
1610
|
+
class="skill-card__footer"
|
|
1611
|
+
@click=${this._stopFooterClick}
|
|
1496
1612
|
>
|
|
1497
|
-
<
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
>
|
|
1505
|
-
|
|
1506
|
-
stroke-linecap="round"
|
|
1507
|
-
stroke-linejoin="round"
|
|
1508
|
-
d=${!!skill.enabled ? 'm4.5 12.75 6 6 9-13.5' : 'M12 4.5v15m7.5-7.5h-15'}
|
|
1509
|
-
/>
|
|
1510
|
-
</svg>
|
|
1511
|
-
</button>
|
|
1613
|
+
<span class="skill-card__price">Free</span>
|
|
1614
|
+
<kr-button
|
|
1615
|
+
class="skill-card__install-btn"
|
|
1616
|
+
size="small"
|
|
1617
|
+
variant=${on ? 'outline' : 'flat'}
|
|
1618
|
+
color=${on ? 'secondary' : 'primary'}
|
|
1619
|
+
@click=${(e) => this._handleGlobalSkillEnable(skill, e)}
|
|
1620
|
+
>${on ? 'Uninstall' : 'Install'}</kr-button>
|
|
1621
|
+
</div>
|
|
1512
1622
|
`;
|
|
1513
1623
|
}
|
|
1514
1624
|
/**
|
|
@@ -1969,9 +2079,7 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
1969
2079
|
</svg>
|
|
1970
2080
|
</button>
|
|
1971
2081
|
</div>
|
|
1972
|
-
` :
|
|
1973
|
-
|
|
1974
|
-
${this._contextLimitReached ? html `
|
|
2082
|
+
` : this._contextLimitReached ? html `
|
|
1975
2083
|
<div class="context-limit" role="alert">
|
|
1976
2084
|
${this._contextLimitMessage
|
|
1977
2085
|
|| 'This conversation has reached its limit. Please start a new one to continue.'}
|
|
@@ -2557,14 +2665,22 @@ KRChatbot.styles = css `
|
|
|
2557
2665
|
|
|
2558
2666
|
/* Context warning banner (user-facing, dismissible, non-blocking) */
|
|
2559
2667
|
.context-warning {
|
|
2560
|
-
|
|
2668
|
+
/* Match the message text column: 768px cap minus its 32px side padding
|
|
2669
|
+
(704px), and viewport minus that padding on narrower screens. */
|
|
2670
|
+
width: 100%;
|
|
2671
|
+
max-width: 735px;
|
|
2672
|
+
margin: 8px auto 0;
|
|
2673
|
+
padding: 10px 14px;
|
|
2674
|
+
border: 1px solid #fde68a;
|
|
2675
|
+
border-radius: 8px;
|
|
2561
2676
|
background: #fffbeb;
|
|
2562
2677
|
color: #92400e;
|
|
2563
2678
|
font-size: 13px;
|
|
2679
|
+
line-height: 1.4;
|
|
2564
2680
|
display: flex;
|
|
2565
2681
|
align-items: center;
|
|
2566
2682
|
justify-content: space-between;
|
|
2567
|
-
gap:
|
|
2683
|
+
gap: 10px;
|
|
2568
2684
|
flex-shrink: 0;
|
|
2569
2685
|
}
|
|
2570
2686
|
|
|
@@ -2577,6 +2693,11 @@ KRChatbot.styles = css `
|
|
|
2577
2693
|
display: flex;
|
|
2578
2694
|
align-items: center;
|
|
2579
2695
|
flex-shrink: 0;
|
|
2696
|
+
opacity: 0.7;
|
|
2697
|
+
}
|
|
2698
|
+
|
|
2699
|
+
.context-warning-dismiss:hover {
|
|
2700
|
+
opacity: 1;
|
|
2580
2701
|
}
|
|
2581
2702
|
|
|
2582
2703
|
.context-warning-dismiss-icon {
|
|
@@ -2586,11 +2707,18 @@ KRChatbot.styles = css `
|
|
|
2586
2707
|
|
|
2587
2708
|
/* Context-limit notice (user-facing, blocking — steers to a new conversation) */
|
|
2588
2709
|
.context-limit {
|
|
2589
|
-
|
|
2710
|
+
/* Match the message text column: 768px cap minus its 32px side padding
|
|
2711
|
+
(704px), and viewport minus that padding on narrower screens. */
|
|
2712
|
+
width: 100%;
|
|
2713
|
+
max-width: 735px;
|
|
2714
|
+
margin: 8px auto 0;
|
|
2715
|
+
padding: 10px 14px;
|
|
2716
|
+
border: 1px solid #fecaca;
|
|
2717
|
+
border-radius: 8px;
|
|
2590
2718
|
background: var(--kr-chatbot-error-bg);
|
|
2591
2719
|
color: var(--kr-chatbot-error-text);
|
|
2592
2720
|
font-size: 13px;
|
|
2593
|
-
line-height: 1.
|
|
2721
|
+
line-height: 1.4;
|
|
2594
2722
|
flex-shrink: 0;
|
|
2595
2723
|
}
|
|
2596
2724
|
|
|
@@ -3118,7 +3246,7 @@ KRChatbot.styles = css `
|
|
|
3118
3246
|
height: 100%;
|
|
3119
3247
|
overflow-y: auto;
|
|
3120
3248
|
display: grid;
|
|
3121
|
-
grid-template-columns: repeat(
|
|
3249
|
+
grid-template-columns: repeat(2, 1fr);
|
|
3122
3250
|
grid-auto-rows: min-content;
|
|
3123
3251
|
align-content: start;
|
|
3124
3252
|
gap: 12px;
|
|
@@ -3146,53 +3274,31 @@ KRChatbot.styles = css `
|
|
|
3146
3274
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
3147
3275
|
}
|
|
3148
3276
|
|
|
3149
|
-
/*
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
display: inline-flex;
|
|
3277
|
+
/* Footer row on global skill cards: install button on the left, price on the
|
|
3278
|
+
right. margin-top:auto pins it to the bottom so cards in a row align their
|
|
3279
|
+
footers regardless of description length. Negative horizontal margins
|
|
3280
|
+
(matching the card's 14px padding) pull the top border out to the full
|
|
3281
|
+
card width; the content is re-padded horizontally to stay inset. */
|
|
3282
|
+
.skill-card__footer {
|
|
3283
|
+
display: flex;
|
|
3157
3284
|
align-items: center;
|
|
3158
|
-
justify-content:
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
color: #374151;
|
|
3166
|
-
cursor: pointer;
|
|
3167
|
-
transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
|
|
3168
|
-
}
|
|
3169
|
-
|
|
3170
|
-
.skill-card__install:hover:not(:disabled) {
|
|
3171
|
-
border-color: var(--kr-chatbot-primary);
|
|
3172
|
-
color: var(--kr-chatbot-primary);
|
|
3173
|
-
}
|
|
3174
|
-
|
|
3175
|
-
.skill-card__install:disabled {
|
|
3176
|
-
opacity: 0.5;
|
|
3177
|
-
cursor: default;
|
|
3178
|
-
}
|
|
3179
|
-
|
|
3180
|
-
/* Installed state: filled check. */
|
|
3181
|
-
.skill-card__install--installed {
|
|
3182
|
-
background: var(--kr-chatbot-primary);
|
|
3183
|
-
border-color: var(--kr-chatbot-primary);
|
|
3184
|
-
color: white;
|
|
3285
|
+
justify-content: space-between;
|
|
3286
|
+
gap: 8px;
|
|
3287
|
+
margin-top: 8px;
|
|
3288
|
+
margin-left: -14px;
|
|
3289
|
+
margin-right: -14px;
|
|
3290
|
+
padding: 14px 14px 0;
|
|
3291
|
+
border-top: 1px solid #e5e7eb;
|
|
3185
3292
|
}
|
|
3186
3293
|
|
|
3187
|
-
.skill-
|
|
3188
|
-
|
|
3189
|
-
|
|
3294
|
+
.skill-card__price {
|
|
3295
|
+
font-size: 13px;
|
|
3296
|
+
font-weight: 600;
|
|
3297
|
+
color: #000000;
|
|
3190
3298
|
}
|
|
3191
3299
|
|
|
3192
3300
|
.skill-card__name {
|
|
3193
3301
|
font-weight: 600;
|
|
3194
|
-
/* Leave room for the corner install button on global cards. */
|
|
3195
|
-
padding-right: 28px;
|
|
3196
3302
|
}
|
|
3197
3303
|
|
|
3198
3304
|
.skill-card__desc {
|
|
@@ -3381,10 +3487,13 @@ __decorate([
|
|
|
3381
3487
|
], KRChatbot.prototype, "_skillQuery", void 0);
|
|
3382
3488
|
__decorate([
|
|
3383
3489
|
state()
|
|
3384
|
-
], KRChatbot.prototype, "
|
|
3490
|
+
], KRChatbot.prototype, "_orgSkills", void 0);
|
|
3491
|
+
__decorate([
|
|
3492
|
+
state()
|
|
3493
|
+
], KRChatbot.prototype, "_globalSkills", void 0);
|
|
3385
3494
|
__decorate([
|
|
3386
3495
|
state()
|
|
3387
|
-
], KRChatbot.prototype, "
|
|
3496
|
+
], KRChatbot.prototype, "_invocableSkills", void 0);
|
|
3388
3497
|
__decorate([
|
|
3389
3498
|
state()
|
|
3390
3499
|
], KRChatbot.prototype, "_aiFrameworkConfig", void 0);
|