(null);
@@ -1759,6 +1982,16 @@ watch(goalBannerCollapsed, async () => {
emitComposerHeight();
});
+// 发送消息或输入锁定时,自动停止语音录音
+watch(
+ () => props.streamingMessage || props.inputLocked,
+ (shouldStop) => {
+ if (shouldStop && isRecording.value) {
+ stopVoiceRecording();
+ }
+ }
+);
+
onMounted(() => {
document.addEventListener('click', closeProjectGitMenu);
adjustTextareaSize();
@@ -1782,6 +2015,7 @@ onMounted(() => {
});
onBeforeUnmount(() => {
+ stopVoiceRecording();
document.removeEventListener('click', closeProjectGitMenu);
editor.value?.destroy();
runtimeQueueItemRefs.forEach((el) => {
diff --git a/static/src/styles/components/input/_composer.scss b/static/src/styles/components/input/_composer.scss
index 648c19d..449099d 100644
--- a/static/src/styles/components/input/_composer.scss
+++ b/static/src/styles/components/input/_composer.scss
@@ -803,6 +803,90 @@ body[data-theme='light'] {
background-color: color-mix(in srgb, var(--on-accent) 40%, transparent);
}
+.input-actions-right {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+}
+
+/* 语音输入按钮 */
+.voice-btn {
+ color: var(--text-primary);
+}
+
+:root[data-theme='classic'] .voice-btn,
+:root:not([data-theme]) .voice-btn {
+ color: var(--accent);
+}
+
+.voice-btn:hover:not(:disabled) {
+ background: var(--hover-bg);
+}
+
+.voice-btn:disabled {
+ opacity: 0.4;
+ cursor: not-allowed;
+}
+
+.voice-btn .mic-icon {
+ display: block;
+ flex-shrink: 0;
+}
+
+/* 声纹动画 */
+.voice-btn--recording {
+ background: var(--hover-bg) !important;
+}
+
+.voice-wave {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 2.5px;
+ height: 18px;
+}
+
+.voice-wave-bar {
+ display: inline-block;
+ width: 2.5px;
+ height: 16px;
+ border-radius: 2px;
+ background: currentColor;
+ transform-origin: center;
+ animation: voice-wave-pulse 0.9s ease-in-out infinite;
+}
+
+.voice-wave-bar:nth-child(1) {
+ animation-delay: 0s;
+ animation-duration: 0.75s;
+}
+
+.voice-wave-bar:nth-child(2) {
+ animation-delay: 0.15s;
+ animation-duration: 0.9s;
+}
+
+.voice-wave-bar:nth-child(3) {
+ animation-delay: 0.3s;
+ animation-duration: 1.05s;
+}
+
+.voice-wave-bar:nth-child(4) {
+ animation-delay: 0.1s;
+ animation-duration: 0.85s;
+}
+
+@keyframes voice-wave-pulse {
+ 0%, 100% {
+ transform: scaleY(0.25);
+ opacity: 0.6;
+ }
+ 50% {
+ transform: scaleY(1);
+ opacity: 1;
+ }
+}
+
.file-input-hidden {
position: absolute;
opacity: 0;
diff --git a/static/voice_test.html b/static/voice_test.html
new file mode 100644
index 0000000..4570cbc
--- /dev/null
+++ b/static/voice_test.html
@@ -0,0 +1,111 @@
+
+
+
+
+ Web Speech API 诊断
+
+
+
+ Web Speech API 诊断
+ 点击按钮后对着麦克风说话,等待 3-5 秒,观察下方日志。
+
+
+
+
+
+
+