113 open controls modal window stoping in the background (#114)

* feat: enhance recording modal behavior and state management

* chore: bump version to 2.4.2 in manifest.json and package.json
This commit is contained in:
Mike A 2026-07-15 13:39:37 -04:00 committed by GitHub
parent b5e055c6a1
commit 5f91b2398e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 59 additions and 36 deletions

View file

@ -192,6 +192,8 @@ Always includes a `fileTitle` field for note renaming.
### Recording UX State Model
- Modal, command palette, and ribbon all treat paused recordings as in-progress sessions.
- Closing the controls modal does NOT cancel an active recording — it hands off to the persistent tappable recording notice; reopening the modal hides the notice and re-attaches to the live recording. Cancelling is always explicit (modal Reset, ribbon Cancel).
- Per-session modal options persist in `state.sessionScribeOptions` while a recording is active; `scribe()` falls back to them when called without options (notice tap, ribbon Stop, palette toggle). Cleared on cancel/cleanup/modal-close-when-idle.
- Recording notice/timer messages are derived from recorder-aware elapsed duration (paused time not counted).
- `ScribePlugin` exposes helper methods used across entry points to avoid state drift:
- `isRecordingActive()`

View file

@ -1,7 +1,7 @@
{
"id": "scribe",
"name": "Scribe",
"version": "2.4.1",
"version": "2.4.2",
"minAppVersion": "1.8.7",
"description": "Record, transcribe, and transform voice notes into structured insights. Leverage Whisper or AssemblyAI and ChatGPT to fill in gaps, generate summaries, and visualize ideas — all seamlessly integrated within your vault.",
"author": "Mike Alicea",

View file

@ -1,6 +1,6 @@
{
"name": "obsidian-scribe-plugin",
"version": "2.4.1",
"version": "2.4.2",
"description": "An Obsidian plugin for recording voice notes, transcribing the audio, and summarizing the text - All in one",
"main": "build/main.js",
"scripts": {

View file

@ -1,13 +1,11 @@
import type ScribePlugin from 'src';
import { ScribeControlsModal } from 'src/modal/scribeControlsModal';
export function handleCommands(plugin: ScribePlugin) {
plugin.addCommand({
id: 'scribe-recording-modal',
name: 'Open recording modal',
callback: () => {
plugin.state.isOpen = true;
new ScribeControlsModal(plugin).open();
plugin.controlModal.open();
},
});
plugin.addCommand({

View file

@ -55,6 +55,7 @@ export interface ScribeState {
counter: number;
audioRecord: AudioRecord | null;
isProcessing: boolean;
sessionScribeOptions: ScribeOptions | null;
}
const DEFAULT_STATE: ScribeState = {
@ -62,6 +63,7 @@ const DEFAULT_STATE: ScribeState = {
counter: 0,
audioRecord: null,
isProcessing: false,
sessionScribeOptions: null,
};
export interface ScribeOptions {
@ -186,27 +188,29 @@ export default class ScribePlugin extends Plugin {
async cancelRecording() {
this.hideRecordingNotice();
this.state.sessionScribeOptions = null;
if (this.state.audioRecord?.mediaRecorder) {
new Notice('Scribe: 🛑️ Recording cancelled');
await this.state.audioRecord?.stopRecording();
}
}
async scribe(
scribeOptions: ScribeOptions = {
isAppendToActiveFile: this.settings.isAppendToActiveFile,
isOnlyTranscribeActive: this.settings.isOnlyTranscribeActive,
isMultiSpeakerEnabled: this.settings.isMultiSpeakerEnabled,
isSaveAudioFileActive: this.settings.isSaveAudioFileActive,
isDisableLlmTranscription: this.settings.isDisableLlmTranscription,
audioFileLanguage: this.settings.audioFileLanguage,
scribeOutputLanguage: this.settings.scribeOutputLanguage,
transcriptPlatform: this.settings.transcriptPlatform,
processPlatform: this.settings.processPlatform,
llmModel: resolveLlmConfig(this.settings).model,
activeNoteTemplate: this.settings.activeNoteTemplate,
},
) {
async scribe(scribeOptionsOverride?: ScribeOptions) {
const scribeOptions: ScribeOptions = scribeOptionsOverride ??
this.state.sessionScribeOptions ?? {
isAppendToActiveFile: this.settings.isAppendToActiveFile,
isOnlyTranscribeActive: this.settings.isOnlyTranscribeActive,
isMultiSpeakerEnabled: this.settings.isMultiSpeakerEnabled,
isSaveAudioFileActive: this.settings.isSaveAudioFileActive,
isDisableLlmTranscription: this.settings.isDisableLlmTranscription,
audioFileLanguage: this.settings.audioFileLanguage,
scribeOutputLanguage: this.settings.scribeOutputLanguage,
transcriptPlatform: this.settings.transcriptPlatform,
processPlatform: this.settings.processPlatform,
llmModel: resolveLlmConfig(this.settings).model,
activeNoteTemplate: this.settings.activeNoteTemplate,
};
this.state.isProcessing = true;
try {
const baseFileName = formatFilenamePrefix(
@ -602,6 +606,7 @@ export default class ScribePlugin extends Plugin {
this.controlModal.close();
this.state.audioRecord = null;
this.state.isProcessing = false;
this.state.sessionScribeOptions = null;
}
showRecordingNotice() {

View file

@ -21,6 +21,7 @@ export class ScribeControlsModal extends Modal {
onOpen() {
this.plugin.state.isOpen = true;
this.plugin.hideRecordingNotice();
this.initModal();
}
@ -28,8 +29,13 @@ export class ScribeControlsModal extends Modal {
const { contentEl } = this;
contentEl.empty();
this.plugin.state.isOpen = false;
void this.plugin.cancelRecording();
this.root?.unmount();
if (this.plugin.isRecordingActive()) {
this.plugin.showRecordingNotice();
} else {
this.plugin.state.sessionScribeOptions = null;
}
}
initModal() {
@ -58,20 +64,26 @@ const ScribeModal: React.FC<{ plugin: ScribePlugin }> = ({ plugin }) => {
const [elapsedRecordingTimeMs, setElapsedRecordingTimeMs] = useState<number>(
plugin.getRecordingDurationMs(),
);
const [scribeOptions, setScribeOptions] = useState<ScribeOptions>({
isAppendToActiveFile: plugin.settings.isAppendToActiveFile,
isOnlyTranscribeActive: plugin.settings.isOnlyTranscribeActive,
isSaveAudioFileActive: plugin.settings.isSaveAudioFileActive,
isMultiSpeakerEnabled: plugin.settings.isMultiSpeakerEnabled,
isDisableLlmTranscription: plugin.settings.isDisableLlmTranscription,
audioFileLanguage: plugin.settings.audioFileLanguage,
scribeOutputLanguage: plugin.settings.scribeOutputLanguage,
transcriptPlatform: plugin.settings.transcriptPlatform,
processPlatform: plugin.settings.processPlatform,
llmModel: resolveLlmConfig(plugin.settings).model,
activeNoteTemplate: plugin.settings.activeNoteTemplate,
additionalSystemPrompt: '',
});
const [scribeOptions, setScribeOptions] = useState<ScribeOptions>(
(isRecordingInProgress && plugin.state.sessionScribeOptions) || {
isAppendToActiveFile: plugin.settings.isAppendToActiveFile,
isOnlyTranscribeActive: plugin.settings.isOnlyTranscribeActive,
isSaveAudioFileActive: plugin.settings.isSaveAudioFileActive,
isMultiSpeakerEnabled: plugin.settings.isMultiSpeakerEnabled,
isDisableLlmTranscription: plugin.settings.isDisableLlmTranscription,
audioFileLanguage: plugin.settings.audioFileLanguage,
scribeOutputLanguage: plugin.settings.scribeOutputLanguage,
transcriptPlatform: plugin.settings.transcriptPlatform,
processPlatform: plugin.settings.processPlatform,
llmModel: resolveLlmConfig(plugin.settings).model,
activeNoteTemplate: plugin.settings.activeNoteTemplate,
additionalSystemPrompt: '',
},
);
useEffect(() => {
plugin.state.sessionScribeOptions = scribeOptions;
}, [scribeOptions, plugin]);
useEffect(() => {
let timer: number | null = null;

View file

@ -21,6 +21,13 @@ function scribeDropDownMenu(plugin: ScribePlugin): Menu {
const recordingState = plugin.getRecordingState();
if (showRecordingInProgressControls) {
menu.addItem((item) => {
item.setIcon('joystick');
item.setTitle('Open Controls');
item.onClick(() => {
plugin.controlModal.open();
});
});
menu.addItem((item) => {
item.setIcon(recordingState === 'paused' ? 'play' : 'pause');
item.setTitle(
@ -49,7 +56,6 @@ function scribeDropDownMenu(plugin: ScribePlugin): Menu {
item.setIcon('joystick');
item.setTitle('Open Controls');
item.onClick(() => {
plugin.state.isOpen = true;
plugin.controlModal.open();
});
});