fix: spinner clockwise, lock target on start, context-aware shortcut (#18)

## Что сделано

### Спиннер крутится по часовой стрелке
- Зеркально отражён SVG-arrow (стрелка указывает вправо)
- Вращение изменено с counterclockwise (-360deg) на clockwise (360deg)

### Fix: «Could not find input field» в question prompt
- **Корень проблемы**: `currentTarget` перезаписывался при каждом вызове
`toggleDictation`, включая остановку. Если запись начата с микрофона
question prompt (target="question"), а остановлена через Ctrl+Space
(target всегда "composer"), target перезаписывался → текст вставлялся не
туда.
- **Фикс**: `currentTarget` устанавливается только при старте записи
(state="idle")
- Ctrl+Space теперь контекстно-зависимый: если открыт question prompt —
target="question", иначе "composer"
- Auto-submit для question prompt уже был отключён — без изменений

Co-authored-by: opencode-agent <agent@slaid098.dev>
This commit is contained in:
Sergey 2026-07-09 06:37:23 +03:00 committed by GitHub
parent 12c3974960
commit 3eec9f311c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 15 additions and 5 deletions

View file

@ -6,7 +6,13 @@ import {
setConfig,
validateApiKey,
} from "./config.js";
import { type InsertTarget, insertText, isOpencodePage, submitPrompt } from "./insert.js";
import {
type InsertTarget,
insertText,
isOpencodePage,
isQuestionPromptOpen,
submitPrompt,
} from "./insert.js";
import { setupKeyboardShortcut } from "./keyboard.js";
import { transcribe } from "./transcribe.js";
import type { DictationState } from "./types.js";
@ -35,8 +41,8 @@ function stopTimer(): void {
}
async function toggleDictation(target: InsertTarget): Promise<void> {
currentTarget = target;
if (currentState === "idle") {
currentTarget = target;
await startRecording();
} else if (currentState === "recording") {
await stopAndTranscribe();
@ -182,7 +188,7 @@ function init(): void {
});
setupKeyboardShortcut(() => {
void toggleDictation("composer");
void toggleDictation(isQuestionPromptOpen() ? "question" : "composer");
});
registerMenuCommands({

View file

@ -8,6 +8,10 @@ export function isOpencodePage(): boolean {
return document.querySelector(PROMPT_INPUT_SELECTOR) !== null;
}
export function isQuestionPromptOpen(): boolean {
return document.querySelector(QUESTION_INPUT_SELECTOR) !== null;
}
export function insertText(text: string, target: InsertTarget = "composer"): boolean {
if (target === "question") {
return insertIntoTextarea(text);

View file

@ -14,7 +14,7 @@ const QUESTION_INPUT_SELECTOR = '[data-slot="question-custom-input"]';
const ICON_MIC = `<svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3z"/><path d="M17 11c0 2.76-2.24 5-5 5s-5-2.24-5-5H5c0 3.53 2.61 6.43 6 6.92V21h2v-3.08c3.39-.49 6-3.39 6-6.92h-2z"/></svg>`;
const ICON_STOP = `<svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><rect x="6" y="6" width="12" height="12" rx="3"/></svg>`;
const ICON_CANCEL = `<svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor"><path d="M18.3 5.71L12 12l6.3 6.29-1.42 1.42L10.58 13.4 4.29 19.71 2.87 18.3 9.16 12 2.87 5.71 4.29 4.29 10.58 10.58l6.29-6.29z"/></svg>`;
const ICON_SPINNER = `<svg class="ocvd-spin" viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01-.25 1.97-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0-4.42-3.58-8-8-8z"/></svg>`;
const ICON_SPINNER = `<svg class="ocvd-spin" viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M12 4V1L16 5l-4 4V6c-3.31 0-6 2.69-6 6 0 1.01.25 1.97.7 2.8L5.24 16.26C4.46 15.03 4 13.57 4 12c0-4.42 3.58-8 8-8z"/></svg>`;
function createButtonStyle(): string {
return `
@ -119,7 +119,7 @@ function createButtonStyle(): string {
}
@keyframes ocvd-spin {
from { transform: rotate(0deg); }
to { transform: rotate(-360deg); }
to { transform: rotate(360deg); }
}
.ocvd-spin {
animation: ocvd-spin 0.8s linear infinite;