mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
fix(desktop): scope dictation to composer context
Signed-off-by: John Tennant <jtennant@squareup.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { useCallback, useRef } from "react";
|
||||
import { replaceTrailingTranscribedText } from "../lib/voiceInput";
|
||||
import { useCallback } from "react";
|
||||
import { appendTranscribedText } from "../lib/voiceInput";
|
||||
import { useLocalDictation } from "./useLocalDictation";
|
||||
|
||||
interface UseDictationOptions {
|
||||
@@ -16,30 +16,15 @@ export function useDictation({
|
||||
getText,
|
||||
setText,
|
||||
}: UseDictationOptions) {
|
||||
const lastTranscriptRef = useRef("");
|
||||
|
||||
const handleTranscript = useCallback(
|
||||
(transcript: string) => {
|
||||
const previous = lastTranscriptRef.current;
|
||||
const latest = getText();
|
||||
const merged = replaceTrailingTranscribedText(
|
||||
latest,
|
||||
previous,
|
||||
transcript,
|
||||
);
|
||||
setText(merged);
|
||||
// Each native flush is an independent segment, so the next transcript
|
||||
// appends instead of replacing this one.
|
||||
lastTranscriptRef.current = "";
|
||||
setText(appendTranscribedText(getText(), transcript));
|
||||
},
|
||||
[getText, setText],
|
||||
);
|
||||
|
||||
const dictation = useLocalDictation({
|
||||
disabled,
|
||||
onRecordingStart: () => {
|
||||
lastTranscriptRef.current = "";
|
||||
},
|
||||
onTranscriptText: handleTranscript,
|
||||
});
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import {
|
||||
appendTranscribedText,
|
||||
getDictationSendDecision,
|
||||
replaceTrailingTranscribedText,
|
||||
shouldAutoSubmitDictation,
|
||||
} from "./voiceInput.ts";
|
||||
|
||||
@@ -71,46 +71,16 @@ test("dictation auto-submit waits until capture and transcription settle", () =>
|
||||
);
|
||||
});
|
||||
|
||||
// ── replaceTrailingTranscribedText ──────────────────────────────────────────
|
||||
// ── appendTranscribedText ───────────────────────────────────────────────────
|
||||
|
||||
test("replaceTrailingTranscribedText_appendsWhenNoPrevious", () => {
|
||||
assert.equal(
|
||||
replaceTrailingTranscribedText("Hello", "", "world"),
|
||||
"Hello world",
|
||||
);
|
||||
test("appendTranscribedText_appendsToExistingText", () => {
|
||||
assert.equal(appendTranscribedText("Hello", "world"), "Hello world");
|
||||
});
|
||||
|
||||
test("replaceTrailingTranscribedText_appendsToEmptyBase", () => {
|
||||
assert.equal(replaceTrailingTranscribedText("", "", "hello"), "hello");
|
||||
test("appendTranscribedText_appendsToEmptyBase", () => {
|
||||
assert.equal(appendTranscribedText("", "hello"), "hello");
|
||||
});
|
||||
|
||||
test("replaceTrailingTranscribedText_replacesTrailingInterim", () => {
|
||||
// Interim "hello wor" is refined to "hello world".
|
||||
assert.equal(
|
||||
replaceTrailingTranscribedText("hello wor", "hello wor", "hello world"),
|
||||
"hello world",
|
||||
);
|
||||
});
|
||||
|
||||
test("replaceTrailingTranscribedText_preservesTextTypedBeforeDictation", () => {
|
||||
// User typed "Note: " then dictated; the manual prefix must survive.
|
||||
assert.equal(
|
||||
replaceTrailingTranscribedText("Note: hi", "hi", "hi there"),
|
||||
"Note: hi there",
|
||||
);
|
||||
});
|
||||
|
||||
test("replaceTrailingTranscribedText_appendsWhenPreviousNoLongerMatches", () => {
|
||||
// If the previous transcript isn't the trailing text anymore, append.
|
||||
assert.equal(
|
||||
replaceTrailingTranscribedText("edited text", "old", "new"),
|
||||
"edited text new",
|
||||
);
|
||||
});
|
||||
|
||||
test("replaceTrailingTranscribedText_noDoubleSpaceBeforePunctuation", () => {
|
||||
assert.equal(
|
||||
replaceTrailingTranscribedText("Hello", "", ", world"),
|
||||
"Hello, world",
|
||||
);
|
||||
test("appendTranscribedText_avoidsSpaceBeforePunctuation", () => {
|
||||
assert.equal(appendTranscribedText("Hello", ", world"), "Hello, world");
|
||||
});
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
function appendTranscribedText(baseText: string, fragment: string): string {
|
||||
export function appendTranscribedText(
|
||||
baseText: string,
|
||||
fragment: string,
|
||||
): string {
|
||||
const normalizedFragment = fragment.replace(/\s+/g, " ").trim();
|
||||
if (!normalizedFragment) return baseText;
|
||||
if (!baseText.trim()) return normalizedFragment;
|
||||
@@ -37,30 +40,3 @@ export function shouldAutoSubmitDictation({
|
||||
}): boolean {
|
||||
return requested && !isRecording && !isStarting && !isTranscribing;
|
||||
}
|
||||
|
||||
export function replaceTrailingTranscribedText(
|
||||
fullText: string,
|
||||
previousTranscribedText: string,
|
||||
nextTranscribedText: string,
|
||||
): string {
|
||||
if (!previousTranscribedText) {
|
||||
return appendTranscribedText(fullText, nextTranscribedText);
|
||||
}
|
||||
|
||||
if (fullText.endsWith(previousTranscribedText)) {
|
||||
return appendTranscribedText(
|
||||
fullText.slice(0, -previousTranscribedText.length),
|
||||
nextTranscribedText,
|
||||
);
|
||||
}
|
||||
|
||||
const trimmedPreviousText = previousTranscribedText.trim();
|
||||
if (trimmedPreviousText && fullText.endsWith(trimmedPreviousText)) {
|
||||
return appendTranscribedText(
|
||||
fullText.slice(0, -trimmedPreviousText.length),
|
||||
nextTranscribedText,
|
||||
);
|
||||
}
|
||||
|
||||
return appendTranscribedText(fullText, nextTranscribedText);
|
||||
}
|
||||
|
||||
@@ -274,7 +274,7 @@ function MessageComposerImpl({
|
||||
isUploadingRef,
|
||||
setComposerContent,
|
||||
setEditorContent: richText.setContent,
|
||||
draftKey: effectiveDraftKey,
|
||||
draftKey: `${effectiveDraftKey}\0${editTarget?.id ?? ""}\0${replyTarget?.id ?? ""}`,
|
||||
composerRef: composerScrollRef,
|
||||
submitMessageRef,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user