@bryan-gc/transcribe-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +46 -0
  2. package/dist/audio/audioPlayer.d.ts +7 -0
  3. package/dist/audio/audioPlayer.d.ts.map +1 -0
  4. package/dist/audio/audioPlayer.js +49 -0
  5. package/dist/audio/audioPlayer.js.map +1 -0
  6. package/dist/audio/micDevices.d.ts +17 -0
  7. package/dist/audio/micDevices.d.ts.map +1 -0
  8. package/dist/audio/micDevices.js +120 -0
  9. package/dist/audio/micDevices.js.map +1 -0
  10. package/dist/audio/recorder.d.ts +16 -0
  11. package/dist/audio/recorder.d.ts.map +1 -0
  12. package/dist/audio/recorder.js +149 -0
  13. package/dist/audio/recorder.js.map +1 -0
  14. package/dist/components/App.d.ts +5 -0
  15. package/dist/components/App.d.ts.map +1 -0
  16. package/dist/components/App.js +279 -0
  17. package/dist/components/App.js.map +1 -0
  18. package/dist/components/MicTest.d.ts +10 -0
  19. package/dist/components/MicTest.d.ts.map +1 -0
  20. package/dist/components/MicTest.js +150 -0
  21. package/dist/components/MicTest.js.map +1 -0
  22. package/dist/components/Picker.d.ts +13 -0
  23. package/dist/components/Picker.d.ts.map +1 -0
  24. package/dist/components/Picker.js +20 -0
  25. package/dist/components/Picker.js.map +1 -0
  26. package/dist/components/SetupPrompt.d.ts +8 -0
  27. package/dist/components/SetupPrompt.d.ts.map +1 -0
  28. package/dist/components/SetupPrompt.js +39 -0
  29. package/dist/components/SetupPrompt.js.map +1 -0
  30. package/dist/config/configManager.d.ts +15 -0
  31. package/dist/config/configManager.d.ts.map +1 -0
  32. package/dist/config/configManager.js +50 -0
  33. package/dist/config/configManager.js.map +1 -0
  34. package/dist/constants.d.ts +134 -0
  35. package/dist/constants.d.ts.map +1 -0
  36. package/dist/constants.js +169 -0
  37. package/dist/constants.js.map +1 -0
  38. package/dist/index.d.ts +3 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +34 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/src/audio/audioPlayer.d.ts +7 -0
  43. package/dist/src/audio/audioPlayer.d.ts.map +1 -0
  44. package/dist/src/audio/audioPlayer.js +49 -0
  45. package/dist/src/audio/audioPlayer.js.map +1 -0
  46. package/dist/src/audio/micDevices.d.ts +17 -0
  47. package/dist/src/audio/micDevices.d.ts.map +1 -0
  48. package/dist/src/audio/micDevices.js +120 -0
  49. package/dist/src/audio/micDevices.js.map +1 -0
  50. package/dist/src/audio/recorder.d.ts +16 -0
  51. package/dist/src/audio/recorder.d.ts.map +1 -0
  52. package/dist/src/audio/recorder.js +149 -0
  53. package/dist/src/audio/recorder.js.map +1 -0
  54. package/dist/src/components/App.d.ts +5 -0
  55. package/dist/src/components/App.d.ts.map +1 -0
  56. package/dist/src/components/App.js +279 -0
  57. package/dist/src/components/App.js.map +1 -0
  58. package/dist/src/components/MicTest.d.ts +10 -0
  59. package/dist/src/components/MicTest.d.ts.map +1 -0
  60. package/dist/src/components/MicTest.js +150 -0
  61. package/dist/src/components/MicTest.js.map +1 -0
  62. package/dist/src/components/Picker.d.ts +13 -0
  63. package/dist/src/components/Picker.d.ts.map +1 -0
  64. package/dist/src/components/Picker.js +20 -0
  65. package/dist/src/components/Picker.js.map +1 -0
  66. package/dist/src/components/SetupPrompt.d.ts +8 -0
  67. package/dist/src/components/SetupPrompt.d.ts.map +1 -0
  68. package/dist/src/components/SetupPrompt.js +39 -0
  69. package/dist/src/components/SetupPrompt.js.map +1 -0
  70. package/dist/src/config/configManager.d.ts +15 -0
  71. package/dist/src/config/configManager.d.ts.map +1 -0
  72. package/dist/src/config/configManager.js +50 -0
  73. package/dist/src/config/configManager.js.map +1 -0
  74. package/dist/src/constants.d.ts +134 -0
  75. package/dist/src/constants.d.ts.map +1 -0
  76. package/dist/src/constants.js +169 -0
  77. package/dist/src/constants.js.map +1 -0
  78. package/dist/src/index.d.ts +3 -0
  79. package/dist/src/index.d.ts.map +1 -0
  80. package/dist/src/index.js +34 -0
  81. package/dist/src/index.js.map +1 -0
  82. package/dist/src/transcriber/ITranscriber.d.ts +12 -0
  83. package/dist/src/transcriber/ITranscriber.d.ts.map +1 -0
  84. package/dist/src/transcriber/ITranscriber.js +2 -0
  85. package/dist/src/transcriber/ITranscriber.js.map +1 -0
  86. package/dist/src/transcriber/WhisperTranscriber.d.ts +8 -0
  87. package/dist/src/transcriber/WhisperTranscriber.d.ts.map +1 -0
  88. package/dist/src/transcriber/WhisperTranscriber.js +35 -0
  89. package/dist/src/transcriber/WhisperTranscriber.js.map +1 -0
  90. package/dist/src/utils/clipboard.d.ts +11 -0
  91. package/dist/src/utils/clipboard.d.ts.map +1 -0
  92. package/dist/src/utils/clipboard.js +56 -0
  93. package/dist/src/utils/clipboard.js.map +1 -0
  94. package/dist/src/utils/srtParser.d.ts +6 -0
  95. package/dist/src/utils/srtParser.d.ts.map +1 -0
  96. package/dist/src/utils/srtParser.js +25 -0
  97. package/dist/src/utils/srtParser.js.map +1 -0
  98. package/dist/transcriber/ITranscriber.d.ts +12 -0
  99. package/dist/transcriber/ITranscriber.d.ts.map +1 -0
  100. package/dist/transcriber/ITranscriber.js +2 -0
  101. package/dist/transcriber/ITranscriber.js.map +1 -0
  102. package/dist/transcriber/WhisperTranscriber.d.ts +8 -0
  103. package/dist/transcriber/WhisperTranscriber.d.ts.map +1 -0
  104. package/dist/transcriber/WhisperTranscriber.js +35 -0
  105. package/dist/transcriber/WhisperTranscriber.js.map +1 -0
  106. package/dist/utils/clipboard.d.ts +11 -0
  107. package/dist/utils/clipboard.d.ts.map +1 -0
  108. package/dist/utils/clipboard.js +56 -0
  109. package/dist/utils/clipboard.js.map +1 -0
  110. package/dist/utils/srtParser.d.ts +6 -0
  111. package/dist/utils/srtParser.d.ts.map +1 -0
  112. package/dist/utils/srtParser.js +25 -0
  113. package/dist/utils/srtParser.js.map +1 -0
  114. package/package.json +51 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recorder.d.ts","sourceRoot":"","sources":["../../../src/audio/recorder.ts"],"names":[],"mappings":"AAiBA,qBAAa,aAAa;IACxB,OAAO,CAAC,EAAE,CAA6B;IACvC,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,QAAQ,CAAc;IAC9B,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,SAAS,CAAkB;IAEnC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAO/B,SAAS,IAAI,MAAM;IAInB,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAoE7B,KAAK,IAAI,IAAI;IAab,MAAM,IAAI,IAAI;IAOd,gGAAgG;IAChG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAqDrB,WAAW,IAAI,MAAM;CAGtB"}
@@ -0,0 +1,149 @@
1
+ import { spawn } from 'child_process';
2
+ import fs from 'fs';
3
+ import os from 'os';
4
+ import { DEFAULT_DEVICE_ID, Platform, Cmd, Signal, AUDIO_CONFIG, ALSA_PREFIXES, Encoding, StdioOption, StreamEvent, ProcessEvent, } from '../constants';
5
+ export class AudioRecorder {
6
+ cp = null;
7
+ fileStream = null;
8
+ filepath = '';
9
+ device = DEFAULT_DEVICE_ID;
10
+ _isPaused = false;
11
+ setDevice(device) {
12
+ if (this.cp) {
13
+ throw new Error('Cannot change device while recording is in progress.');
14
+ }
15
+ this.device = device;
16
+ }
17
+ getDevice() {
18
+ return this.device;
19
+ }
20
+ start(filepath) {
21
+ if (this.cp) {
22
+ throw new Error('A recording is already in progress.');
23
+ }
24
+ this.filepath = filepath;
25
+ this.fileStream = fs.createWriteStream(filepath, { encoding: Encoding.BINARY });
26
+ this._isPaused = false;
27
+ let env = process.env;
28
+ if (os.platform() === Platform.DARWIN) {
29
+ const args = [
30
+ '-d',
31
+ '-q',
32
+ '-r',
33
+ AUDIO_CONFIG.SAMPLE_RATE,
34
+ '-c',
35
+ AUDIO_CONFIG.CHANNELS,
36
+ '-e',
37
+ AUDIO_CONFIG.ENCODING_SOX,
38
+ '-b',
39
+ AUDIO_CONFIG.BITS,
40
+ '-t',
41
+ AUDIO_CONFIG.TYPE,
42
+ '-',
43
+ ];
44
+ if (this.device !== DEFAULT_DEVICE_ID) {
45
+ env = { ...process.env, AUDIODEV: this.device };
46
+ }
47
+ this.cp = spawn(Cmd.SOX, args, {
48
+ stdio: [StdioOption.IGNORE, StdioOption.PIPE, StdioOption.IGNORE],
49
+ env,
50
+ });
51
+ this.cp.stdout?.pipe(this.fileStream);
52
+ return;
53
+ }
54
+ // Linux (arecord)
55
+ const args = [
56
+ '-q',
57
+ '-f',
58
+ AUDIO_CONFIG.FORMAT_ARECORD,
59
+ '-c',
60
+ AUDIO_CONFIG.CHANNELS,
61
+ '-r',
62
+ AUDIO_CONFIG.SAMPLE_RATE,
63
+ '-t',
64
+ AUDIO_CONFIG.TYPE,
65
+ ];
66
+ if (this.device !== DEFAULT_DEVICE_ID) {
67
+ // If the device ID is from PipeWire/PulseAudio (e.g. alsa_input.usb-...)
68
+ // we use the 'pulse' ALSA device and tell Pulse/PipeWire which source to use.
69
+ const isPulse = this.device.startsWith(ALSA_PREFIXES.ALSA_INPUT) ||
70
+ this.device.startsWith(ALSA_PREFIXES.BLUEZ_INPUT);
71
+ args.push('-D', isPulse ? AUDIO_CONFIG.DEVICE_PULSE : this.device);
72
+ if (isPulse)
73
+ env = { ...process.env, PULSE_SOURCE: this.device };
74
+ }
75
+ this.cp = spawn(Cmd.ARECORD, args, {
76
+ stdio: [StdioOption.IGNORE, StdioOption.PIPE, StdioOption.IGNORE],
77
+ env,
78
+ });
79
+ this.cp.stdout?.pipe(this.fileStream);
80
+ }
81
+ pause() {
82
+ if (!this.cp || this._isPaused)
83
+ return;
84
+ // Delaying the SIGSTOP by a short moment allows the OS audio buffer to flush
85
+ // the last spoken words before the process freezes.
86
+ setTimeout(() => {
87
+ if (!this.cp || this._isPaused)
88
+ return;
89
+ this.cp.kill(Signal.SIGSTOP);
90
+ this._isPaused = true;
91
+ }, 500);
92
+ }
93
+ resume() {
94
+ if (!this.cp || !this._isPaused)
95
+ return;
96
+ this.cp.kill(Signal.SIGCONT);
97
+ this._isPaused = false;
98
+ }
99
+ /** Stops the recording and resolves only when the audio file has been fully written to disk. */
100
+ stop() {
101
+ return new Promise((resolve) => {
102
+ if (!this.cp) {
103
+ resolve();
104
+ return;
105
+ }
106
+ // If the process was paused, we MUST resume it first so it can process
107
+ // the SIGINT/SIGTERM and flush any remaining buffer.
108
+ if (this._isPaused) {
109
+ this.cp.kill(Signal.SIGCONT);
110
+ this._isPaused = false;
111
+ }
112
+ // Wait a brief moment to capture the trailing audio from the hardware buffer
113
+ setTimeout(() => {
114
+ if (this.cp) {
115
+ // Attempt a graceful exit first (SIGINT) so wav headers and buffers can be flushed
116
+ this.cp.kill(Signal.SIGINT);
117
+ // Safety fallback to SIGTERM if it doesn't exit
118
+ const fallback = setTimeout(() => {
119
+ if (this.cp)
120
+ this.cp.kill(Signal.SIGTERM);
121
+ }, 500);
122
+ this.cp.on(ProcessEvent.EXIT, () => clearTimeout(fallback));
123
+ this.cp = null;
124
+ }
125
+ if (!this.fileStream) {
126
+ resolve();
127
+ return;
128
+ }
129
+ const stream = this.fileStream;
130
+ this.fileStream = null;
131
+ let resolved = false;
132
+ const done = () => {
133
+ if (resolved)
134
+ return;
135
+ resolved = true;
136
+ resolve();
137
+ };
138
+ stream.on(StreamEvent.FINISH, done);
139
+ stream.on(StreamEvent.CLOSE, done);
140
+ stream.on(StreamEvent.ERROR, done);
141
+ setTimeout(done, 1500);
142
+ }, 500);
143
+ });
144
+ }
145
+ getFilepath() {
146
+ return this.filepath;
147
+ }
148
+ }
149
+ //# sourceMappingURL=recorder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recorder.js","sourceRoot":"","sources":["../../../src/audio/recorder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import { type AppConfig } from '../config/configManager.js';
2
+ export declare function App({ appConfig }: {
3
+ appConfig: AppConfig;
4
+ }): import("react/jsx-runtime").JSX.Element;
5
+ //# sourceMappingURL=App.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../../src/components/App.tsx"],"names":[],"mappings":"AAYA,OAAO,EAAE,KAAK,SAAS,EAAiB,MAAM,4BAA4B,CAAC;AAsE3E,wBAAgB,GAAG,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,EAAE,SAAS,CAAA;CAAE,2CA0V1D"}
@@ -0,0 +1,279 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useRef, useCallback } from 'react';
3
+ import { Box, Text, useInput, useApp } from 'ink';
4
+ import path from 'path';
5
+ import fs from 'fs';
6
+ import { Picker } from './Picker.js';
7
+ import { MicTest } from './MicTest.js';
8
+ import { AudioRecorder } from '../audio/recorder.js';
9
+ import { listMicDevices } from '../audio/micDevices.js';
10
+ import { WhisperTranscriber } from '../transcriber/WhisperTranscriber.js';
11
+ import { extractTextFromSrt } from '../utils/srtParser.js';
12
+ import { copyTextToClipboard } from '../utils/clipboard.js';
13
+ import { ConfigManager } from '../config/configManager.js';
14
+ import { DIR, EXT, DEFAULT_DEVICE_ID, DEFAULT_DEVICE_LABEL, NONE_OPTION_VALUE, HOTKEY_EXIT, Encoding, LanguageCode, LANGUAGE_NAMES, AVAILABLE_LANGUAGES, TranscriptionFormat, ViewMode, MenuAction, getPaths, } from '../constants.js';
15
+ // ─── Constants ────────────────────────────────────────────────────────────────
16
+ const MAIN_OPTIONS = [
17
+ { id: MenuAction.RECORD, label: `Record [${MenuAction.RECORD}]` },
18
+ { id: MenuAction.PAUSE, label: `Pause [${MenuAction.PAUSE}]` },
19
+ { id: MenuAction.STOP, label: `Stop [${MenuAction.STOP}]` },
20
+ { id: MenuAction.TRANSCRIBE, label: `Transcribe [${MenuAction.TRANSCRIBE}]` },
21
+ { id: MenuAction.CHANGE_LANGUAGE, label: `Change Language [${MenuAction.CHANGE_LANGUAGE}]` },
22
+ { id: MenuAction.CHANGE_GLOSSARY, label: `Change Glossary [${MenuAction.CHANGE_GLOSSARY}]` },
23
+ {
24
+ id: MenuAction.CHANGE_MICROPHONE,
25
+ label: `Change Microphone [${MenuAction.CHANGE_MICROPHONE}]`,
26
+ },
27
+ { id: MenuAction.QUIT, label: `Quit [${MenuAction.QUIT}]` },
28
+ ];
29
+ const VALID_ACTION_KEYS = new Set(Object.values(MenuAction));
30
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
31
+ function loadGlossaryFiles(basePath) {
32
+ const dir = getPaths(basePath).GLOSSARIES_DIR;
33
+ if (!fs.existsSync(dir))
34
+ fs.mkdirSync(dir, { recursive: true });
35
+ return fs
36
+ .readdirSync(dir)
37
+ .filter((f) => f.endsWith(EXT.GLOSSARY))
38
+ .sort();
39
+ }
40
+ function readGlossaryContent(basePath, filename) {
41
+ if (!filename)
42
+ return undefined;
43
+ const filepath = path.join(getPaths(basePath).GLOSSARIES_DIR, filename);
44
+ const content = fs.existsSync(filepath) ? fs.readFileSync(filepath, Encoding.UTF8).trim() : '';
45
+ return content || undefined;
46
+ }
47
+ function getTimestampPaths(basePath) {
48
+ const now = new Date();
49
+ const pad = (n) => String(n).padStart(2, '0');
50
+ const folder = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
51
+ const base = `${pad(now.getHours())}-${pad(now.getMinutes())}-${pad(now.getSeconds())}`;
52
+ const tmpDir = path.resolve(basePath, DIR.TMP, folder);
53
+ if (!fs.existsSync(tmpDir))
54
+ fs.mkdirSync(tmpDir, { recursive: true });
55
+ return {
56
+ audioPath: path.join(tmpDir, `${base}${EXT.AUDIO}`),
57
+ srtPath: path.join(tmpDir, `${base}${EXT.SUBTITLES}`),
58
+ textPath: path.join(tmpDir, `${base}${EXT.TEXT}`),
59
+ };
60
+ }
61
+ // ─── Component ────────────────────────────────────────────────────────────────
62
+ export function App({ appConfig }) {
63
+ const { exit } = useApp();
64
+ // ── State ──────────────────────────────────────────────────────────────────
65
+ const [viewMode, setViewMode] = useState(ViewMode.MAIN);
66
+ const [selectedIndex, setSelectedIndex] = useState(0);
67
+ const [statusText, setStatusText] = useState('Ready to record.');
68
+ const [isRecording, setIsRecording] = useState(false);
69
+ const [isPaused, setIsPaused] = useState(false);
70
+ const [isTranscribing, setIsTranscribing] = useState(false);
71
+ const [transcriptionResult, setTranscriptionResult] = useState('');
72
+ const [clipboardEnabled, setClipboardEnabled] = useState(true);
73
+ const [currentAudioPath, setCurrentAudioPath] = useState('');
74
+ const [currentSrtPath, setCurrentSrtPath] = useState('');
75
+ const [currentTextPath, setCurrentTextPath] = useState('');
76
+ const [activeLanguage, setActiveLanguage] = useState(AVAILABLE_LANGUAGES.includes(appConfig.selectedLanguage)
77
+ ? appConfig.selectedLanguage
78
+ : LanguageCode.ENGLISH);
79
+ const initialGlossaries = loadGlossaryFiles(appConfig.basePath);
80
+ const [activeGlossary, setActiveGlossary] = useState(initialGlossaries[0] ?? '');
81
+ const initialMics = listMicDevices();
82
+ const fallbackMic = { id: DEFAULT_DEVICE_ID, label: DEFAULT_DEVICE_LABEL };
83
+ const savedMic = initialMics.find((m) => m.id === appConfig.selectedMicrophone);
84
+ const [activeMic, setActiveMic] = useState(savedMic ?? fallbackMic);
85
+ const [pendingMic, setPendingMic] = useState(savedMic ?? fallbackMic);
86
+ const [micDevices, setMicDevices] = useState(initialMics);
87
+ const [glossaryFiles, setGlossaryFiles] = useState(initialGlossaries);
88
+ const saveConfig = (updates) => {
89
+ const newConfig = { ...appConfig, ...updates };
90
+ ConfigManager.save(newConfig);
91
+ Object.assign(appConfig, updates); // mutate for current session ref
92
+ };
93
+ // ── Refs ───────────────────────────────────────────────────────────────────
94
+ const recorderRef = useRef(new AudioRecorder());
95
+ const transcriber = useRef(new WhisperTranscriber(appConfig.apiKey));
96
+ const recorder = recorderRef.current;
97
+ try {
98
+ recorder.setDevice(activeMic.id);
99
+ }
100
+ catch {
101
+ /* recording in progress */
102
+ }
103
+ // ── Actions ────────────────────────────────────────────────────────────────
104
+ const handleAction = useCallback(async (actionId) => {
105
+ switch (actionId) {
106
+ case MenuAction.RECORD:
107
+ if (!isRecording) {
108
+ const p = getTimestampPaths(appConfig.basePath);
109
+ setCurrentAudioPath(p.audioPath);
110
+ setCurrentSrtPath(p.srtPath);
111
+ setCurrentTextPath(p.textPath);
112
+ recorder.start(p.audioPath);
113
+ setIsRecording(true);
114
+ setIsPaused(false);
115
+ setTranscriptionResult('');
116
+ setStatusText('🔴 Recording...');
117
+ return;
118
+ }
119
+ if (isPaused) {
120
+ recorder.resume();
121
+ setIsPaused(false);
122
+ setStatusText('🔴 Recording...');
123
+ return;
124
+ }
125
+ setStatusText('Already recording.');
126
+ break;
127
+ case MenuAction.PAUSE:
128
+ if (isRecording && !isPaused) {
129
+ recorder.pause();
130
+ setIsPaused(true);
131
+ setStatusText('⏸️ Paused');
132
+ return;
133
+ }
134
+ setStatusText('No active recording to pause.');
135
+ break;
136
+ case MenuAction.STOP:
137
+ if (isRecording) {
138
+ await recorder.stop();
139
+ setIsRecording(false);
140
+ setIsPaused(false);
141
+ setStatusText('⏹️ Stopped. File saved.');
142
+ return;
143
+ }
144
+ setStatusText('No recording in progress.');
145
+ break;
146
+ case MenuAction.CHANGE_LANGUAGE:
147
+ setViewMode(ViewMode.LANGUAGES);
148
+ break;
149
+ case MenuAction.CHANGE_GLOSSARY:
150
+ setGlossaryFiles(loadGlossaryFiles(appConfig.basePath));
151
+ setViewMode(ViewMode.GLOSSARIES);
152
+ break;
153
+ case MenuAction.CHANGE_MICROPHONE: {
154
+ const fresh = listMicDevices();
155
+ setMicDevices(fresh);
156
+ setPendingMic(activeMic);
157
+ setViewMode(ViewMode.MICROPHONES);
158
+ break;
159
+ }
160
+ case MenuAction.TOGGLE_CLIPBOARD:
161
+ setClipboardEnabled((prev) => !prev);
162
+ break;
163
+ case MenuAction.TRANSCRIBE: {
164
+ if (isRecording) {
165
+ setStatusText('Please stop the recording before transcribing.');
166
+ return;
167
+ }
168
+ if (!currentAudioPath || !fs.existsSync(currentAudioPath)) {
169
+ setStatusText('No recent audio file found to transcribe.');
170
+ return;
171
+ }
172
+ const glossaryPrompt = readGlossaryContent(appConfig.basePath, activeGlossary);
173
+ setIsTranscribing(true);
174
+ setStatusText(`⏳ Transcribing (${LANGUAGE_NAMES[activeLanguage]}${glossaryPrompt ? ' + glossary' : ''}) - Initializing...`);
175
+ try {
176
+ const srtContent = await transcriber.current.transcribe(currentAudioPath, activeLanguage, TranscriptionFormat.SRT, glossaryPrompt, (msg) => setStatusText(`⏳ Transcribing: ${msg}`));
177
+ setStatusText('⏳ Formatting text and saving files...');
178
+ fs.writeFileSync(currentSrtPath, srtContent, Encoding.UTF8);
179
+ const cleanText = extractTextFromSrt(srtContent);
180
+ fs.writeFileSync(currentTextPath, cleanText, Encoding.UTF8);
181
+ setTranscriptionResult(cleanText);
182
+ if (clipboardEnabled) {
183
+ copyTextToClipboard(cleanText);
184
+ setStatusText('✅ Transcription done — copied to clipboard.');
185
+ return;
186
+ }
187
+ setStatusText('✅ Transcription completed and saved.');
188
+ }
189
+ catch (err) {
190
+ setStatusText(`❌ Error: ${err instanceof Error ? err.message : String(err)}`);
191
+ }
192
+ finally {
193
+ setIsTranscribing(false);
194
+ }
195
+ break;
196
+ }
197
+ case MenuAction.QUIT:
198
+ if (isRecording)
199
+ await recorder.stop();
200
+ exit();
201
+ break;
202
+ }
203
+ }, [
204
+ isRecording,
205
+ isPaused,
206
+ currentAudioPath,
207
+ currentSrtPath,
208
+ currentTextPath,
209
+ activeLanguage,
210
+ activeGlossary,
211
+ activeMic,
212
+ clipboardEnabled,
213
+ recorder,
214
+ exit,
215
+ appConfig.basePath,
216
+ ]);
217
+ // ── Keyboard (main menu) ───────────────────────────────────────────────────
218
+ useInput((input, key) => {
219
+ if (key.upArrow) {
220
+ setSelectedIndex((i) => (i > 0 ? i - 1 : MAIN_OPTIONS.length - 1));
221
+ return;
222
+ }
223
+ if (key.downArrow) {
224
+ setSelectedIndex((i) => (i < MAIN_OPTIONS.length - 1 ? i + 1 : 0));
225
+ return;
226
+ }
227
+ if (key.return) {
228
+ const action = MAIN_OPTIONS[selectedIndex]?.id;
229
+ if (action)
230
+ handleAction(action);
231
+ return;
232
+ }
233
+ if (key.ctrl && input === HOTKEY_EXIT) {
234
+ if (isRecording)
235
+ recorder.stop().catch(() => { });
236
+ exit();
237
+ return;
238
+ }
239
+ if (VALID_ACTION_KEYS.has(input.toLowerCase())) {
240
+ handleAction(input.toLowerCase());
241
+ }
242
+ }, { isActive: viewMode === ViewMode.MAIN && !isTranscribing });
243
+ // ── Derived ────────────────────────────────────────────────────────────────
244
+ const glossaryLabel = activeGlossary ? path.basename(activeGlossary, EXT.GLOSSARY) : '(none)';
245
+ const languageOptions = AVAILABLE_LANGUAGES.map((lang) => ({
246
+ label: LANGUAGE_NAMES[lang],
247
+ value: lang,
248
+ }));
249
+ const glossaryOptions = [
250
+ { label: '(none)', value: NONE_OPTION_VALUE },
251
+ ...glossaryFiles.map((f) => ({ label: path.basename(f, EXT.GLOSSARY), value: f })),
252
+ ];
253
+ const micOptions = micDevices.map((mic) => ({
254
+ label: mic.id === activeMic.id ? `${mic.label} (current)` : mic.label,
255
+ value: mic.id,
256
+ }));
257
+ // ── Render ─────────────────────────────────────────────────────────────────
258
+ return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, children: '=== transcribe-cli ===' }), _jsxs(Box, { flexDirection: "column", marginTop: 1, marginBottom: 1, children: [_jsxs(Text, { children: ["Status: ", _jsx(Text, { color: "yellow", children: statusText })] }), _jsxs(Text, { children: ["Language: ", _jsx(Text, { color: "magenta", children: LANGUAGE_NAMES[activeLanguage] })] }), _jsxs(Text, { children: ["Glossary: ", _jsx(Text, { color: "magenta", children: glossaryLabel })] }), _jsxs(Text, { children: ["Microphone: ", _jsx(Text, { color: "magenta", children: activeMic.label })] }), _jsxs(Text, { children: ["Clipboard:", ' ', _jsx(Text, { color: clipboardEnabled ? 'green' : 'red', children: clipboardEnabled ? '✅ On' : '❌ Off' }), _jsx(Text, { dimColor: true, children: " [c]" })] })] }), viewMode === ViewMode.MAIN && (_jsxs(Box, { flexDirection: "column", children: [MAIN_OPTIONS.map((opt, i) => (_jsxs(Text, { color: i === selectedIndex ? 'cyan' : undefined, children: [i === selectedIndex ? '> ' : ' ', opt.label] }, opt.id))), transcriptionResult !== '' && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "Last transcription:" }), _jsx(Text, { children: transcriptionResult }), _jsxs(Text, { dimColor: true, children: ["\uD83D\uDCC4 ", currentTextPath] })] })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "\u2191\u2193 navigate \u00B7 Enter confirm \u00B7 or press key in [brackets]" }) })] })), viewMode === ViewMode.LANGUAGES && (_jsx(Picker, { title: "Select Language", options: languageOptions, onSelect: (value) => {
259
+ setActiveLanguage(value);
260
+ saveConfig({ selectedLanguage: value });
261
+ setStatusText(`Language changed to ${LANGUAGE_NAMES[value]}.`);
262
+ setViewMode(ViewMode.MAIN);
263
+ }, onCancel: () => setViewMode(ViewMode.MAIN) })), viewMode === ViewMode.GLOSSARIES && (_jsx(Picker, { title: "Select Glossary", options: glossaryOptions, onSelect: (value) => {
264
+ const finalValue = value === NONE_OPTION_VALUE ? '' : value;
265
+ setActiveGlossary(finalValue);
266
+ setStatusText(`Glossary changed to: ${finalValue ? path.basename(finalValue, EXT.GLOSSARY) : 'none'}.`);
267
+ setViewMode(ViewMode.MAIN);
268
+ }, onCancel: () => setViewMode(ViewMode.MAIN) })), viewMode === ViewMode.MICROPHONES && (_jsx(Picker, { title: "Select Microphone", options: micOptions, onSelect: (value) => {
269
+ const mic = micDevices.find((d) => d.id === value) ?? { id: value, label: value };
270
+ setPendingMic(mic);
271
+ setViewMode(ViewMode.MIC_TEST);
272
+ }, onCancel: () => setViewMode(ViewMode.MAIN) })), viewMode === ViewMode.MIC_TEST && (_jsx(MicTest, { mic: pendingMic, basePath: appConfig.basePath, onConfirm: () => {
273
+ setActiveMic(pendingMic);
274
+ saveConfig({ selectedMicrophone: pendingMic.id });
275
+ setStatusText(`Microphone set to: ${pendingMic.label}.`);
276
+ setViewMode(ViewMode.MAIN);
277
+ }, onCancel: () => setViewMode(ViewMode.MICROPHONES) }))] }));
278
+ }
279
+ //# sourceMappingURL=App.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"App.js","sourceRoot":"","sources":["../../../src/components/App.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { MicDevice } from '../audio/micDevices.js';
2
+ interface MicTestProps {
3
+ mic: MicDevice;
4
+ basePath: string;
5
+ onConfirm: () => void;
6
+ onCancel: () => void;
7
+ }
8
+ export declare function MicTest({ mic, basePath, onConfirm, onCancel }: MicTestProps): import("react/jsx-runtime").JSX.Element;
9
+ export {};
10
+ //# sourceMappingURL=MicTest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MicTest.d.ts","sourceRoot":"","sources":["../../../src/components/MicTest.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAGxD,UAAU,YAAY;IACpB,GAAG,EAAE,SAAS,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAID,wBAAgB,OAAO,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,YAAY,2CAsM3E"}
@@ -0,0 +1,150 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { useState, useRef, useEffect, useCallback } from 'react';
3
+ import { Box, Text, useInput } from 'ink';
4
+ import path from 'path';
5
+ import fs from 'fs';
6
+ import { AudioRecorder } from '../audio/recorder.js';
7
+ import { playAudio, stopPlayback } from '../audio/audioPlayer.js';
8
+ import { RECORDING_TICK_MS, ActionHotkey, TestStatus, getPaths } from '../constants.js';
9
+ const AUTO_STOP_SECS = 5;
10
+ export function MicTest({ mic, basePath, onConfirm, onCancel }) {
11
+ const [status, setStatus] = useState(TestStatus.IDLE);
12
+ const [hasRecording, setHasRecording] = useState(false);
13
+ const [elapsed, setElapsed] = useState(0);
14
+ const [errorText, setErrorText] = useState('');
15
+ const testFilePath = getPaths(basePath).TEST_FILE;
16
+ const recorderRef = useRef(new AudioRecorder());
17
+ const timerRef = useRef(null);
18
+ const stoppingRef = useRef(false);
19
+ useEffect(() => {
20
+ const recorderInstance = recorderRef.current;
21
+ return () => {
22
+ clearTimer();
23
+ stoppingRef.current = true;
24
+ recorderInstance.stop().catch(() => { });
25
+ stopPlayback();
26
+ };
27
+ }, []);
28
+ function clearTimer() {
29
+ if (timerRef.current) {
30
+ clearInterval(timerRef.current);
31
+ timerRef.current = null;
32
+ }
33
+ }
34
+ function startRecording() {
35
+ if (status === TestStatus.RECORDING)
36
+ return;
37
+ try {
38
+ const tmpDir = path.dirname(testFilePath);
39
+ if (!fs.existsSync(tmpDir))
40
+ fs.mkdirSync(tmpDir, { recursive: true });
41
+ recorderRef.current.setDevice(mic.id);
42
+ recorderRef.current.start(testFilePath);
43
+ setStatus(TestStatus.RECORDING);
44
+ setElapsed(0);
45
+ setErrorText('');
46
+ timerRef.current = setInterval(() => {
47
+ setElapsed((prev) => {
48
+ const next = prev + 1;
49
+ if (next >= AUTO_STOP_SECS)
50
+ stopRecording();
51
+ return next;
52
+ });
53
+ }, RECORDING_TICK_MS);
54
+ }
55
+ catch (e) {
56
+ setErrorText(`Recording error: ${e instanceof Error ? e.message : String(e)}`);
57
+ }
58
+ }
59
+ const stopRecording = useCallback(async () => {
60
+ if (stoppingRef.current)
61
+ return;
62
+ stoppingRef.current = true;
63
+ clearTimer();
64
+ setStatus(TestStatus.SAVING);
65
+ await recorderRef.current.stop();
66
+ stoppingRef.current = false;
67
+ setStatus(TestStatus.RECORDED);
68
+ setHasRecording(true);
69
+ }, []);
70
+ async function handlePlayback() {
71
+ if (!hasRecording || !fs.existsSync(testFilePath)) {
72
+ setErrorText('No test recording found. Record first.');
73
+ return;
74
+ }
75
+ setStatus(TestStatus.PLAYING);
76
+ setErrorText('');
77
+ try {
78
+ await playAudio(testFilePath);
79
+ }
80
+ catch (e) {
81
+ setErrorText(`Playback error: ${e instanceof Error ? e.message : String(e)}`);
82
+ }
83
+ finally {
84
+ setStatus(TestStatus.RECORDED);
85
+ }
86
+ }
87
+ function handleStopPlayback() {
88
+ stopPlayback();
89
+ setStatus(TestStatus.RECORDED);
90
+ }
91
+ useInput((input, key) => {
92
+ const k = input.toLowerCase();
93
+ if (key.escape || k === ActionHotkey.CANCEL_C) {
94
+ onCancel();
95
+ return;
96
+ }
97
+ if (status === TestStatus.IDLE || status === TestStatus.RECORDED) {
98
+ if (k === ActionHotkey.RECORD) {
99
+ startRecording();
100
+ return;
101
+ }
102
+ if (k === ActionHotkey.PLAYBACK && hasRecording) {
103
+ handlePlayback();
104
+ return;
105
+ }
106
+ if (k === ActionHotkey.CONFIRM || key.return) {
107
+ onConfirm();
108
+ return;
109
+ }
110
+ }
111
+ if (status === TestStatus.RECORDING && k === ActionHotkey.STOP) {
112
+ stopRecording();
113
+ return;
114
+ }
115
+ if (status === TestStatus.PLAYING && k === ActionHotkey.STOP) {
116
+ handleStopPlayback();
117
+ return;
118
+ }
119
+ });
120
+ // ─── Status line ──────────────────────────────────────────────────────────
121
+ const statusLine = () => {
122
+ switch (status) {
123
+ case TestStatus.RECORDING:
124
+ return (_jsxs(Text, { color: "red", children: ["\uD83D\uDD34 Recording... ", elapsed, "s / ", AUTO_STOP_SECS, "s (auto-stops)"] }));
125
+ case TestStatus.SAVING:
126
+ return _jsx(Text, { color: "yellow", children: "\uD83D\uDCBE Saving audio file, please wait..." });
127
+ case TestStatus.PLAYING:
128
+ return _jsxs(Text, { color: "green", children: ["\uD83D\uDD0A Playing back... press [", ActionHotkey.STOP, "] to stop"] });
129
+ case TestStatus.RECORDED:
130
+ return _jsx(Text, { color: "green", children: "\u23F9\uFE0F Test recorded. Ready to play back or confirm." });
131
+ default:
132
+ return _jsxs(Text, { dimColor: true, children: ["Press [", ActionHotkey.RECORD, "] to start a test recording."] });
133
+ }
134
+ };
135
+ // ─── Action list ──────────────────────────────────────────────────────────
136
+ const actions = () => {
137
+ switch (status) {
138
+ case TestStatus.RECORDING:
139
+ return _jsxs(Text, { color: "yellow", children: ["[", ActionHotkey.STOP, "] Stop recording early"] });
140
+ case TestStatus.SAVING:
141
+ return (_jsx(Text, { color: "yellow", dimColor: true, children: "Saving..." }));
142
+ case TestStatus.PLAYING:
143
+ return _jsxs(Text, { color: "yellow", children: ["[", ActionHotkey.STOP, "] Stop playback"] });
144
+ default:
145
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "cyan", children: ["[", ActionHotkey.RECORD, "]", ' ', status === TestStatus.RECORDED ? 'Record Again' : 'Record Test', ' ', _jsxs(Text, { dimColor: true, children: ["(auto-stops at ", AUTO_STOP_SECS, "s)"] })] }), hasRecording && _jsxs(Text, { color: "cyan", children: ["[", ActionHotkey.PLAYBACK, "] Play Back"] }), _jsxs(Text, { color: "green", children: ["[", ActionHotkey.CONFIRM, "] Confirm \u2014 use this microphone"] }), _jsxs(Text, { color: "red", children: ["[", ActionHotkey.CANCEL_C, "] Cancel \u2014 choose a different mic"] })] }));
146
+ }
147
+ };
148
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: '=== Microphone Test ===' }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { children: ["Microphone: ", _jsx(Text, { color: "magenta", children: mic.label })] }), _jsxs(Text, { children: ["Device ID: ", _jsx(Text, { dimColor: true, children: mic.id })] })] }), _jsx(Box, { marginTop: 1, marginBottom: 1, children: statusLine() }), actions(), errorText !== '' && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "red", children: ["\u26A0\uFE0F ", errorText] }) })), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["Esc/[", ActionHotkey.CANCEL_C, "] cancel \u00B7 [", ActionHotkey.CONFIRM, "]/Enter confirm"] }) })] }));
149
+ }
150
+ //# sourceMappingURL=MicTest.js.map