pi-web-voice 0.1.4 → 0.1.5

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.
package/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  Voice input for [pi-web](https://github.com/agegr/pi-web), added from the outside.
4
4
 
5
- A microphone button appears in the chat composer. Tap it to start and tap again to stop,
6
- or press and hold to talk and release when done the gesture decides, there is nothing
7
- to configure. The transcript lands at your caret; you review it, then send. pi-web is never modified: the
5
+ A microphone button appears in the chat composer. Click it to start, click again to stop.
6
+ The transcript lands at your caret; you review it, then send. Keyboard, VoiceOver and the
7
+ accessibility API can press it too. pi-web is never modified: the
8
8
  whole thing is one `--require` hook that injects a single `<script>` tag into HTML
9
9
  responses and serves two routes of its own.
10
10
 
@@ -87,7 +87,7 @@ file, loaded by Node itself — no dependency, no parser of ours:
87
87
  ```sh
88
88
  AZURE_SPEECH_ENDPOINT=https://my-resource.cognitiveservices.azure.com
89
89
  AZURE_SPEECH_KEY=abc123...
90
- PI_VOICE_MODE=hold
90
+ PI_VOICE_PROVIDER=azure-speech
91
91
  ```
92
92
 
93
93
  Anything already exported wins over the file, so `AZURE_SPEECH_KEY=other pi-web-voice`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-web-voice",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Voice input for pi-web. A NODE_OPTIONS hook that injects a microphone button into the chat composer and transcribes speech with Azure AI Speech, Azure OpenAI, or any OpenAI-compatible endpoint. No fork, no patching, no rebuild.",
5
5
  "keywords": [
6
6
  "pi",
package/public/inject.js CHANGED
@@ -17,9 +17,6 @@
17
17
  window.__PI_WEB_VOICE__ || {},
18
18
  );
19
19
 
20
- // A press shorter than this latches recording on, so a tap toggles and a
21
- // long press is walkie-talkie. No setting needed: the gesture says which.
22
- const HOLD_THRESHOLD_MS = 400;
23
20
  const MAX_SECONDS = 180;
24
21
  const SHORTCUT = "mod+shift+v";
25
22
 
@@ -89,9 +86,8 @@
89
86
  const zh = (navigator.language || "").toLowerCase().startsWith("zh");
90
87
  const T = zh
91
88
  ? {
92
- idle: "语音输入 — 点击开始,或按住说话",
89
+ idle: "语音输入 — 点击开始,再点一次结束",
93
90
  recording: "正在录音 — 点击停止",
94
- holding: "松开结束录音",
95
91
  working: "转写中…",
96
92
  insecure: "浏览器只在 HTTPS 或 localhost 下允许使用麦克风",
97
93
  denied: "麦克风权限被拒绝",
@@ -100,9 +96,8 @@
100
96
  noComposer: "找不到输入框,转写结果",
101
97
  }
102
98
  : {
103
- idle: "Voice input — click, or press and hold",
99
+ idle: "Voice input — click to start, click again to stop",
104
100
  recording: "Recording — click to stop",
105
- holding: "Release to stop",
106
101
  working: "Transcribing…",
107
102
  insecure: "Microphone needs HTTPS or localhost",
108
103
  denied: "Microphone permission denied",
@@ -343,30 +338,16 @@
343
338
  "transition:color .15s,background .15s,transform .1s",
344
339
  ].join(";");
345
340
 
346
- // One button, two gestures. A quick tap latches recording on and the
347
- // next tap ends it; holding records only while held.
348
- let pressedAt = 0;
349
- let latched = false;
350
-
351
- button.addEventListener("pointerdown", (event) => {
352
- event.preventDefault();
353
- if (this.state === "recording") {
354
- latched = false;
355
- this.stop();
356
- return;
357
- }
358
- pressedAt = Date.now();
359
- latched = false;
360
- this.start();
361
- });
341
+ // One button, one gesture: click to start, click again to stop. Going
342
+ // through `click` rather than pointer events is what makes the keyboard,
343
+ // VoiceOver and the accessibility API able to press it at all — none of
344
+ // them produce pointer events.
345
+ button.addEventListener("click", () => this.toggle());
362
346
 
363
- const release = () => {
364
- if (this.state !== "recording" || latched) return;
365
- if (Date.now() - pressedAt < HOLD_THRESHOLD_MS) latched = true; // a tap
366
- else this.stop(); // a hold
367
- };
368
- button.addEventListener("pointerup", release);
369
- button.addEventListener("pointercancel", release);
347
+ // A button steals focus from the composer on mousedown. Refusing that
348
+ // default keeps the caret where it was, and on a phone keeps the
349
+ // on-screen keyboard from collapsing under the composer.
350
+ button.addEventListener("mousedown", (event) => event.preventDefault());
370
351
 
371
352
  anchor.parentElement.insertBefore(button, anchor);
372
353
  this.button = button;