@olares/lares-mobile 0.15.28 → 0.15.29
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/package.json +2 -2
- package/src/App.vue +8 -7
- package/src/chat/Composer.vue +46 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@olares/lares-mobile",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.29",
|
|
4
4
|
"description": "Lares chat component for LarePass",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"vue": "^3.2.0"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@olares/lares-core": "0.15.
|
|
31
|
+
"@olares/lares-core": "0.15.29",
|
|
32
32
|
"markdown-it": "^14.3.0"
|
|
33
33
|
}
|
|
34
34
|
}
|
package/src/App.vue
CHANGED
|
@@ -730,8 +730,11 @@ export default {
|
|
|
730
730
|
this.items = snap.items;
|
|
731
731
|
this.running = snap.running;
|
|
732
732
|
this.runningSince = snap.runningSince || 0;
|
|
733
|
-
|
|
734
|
-
|
|
733
|
+
// A paused call is not speaking. Only an open call owns the reply;
|
|
734
|
+
// leaving it set after the keyboard returns is how later answers stay silent.
|
|
735
|
+
const voiceSpeaking = this.voiceCall?.connection === "open";
|
|
736
|
+
if (voiceSpeaking) this.reader.stop();
|
|
737
|
+
const target = this.voiceSpeak && !voiceSpeaking && !document.hidden ? speakable(utterance, this.voiceStreaming) : null;
|
|
735
738
|
if (target) this.reader.follow({ ...target, sessionId: snap.sessionId, source: { sessionId: snap.sessionId, turn: target.turn, step: target.step, attemptId: target.attemptId }, key: `${snap.sessionId}:${target.key}` });
|
|
736
739
|
this.failed = snap.failed;
|
|
737
740
|
this.error = snap.error;
|
|
@@ -1546,11 +1549,9 @@ export default {
|
|
|
1546
1549
|
const text = this.draft.trim();
|
|
1547
1550
|
if (!this.canSend) return;
|
|
1548
1551
|
this.player?.unlock();
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
catch (error) { this.voiceError = error?.code || error?.message || "voice_failed"; }
|
|
1553
|
-
} else if (this.voiceCall?.connection === "open") await this.voicePanel.pause();
|
|
1552
|
+
// A typed reply is the chat's. An open voice call would keep the reader
|
|
1553
|
+
// quiet and then not speak this text, so pause it and let the reader take the answer.
|
|
1554
|
+
if (this.voiceCall?.connection === "open") await this.voicePanel.pause();
|
|
1554
1555
|
this.reader.stop();
|
|
1555
1556
|
const sessionId = this.sessionId;
|
|
1556
1557
|
const images = this.draftImages.filter((row) => draftImageStatus(row) === "ready");
|
package/src/chat/Composer.vue
CHANGED
|
@@ -93,6 +93,10 @@
|
|
|
93
93
|
</button>
|
|
94
94
|
</figure>
|
|
95
95
|
</div>
|
|
96
|
+
<div v-if="playbackChrome" class="lares-composer__playback" role="status">
|
|
97
|
+
<span>{{ t(playbackChrome.status) }}</span>
|
|
98
|
+
<button type="button" @click="stopPlayback">{{ t(playbackChrome.stopLabel) }}</button>
|
|
99
|
+
</div>
|
|
96
100
|
<div class="lares-composer__line" :data-expanded="expanded ? 'true' : 'false'">
|
|
97
101
|
<button
|
|
98
102
|
type="button"
|
|
@@ -170,7 +174,8 @@
|
|
|
170
174
|
|
|
171
175
|
<script>
|
|
172
176
|
import { messageFromCode } from "@olares/lares-core/i18n/t";
|
|
173
|
-
import { voiceCallFailure } from "@olares/lares-core/voice/presentation";
|
|
177
|
+
import { voiceCallFailure, voicePlaybackChrome } from "@olares/lares-core/voice/presentation";
|
|
178
|
+
import { readoutState } from "@olares/lares-core/voice/speech";
|
|
174
179
|
import LaresVoiceInput from "./VoiceInput.vue";
|
|
175
180
|
import {
|
|
176
181
|
REFERENCE_DEBOUNCE_MS,
|
|
@@ -214,6 +219,8 @@ export default {
|
|
|
214
219
|
],
|
|
215
220
|
data() {
|
|
216
221
|
return {
|
|
222
|
+
readout: null,
|
|
223
|
+
readoutUnsub: null,
|
|
217
224
|
inputMultiline: false,
|
|
218
225
|
inputWidth: 0,
|
|
219
226
|
inputResizeObserver: null,
|
|
@@ -252,6 +259,9 @@ export default {
|
|
|
252
259
|
callFailure() {
|
|
253
260
|
return this.voiceView?.inputActive ? voiceCallFailure(this.voiceView) : null;
|
|
254
261
|
},
|
|
262
|
+
playbackChrome() {
|
|
263
|
+
return voicePlaybackChrome(this.voiceView, this.readout);
|
|
264
|
+
},
|
|
255
265
|
callFailureText() {
|
|
256
266
|
return this.callFailure ? messageFromCode(this.t, this.callFailure.code, "error.voice_failed") : "";
|
|
257
267
|
},
|
|
@@ -269,6 +279,8 @@ export default {
|
|
|
269
279
|
},
|
|
270
280
|
},
|
|
271
281
|
mounted() {
|
|
282
|
+
this.readout = readoutState.getSnapshot();
|
|
283
|
+
this.readoutUnsub = readoutState.subscribe(() => { this.readout = readoutState.getSnapshot(); });
|
|
272
284
|
this.autosize();
|
|
273
285
|
this.inputResizeObserver = new ResizeObserver(([entry]) => {
|
|
274
286
|
const width = entry.contentRect.width;
|
|
@@ -279,11 +291,16 @@ export default {
|
|
|
279
291
|
this.inputResizeObserver.observe(this.$refs.input);
|
|
280
292
|
},
|
|
281
293
|
beforeUnmount() {
|
|
294
|
+
this.readoutUnsub?.();
|
|
282
295
|
this.inputResizeObserver?.disconnect();
|
|
283
296
|
window.clearTimeout(this.referenceTimer);
|
|
284
297
|
window.removeEventListener("pointerdown", this.onTokenPointer, true);
|
|
285
298
|
},
|
|
286
299
|
methods: {
|
|
300
|
+
stopPlayback() {
|
|
301
|
+
if (this.playbackChrome?.stop === "readout") this.readout?.stop?.();
|
|
302
|
+
else this.$emit("voice-action", { action: "stopPlayback" });
|
|
303
|
+
},
|
|
287
304
|
onInput(event) {
|
|
288
305
|
const value = event.target.value;
|
|
289
306
|
this.$emit("update:draft", value);
|
|
@@ -703,6 +720,34 @@ export default {
|
|
|
703
720
|
stroke-linecap: round;
|
|
704
721
|
}
|
|
705
722
|
|
|
723
|
+
.lares-composer__playback {
|
|
724
|
+
display: flex;
|
|
725
|
+
align-items: center;
|
|
726
|
+
justify-content: space-between;
|
|
727
|
+
gap: 12px;
|
|
728
|
+
padding: 4px 4px 8px;
|
|
729
|
+
color: var(--q-ink-2);
|
|
730
|
+
font-size: 13px;
|
|
731
|
+
line-height: 20px;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
.lares-composer__playback button {
|
|
735
|
+
flex: none;
|
|
736
|
+
min-height: 32px;
|
|
737
|
+
padding: 4px 12px;
|
|
738
|
+
border: 1px solid var(--q-separator);
|
|
739
|
+
border-radius: 999px;
|
|
740
|
+
background: transparent;
|
|
741
|
+
color: var(--q-ink-1);
|
|
742
|
+
font: inherit;
|
|
743
|
+
font-size: 13px;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
.lares-composer__playback button:focus-visible {
|
|
747
|
+
outline: 2px solid var(--q-blue-default, #3377ff);
|
|
748
|
+
outline-offset: 2px;
|
|
749
|
+
}
|
|
750
|
+
|
|
706
751
|
.lares-composer__line {
|
|
707
752
|
display: flex;
|
|
708
753
|
width: 100%;
|