@codexstar/pi-listen 1.0.11 → 1.0.12

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.
@@ -351,6 +351,8 @@ export default function (pi: ExtensionAPI) {
351
351
 
352
352
  // ─── Voice: Start / Stop / Transcribe ────────────────────────────────────
353
353
 
354
+ const MAX_RECORDING_SECS = 30; // Safety cap: auto-stop after 30s
355
+
354
356
  async function startVoiceRecording(target: "editor" | "btw" = "editor"): Promise<boolean> {
355
357
  if (voiceState !== "idle" || !ctx) return false;
356
358
 
@@ -363,14 +365,22 @@ export default function (pi: ExtensionAPI) {
363
365
  recordingStart = Date.now();
364
366
  setVoiceState("recording");
365
367
  statusTimer = setInterval(() => {
366
- if (voiceState === "recording") updateVoiceStatus();
368
+ if (voiceState === "recording") {
369
+ updateVoiceStatus();
370
+ // Safety: auto-stop after MAX_RECORDING_SECS
371
+ const elapsed = (Date.now() - recordingStart) / 1000;
372
+ if (elapsed >= MAX_RECORDING_SECS) {
373
+ isHolding = false;
374
+ stopVoiceRecording(target);
375
+ }
376
+ }
367
377
  }, 1000);
368
378
 
369
379
  if (ctx.hasUI) {
370
380
  ctx.ui.setWidget("voice-recording", [
371
381
  target === "btw"
372
- ? " BTW Recording... release to send"
373
- : " Recording... release to transcribe",
382
+ ? " 🎙 BTW Recording... Ctrl+Shift+V to stop"
383
+ : " 🎙 Recording... Ctrl+Shift+V to stop (or release SPACE)",
374
384
  ], { placement: "aboveEditor" });
375
385
  }
376
386
  return true;
@@ -679,6 +689,7 @@ export default function (pi: ExtensionAPI) {
679
689
  if (voiceState === "idle") {
680
690
  await startVoiceRecording("editor");
681
691
  } else if (voiceState === "recording") {
692
+ isHolding = false;
682
693
  await stopVoiceRecording("editor");
683
694
  }
684
695
  },
@@ -744,7 +755,7 @@ export default function (pi: ExtensionAPI) {
744
755
  // ─── /voice command ──────────────────────────────────────────────────────
745
756
 
746
757
  pi.registerCommand("voice", {
747
- description: "Voice input: /voice [on|off|test|info|setup|reconfigure|doctor|backends|daemon]",
758
+ description: "Voice input: /voice [on|off|stop|test|info|setup|reconfigure|doctor|backends|daemon]",
748
759
  handler: async (args, cmdCtx) => {
749
760
  ctx = cmdCtx;
750
761
  const sub = (args || "").trim().toLowerCase();
@@ -754,7 +765,7 @@ export default function (pi: ExtensionAPI) {
754
765
  updateVoiceStatus();
755
766
  setupHoldToTalk();
756
767
  ensureDaemon(config).catch(() => {});
757
- cmdCtx.ui.notify("Voice enabled. Hold SPACE (empty editor) to record.", "info");
768
+ cmdCtx.ui.notify("Voice enabled.\n Hold SPACE (empty editor) → release to transcribe\n Ctrl+Shift+V → toggle recording on/off\n Auto-stops after 30s", "info");
758
769
  return;
759
770
  }
760
771
 
@@ -767,6 +778,18 @@ export default function (pi: ExtensionAPI) {
767
778
  return;
768
779
  }
769
780
 
781
+ if (sub === "stop") {
782
+ // Emergency stop — cancel any active recording
783
+ if (voiceState === "recording") {
784
+ isHolding = false;
785
+ await stopVoiceRecording("editor");
786
+ cmdCtx.ui.notify("Recording stopped and transcribed.", "info");
787
+ } else {
788
+ cmdCtx.ui.notify("No recording in progress.", "info");
789
+ }
790
+ return;
791
+ }
792
+
770
793
  if (sub === "test") {
771
794
  cmdCtx.ui.notify("Testing voice setup...", "info");
772
795
  const diagnostics = scanEnvironment(TRANSCRIBE_SCRIPT);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codexstar/pi-listen",
3
- "version": "1.0.11",
3
+ "version": "1.0.12",
4
4
  "description": "Voice input, first-run onboarding, and side-channel BTW conversations for Pi",
5
5
  "type": "module",
6
6
  "keywords": [