Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
251505c
feat: implement dual-phase contextual transcription for dictation (GH…
rosscado Oct 26, 2025
7fa17a2
fix: add type annotations to EventBus listeners
rosscado Oct 26, 2025
6168c19
fix: add type annotations to remaining EventBus listeners
rosscado Oct 26, 2025
e45fd7e
refactor: export and reuse DictationTranscribedEvent type (DRY)
rosscado Oct 26, 2025
33069fc
fix(i18n): abort on JSON parse failure to prevent data loss
rosscado Oct 26, 2025
24a9d5f
fix: increase audio buffer limit to 120s for full-context refinement
rosscado Oct 26, 2025
63d1dd2
Merge branch 'main' into feature/gh-256-dual-phase-transcription
rosscado Oct 26, 2025
59500eb
fix: map refinement sequence to target for response routing
rosscado Oct 26, 2025
ae3e7bd
refactor: address Copilot code review feedback
rosscado Oct 26, 2025
a4e1d63
refactor: address Cheetah code review feedback
rosscado Oct 26, 2025
2a56558
fix: address Codex code review - target switch and manual edit bugs
rosscado Oct 26, 2025
d02529f
feat: enhance dictation machine with refinement sequence tracking
rosscado Oct 26, 2025
bf0f3d9
feat: document conversation machine with additional parameters for au…
rosscado Oct 26, 2025
e38bd7a
feat: integrate audio segment persistence in state machines
rosscado Oct 27, 2025
c205943
feat: implement dual-phase transcription refinement process
rosscado Oct 27, 2025
37b4ca6
Update src/state-machines/DictationMachine.ts
rosscado Oct 27, 2025
f2d0644
Update src/state-machines/DictationMachine.ts
rosscado Oct 27, 2025
3b036d2
Update src/state-machines/DictationMachine.ts
rosscado Oct 27, 2025
357fcf0
Update src/audio/AudioSegmentPersistence.ts
rosscado Oct 27, 2025
e3fa8dc
Update src/TranscriptionModule.ts
rosscado Oct 27, 2025
4372dee
docs: address Copilot code review feedback on PR-259
rosscado Oct 27, 2025
49361ec
docs: update CLAUDE.md and refine comments in transcription and dicta…
rosscado Oct 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions src/UniversalDictationModule.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Observation } from "./dom/Observation";
import { addChild } from "./dom/DOMModule";
import { createDictationMachine } from "./state-machines/DictationMachine";
import { createDictationMachine, DictationTranscribedEvent } from "./state-machines/DictationMachine";
import { interpret } from "xstate";
import EventBus from "./events/EventBus.js";
import { IconModule } from "./icons/IconModule";
Expand Down Expand Up @@ -328,6 +328,19 @@ export class UniversalDictationModule {
};

const hideButton = () => {
// Trigger refinement if dictation is active for this element
if (target.machine) {
const state = target.machine.getSnapshot();
// Check if machine is in a state where refinement makes sense
if (state.matches("listening")) {
console.debug("[UniversalDictation] Field blur - triggering refinement for element:", element);
target.machine.send({
type: "saypi:refineTranscription",
targetElement: element,
});
}
}

// Use setTimeout to delay hiding so click event can fire first
setTimeout(() => {
if (button && !this.currentActiveTarget) {
Expand Down Expand Up @@ -387,14 +400,14 @@ export class UniversalDictationModule {
element.addEventListener("input", handleContentChange);

// Listen for dictation updates to track dictated content
EventBus.on("dictation:contentUpdated", (data) => {
EventBus.on("dictation:contentUpdated", (data: { targetElement: HTMLElement }) => {
if (data.targetElement === element) {
markDictationUpdate();
}
});

// Listen for dictation termination due to manual edit
EventBus.on("dictation:terminatedByManualEdit", (data) => {
EventBus.on("dictation:terminatedByManualEdit", (data: { targetElement: HTMLElement; reason: string }) => {
if (data.targetElement === element && this.currentActiveTarget?.element === element) {
console.debug("Dictation terminated due to manual edit on element:", element);
// Clean up the active dictation state
Expand Down Expand Up @@ -1026,7 +1039,7 @@ export class UniversalDictationModule {

// Events with additional data
[USER_STOPPED_SPEAKING, AUDIO_DEVICE_CONNECTED, SESSION_ASSIGNED].forEach((eventName) => {
EventBus.on(eventName, (detail) => {
EventBus.on(eventName, (detail: any) => {
if (detail) {
// sanitise the detail object to replace any `frames` property with `[REDACTED]`
const sanitisedDetail = { ...detail };
Expand All @@ -1042,7 +1055,7 @@ export class UniversalDictationModule {
});

// Listen for transcription events
EventBus.on("saypi:transcription:completed", (detail) => {
EventBus.on("saypi:transcription:completed", (detail: Omit<DictationTranscribedEvent, 'type'>) => {
logger.debug(`[UniversalDictationModule] Forwarding transcription to dictation machine`, detail);
dictationService.send({ type: "saypi:transcribed", ...detail });
});
Expand Down
Loading