@kodaris/krubble-app-components 1.0.80 → 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 +41 -18
- package/dist/chatbot.d.ts.map +1 -1
- package/dist/chatbot.js +210 -119
- package/dist/chatbot.js.map +1 -1
- package/dist/krubble-app.bundled.js +210 -119
- package/dist/krubble-app.bundled.js.map +1 -1
- package/dist/krubble-app.bundled.min.js +59 -90
- package/dist/krubble-app.bundled.min.js.map +1 -1
- package/dist/krubble-app.umd.js +210 -119
- package/dist/krubble-app.umd.js.map +1 -1
- package/dist/krubble-app.umd.min.js +57 -88
- 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,20 +196,27 @@ 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
|
|
@@ -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;
|
|
@@ -697,14 +706,14 @@ let KRChatbot = class KRChatbot extends LitElement {
|
|
|
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.
|
|
@@ -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
|
/**
|
|
@@ -3136,7 +3246,7 @@ KRChatbot.styles = css `
|
|
|
3136
3246
|
height: 100%;
|
|
3137
3247
|
overflow-y: auto;
|
|
3138
3248
|
display: grid;
|
|
3139
|
-
grid-template-columns: repeat(
|
|
3249
|
+
grid-template-columns: repeat(2, 1fr);
|
|
3140
3250
|
grid-auto-rows: min-content;
|
|
3141
3251
|
align-content: start;
|
|
3142
3252
|
gap: 12px;
|
|
@@ -3164,53 +3274,31 @@ KRChatbot.styles = css `
|
|
|
3164
3274
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
3165
3275
|
}
|
|
3166
3276
|
|
|
3167
|
-
/*
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
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;
|
|
3175
3284
|
align-items: center;
|
|
3176
|
-
justify-content:
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
color: #374151;
|
|
3184
|
-
cursor: pointer;
|
|
3185
|
-
transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
|
|
3186
|
-
}
|
|
3187
|
-
|
|
3188
|
-
.skill-card__install:hover:not(:disabled) {
|
|
3189
|
-
border-color: var(--kr-chatbot-primary);
|
|
3190
|
-
color: var(--kr-chatbot-primary);
|
|
3191
|
-
}
|
|
3192
|
-
|
|
3193
|
-
.skill-card__install:disabled {
|
|
3194
|
-
opacity: 0.5;
|
|
3195
|
-
cursor: default;
|
|
3196
|
-
}
|
|
3197
|
-
|
|
3198
|
-
/* Installed state: filled check. */
|
|
3199
|
-
.skill-card__install--installed {
|
|
3200
|
-
background: var(--kr-chatbot-primary);
|
|
3201
|
-
border-color: var(--kr-chatbot-primary);
|
|
3202
|
-
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;
|
|
3203
3292
|
}
|
|
3204
3293
|
|
|
3205
|
-
.skill-
|
|
3206
|
-
|
|
3207
|
-
|
|
3294
|
+
.skill-card__price {
|
|
3295
|
+
font-size: 13px;
|
|
3296
|
+
font-weight: 600;
|
|
3297
|
+
color: #000000;
|
|
3208
3298
|
}
|
|
3209
3299
|
|
|
3210
3300
|
.skill-card__name {
|
|
3211
3301
|
font-weight: 600;
|
|
3212
|
-
/* Leave room for the corner install button on global cards. */
|
|
3213
|
-
padding-right: 28px;
|
|
3214
3302
|
}
|
|
3215
3303
|
|
|
3216
3304
|
.skill-card__desc {
|
|
@@ -3399,10 +3487,13 @@ __decorate([
|
|
|
3399
3487
|
], KRChatbot.prototype, "_skillQuery", void 0);
|
|
3400
3488
|
__decorate([
|
|
3401
3489
|
state()
|
|
3402
|
-
], KRChatbot.prototype, "
|
|
3490
|
+
], KRChatbot.prototype, "_orgSkills", void 0);
|
|
3491
|
+
__decorate([
|
|
3492
|
+
state()
|
|
3493
|
+
], KRChatbot.prototype, "_globalSkills", void 0);
|
|
3403
3494
|
__decorate([
|
|
3404
3495
|
state()
|
|
3405
|
-
], KRChatbot.prototype, "
|
|
3496
|
+
], KRChatbot.prototype, "_invocableSkills", void 0);
|
|
3406
3497
|
__decorate([
|
|
3407
3498
|
state()
|
|
3408
3499
|
], KRChatbot.prototype, "_aiFrameworkConfig", void 0);
|