@kodaris/krubble-app-components 1.0.100 → 1.0.102
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 +47 -0
- package/dist/chatbot.d.ts.map +1 -1
- package/dist/chatbot.js +348 -58
- package/dist/chatbot.js.map +1 -1
- package/dist/krubble-app.bundled.js +348 -58
- package/dist/krubble-app.bundled.js.map +1 -1
- package/dist/krubble-app.bundled.min.js +188 -72
- package/dist/krubble-app.bundled.min.js.map +1 -1
- package/dist/krubble-app.umd.js +348 -58
- package/dist/krubble-app.umd.js.map +1 -1
- package/dist/krubble-app.umd.min.js +187 -71
- package/dist/krubble-app.umd.min.js.map +1 -1
- package/package.json +1 -1
|
@@ -5626,6 +5626,24 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
5626
5626
|
* in place; call requestUpdate() after changing it to re-render.
|
|
5627
5627
|
*/
|
|
5628
5628
|
this._globalSkillPending = new Set();
|
|
5629
|
+
/**
|
|
5630
|
+
* The live Web Speech recognizer while dictating, else null. Not reactive —
|
|
5631
|
+
* `_isDictating` drives the UI; this is just the handle used to stop it.
|
|
5632
|
+
*/
|
|
5633
|
+
this._speechRecognition = null;
|
|
5634
|
+
/**
|
|
5635
|
+
* The composer text as it stood when dictation started. Each `result` event
|
|
5636
|
+
* re-reports the whole utterance (final + interim), so the value is rebuilt as
|
|
5637
|
+
* this base + the transcript rather than appended to — appending would repeat
|
|
5638
|
+
* the interim text on every event.
|
|
5639
|
+
*/
|
|
5640
|
+
this._speechBaseValue = '';
|
|
5641
|
+
/**
|
|
5642
|
+
* Transcript of utterances the recognizer has marked final this session.
|
|
5643
|
+
* Held separately because `results` is pruned in continuous mode on some
|
|
5644
|
+
* browsers; without this, earlier sentences would vanish mid-dictation.
|
|
5645
|
+
*/
|
|
5646
|
+
this._speechFinalTranscript = '';
|
|
5629
5647
|
// --- @property (public API) ---
|
|
5630
5648
|
/**
|
|
5631
5649
|
* Callback function for sending messages. Receives the user's message and
|
|
@@ -5775,6 +5793,33 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
5775
5793
|
this._skillBrowseSource = 'org';
|
|
5776
5794
|
/** Search text in the browse dialog, routed to the active tab's search. */
|
|
5777
5795
|
this._skillBrowseQuery = '';
|
|
5796
|
+
/** True while the mic is live and speech is being transcribed into the composer. */
|
|
5797
|
+
this._isDictating = false;
|
|
5798
|
+
/**
|
|
5799
|
+
* Close the `/` skill menu when the click lands outside it.
|
|
5800
|
+
*
|
|
5801
|
+
* Bound at the document because a click anywhere — including outside this
|
|
5802
|
+
* component entirely — should dismiss the menu. `composedPath()` rather than
|
|
5803
|
+
* `e.target` since the menu lives in this component's shadow root, where
|
|
5804
|
+
* `target` on a document listener is retargeted to the host element and every
|
|
5805
|
+
* inside click would look like an outside one.
|
|
5806
|
+
*/
|
|
5807
|
+
this._handleDocumentClick = (e) => {
|
|
5808
|
+
if (!this._skillMenuOpened) {
|
|
5809
|
+
return;
|
|
5810
|
+
}
|
|
5811
|
+
// Excluded: the textarea, since clicking it keeps editing the same `/` command
|
|
5812
|
+
// and dismissing there would fight the user still typing it; and the plus
|
|
5813
|
+
// button, which owns the menu's open/close itself. The plus exclusion matters
|
|
5814
|
+
// in both directions — this listener runs after the button's own handler, so
|
|
5815
|
+
// without it a click that just opened the menu would immediately close it
|
|
5816
|
+
// again, and a click meant to toggle it shut would be double-handled.
|
|
5817
|
+
const path = e.composedPath();
|
|
5818
|
+
if (path.some((el) => el instanceof HTMLElement && (el.classList.contains('skill-menu') || el.classList.contains('input') || el.classList.contains('input-plus')))) {
|
|
5819
|
+
return;
|
|
5820
|
+
}
|
|
5821
|
+
this._skillMenuOpened = false;
|
|
5822
|
+
};
|
|
5778
5823
|
this._handleMouseMove = (e) => {
|
|
5779
5824
|
if (this._isDragging) {
|
|
5780
5825
|
let newLeft = this._dragStartLeft + (e.clientX - this._dragStartX);
|
|
@@ -5918,6 +5963,7 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
5918
5963
|
this.opened = true;
|
|
5919
5964
|
}
|
|
5920
5965
|
this._restoreState();
|
|
5966
|
+
document.addEventListener('click', this._handleDocumentClick);
|
|
5921
5967
|
}
|
|
5922
5968
|
disconnectedCallback() {
|
|
5923
5969
|
super.disconnectedCallback();
|
|
@@ -5927,6 +5973,15 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
5927
5973
|
}
|
|
5928
5974
|
document.removeEventListener('mousemove', this._handleMouseMove);
|
|
5929
5975
|
document.removeEventListener('mouseup', this._handleMouseUp);
|
|
5976
|
+
document.removeEventListener('click', this._handleDocumentClick);
|
|
5977
|
+
// Release the mic if the chatbot is torn down mid-dictation — otherwise the
|
|
5978
|
+
// recognizer keeps the browser's recording indicator lit with nothing to
|
|
5979
|
+
// transcribe into. abort() (not stop()) since the discarded text has nowhere to go.
|
|
5980
|
+
if (this._speechRecognition) {
|
|
5981
|
+
this._speechRecognition.abort();
|
|
5982
|
+
this._speechRecognition = null;
|
|
5983
|
+
this._isDictating = false;
|
|
5984
|
+
}
|
|
5930
5985
|
// Release any object URLs still held by staged attachments.
|
|
5931
5986
|
this._pendingConversationFiles.forEach((p) => {
|
|
5932
5987
|
if (p.previewUrl) {
|
|
@@ -6140,7 +6195,12 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
6140
6195
|
}
|
|
6141
6196
|
const state = JSON.parse(raw);
|
|
6142
6197
|
// Draft text and the debug drawer are persisted for all variants.
|
|
6143
|
-
|
|
6198
|
+
// A lone `/` is dropped — it's the in-progress half of a menu interaction,
|
|
6199
|
+
// not a draft, and restoring it would revive a stale menu the user never
|
|
6200
|
+
// used. Anything with an actual command after the slash (`/code`, `/code do
|
|
6201
|
+
// the thing`) is a real draft and restores normally. Trimmed before testing
|
|
6202
|
+
// so a stray space around the slash doesn't defeat the check.
|
|
6203
|
+
if (typeof state.message === 'string' && state.message.trim() !== '/') {
|
|
6144
6204
|
this._inputValue = state.message;
|
|
6145
6205
|
}
|
|
6146
6206
|
this._debugPanelOpen = state.debugPanelOpen === 'true';
|
|
@@ -6533,6 +6593,92 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
6533
6593
|
this._skillMenuOpened = true;
|
|
6534
6594
|
this._searchInvocableSkills('');
|
|
6535
6595
|
}
|
|
6596
|
+
/**
|
|
6597
|
+
* The mic button toggles dictation. Speech is transcribed straight into the
|
|
6598
|
+
* composer as editable text — the user still reviews and presses send, so a
|
|
6599
|
+
* misheard word is fixable and nothing is sent by voice alone.
|
|
6600
|
+
*
|
|
6601
|
+
* Recognition runs entirely in the browser's Web Speech API (Chrome/Edge send
|
|
6602
|
+
* audio to Google's service, Safari to Apple's). No audio reaches Kodaris, and
|
|
6603
|
+
* there's no backend call or cost on our side.
|
|
6604
|
+
*/
|
|
6605
|
+
_handleMicClick() {
|
|
6606
|
+
// Second click stops. `stop()` flushes the pending utterance as a final
|
|
6607
|
+
// result, so the last words aren't lost; onend clears the state.
|
|
6608
|
+
if (this._speechRecognition) {
|
|
6609
|
+
this._speechRecognition.stop();
|
|
6610
|
+
return;
|
|
6611
|
+
}
|
|
6612
|
+
// Prefixed in Chrome/Edge/Safari; the unprefixed name is checked first for
|
|
6613
|
+
// whenever they drop the prefix. The mic button isn't rendered without one of
|
|
6614
|
+
// these, so reaching here unsupported means a mid-session change — bail quietly.
|
|
6615
|
+
const SpeechRecognitionCtor = window.SpeechRecognition || window.webkitSpeechRecognition;
|
|
6616
|
+
if (!SpeechRecognitionCtor) {
|
|
6617
|
+
return;
|
|
6618
|
+
}
|
|
6619
|
+
const recognition = new SpeechRecognitionCtor();
|
|
6620
|
+
// Keep listening through natural pauses — a chat message is often several
|
|
6621
|
+
// sentences, and the default stops at the first one.
|
|
6622
|
+
recognition.continuous = true;
|
|
6623
|
+
// Show words as they're recognized rather than only at end of utterance.
|
|
6624
|
+
recognition.interimResults = true;
|
|
6625
|
+
// `lang` is deliberately left unset so the recognizer follows the page/OS
|
|
6626
|
+
// language, which is correct for every consumer of this component.
|
|
6627
|
+
// Dictation ADDS to whatever is already typed rather than replacing it, so
|
|
6628
|
+
// the user can type half a message and speak the rest. The separating space
|
|
6629
|
+
// is part of the base so the transcript never runs into the existing word.
|
|
6630
|
+
this._speechBaseValue = this._inputValue;
|
|
6631
|
+
if (this._speechBaseValue && !this._speechBaseValue.endsWith(' ')) {
|
|
6632
|
+
this._speechBaseValue += ' ';
|
|
6633
|
+
}
|
|
6634
|
+
this._speechFinalTranscript = '';
|
|
6635
|
+
recognition.onresult = (e) => {
|
|
6636
|
+
// Everything from resultIndex on is new since the last event: finalized
|
|
6637
|
+
// pieces move to _speechFinalTranscript permanently, while non-final ones
|
|
6638
|
+
// are rebuilt each event because the browser revises its guess as it hears
|
|
6639
|
+
// more. Rebuilding the whole value (base + final + interim) is what keeps
|
|
6640
|
+
// those revisions from stacking up as duplicated text.
|
|
6641
|
+
let interim = '';
|
|
6642
|
+
for (let i = e.resultIndex; i < e.results.length; i++) {
|
|
6643
|
+
if (e.results[i].isFinal) {
|
|
6644
|
+
this._speechFinalTranscript += e.results[i][0].transcript;
|
|
6645
|
+
}
|
|
6646
|
+
else {
|
|
6647
|
+
interim += e.results[i][0].transcript;
|
|
6648
|
+
}
|
|
6649
|
+
}
|
|
6650
|
+
this._inputValue = this._speechBaseValue + this._speechFinalTranscript + interim;
|
|
6651
|
+
this._saveState();
|
|
6652
|
+
// Grow the textarea as dictated text wraps — the same clamp the typing path
|
|
6653
|
+
// uses, since @input doesn't fire for programmatic value changes.
|
|
6654
|
+
if (this._inputEl) {
|
|
6655
|
+
this._inputEl.style.height = 'auto';
|
|
6656
|
+
this._inputEl.style.height = Math.min(this._inputEl.scrollHeight, 120) + 'px';
|
|
6657
|
+
}
|
|
6658
|
+
};
|
|
6659
|
+
recognition.onerror = (e) => {
|
|
6660
|
+
// Denied mic permission is the one error worth explaining — the user has to
|
|
6661
|
+
// fix it in browser settings, and the button would otherwise look broken.
|
|
6662
|
+
if (e.error === 'not-allowed' || e.error === 'service-not-allowed') {
|
|
6663
|
+
this._error = 'Microphone access is blocked. Allow it in your browser settings to use voice input.';
|
|
6664
|
+
return;
|
|
6665
|
+
}
|
|
6666
|
+
// `no-speech` (silence) and `aborted` (user stopped) are normal; anything
|
|
6667
|
+
// else is a transient service hiccup. onend still runs, so the mic resets.
|
|
6668
|
+
};
|
|
6669
|
+
// Fires on stop(), on error, and when the browser ends recognition on its own
|
|
6670
|
+
// (e.g. a long silence) — the single place the live state is torn down.
|
|
6671
|
+
recognition.onend = () => {
|
|
6672
|
+
this._speechRecognition = null;
|
|
6673
|
+
this._isDictating = false;
|
|
6674
|
+
};
|
|
6675
|
+
// start() is what triggers the browser's mic permission prompt.
|
|
6676
|
+
this._speechRecognition = recognition;
|
|
6677
|
+
this._isDictating = true;
|
|
6678
|
+
this._error = '';
|
|
6679
|
+
recognition.start();
|
|
6680
|
+
this._inputEl?.focus();
|
|
6681
|
+
}
|
|
6536
6682
|
/** Insert `/code ` into the composer and focus it, ready for the message. */
|
|
6537
6683
|
_handleSkillSelect(skill) {
|
|
6538
6684
|
// A global skill must be installed before it can be chosen — inserting /code
|
|
@@ -6602,6 +6748,15 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
6602
6748
|
// the menu interaction, and the picker is a separate window so the menu would
|
|
6603
6749
|
// otherwise linger open behind it (and after a file is chosen).
|
|
6604
6750
|
this._skillMenuOpened = false;
|
|
6751
|
+
// A lone `/` was only the means of reaching this action, so it shouldn't be
|
|
6752
|
+
// left behind in the composer once the picker opens. Nothing else is touched:
|
|
6753
|
+
// a real command (`/code`) stays because `/code` plus an attachment is a valid
|
|
6754
|
+
// invocation, and the plus button reaches this same method with an arbitrary
|
|
6755
|
+
// draft in the composer, which must survive.
|
|
6756
|
+
if (this._inputValue.trim() === '/') {
|
|
6757
|
+
this._inputValue = '';
|
|
6758
|
+
this._saveState();
|
|
6759
|
+
}
|
|
6605
6760
|
this._pendingAttachAnalyze = aiAnalyze;
|
|
6606
6761
|
this._fileInputEl?.click();
|
|
6607
6762
|
}
|
|
@@ -6803,6 +6958,13 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
6803
6958
|
previewUrl: p.previewUrl,
|
|
6804
6959
|
}));
|
|
6805
6960
|
}
|
|
6961
|
+
// The message is on its way, so stop the mic if it's still live — otherwise
|
|
6962
|
+
// it would keep transcribing into the now-cleared composer. abort() rather
|
|
6963
|
+
// than stop() because a trailing pending utterance belongs to the message
|
|
6964
|
+
// just sent; flushing it would drop stray words into the next one.
|
|
6965
|
+
if (this._speechRecognition) {
|
|
6966
|
+
this._speechRecognition.abort();
|
|
6967
|
+
}
|
|
6806
6968
|
this.messages = [...this.messages, userMessage];
|
|
6807
6969
|
this._inputValue = '';
|
|
6808
6970
|
this._saveState();
|
|
@@ -7156,6 +7318,122 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
7156
7318
|
}
|
|
7157
7319
|
}
|
|
7158
7320
|
// --- Render ---
|
|
7321
|
+
/**
|
|
7322
|
+
* The composer's controls: attach, the textarea, the mic, and send/stop.
|
|
7323
|
+
*
|
|
7324
|
+
* While dictating, only the mic swaps — to a stop button — so the textarea
|
|
7325
|
+
* stays visible with the transcript streaming into it and send stays reachable.
|
|
7326
|
+
*/
|
|
7327
|
+
_renderComposerControls() {
|
|
7328
|
+
return b `
|
|
7329
|
+
<button
|
|
7330
|
+
class="input-plus"
|
|
7331
|
+
@click=${this._handlePlusClick}
|
|
7332
|
+
>+</button>
|
|
7333
|
+
<textarea
|
|
7334
|
+
class="input"
|
|
7335
|
+
.value=${this._inputValue}
|
|
7336
|
+
placeholder=${this.placeholder}
|
|
7337
|
+
rows="1"
|
|
7338
|
+
@input=${this._handleInputChange}
|
|
7339
|
+
@keydown=${this._handleKeyDown}
|
|
7340
|
+
@paste=${this._handlePaste}
|
|
7341
|
+
></textarea>
|
|
7342
|
+
${window.SpeechRecognition || window.webkitSpeechRecognition ? b `
|
|
7343
|
+
${this._isDictating ? b `
|
|
7344
|
+
<button
|
|
7345
|
+
class="mic-stop"
|
|
7346
|
+
title="Stop dictation"
|
|
7347
|
+
aria-label="Stop dictation"
|
|
7348
|
+
@click=${this._handleMicClick}
|
|
7349
|
+
>
|
|
7350
|
+
<svg
|
|
7351
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
7352
|
+
fill="currentColor"
|
|
7353
|
+
viewBox="0 0 24 24"
|
|
7354
|
+
class="mic-stop-icon"
|
|
7355
|
+
aria-hidden="true"
|
|
7356
|
+
>
|
|
7357
|
+
<rect
|
|
7358
|
+
x="6"
|
|
7359
|
+
y="6"
|
|
7360
|
+
width="12"
|
|
7361
|
+
height="12"
|
|
7362
|
+
rx="2"
|
|
7363
|
+
/>
|
|
7364
|
+
</svg>
|
|
7365
|
+
</button>
|
|
7366
|
+
` : b `
|
|
7367
|
+
<button
|
|
7368
|
+
class="mic"
|
|
7369
|
+
title="Dictate a message"
|
|
7370
|
+
aria-label="Dictate a message"
|
|
7371
|
+
@click=${this._handleMicClick}
|
|
7372
|
+
>
|
|
7373
|
+
<svg
|
|
7374
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
7375
|
+
viewBox="0 0 24 24"
|
|
7376
|
+
fill="none"
|
|
7377
|
+
stroke="currentColor"
|
|
7378
|
+
stroke-width="1.75"
|
|
7379
|
+
stroke-linecap="round"
|
|
7380
|
+
stroke-linejoin="round"
|
|
7381
|
+
class="mic-icon"
|
|
7382
|
+
aria-hidden="true"
|
|
7383
|
+
>
|
|
7384
|
+
<path d="M12 15a3 3 0 0 0 3-3V6a3 3 0 0 0-6 0v6a3 3 0 0 0 3 3Z"/>
|
|
7385
|
+
<path d="M19 11a7 7 0 0 1-14 0"/>
|
|
7386
|
+
<path d="M12 18v3"/>
|
|
7387
|
+
</svg>
|
|
7388
|
+
</button>
|
|
7389
|
+
`}
|
|
7390
|
+
` : A}
|
|
7391
|
+
${this._isStreaming ? b `
|
|
7392
|
+
<button
|
|
7393
|
+
class="stop"
|
|
7394
|
+
@click=${this._handleStop}
|
|
7395
|
+
title="Stop generating"
|
|
7396
|
+
>
|
|
7397
|
+
<svg
|
|
7398
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
7399
|
+
fill="currentColor"
|
|
7400
|
+
viewBox="0 0 24 24"
|
|
7401
|
+
class="stop-icon"
|
|
7402
|
+
>
|
|
7403
|
+
<rect
|
|
7404
|
+
x="6"
|
|
7405
|
+
y="6"
|
|
7406
|
+
width="12"
|
|
7407
|
+
height="12"
|
|
7408
|
+
rx="2"
|
|
7409
|
+
/>
|
|
7410
|
+
</svg>
|
|
7411
|
+
</button>
|
|
7412
|
+
` : b `
|
|
7413
|
+
<button
|
|
7414
|
+
class=${e({
|
|
7415
|
+
'send': true,
|
|
7416
|
+
'send--disabled': !this._inputValue.trim() && !this._pendingConversationFiles.length,
|
|
7417
|
+
})}
|
|
7418
|
+
@click=${this._handleSubmit}
|
|
7419
|
+
title="Send message"
|
|
7420
|
+
>
|
|
7421
|
+
<svg
|
|
7422
|
+
width="16"
|
|
7423
|
+
height="16"
|
|
7424
|
+
viewBox="0 0 24 24"
|
|
7425
|
+
fill="none"
|
|
7426
|
+
stroke="currentColor"
|
|
7427
|
+
stroke-width="2.5"
|
|
7428
|
+
stroke-linecap="round"
|
|
7429
|
+
class="send-icon"
|
|
7430
|
+
>
|
|
7431
|
+
<path d="M12 19V5M5 12l7-7 7 7"/>
|
|
7432
|
+
</svg>
|
|
7433
|
+
</button>
|
|
7434
|
+
`}
|
|
7435
|
+
`;
|
|
7436
|
+
}
|
|
7159
7437
|
/** The `/` skill menu shown above the composer while typing a slash command. */
|
|
7160
7438
|
_renderSkillMenu() {
|
|
7161
7439
|
const skills = this._invocableSkills;
|
|
@@ -7862,63 +8140,7 @@ let KRChatbot = class KRChatbot extends i$3 {
|
|
|
7862
8140
|
/>
|
|
7863
8141
|
${this._skillMenuOpened ? this._renderSkillMenu() : A}
|
|
7864
8142
|
<div class="input-wrapper">
|
|
7865
|
-
|
|
7866
|
-
class="input-plus"
|
|
7867
|
-
@click=${this._handlePlusClick}
|
|
7868
|
-
>+</button>
|
|
7869
|
-
<textarea
|
|
7870
|
-
class="input"
|
|
7871
|
-
.value=${this._inputValue}
|
|
7872
|
-
placeholder=${this.placeholder}
|
|
7873
|
-
rows="1"
|
|
7874
|
-
@input=${this._handleInputChange}
|
|
7875
|
-
@keydown=${this._handleKeyDown}
|
|
7876
|
-
@paste=${this._handlePaste}
|
|
7877
|
-
></textarea>
|
|
7878
|
-
${this._isStreaming ? b `
|
|
7879
|
-
<button
|
|
7880
|
-
class="stop"
|
|
7881
|
-
@click=${this._handleStop}
|
|
7882
|
-
title="Stop generating"
|
|
7883
|
-
>
|
|
7884
|
-
<svg
|
|
7885
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
7886
|
-
fill="currentColor"
|
|
7887
|
-
viewBox="0 0 24 24"
|
|
7888
|
-
class="stop-icon"
|
|
7889
|
-
>
|
|
7890
|
-
<rect
|
|
7891
|
-
x="6"
|
|
7892
|
-
y="6"
|
|
7893
|
-
width="12"
|
|
7894
|
-
height="12"
|
|
7895
|
-
rx="2"
|
|
7896
|
-
/>
|
|
7897
|
-
</svg>
|
|
7898
|
-
</button>
|
|
7899
|
-
` : b `
|
|
7900
|
-
<button
|
|
7901
|
-
class=${e({
|
|
7902
|
-
'send': true,
|
|
7903
|
-
'send--disabled': !this._inputValue.trim() && !this._pendingConversationFiles.length,
|
|
7904
|
-
})}
|
|
7905
|
-
@click=${this._handleSubmit}
|
|
7906
|
-
title="Send message"
|
|
7907
|
-
>
|
|
7908
|
-
<svg
|
|
7909
|
-
width="16"
|
|
7910
|
-
height="16"
|
|
7911
|
-
viewBox="0 0 24 24"
|
|
7912
|
-
fill="none"
|
|
7913
|
-
stroke="currentColor"
|
|
7914
|
-
stroke-width="2.5"
|
|
7915
|
-
stroke-linecap="round"
|
|
7916
|
-
class="send-icon"
|
|
7917
|
-
>
|
|
7918
|
-
<path d="M12 19V5M5 12l7-7 7 7"/>
|
|
7919
|
-
</svg>
|
|
7920
|
-
</button>
|
|
7921
|
-
`}
|
|
8143
|
+
${this._renderComposerControls()}
|
|
7922
8144
|
</div>
|
|
7923
8145
|
</div>
|
|
7924
8146
|
${this._skillBrowseOpened ? this._renderSkillBrowse() : A}
|
|
@@ -8718,6 +8940,71 @@ KRChatbot.styles = i$6 `
|
|
|
8718
8940
|
color: #b0b0b0;
|
|
8719
8941
|
}
|
|
8720
8942
|
|
|
8943
|
+
/* margin-right rather than a wider .input-wrapper gap, which would also push
|
|
8944
|
+
the plus button and textarea apart. Applied to both mic states so the
|
|
8945
|
+
spacing doesn't shift when dictation swaps one for the other. */
|
|
8946
|
+
.mic,
|
|
8947
|
+
.mic-stop {
|
|
8948
|
+
margin-right: 4px;
|
|
8949
|
+
}
|
|
8950
|
+
|
|
8951
|
+
.mic {
|
|
8952
|
+
width: 34px;
|
|
8953
|
+
height: 34px;
|
|
8954
|
+
border: none;
|
|
8955
|
+
background: transparent;
|
|
8956
|
+
color: var(--kr-chatbot-text);
|
|
8957
|
+
border-radius: 50%;
|
|
8958
|
+
cursor: pointer;
|
|
8959
|
+
display: flex;
|
|
8960
|
+
align-items: center;
|
|
8961
|
+
justify-content: center;
|
|
8962
|
+
flex-shrink: 0;
|
|
8963
|
+
transition: background 0.15s, color 0.15s;
|
|
8964
|
+
}
|
|
8965
|
+
|
|
8966
|
+
.mic:hover {
|
|
8967
|
+
background: #f8f9fc;
|
|
8968
|
+
}
|
|
8969
|
+
|
|
8970
|
+
.mic-icon {
|
|
8971
|
+
width: 21px;
|
|
8972
|
+
height: 21px;
|
|
8973
|
+
}
|
|
8974
|
+
|
|
8975
|
+
/* Dictation swaps the mic for this stop button, sitting in the mic's slot so
|
|
8976
|
+
the composer's layout is unchanged and the textarea stays visible with the
|
|
8977
|
+
transcript streaming into it. Structurally identical to .stop; only the
|
|
8978
|
+
colors differ — dictation isn't an error state, so it takes a neutral grey
|
|
8979
|
+
instead of the error red, and that also keeps it distinct from the solid
|
|
8980
|
+
send button sitting right beside it. The icon is dark rather than white
|
|
8981
|
+
because white on this grey wouldn't meet contrast. */
|
|
8982
|
+
.mic-stop {
|
|
8983
|
+
width: 34px;
|
|
8984
|
+
height: 34px;
|
|
8985
|
+
border-radius: 50%;
|
|
8986
|
+
background: #eae7e7;
|
|
8987
|
+
color: #1f1f1f;
|
|
8988
|
+
border: none;
|
|
8989
|
+
cursor: pointer;
|
|
8990
|
+
display: flex;
|
|
8991
|
+
align-items: center;
|
|
8992
|
+
justify-content: center;
|
|
8993
|
+
flex-shrink: 0;
|
|
8994
|
+
transition: all 0.15s;
|
|
8995
|
+
}
|
|
8996
|
+
|
|
8997
|
+
.mic-stop:hover {
|
|
8998
|
+
background: #ded9d9;
|
|
8999
|
+
}
|
|
9000
|
+
|
|
9001
|
+
/* Larger than .stop-icon's 14px: the rect fills only half its 24-unit
|
|
9002
|
+
viewBox, so the drawn square is half this value. */
|
|
9003
|
+
.mic-stop-icon {
|
|
9004
|
+
width: 20px;
|
|
9005
|
+
height: 20px;
|
|
9006
|
+
}
|
|
9007
|
+
|
|
8721
9008
|
.send {
|
|
8722
9009
|
width: 34px;
|
|
8723
9010
|
height: 34px;
|
|
@@ -9345,6 +9632,9 @@ __decorate([
|
|
|
9345
9632
|
__decorate([
|
|
9346
9633
|
r()
|
|
9347
9634
|
], KRChatbot.prototype, "_skillBrowseQuery", void 0);
|
|
9635
|
+
__decorate([
|
|
9636
|
+
r()
|
|
9637
|
+
], KRChatbot.prototype, "_isDictating", void 0);
|
|
9348
9638
|
__decorate([
|
|
9349
9639
|
e$3('.panel')
|
|
9350
9640
|
], KRChatbot.prototype, "_panelEl", void 0);
|