diff --git a/AGENTS.md b/AGENTS.md index b038ce3..709c40e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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()` diff --git a/manifest.json b/manifest.json index b4918dc..706c1e2 100644 --- a/manifest.json +++ b/manifest.json @@ -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", diff --git a/package.json b/package.json index 0bc4c48..75e9b76 100644 --- a/package.json +++ b/package.json @@ -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": { diff --git a/src/commands/commands.ts b/src/commands/commands.ts index f1c14e6..37ebc67 100644 --- a/src/commands/commands.ts +++ b/src/commands/commands.ts @@ -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({ diff --git a/src/index.ts b/src/index.ts index 2f95fc0..5c9b764 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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() { diff --git a/src/modal/scribeControlsModal.tsx b/src/modal/scribeControlsModal.tsx index 728c763..4df1ee9 100644 --- a/src/modal/scribeControlsModal.tsx +++ b/src/modal/scribeControlsModal.tsx @@ -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( plugin.getRecordingDurationMs(), ); - const [scribeOptions, setScribeOptions] = useState({ - 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( + (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; diff --git a/src/ribbon/ribbon.ts b/src/ribbon/ribbon.ts index afd0c37..fcddb1f 100644 --- a/src/ribbon/ribbon.ts +++ b/src/ribbon/ribbon.ts @@ -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(); }); });