@kodaris/krubble-app-components 1.0.77 → 1.0.79
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 +73 -9
- package/dist/chatbot.d.ts.map +1 -1
- package/dist/chatbot.js +323 -36
- package/dist/chatbot.js.map +1 -1
- package/dist/krubble-app.bundled.js +323 -36
- package/dist/krubble-app.bundled.js.map +1 -1
- package/dist/krubble-app.bundled.min.js +122 -37
- package/dist/krubble-app.bundled.min.js.map +1 -1
- package/dist/krubble-app.umd.js +323 -36
- package/dist/krubble-app.umd.js.map +1 -1
- package/dist/krubble-app.umd.min.js +107 -22
- package/dist/krubble-app.umd.min.js.map +1 -1
- package/package.json +1 -1
package/dist/krubble-app.umd.js
CHANGED
|
@@ -5620,10 +5620,18 @@
|
|
|
5620
5620
|
/** Monotonic id source for pending attachments. */
|
|
5621
5621
|
this._pendingSeq = 0;
|
|
5622
5622
|
/**
|
|
5623
|
-
* Request-id
|
|
5624
|
-
* responses are dropped (mirrors the kr-combo-box `_requestId` pattern).
|
|
5623
|
+
* Request-id guards for skill searches — bumped on each search so out-of-order
|
|
5624
|
+
* responses are dropped (mirrors the kr-combo-box `_requestId` pattern). Org and
|
|
5625
|
+
* global searches run concurrently into separate buffers, so each has its own.
|
|
5625
5626
|
*/
|
|
5626
5627
|
this._skillRequestId = 0;
|
|
5628
|
+
this._globalSkillRequestId = 0;
|
|
5629
|
+
/**
|
|
5630
|
+
* Codes of global skills with an in-flight enable/disable request. The `+`
|
|
5631
|
+
* button reads this to show a pending state and ignore repeat clicks. Mutated
|
|
5632
|
+
* in place; call requestUpdate() after changing it to re-render.
|
|
5633
|
+
*/
|
|
5634
|
+
this._globalSkillPending = new Set();
|
|
5627
5635
|
// --- @property (public API) ---
|
|
5628
5636
|
/**
|
|
5629
5637
|
* Callback function for sending messages. Receives the user's message and
|
|
@@ -5729,10 +5737,24 @@
|
|
|
5729
5737
|
/** Current `/` query (text after the slash), sent to `_searchSkills`. */
|
|
5730
5738
|
this._skillQuery = '';
|
|
5731
5739
|
/**
|
|
5732
|
-
* Latest skill search results
|
|
5733
|
-
*
|
|
5740
|
+
* Latest org skill search results (this instance's own skills). Backs the `/`
|
|
5741
|
+
* menu and the browse dialog's "Your Org" tab.
|
|
5734
5742
|
*/
|
|
5735
5743
|
this._skillResults = [];
|
|
5744
|
+
/**
|
|
5745
|
+
* Latest shared-Kodaris (global) skill results, for the browse dialog's
|
|
5746
|
+
* "Kodaris" tab. Kept separate from `_skillResults` so switching tabs shows
|
|
5747
|
+
* fresh results immediately instead of the other tab's stale list — the browse
|
|
5748
|
+
* search fills both buffers at once.
|
|
5749
|
+
*/
|
|
5750
|
+
this._globalSkillResults = [];
|
|
5751
|
+
/**
|
|
5752
|
+
* AI framework UI config flags fetched from `aiFrameworkConfig` on connect.
|
|
5753
|
+
* `debugTelemetryEnabled` (same server-side gate as TOKEN_USAGE emission) turns
|
|
5754
|
+
* on the debug drawer toggle and the `/test-warning` / `/test-limit` composer
|
|
5755
|
+
* options — known up front, before any message.
|
|
5756
|
+
*/
|
|
5757
|
+
this._aiFrameworkConfig = {};
|
|
5736
5758
|
/** Highlighted item in the skill menu for keyboard navigation (-1 = none). */
|
|
5737
5759
|
this._skillHighlighted = 0;
|
|
5738
5760
|
/** Index of the message whose copy button just flashed "Copied" (-1 = none). */
|
|
@@ -5740,11 +5762,11 @@
|
|
|
5740
5762
|
/** Whether the "Browse all skills" dialog is open. */
|
|
5741
5763
|
this._skillBrowseOpened = false;
|
|
5742
5764
|
/**
|
|
5743
|
-
* Active tab in the browse dialog. `org`
|
|
5744
|
-
*
|
|
5765
|
+
* Active tab in the browse dialog. `org` shows `_skillResults` (this instance's
|
|
5766
|
+
* skills); `kodaris` shows `_globalSkillResults` (the shared global catalog).
|
|
5745
5767
|
*/
|
|
5746
5768
|
this._skillBrowseSource = 'org';
|
|
5747
|
-
/** Search text in the browse dialog,
|
|
5769
|
+
/** Search text in the browse dialog, routed to the active tab's search. */
|
|
5748
5770
|
this._skillBrowseQuery = '';
|
|
5749
5771
|
this._handleMouseMove = (e) => {
|
|
5750
5772
|
if (this._isDragging) {
|
|
@@ -5842,6 +5864,17 @@
|
|
|
5842
5864
|
this.messages = messages;
|
|
5843
5865
|
}
|
|
5844
5866
|
}).catch(() => { });
|
|
5867
|
+
// Fetch the framework config on load (debugTelemetryEnabled, v2ChatEnabled,
|
|
5868
|
+
// registryServer). debugTelemetryEnabled gates the debug affordances and is
|
|
5869
|
+
// known before any message.
|
|
5870
|
+
krubbleHttp.Http.fetch({
|
|
5871
|
+
url: '/api/system/integration/aws/bedrock/aiFrameworkConfig',
|
|
5872
|
+
})
|
|
5873
|
+
.then((r) => r.json())
|
|
5874
|
+
.then((res) => {
|
|
5875
|
+
this._aiFrameworkConfig = res?.data || {};
|
|
5876
|
+
})
|
|
5877
|
+
.catch(() => { });
|
|
5845
5878
|
// Inline mode fills its container and is always open — the floating widget's
|
|
5846
5879
|
// toggle/drag/resize state doesn't apply, but the composer message and debug
|
|
5847
5880
|
// drawer state (restored inside _restoreState) do.
|
|
@@ -6136,11 +6169,16 @@
|
|
|
6136
6169
|
}
|
|
6137
6170
|
}
|
|
6138
6171
|
_handleKeyDown(e) {
|
|
6139
|
-
// When the skill menu is open, arrow keys / Enter / Escape drive it. The
|
|
6140
|
-
//
|
|
6141
|
-
// filtered skills
|
|
6172
|
+
// When the skill menu is open, arrow keys / Enter / Escape drive it. The top
|
|
6173
|
+
// actions (Attach Files, Browse All Skills, plus the two debug test triggers
|
|
6174
|
+
// when enabled) come first, then the filtered skills — all one navigable list.
|
|
6142
6175
|
if (this._skillMenuOpened) {
|
|
6143
|
-
const
|
|
6176
|
+
const debugActions = this.variant === 'inline' && this._aiFrameworkConfig.debugTelemetryEnabled === true;
|
|
6177
|
+
let actionCount = 2;
|
|
6178
|
+
if (debugActions) {
|
|
6179
|
+
actionCount = 4;
|
|
6180
|
+
}
|
|
6181
|
+
const count = actionCount + this._skillResults.length;
|
|
6144
6182
|
if (e.key === 'ArrowDown') {
|
|
6145
6183
|
e.preventDefault();
|
|
6146
6184
|
this._skillHighlighted = (this._skillHighlighted + 1) % count;
|
|
@@ -6167,8 +6205,13 @@
|
|
|
6167
6205
|
this._handleSubmit();
|
|
6168
6206
|
}
|
|
6169
6207
|
}
|
|
6170
|
-
/** Run the skill-menu item at the given flat index (
|
|
6208
|
+
/** Run the skill-menu item at the given flat index (actions first, then skills). */
|
|
6171
6209
|
_activateSkillMenuItem(index) {
|
|
6210
|
+
const debugActions = this.variant === 'inline' && this._aiFrameworkConfig.debugTelemetryEnabled === true;
|
|
6211
|
+
let actionCount = 2;
|
|
6212
|
+
if (debugActions) {
|
|
6213
|
+
actionCount = 4;
|
|
6214
|
+
}
|
|
6172
6215
|
if (index === 0) {
|
|
6173
6216
|
this._openFilePicker();
|
|
6174
6217
|
return;
|
|
@@ -6177,7 +6220,23 @@
|
|
|
6177
6220
|
this._handleSkillBrowseOpen();
|
|
6178
6221
|
return;
|
|
6179
6222
|
}
|
|
6180
|
-
|
|
6223
|
+
if (debugActions && index === 2) {
|
|
6224
|
+
this._skillMenuOpened = false;
|
|
6225
|
+
this._send({
|
|
6226
|
+
message: '/test-warning',
|
|
6227
|
+
internalFiles: [],
|
|
6228
|
+
});
|
|
6229
|
+
return;
|
|
6230
|
+
}
|
|
6231
|
+
if (debugActions && index === 3) {
|
|
6232
|
+
this._skillMenuOpened = false;
|
|
6233
|
+
this._send({
|
|
6234
|
+
message: '/test-limit',
|
|
6235
|
+
internalFiles: [],
|
|
6236
|
+
});
|
|
6237
|
+
return;
|
|
6238
|
+
}
|
|
6239
|
+
const skill = this._skillResults[index - actionCount];
|
|
6181
6240
|
if (skill) {
|
|
6182
6241
|
this._handleSkillSelect(skill);
|
|
6183
6242
|
}
|
|
@@ -6206,17 +6265,33 @@
|
|
|
6206
6265
|
// specials so user input is treated literally, then wrap with `*...*`.
|
|
6207
6266
|
let must = { match_all: {} };
|
|
6208
6267
|
if (trimmed) {
|
|
6209
|
-
const pattern = `*${trimmed.replace(/([*?\\])/g, '\\$1')}*`;
|
|
6210
|
-
const value = {
|
|
6211
|
-
value: pattern,
|
|
6212
|
-
case_insensitive: true,
|
|
6213
|
-
};
|
|
6214
6268
|
must = {
|
|
6215
6269
|
bool: {
|
|
6216
6270
|
should: [
|
|
6217
|
-
{
|
|
6218
|
-
|
|
6219
|
-
|
|
6271
|
+
{
|
|
6272
|
+
wildcard: {
|
|
6273
|
+
'name.raw': {
|
|
6274
|
+
value: `*${trimmed.replace(/([*?\\])/g, '\\$1')}*`,
|
|
6275
|
+
case_insensitive: true,
|
|
6276
|
+
}
|
|
6277
|
+
}
|
|
6278
|
+
},
|
|
6279
|
+
{
|
|
6280
|
+
wildcard: {
|
|
6281
|
+
'code.raw': {
|
|
6282
|
+
value: `*${trimmed.replace(/([*?\\])/g, '\\$1')}*`,
|
|
6283
|
+
case_insensitive: true,
|
|
6284
|
+
}
|
|
6285
|
+
}
|
|
6286
|
+
},
|
|
6287
|
+
{
|
|
6288
|
+
wildcard: {
|
|
6289
|
+
'description.raw': {
|
|
6290
|
+
value: `*${trimmed.replace(/([*?\\])/g, '\\$1')}*`,
|
|
6291
|
+
case_insensitive: true,
|
|
6292
|
+
}
|
|
6293
|
+
}
|
|
6294
|
+
},
|
|
6220
6295
|
],
|
|
6221
6296
|
minimum_should_match: 1,
|
|
6222
6297
|
},
|
|
@@ -6247,6 +6322,66 @@
|
|
|
6247
6322
|
console.error('kr-chatbot: skill search failed', error);
|
|
6248
6323
|
});
|
|
6249
6324
|
}
|
|
6325
|
+
/**
|
|
6326
|
+
* Search the shared Kodaris global-skill registry into `_globalSkillResults`
|
|
6327
|
+
* (the browse dialog's "Kodaris" tab). Unlike `/aiskills/search`,
|
|
6328
|
+
* `/aiskills/global/search` returns a digested list
|
|
6329
|
+
* (`data: [{ code, name, description, enabled }]`) already in `KRChatbotSkill`
|
|
6330
|
+
* shape, so no `_source` lifting is needed. Only the browse dialog calls this
|
|
6331
|
+
* (the `/` menu is org-only), so it isn't run per slash keystroke.
|
|
6332
|
+
*/
|
|
6333
|
+
_searchGlobalSkills(query) {
|
|
6334
|
+
this._globalSkillRequestId++;
|
|
6335
|
+
const requestId = this._globalSkillRequestId;
|
|
6336
|
+
krubbleHttp.Http.fetch({
|
|
6337
|
+
url: '/api/system/opensearch/aiskills/global/search',
|
|
6338
|
+
method: 'POST',
|
|
6339
|
+
body: { query: query.trim() },
|
|
6340
|
+
})
|
|
6341
|
+
.then((r) => r.json())
|
|
6342
|
+
.then((res) => {
|
|
6343
|
+
// Ignore a stale response superseded by a newer search.
|
|
6344
|
+
if (requestId !== this._globalSkillRequestId) {
|
|
6345
|
+
return;
|
|
6346
|
+
}
|
|
6347
|
+
this._globalSkillResults = (res?.data || []);
|
|
6348
|
+
})
|
|
6349
|
+
.catch((error) => {
|
|
6350
|
+
console.error('kr-chatbot: global skill search failed', error);
|
|
6351
|
+
});
|
|
6352
|
+
}
|
|
6353
|
+
/**
|
|
6354
|
+
* Enable or disable a global skill on this instance via
|
|
6355
|
+
* `/aiskills/global/{code}/enabled`, then reflect the server's result on the
|
|
6356
|
+
* matching card. Ignores clicks while a request for the same code is pending.
|
|
6357
|
+
*/
|
|
6358
|
+
_handleGlobalSkillEnable(skill, e) {
|
|
6359
|
+
// The card body inserts the skill; the `+` button must not also do that.
|
|
6360
|
+
e.stopPropagation();
|
|
6361
|
+
if (this._globalSkillPending.has(skill.code)) {
|
|
6362
|
+
return;
|
|
6363
|
+
}
|
|
6364
|
+
this._globalSkillPending.add(skill.code);
|
|
6365
|
+
this.requestUpdate();
|
|
6366
|
+
krubbleHttp.Http.fetch({
|
|
6367
|
+
url: `/api/system/opensearch/aiskills/global/${encodeURIComponent(skill.code)}/enabled`,
|
|
6368
|
+
method: 'POST',
|
|
6369
|
+
body: { enabled: !skill.enabled },
|
|
6370
|
+
})
|
|
6371
|
+
.then((r) => r.json())
|
|
6372
|
+
.then((res) => {
|
|
6373
|
+
// Reflect the server's returned flag on the card.
|
|
6374
|
+
skill.enabled = res?.data?.enabled;
|
|
6375
|
+
this.requestUpdate();
|
|
6376
|
+
})
|
|
6377
|
+
.catch((error) => {
|
|
6378
|
+
console.error('kr-chatbot: global skill enable failed', error);
|
|
6379
|
+
})
|
|
6380
|
+
.finally(() => {
|
|
6381
|
+
this._globalSkillPending.delete(skill.code);
|
|
6382
|
+
this.requestUpdate();
|
|
6383
|
+
});
|
|
6384
|
+
}
|
|
6250
6385
|
/** The `+` button toggles the skill/attachment menu open (showing all skills). */
|
|
6251
6386
|
_handlePlusClick() {
|
|
6252
6387
|
if (this._skillMenuOpened) {
|
|
@@ -6266,6 +6401,13 @@
|
|
|
6266
6401
|
this._skillBrowseOpened = false;
|
|
6267
6402
|
this._inputEl?.focus();
|
|
6268
6403
|
}
|
|
6404
|
+
/** Activate a skill card via keyboard (Enter/Space), since it's a div role=button. */
|
|
6405
|
+
_handleSkillCardKeydown(e, skill) {
|
|
6406
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
6407
|
+
e.preventDefault();
|
|
6408
|
+
this._handleSkillSelect(skill);
|
|
6409
|
+
}
|
|
6410
|
+
}
|
|
6269
6411
|
/** Copy a message's text to the clipboard and briefly flash "Copied". */
|
|
6270
6412
|
_handleCopyMessage(content, index) {
|
|
6271
6413
|
navigator.clipboard.writeText(content).then(() => {
|
|
@@ -6281,16 +6423,22 @@
|
|
|
6281
6423
|
this._skillMenuOpened = false;
|
|
6282
6424
|
this._skillBrowseQuery = '';
|
|
6283
6425
|
this._skillBrowseOpened = true;
|
|
6426
|
+
// Fill both tabs up front so switching between them is instant, not stale.
|
|
6284
6427
|
this._searchSkills('');
|
|
6428
|
+
this._searchGlobalSkills('');
|
|
6285
6429
|
}
|
|
6286
6430
|
_handleSkillBrowseClose() {
|
|
6287
6431
|
this._skillBrowseOpened = false;
|
|
6288
6432
|
}
|
|
6289
6433
|
_handleSkillBrowseSearch(e) {
|
|
6290
6434
|
this._skillBrowseQuery = e.target.value;
|
|
6435
|
+
// Filter both catalogs so whichever tab the user switches to is already current.
|
|
6291
6436
|
this._searchSkills(this._skillBrowseQuery);
|
|
6437
|
+
this._searchGlobalSkills(this._skillBrowseQuery);
|
|
6292
6438
|
}
|
|
6293
6439
|
_handleSkillBrowseTabChange(e) {
|
|
6440
|
+
// Both buffers are kept current by open/search, so switching just swaps which
|
|
6441
|
+
// one is shown — no re-search needed.
|
|
6294
6442
|
this._skillBrowseSource = e.detail.activeTabId;
|
|
6295
6443
|
}
|
|
6296
6444
|
// --- Attachments ---
|
|
@@ -6428,9 +6576,21 @@
|
|
|
6428
6576
|
if (submitEvent.defaultPrevented) {
|
|
6429
6577
|
return;
|
|
6430
6578
|
}
|
|
6579
|
+
this._send({
|
|
6580
|
+
message: messageText,
|
|
6581
|
+
internalFiles
|
|
6582
|
+
});
|
|
6583
|
+
}
|
|
6584
|
+
/**
|
|
6585
|
+
* Append the user message and stream the response. Split out of `_handleSubmit`
|
|
6586
|
+
* so debug test triggers can send a specific message without going through the
|
|
6587
|
+
* composer input. `_handleSubmit` owns reading/validating the composer and
|
|
6588
|
+
* firing the cancelable `message-submit` event; this owns the send + stream.
|
|
6589
|
+
*/
|
|
6590
|
+
_send(params) {
|
|
6431
6591
|
const userMessage = {
|
|
6432
6592
|
type: 'user',
|
|
6433
|
-
content:
|
|
6593
|
+
content: params.message,
|
|
6434
6594
|
};
|
|
6435
6595
|
this.messages = [...this.messages, userMessage];
|
|
6436
6596
|
this._inputValue = '';
|
|
@@ -6496,9 +6656,9 @@
|
|
|
6496
6656
|
};
|
|
6497
6657
|
}
|
|
6498
6658
|
this.send({
|
|
6499
|
-
message:
|
|
6659
|
+
message: params.message,
|
|
6500
6660
|
messages: this.messages.slice(0, -1),
|
|
6501
|
-
internalFiles,
|
|
6661
|
+
internalFiles: params.internalFiles,
|
|
6502
6662
|
}).then((response) => {
|
|
6503
6663
|
if (!response.ok) {
|
|
6504
6664
|
this._handleStreamError('Request failed: ' + response.status);
|
|
@@ -6719,6 +6879,14 @@
|
|
|
6719
6879
|
/** The `/` skill menu shown above the composer while typing a slash command. */
|
|
6720
6880
|
_renderSkillMenu() {
|
|
6721
6881
|
const skills = this._skillResults;
|
|
6882
|
+
// Debug test triggers are extra actions after Attach Files / Browse. The
|
|
6883
|
+
// flat keyboard index counts actions first, then skills, so the skills'
|
|
6884
|
+
// index offset is the number of actions shown.
|
|
6885
|
+
const debugActions = this.variant === 'inline' && this._aiFrameworkConfig.debugTelemetryEnabled === true;
|
|
6886
|
+
let actionCount = 2;
|
|
6887
|
+
if (debugActions) {
|
|
6888
|
+
actionCount = 4;
|
|
6889
|
+
}
|
|
6722
6890
|
return b `
|
|
6723
6891
|
<div class="skill-menu">
|
|
6724
6892
|
<div class="skill-menu__actions">
|
|
@@ -6740,6 +6908,26 @@
|
|
|
6740
6908
|
>
|
|
6741
6909
|
Browse All Skills
|
|
6742
6910
|
</button>
|
|
6911
|
+
${debugActions ? b `
|
|
6912
|
+
<button
|
|
6913
|
+
class=${e({
|
|
6914
|
+
'skill-menu__action': true,
|
|
6915
|
+
'skill-menu__item--highlighted': this._skillHighlighted === 2,
|
|
6916
|
+
})}
|
|
6917
|
+
@click=${() => this._activateSkillMenuItem(2)}
|
|
6918
|
+
>
|
|
6919
|
+
Test: context warning
|
|
6920
|
+
</button>
|
|
6921
|
+
<button
|
|
6922
|
+
class=${e({
|
|
6923
|
+
'skill-menu__action': true,
|
|
6924
|
+
'skill-menu__item--highlighted': this._skillHighlighted === 3,
|
|
6925
|
+
})}
|
|
6926
|
+
@click=${() => this._activateSkillMenuItem(3)}
|
|
6927
|
+
>
|
|
6928
|
+
Test: context limit
|
|
6929
|
+
</button>
|
|
6930
|
+
` : A}
|
|
6743
6931
|
</div>
|
|
6744
6932
|
${skills.length ? b `
|
|
6745
6933
|
<ul class="skill-menu__list">
|
|
@@ -6748,7 +6936,7 @@
|
|
|
6748
6936
|
<button
|
|
6749
6937
|
class=${e({
|
|
6750
6938
|
'skill-menu__item': true,
|
|
6751
|
-
'skill-menu__item--highlighted': index +
|
|
6939
|
+
'skill-menu__item--highlighted': index + actionCount === this._skillHighlighted,
|
|
6752
6940
|
})}
|
|
6753
6941
|
@click=${() => this._handleSkillSelect(skill)}
|
|
6754
6942
|
>
|
|
@@ -6787,35 +6975,79 @@
|
|
|
6787
6975
|
${this._renderSkillGrid()}
|
|
6788
6976
|
</kr-tab>
|
|
6789
6977
|
<kr-tab id="kodaris" label="Kodaris">
|
|
6790
|
-
|
|
6791
|
-
Shared Kodaris skills are coming soon.
|
|
6792
|
-
</div>
|
|
6978
|
+
${this._renderSkillGrid(true)}
|
|
6793
6979
|
</kr-tab>
|
|
6794
6980
|
</kr-tab-group>
|
|
6795
6981
|
</div>
|
|
6796
6982
|
</kr-dialog>
|
|
6797
6983
|
`;
|
|
6798
6984
|
}
|
|
6799
|
-
/**
|
|
6800
|
-
|
|
6801
|
-
|
|
6985
|
+
/**
|
|
6986
|
+
* The card grid of skill search results for the browse dialog. Clicking a card
|
|
6987
|
+
* inserts `/code ` into the composer. In the shared-Kodaris (`global`) tab each
|
|
6988
|
+
* card also gets a `+` install button that enables the skill on this instance
|
|
6989
|
+
* (turning into a ✓ once installed).
|
|
6990
|
+
*/
|
|
6991
|
+
_renderSkillGrid(global = false) {
|
|
6992
|
+
let skills = this._skillResults;
|
|
6993
|
+
if (global) {
|
|
6994
|
+
skills = this._globalSkillResults;
|
|
6995
|
+
}
|
|
6802
6996
|
if (!skills.length) {
|
|
6803
6997
|
return A;
|
|
6804
6998
|
}
|
|
6805
6999
|
return b `
|
|
6806
7000
|
<div class="skill-browse__grid">
|
|
6807
7001
|
${skills.map((skill) => b `
|
|
6808
|
-
<
|
|
7002
|
+
<div
|
|
6809
7003
|
class="skill-card"
|
|
7004
|
+
role="button"
|
|
7005
|
+
tabindex="0"
|
|
6810
7006
|
@click=${() => this._handleSkillSelect(skill)}
|
|
7007
|
+
@keydown=${(e) => this._handleSkillCardKeydown(e, skill)}
|
|
6811
7008
|
>
|
|
6812
7009
|
<span class="skill-card__name">${skill.name}</span>
|
|
6813
7010
|
${skill.description ? b `
|
|
6814
7011
|
<span class="skill-card__desc">${skill.description}</span>
|
|
6815
7012
|
` : A}
|
|
6816
|
-
|
|
7013
|
+
${global ? this._renderSkillInstall(skill) : A}
|
|
7014
|
+
</div>
|
|
6817
7015
|
`)}
|
|
6818
7016
|
</div>
|
|
7017
|
+
`;
|
|
7018
|
+
}
|
|
7019
|
+
/**
|
|
7020
|
+
* The install affordance on a global skill card: a `+` button that enables the
|
|
7021
|
+
* skill on this instance, showing a ✓ once installed and a disabled state
|
|
7022
|
+
* while the enable/disable request is in flight.
|
|
7023
|
+
*/
|
|
7024
|
+
_renderSkillInstall(skill) {
|
|
7025
|
+
return b `
|
|
7026
|
+
<button
|
|
7027
|
+
class=${e({
|
|
7028
|
+
'skill-card__install': true,
|
|
7029
|
+
'skill-card__install--installed': !!skill.enabled,
|
|
7030
|
+
})}
|
|
7031
|
+
?disabled=${this._globalSkillPending.has(skill.code)}
|
|
7032
|
+
title=${!!skill.enabled ? 'Installed — click to remove' : 'Install this skill'}
|
|
7033
|
+
aria-label=${!!skill.enabled ? 'Remove skill' : 'Install skill'}
|
|
7034
|
+
@click=${(e) => this._handleGlobalSkillEnable(skill, e)}
|
|
7035
|
+
>
|
|
7036
|
+
<svg
|
|
7037
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
7038
|
+
fill="none"
|
|
7039
|
+
viewBox="0 0 24 24"
|
|
7040
|
+
stroke-width="2"
|
|
7041
|
+
stroke="currentColor"
|
|
7042
|
+
class="skill-card__install-icon"
|
|
7043
|
+
>
|
|
7044
|
+
<path
|
|
7045
|
+
stroke-linecap="round"
|
|
7046
|
+
stroke-linejoin="round"
|
|
7047
|
+
d=${!!skill.enabled ? 'm4.5 12.75 6 6 9-13.5' : 'M12 4.5v15m7.5-7.5h-15'}
|
|
7048
|
+
/>
|
|
7049
|
+
</svg>
|
|
7050
|
+
</button>
|
|
6819
7051
|
`;
|
|
6820
7052
|
}
|
|
6821
7053
|
/**
|
|
@@ -6983,6 +7215,9 @@
|
|
|
6983
7215
|
`;
|
|
6984
7216
|
}
|
|
6985
7217
|
render() {
|
|
7218
|
+
// Debug affordances (drawer toggle, drawer, /test-* options) require the
|
|
7219
|
+
// inline variant AND the server-side debug flag from aiFrameworkConfig.
|
|
7220
|
+
const isDebugEnabled = this.variant === 'inline' && this._aiFrameworkConfig.debugTelemetryEnabled === true;
|
|
6986
7221
|
return b `
|
|
6987
7222
|
${(this.hideToggle || this.variant === 'inline') ? A : b `
|
|
6988
7223
|
<button
|
|
@@ -7013,7 +7248,7 @@
|
|
|
7013
7248
|
'panel--opened': this.opened,
|
|
7014
7249
|
'panel--resizing-debug': this._isDebugResizing,
|
|
7015
7250
|
})}
|
|
7016
|
-
style=${o(
|
|
7251
|
+
style=${o(isDebugEnabled && this._debugPanelOpen ? { paddingRight: `${this._debugDrawerWidth}px` } : {})}
|
|
7017
7252
|
@dragenter=${this._handleDragEnter}
|
|
7018
7253
|
@dragover=${this._handleDragOver}
|
|
7019
7254
|
@dragleave=${this._handleDragLeave}
|
|
@@ -7086,7 +7321,7 @@
|
|
|
7086
7321
|
</div>
|
|
7087
7322
|
</div>
|
|
7088
7323
|
<div class="header-actions">
|
|
7089
|
-
${
|
|
7324
|
+
${isDebugEnabled ? b `
|
|
7090
7325
|
<button
|
|
7091
7326
|
class=${e({
|
|
7092
7327
|
'header-action': true,
|
|
@@ -7405,7 +7640,7 @@
|
|
|
7405
7640
|
</div>
|
|
7406
7641
|
</div>
|
|
7407
7642
|
${this._skillBrowseOpened ? this._renderSkillBrowse() : A}
|
|
7408
|
-
${
|
|
7643
|
+
${isDebugEnabled ? this._renderDebugPanel() : A}
|
|
7409
7644
|
</div>
|
|
7410
7645
|
`;
|
|
7411
7646
|
}
|
|
@@ -8430,6 +8665,7 @@
|
|
|
8430
8665
|
}
|
|
8431
8666
|
|
|
8432
8667
|
.skill-card {
|
|
8668
|
+
position: relative;
|
|
8433
8669
|
display: flex;
|
|
8434
8670
|
flex-direction: column;
|
|
8435
8671
|
gap: 6px;
|
|
@@ -8449,8 +8685,53 @@
|
|
|
8449
8685
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
8450
8686
|
}
|
|
8451
8687
|
|
|
8688
|
+
/* Install button on global skill cards. Absolutely positioned in the corner
|
|
8689
|
+
so it sits above the card's own click target; leave room via the name's
|
|
8690
|
+
padding-right below. */
|
|
8691
|
+
.skill-card__install {
|
|
8692
|
+
position: absolute;
|
|
8693
|
+
top: 10px;
|
|
8694
|
+
right: 10px;
|
|
8695
|
+
display: inline-flex;
|
|
8696
|
+
align-items: center;
|
|
8697
|
+
justify-content: center;
|
|
8698
|
+
width: 24px;
|
|
8699
|
+
height: 24px;
|
|
8700
|
+
padding: 0;
|
|
8701
|
+
border: 1px solid #d1d5db;
|
|
8702
|
+
border-radius: 6px;
|
|
8703
|
+
background: white;
|
|
8704
|
+
color: #374151;
|
|
8705
|
+
cursor: pointer;
|
|
8706
|
+
transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
|
|
8707
|
+
}
|
|
8708
|
+
|
|
8709
|
+
.skill-card__install:hover:not(:disabled) {
|
|
8710
|
+
border-color: var(--kr-chatbot-primary);
|
|
8711
|
+
color: var(--kr-chatbot-primary);
|
|
8712
|
+
}
|
|
8713
|
+
|
|
8714
|
+
.skill-card__install:disabled {
|
|
8715
|
+
opacity: 0.5;
|
|
8716
|
+
cursor: default;
|
|
8717
|
+
}
|
|
8718
|
+
|
|
8719
|
+
/* Installed state: filled check. */
|
|
8720
|
+
.skill-card__install--installed {
|
|
8721
|
+
background: var(--kr-chatbot-primary);
|
|
8722
|
+
border-color: var(--kr-chatbot-primary);
|
|
8723
|
+
color: white;
|
|
8724
|
+
}
|
|
8725
|
+
|
|
8726
|
+
.skill-card__install-icon {
|
|
8727
|
+
width: 15px;
|
|
8728
|
+
height: 15px;
|
|
8729
|
+
}
|
|
8730
|
+
|
|
8452
8731
|
.skill-card__name {
|
|
8453
8732
|
font-weight: 600;
|
|
8733
|
+
/* Leave room for the corner install button on global cards. */
|
|
8734
|
+
padding-right: 28px;
|
|
8454
8735
|
}
|
|
8455
8736
|
|
|
8456
8737
|
.skill-card__desc {
|
|
@@ -8640,6 +8921,12 @@
|
|
|
8640
8921
|
__decorate([
|
|
8641
8922
|
r()
|
|
8642
8923
|
], exports.Chatbot.prototype, "_skillResults", void 0);
|
|
8924
|
+
__decorate([
|
|
8925
|
+
r()
|
|
8926
|
+
], exports.Chatbot.prototype, "_globalSkillResults", void 0);
|
|
8927
|
+
__decorate([
|
|
8928
|
+
r()
|
|
8929
|
+
], exports.Chatbot.prototype, "_aiFrameworkConfig", void 0);
|
|
8643
8930
|
__decorate([
|
|
8644
8931
|
r()
|
|
8645
8932
|
], exports.Chatbot.prototype, "_skillHighlighted", void 0);
|