Commit graph

7 commits

Author SHA1 Message Date
Sergey
186403c2d5
chore(repo): bump version to 1.0.4 and add AGENTS.md (#45)
## Что сделано

- Создан `AGENTS.md` в корне репо — проект-specific правила для агентов
(дистилляция memory-файла): version bump (CRITICAL), `@connect`,
dist-деплой, `@icon`, селекторы композера, стек, команды, структура
`src/`, тесты.
- Version bump 1.0.3 → 1.0.4 синхронно в двух файлах: `vite.config.ts`
(`userscript.version`) + `package.json` (`version`).

## Почему

PR #43 смержен с фичами (custom endpoint, temperature, short prompt)
**без** version bump — Tampermonkey/Violentmonkey не видят
автообновление, юзеры не получат новую версию. В репо отсутствовал
`AGENTS.md` — subagent'ы работали вслепую без проект-specific правил,
что и привело к пропущенному bump'у. `AGENTS.md` фиксирует критичные
инварианты для будущих PR-агентов.

## Watch out

- `AGENTS.md` дополняет глобальный `~/.config/opencode/AGENTS.md`, НЕ
заменяет его — opencode грузит оба файла вместе.
- Version bump — patch increment (semver-совместимый). Без bump
`@version` userscript-менеджеры не триггерят автообновление.
- Файлы `src/`, `tests/`, `@connect`, селекторы композера — НЕ тронуты
(вне scope).
- `dist` ветка обновится автоматически через `deploy.yml` после merge в
`main`.

## Pending

- Авто-деплой в `dist` после merge (deploy.yml,
peaceiris/actions-gh-pages@v4).

Closes #44

Closes #44

---------

Co-authored-by: opencode-agent <agent@opencode.local>
2026-08-04 12:49:01 +03:00
Sergey
c074dfd777
feat(transcription): custom endpoint and temperature (#43)
## Что сделано

- `src/types.ts`: добавлены поля `endpoint: string` и `temperature:
number` в интерфейс `AppConfig`.
- `src/config.ts`:
- `DEFAULTS.endpoint =
"https://api.groq.com/openai/v1/audio/transcriptions"` (обратная
совместимость).
  - `DEFAULTS.temperature = 0` (детерминированный вывод).
- `DEFAULTS.whisperPrompt` сокращён с ~270 до 107 символов — термины
через запятую, без предложений.
- `getConfig()` читает новые поля через `GM_getValue` с fallback на
`DEFAULTS`.
- Добавлены 2 меню-команды: «Set STT Endpoint» и «Set Temperature» в
`registerMenuCommands` (параметры `onSetEndpoint`, `onSetTemperature`).
- `src/index.ts`: реализованы `promptForEndpoint` (fallback на дефолт
при пустом вводе) и `promptForTemperature` (clamp к `[0, 1]`,
NaN-валидация, сохранение текущего значения при cancel),
зарегистрированы в `registerMenuCommands`.
- `src/transcribe.ts`: удалён хардкод `GROQ_API_URL`, используется
`config.endpoint`; в `buildFormData` добавлено
`formData.append("temperature", String(config.temperature))`.
- `vite.config.ts`: `@connect api.groq.com` → `@connect *` (разрешает
`GM_xmlhttpRequest` к любому домену).
- `README.md`: добавлены разделы «Custom STT Endpoint» (EN+RU) с
примером nginx `proxy_pass` и «Temperature».
- `tests/`: обновлены `mockConfig` (новые поля), ассерты на
`temperature` в FormData, тест на длину `whisperPrompt < 120`,
`registerMenuCommands` теперь ожидает 7 команд.

## Почему

Groq периодически блокирует прямые IP-запросы к `api.groq.com` — нужен
обход через пользовательский nginx-прокси (свой endpoint). Whisper
галлюцинирует на тишине/шуме — `temperature=0` снижает галлюцинации.
Длинный `whisperPrompt` с целыми предложениями мог «утекать» в вывод
транскрипции — сокращён до списка терминов через запятую (~107
символов). Хардкод `GROQ_API_URL` и `@connect api.groq.com` блокировали
использование кастомных доменов.

## Watch out

- `@connect *` в метаблоке расширяет поверхность запросов userscript-а
на любой домен — юзер должен доверять установленному endpoint. Это
намеренный trade-off для поддержки произвольных прокси.
- Существующие юзеры со старым `whisperPrompt` в `GM_getValue` сохраняют
своё значение (новый короткий default применяется только если ключ не
задан) — обратная совместимость сохранена.
- Кламп температуры: ввод `NaN` → toast «Invalid temperature» (значение
не меняется); ввод `5` → `1`; ввод `-0.3` → `0`.
- Пустой endpoint (юзер ввёл пробелы) → fallback на `DEFAULTS.endpoint`
(`api.groq.com`), не пустая строка.
- Cancel в prompt (Esc) по endpoint/temperature → значение не меняется
(поведение `null`-check).
- Error-сообщения `transcribe.ts` всё ещё упоминают «Groq API» —
оставлено намеренно, т.к. дефолтный endpoint = Groq и большинство юзеров
используют его.

## Pending

- Обновить `@version` в `vite.config.ts` при релизе (сейчас `1.0.3` — не
тронуто, решает релиз-процесс).
- Trim тишины (RMS-based) — явно вне scope этого issue (отдельный PR
если `temperature=0` не поможет).

Closes #42

Closes #42

---------

Co-authored-by: opencode-agent <agent@opencode.local>
2026-08-04 12:32:39 +03:00
Sergey
e4ec12960f
fix(ui): skip composer injection in child session (#33)
## Что сделано

Фикс бага из issue #32: в child session (subagent) кнопка 🎤 появлялась
над disabled-блоком "Prompt is disabled / Back to parent", а клик падал
с toast "Could not find input field".

- **`src/ui.ts` `findComposer()`** — добавлен guard:
`session-prompt-dock` skip'ается в цикле `COMPOSER_SELECTORS`, если
внутри него НЕТ `[data-component="prompt-input"]` или
`[data-component="prompt-input-v2"]`. В child session внутри dock'а
только disabled-блок — guard срабатывает, `findComposer()` возвращает
`null`, кнопка не вставляется.
- **`src/ui.ts` `injectIntoComposer()`** — defence-in-depth: ранний
return, если в документе нет `[data-component="prompt-input"],
[data-component="prompt-input-v2"]`. Страховка от любых future-случаев
отсутствия реального composer (не только child session).
- **Бамп `@version`** 1.0.2 → 1.0.3 синхронно в `package.json` и
`vite.config.ts` — разблокировать автообновление userscript-менеджеров
(ловушка PR #29#30).
- **`tests/ui.test.ts`** (новый, 5 кейсов): dock с `prompt-input-v2` →
кнопка есть; dock с `prompt-input` → кнопка есть; dock без composer
(child session disabled-блок) → кнопки нет; пустой dock → кнопки нет;
question-dock открыт → composer-кнопки нет (PR #31 regression guard).
Тестирует через публичный `setupUI` + side-effect (`.ocvd-btn` в DOM).
- **Handoff** `docs/handoff/pr-XX-child-session-disabled-composer.md` и
**ADR 0004** `docs/decisions/0004-pr-XX-child-session-composer-guard.md`
с placeholder'ом PR-номера (исправлю после получения номера).

Это тот же класс бага, что и PR #31 (question-dock duplicate button) —
`session-prompt-dock` использовался как composer-target когда реальный
composer внутри не отрендерен. PR #31 пофиксил question-dock case, этот
PR закрывает child-session case.

## Почему

- **`session-prompt-dock` — общий wrapper для трёх состояний.** В
`anomalyco/opencode` (`session-composer-region.tsx`) dock рендерится
всегда когда `showComposer()` truthy (`!blocked() || !!parentID()` → в
child session всегда truthy). Внутри условно: question-dock (PR #31),
disabled-блок "Prompt is disabled / Back to parent" (child session, этот
PR), или реальный composer.
- **`findComposer()` выбирал wrapper.** `COMPOSER_SELECTORS` =
[`prompt-input-v2`, `session-prompt-dock`, `session-new-composer`,
`session-composer`] (ADR 0001). В child session `prompt-input-v2`
отсутствует, `session-prompt-dock` присутствует. Guard PR #31 (skip если
внутри `session-question-dock`) не срабатывал — внутри disabled-блок, не
question-dock.
- **Клик падал.** Кнопка вставлялась в dock через `injectIntoElement`
(`position: absolute; top: 8px; right: 8px`). При клике →
`insertIntoContenteditable()` →
`querySelector('[data-component="prompt-input"]')` = `null` → toast
"Could not find input field".
- **Defence-in-depth.** Guard в `findComposer` (не возвращать dock без
composer внутри) + guard в `injectIntoComposer` (не вставлять если в
документе нет composer). Оба слоя независимы — любой один достаточно,
второй страховка на race conditions. Аналог паттерна PR #31 / ADR 0003.

Closes #32

Closes #32

---------

Co-authored-by: opencode-agent <agent@opencode.local>
2026-07-26 17:18:08 +03:00
Sergey
02601cb360
fix(ui): prevent duplicate mic button in question-dock (#31)
## Что сделано
- `findComposer()` в `src/ui.ts` теперь skip `session-prompt-dock` если
внутри есть `session-question-dock` — общий wrapper не используется как
composer-target пока открыт question
- `injectIntoComposer()` в `src/ui.ts` добавлен guard: не вставлять
composer-кнопку если в DOM есть `[data-slot="question-custom-input"]`
(открыт textarea "Свой ответ")
- Бамп `@version` 1.0.1 → 1.0.2 в `package.json` и `vite.config.ts` —
разблокировать автообновление

## Почему
`session-prompt-dock` в OpenCode v1.18.3 — общий wrapper И для
question-dock, И для composer. Когда агент задаёт вопрос
(`questionRequest`), composer скрывается (`blocked=true`), но wrapper
остаётся. `findComposer()` находил wrapper и вставлял кнопку в верх
блока вопросов → дубликат + кнопка застревала после закрытия question.
Defence-in-depth: guard в findComposer + guard в injectIntoComposer.

Closes #28

Closes #28

---------

Co-authored-by: opencode-agent <agent@opencode.local>
2026-07-25 02:23:39 +03:00
Sergey
36169b4b08
fix(ui): broaden page gate + debug logging + version bump (#30)
## Что сделано
- Расширен gate `isOpencodePage()` в `src/insert.ts`: теперь проверяет
любой из 6 селекторов (`prompt-input`, `prompt-input-v2`,
`session-prompt-dock`, `session-composer`, `session-new-composer`,
`question-custom-input`) вместо одного `prompt-input`. Это активирует
скрипт раньше на v1.18.x, где композер может монтироваться lazy.
- Добавлено отладочное логирование с префиксом `[ocvd]` в `src/index.ts`
(init, gate retry) и `src/ui.ts` (composer found/not found, mic button
injected). Пользователь может видеть в DevTools Console где скрипт
останавливается.
- Бамп `@version` до `1.0.1` в `package.json` и `vite.config.ts` —
разблокирует автообновление userscript-менеджеров (после PR #29 версия
осталась `1.0.0`, автообновление не сработает).

## Почему
PR #29 добавил корректные селекторы для v1.18.x, но пользователи не
получили обновление из-за не-бампнутой `@version`. Также gate
`isOpencodePage()` проверял только `prompt-input`, которого может не
быть в DOM на ранних этапах lazy-mount. Добавление логирования поможет
диагностировать runtime-проблемы в DevTools.

Closes #28

---------

Co-authored-by: opencode-agent <agent@opencode.local>
2026-07-25 02:03:39 +03:00
opencode-agent
a2cf35b509 feat: auto-build CI workflow, one-click install URLs, auto-update docs
- Add release.yml: builds and deploys to dist branch on push to main
- Add GitHub Releases creation on version tags
- Update vite.config.ts: updateURL/downloadURL point to dist branch
- Update README: one-click install links for PC and Android
- Add auto-update documentation (EN/RU)
- Add manual install from Releases instructions
- Revert dist/ from gitignore (built by CI, not committed)
2026-07-08 05:45:54 +00:00
opencode-agent
3bd443dde3 feat: initial implementation of OpenCode Voice Dictation
- TypeScript + Vite (vite-plugin-monkey) userscript
- Groq API integration with whisper-large-v3
- Tap-to-toggle recording with noise suppression
- Contenteditable text insertion for OpenCode web
- Ctrl+Space keyboard shortcut (desktop)
- Custom Whisper prompt for software development context
- Recording timer, toast notifications, auto-submit option
- 44 unit tests, 100% line coverage
- Biome linter, Knip, Vitest, CI/CD pipeline
- Bilingual README (EN/RU) with compatibility table
2026-07-08 05:37:17 +00:00