Skip to content

fix(terminal): preserve terminal selection#407

Merged
skulidropek merged 7 commits into
mainfrom
issue-404-5a7f728e1091
Jun 15, 2026
Merged

fix(terminal): preserve terminal selection#407
skulidropek merged 7 commits into
mainfrom
issue-404-5a7f728e1091

Conversation

@skulidropek

@skulidropek skulidropek commented Jun 15, 2026

Copy link
Copy Markdown
Member

Source TZ / Issues

Summary

  • Preloads bounded tmux scrollback before browser attach so native xterm scrolling has recent pane history locally.
  • Preserves terminal copy text across Claude Code redraw/clear cycles by snapshotting non-empty xterm selection changes.
  • Restores visual xterm selection after redraw when cached coordinates still match the current buffer shape.
  • Keeps the native browser context-menu Copy item available by preparing xterm's hidden textarea from the cached selection before right-click menus open.

Requirements Alignment

  • Implemented: tmux/xterm scrollback preload is rendered before tmux attach-session and covered by packages/api/tests/terminal-sessions.test.ts assertions for tmux capture-pane, -S -10000, bounded tail -c 1048576, CR conversion, and command ordering before attach.
  • Implemented: selection restore invariants are covered with fast-check properties in terminal-copy-selection-restore.test.ts for generated coordinates, terminal widths, selected text lengths, and active buffer types.
  • Implemented: property-run cleanup is guaranteed with Effect.acquireRelease/Effect.scoped, so harness disposal runs even when an invariant check fails.
  • Implemented: protected context menu and snapshot-copy behavior are covered with fast-check properties in terminal-copy-interaction.test.ts and terminal-copy-right-click-interaction.test.ts.
  • Implemented: native copy-menu DOM shell contracts are documented in terminal-copy-native-menu.ts with TSDoc, functional markers, pre/postconditions, invariants, and complexity notes.
  • Out of scope: package-wide Effect-TS refactor of existing web DOM event handlers; this PR keeps the current terminal web adapter style and documents the SHELL effects instead.
  • Security-sensitive changes: no credential, filesystem, process, or network behavior added in this follow-up; terminal shell command changes remain bounded and shell-quoted in the earlier scrollback preload commit.

Proof of fix

  • Причина: xterm stores selection as mutable buffer coordinates, not as an independent browser selection. Claude Code redraw/clear cycles can make xterm emit an empty selection event, which removes the visual highlight even though our cached copy text still exists.
  • Решение: cache the xterm selection range, column count, and active buffer type with the text snapshot. When a redraw clears live selection, restore the real xterm selection via terminal.select(column, row, length) only if the current buffer shape still matches; otherwise keep the copy snapshot fallback.
  • Доказательство: terminal-copy-selection-restore.test.ts now includes property-based tests for coordinate restore, column mismatch fallback, and buffer-type mismatch fallback, with guaranteed harness cleanup; fixed examples still cover intentional keyboard clearing.

Verification

  • rtk bun run --cwd packages/terminal lint
  • rtk bun run --cwd packages/terminal typecheck
  • rtk bun run --cwd packages/terminal test — 20 files, 150 tests
  • rtk bun run --cwd packages/terminal build
  • rtk bun run --cwd packages/api test -- terminal-sessions.test.ts — 1 file, 12 tests

Refs #404

@skulidropek

skulidropek commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

AI Session Backup

Commit: b521a1b
Status: success
Files: 5 (5.18 MB)
Links: README | Manifest

git status

On branch issue-404-5a7f728e1091
nothing to commit, working tree clean

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Добавлена подсистема для защиты терминального выделения при копировании через кэширование текста выделения перед перерисовками. Реализована подготовка нативного браузерного меню копирования позиционированием скрытого textarea в месте события. Контроллер TerminalCopyInteractionController теперь подписывается на onSelectionChange, условно защищает contextmenu при наличии активного mouse tracking и выделения, подготавливает нативное меню и подавляет терминальный mouse-reporting. Добавлены интеграционные и property-based тесты для проверки подавления защищённого context menu, сохранения snapshot-выделения при очистке live-selection перед копированием, и восстановления координат выделения при изменении количества колонок или типа буфера.

Changes

Selection snapshot caching and protected contextmenu with native menu

Layer / File(s) Summary
Правила копирования и предикаты решений
packages/terminal/src/web/terminal-copy-rules.ts
TerminalMouseTrackingMode и TerminalCopyKeyboardEvent устанавливают контракты для состояния трекинга и клавиатурного ввода. hasActiveMouseTracking определяет активный режим трекинга. isKeyboardCopyShortcut распознаёт Ctrl/Cmd+C без alt. shouldLetBrowserHandleTerminalCopyShortcut разрешает браузеру только при совпадении шортката и непустом выделении. shouldForceBrowserTerminalSelection и shouldForceTerminalSelectionContext определяют, когда первичная или вторичная кнопка должна защитить контекст выделения. writeTerminalSelectionToClipboardData записывает выделение в буфер обмена в формате text/plain.
Механизм кэширования и восстановления snapshot-выделения
packages/terminal/src/web/terminal-copy-selection-snapshot.ts
Контракты TerminalCopyClipboardData, TerminalSelectionTarget, TerminalSelectionRestoreTarget определяют буфер обмена, чтение и восстановление selection. Утилиты валидируют параметры, координаты в пределах буфера, нормализуют диапазон. Чтение снапшота извлекает cols буфера и валидирует диапазон координат. TerminalSelectionContextSnapshot кэширует последнюю непустую строку selection, инвалидирует кэш с TTL, восстанавливает из снапшота координат и записывает в буфер обмена.
Инфраструктура нативного браузерного меню копирования
packages/terminal/src/web/terminal-copy-native-menu.ts
TerminalCopyTextarea и TerminalNativeCopyMenuHost определяют контракты для textarea и DOM-запросов host. Резолверы находят .xterm-screen через querySelector с fallback на host.getBoundingClientRect. prepareNativeBrowserCopyMenu позиционирует textarea по координатам события, заполняет текстом выделения, фокусирует и выделяет содержимое для нативного копирования. clearNativeBrowserCopyMenu очищает значение textarea.
Инициализация и интеграция TerminalCopyInteractionController
packages/terminal/src/web/terminal-copy-interaction.ts (часть 1)
Контроллер импортирует функции подготовки нативного меню и TerminalSelectionContextSnapshot. Интерфейс TerminalCopyInteractionTerminal расширен полями onSelectionChange (обработчик подписки) и textarea (для подготовки меню). TerminalCopyInteractionHost композирован с TerminalNativeCopyMenuHost для использования методов разрешения экрана.
Подписка на изменения selection и управление кэшем
packages/terminal/src/web/terminal-copy-interaction.ts (часть 2)
attach() подписывается на terminal.onSelectionChange (если обработчик присутствует), сохраняя disposable для cleanup. Обработчик onTerminalSelectionChange вызывает restore() при очистке live-выделения (для fallback на кэш) и refresh() при наличии выделения. Логика клавиатурного copy-шортката обновлена с учётом selectionContext.has(). Методы hasProtectedSelectionContext(), prepareNativeMenu() и clearNativeMenu() инкапсулируют управление защитой и меню.
Защита и подготовка меню при mouseDown и contextMenu
packages/terminal/src/web/terminal-copy-interaction.ts (часть 3)
onMouseDown очищает меню на первичной кнопке; при необходимости защиты обновляет кэш selection, подготавливает нативное меню и подавляет mouse reporting. При вторичной кнопке без защиты очищает кэш и меню. onContextMenu проверяет hasProtectedSelectionContext() и при отсутствии защиты возвращается; при наличии форсирует selection-модификатор, обновляет кэш, подготавливает меню и подавляет mouse reporting в конце.
Завершение copy и cleanup
packages/terminal/src/web/terminal-copy-interaction.ts (часть 4)
onCopy очищает нативное меню перед preventDefault() и stopPropagation() для обеспечения освобождения фокуса textarea. dispose() освобождает selectionChangeDisposable, очищает нативное меню и кэш selection наряду с другим cleanup.
Unit-тесты подавления защищённого contextmenu
packages/terminal/tests/web/terminal-copy-interaction.test.ts
Property-based effect-тест с параметризацией по режимам mouse tracking проверяет поведение защищённого contextmenu при выделенном тексте: принудительная установка shiftKey, отсутствие preventDefault, наличие stopImmediatePropagation и stopPropagation, отсутствие доставки bubble-слушателям. Тесты для первичной (0) и вторичной (2) кнопки; вторичная включает предшествующий mousedown.
Интеграционные тесты сохранения snapshot при очистке live-selection
packages/terminal/tests/web/terminal-copy-right-click-interaction.test.ts
Обновлены ожидания существующего теста по счётчикам stopImmediatePropagation/stopPropagation, накопленным событиям и восстановлению исходного selectedText. Новый effect-тест моделирует очистку live-selection между mousedown и contextmenu, затем проверяет, что copy сохраняет snapshot исходного текста в clipboard, соответствует цепочка подавления событий для contextMenu, и отсутствуют drag-слушатели.
Тест взаимодействия при перерисовке с проверкой clipboard
packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts
Новая инфраструктура: FakeTerminalCopyTextarea отслеживает вызовы focus()/select(), FakeTerminalCopyScreenHost поддерживает querySelector для .xterm-screen. Effect-тест моделирует right-click и contextmenu во время выделения, очищает live-выделение, проверяет через host.dispatchCopy что clipboard получает "selected before redraw" в text/plain, события подавляются, textarea получает фокус, и disposable.dispose() очищает обработчики selectionChangeHandlers.
Тесты восстановления координат выделения и совместимости буферов
packages/terminal/tests/web/terminal-copy-selection-restore.test.ts
Полный набор unit-тестов для восстановления snapshot: createSelectionRestoreHarness создаёт фейковый терминал с управляемым selection/буфером и подключает attachTerminalCopyInteraction. Тесты проверяют: восстановление координат после очистки live-выделения; пропуск reselect при изменении числа колонок (но сохранение clipboard); пропуск reselect при смене типа активного буфера (но корректный clipboard); отсутствие reselect при очистке snapshot клавиатурным вводом (Enter); отсутствие подавления событий без prior snapshot (события проходят, textarea не фокусируется, clipboard не пишется).

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Controller as TerminalCopyInteractionController
  participant SnapCache as TerminalSelectionContextSnapshot
  participant NativeMenu as Native Menu Preparation
  participant Terminal as Terminal/xterm

  rect rgba(200, 150, 255, 0.5)
  Note over User,Terminal: Selection change subscription setup
  Controller->>Terminal: subscribe to onSelectionChange
  end

  rect rgba(255, 200, 150, 0.5)
  Note over User,Terminal: Terminal redraw clears live selection
  Terminal->>Terminal: redraw occurs
  Terminal->>SnapCache: onSelectionChange fires (empty)
  SnapCache->>SnapCache: restore() called to fallback to cache
  end

  rect rgba(150, 255, 150, 0.5)
  Note over User,Terminal: Right-click protected contextmenu flow
  User->>Controller: mousedown (secondary button)
  Controller->>NativeMenu: clearNativeBrowserCopyMenu()
  Controller->>SnapCache: refresh() if needed
  
  User->>Controller: contextmenu event
  Controller->>Controller: hasProtectedSelectionContext()?
  alt Protection required (tracking + selection)
    Controller->>Terminal: force selection modifier
    Controller->>SnapCache: refresh() if live selection
    Controller->>NativeMenu: prepareNativeBrowserCopyMenu()
    NativeMenu->>NativeMenu: position textarea at event,<br/>focus, select content
    Controller->>Terminal: suppressTerminalMouseReport()
  else No protection
    Controller-->>User: return (allow native menu)
  end
  end

  rect rgba(150, 200, 255, 0.5)
  Note over User,Terminal: Copy via keyboard or native menu
  User->>Controller: copy event
  Controller->>NativeMenu: clearNativeBrowserCopyMenu()
  Controller->>SnapCache: writeToClipboardData()
  SnapCache-->>User: clipboard receives snapshot or live text
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Possibly related PRs

  • ProverCoderAI/docker-git#382: Both PRs modify the terminal copy interaction's keyboard Ctrl/Cmd+C decision path—main PR adjusts it to use cached/protected selection, while #382 adds custom key-event interception and shouldLetBrowserHandleTerminalCopyShortcut logic for non-empty selection.
  • ProverCoderAI/docker-git#322: Main PR's changes to terminal copy interaction (forcing browser selection/modifiers for mouse buttons, caching/refreshing selection, writing selection to clipboard) directly build on core behavior introduced in #322.
  • ProverCoderAI/docker-git#325: Both PRs modify attachTerminalCopyInteraction logic around forcing/preserving terminal selection and native contextmenu restoration through onMouseDown/right-click and forced selection-modifier handling, so main PR changes directly intersect that functional code path.

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error)

Check name Status Explanation Resolution
Requirements Alignment ❌ Error PR description claims four components, but only three are implemented: scrollback preload is not in the code or tests. Components 2-4 (Selection Snapshotting, Restoration, Native Menu) are properly... Remove the 'Preloads tmux/xterm scrollback' claim from PR description or implement the scrollback preload functionality. Alternatively, clarify that this component is deferred or handled separately.
✅ Passed checks (6 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(terminal): preserve terminal selection' directly and specifically describes the main objective of the PR - preserving terminal selection across redraws and clears.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Security Regression ✅ Passed No security regressions found. Code handles terminal text/coordinates with proper validation, uses safe clipboard operations, and contains no file I/O, process execution, path traversal, credential...
Description check ✅ Passed PR description fully matches template with all required sections: Source TZ/Issues, Summary, Requirements Alignment (Implemented/Out of scope/Security), and Verification details.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-404-5a7f728e1091

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@skulidropek skulidropek changed the title issue 404 5a7f728e1091 fix(terminal): preserve selection on context menu Jun 15, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/terminal/tests/web/terminal-copy-interaction.test.ts`:
- Around line 241-261: Refactor both test cases to use property-based testing
with fast-check according to the project's testing policy for *.test.ts files.
In packages/terminal/tests/web/terminal-copy-interaction.test.ts (lines
241-261), convert the "suppresses protected context menus without blocking the
native menu" test to use fc.property with invariants that verify when
mouseTrackingMode is not "none" and selectedText length is greater than 0,
context menu suppression occurs without calling preventDefault. In
packages/terminal/tests/web/terminal-copy-right-click-interaction.test.ts (lines
208-230), convert the snapshot-before-contextmenu test case to use fc.property
with invariants that verify for any selectedText value, after clearing
live-selection the snapshot is correctly copied. Both refactored tests should
use Effect test utilities without async/await rather than example-based testing
patterns.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6d9148d1-e796-4c59-bd19-64bc3df03d66

📥 Commits

Reviewing files that changed from the base of the PR and between 5cdb97f and b521a1b.

📒 Files selected for processing (3)
  • packages/terminal/src/web/terminal-copy-interaction.ts
  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-right-click-interaction.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: E2E (OpenCode)
  • GitHub Check: E2E (Clone auto-open SSH)
  • GitHub Check: E2E (Login context)
  • GitHub Check: Lint
  • GitHub Check: E2E (Clone cache)
  • GitHub Check: Test
  • GitHub Check: E2E (Runtime volumes + SSH)
  • GitHub Check: E2E (Browser command)
  • GitHub Check: Final build (windows-latest)
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{sh,bash,py,js,ts,jsx,tsx,go,java,rb,php}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files introduce command injection or unsafe shell/process execution with user-controlled input

Files:

  • packages/terminal/src/web/terminal-copy-interaction.ts
  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-right-click-interaction.test.ts
**/*.{py,js,ts,jsx,tsx,go,java,rb,php,sh,bash,c,cpp}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files introduce path traversal or writes outside intended project/container state directories

Files:

  • packages/terminal/src/web/terminal-copy-interaction.ts
  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-right-click-interaction.test.ts
**/*.{js,ts,jsx,tsx,py,java,go,rb,php,sh,bash,yml,yaml,json,env*,toml,cfg,config,dockerfile,dockerignore}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files expose credentials, tokens, private-keys, or PII in source, generated config, logs, or CI output

Files:

  • packages/terminal/src/web/terminal-copy-interaction.ts
  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-right-click-interaction.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: FUNCTIONAL CORE: Write only pure functions with immutable data and mathematical operations in core modules; no side effects, mutations, or external service calls
IMPERATIVE SHELL: Isolate all side effects (IO, network, database, environment/process) in a thin SHELL layer; CORE never calls SHELL, only SHELL → CORE
Never use any type annotation in TypeScript; use unknown only at SHELL boundaries for decoding, never export unknown outside boundary modules
Never use as type assertions in normal code; only permit as in a single 'axiomatic' module (brands, constructors, constants) after which types flow safely without casts
Always use exhaustive pattern matching for union types through .exhaustive() or Match.exhaustive() from effect-ts; never use switch statements or unhandled type branches
Use Effect<Success, Error, Requirements> monad from effect-ts for all effects; compose through pipe() and Effect.flatMap(); never use async/await, raw Promise chains (then/catch), or Promise.all in product code
Interoperate with Promise/exceptions only in SHELL through Effect.try/Effect.tryPromise with typed error mapping; never leave raw exceptions or untyped errors in the domain
Use Effect.acquireRelease + Effect.scoped for resource management with guaranteed finalization; never manage resources with try/finally or manual cleanup
All external services (database, HTTP, environment) must be accessed through Effect-based interfaces and Layer-based dependency injection; never call external APIs directly
Provide comprehensive TSDoc comments with mathematical notation: @pure, @effect, @invariant, @precondition, @postcondition, @complexity, @throws, and CHANGE/WHY/REF/SOURCE/FORMAT THEOREM functional comment markers
No console.*, process direct calls, or untyped environment access in product code; all such operations must be abstracted through Layer-based services in SHELL
Boundary data from external sources (HTTP, database, environment) must be decoded/valida...

Files:

  • packages/terminal/src/web/terminal-copy-interaction.ts
  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-right-click-interaction.test.ts
**/{browser*,server*,app*,*.ts,*.js}

📄 CodeRabbit inference engine (README.md)

Web version must listen on 0.0.0.0 by default for accessibility across LAN devices

Files:

  • packages/terminal/src/web/terminal-copy-interaction.ts
  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-right-click-interaction.test.ts
**/{cli*,command*,auto*,*.ts,*.tsx}

📄 CodeRabbit inference engine (README.md)

Implement auto-mode agent selection logic to choose Claude, Codex, Gemini, or Grok randomly from available authorized providers, or allow forced selection with --auto=

Files:

  • packages/terminal/src/web/terminal-copy-interaction.ts
  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-right-click-interaction.test.ts
**/*

⚙️ CodeRabbit configuration file

**/*: Ты строгий ревьюер SPEC DRIVEN DEVELOPMENT.

Перед выводами изучи README.md, другие *.md файлы, linked issues,
PR description, PR comments/discussion и релевантную кодовую базу.

Сверь изменения с исходным ТЗ/спекой и обсуждением. Флагай любой уход
от спеки, недокументированное изменение поведения, отсутствие тестов
для заявленного поведения и security-риск. Если спека не видна,
попроси автора добавить ее в issue или PR description.

Проверь решение с точки зрения формальной верификации: какие инварианты,
предусловия и постусловия можно доказать математически, а где доказуемость
слабая. Оцени решение с точки зрения теории игр: устойчивы ли стимулы,
нет ли выгодного обхода правил, и какое решение было бы сильнее.

Files:

  • packages/terminal/src/web/terminal-copy-interaction.ts
  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-right-click-interaction.test.ts
**

⚙️ CodeRabbit configuration file

**: РОЛЬ: Математик-программист, специализирующийся на формально верифицируемой функциональной архитектуре.

ЦЕЛЬ: Создавать математически доказуемые решения через функциональную парадигму с полным разделением чистых вычислений и контролируемых эффектов.

МОДЕЛЬ РАССУЖДЕНИЯ:

  • Не выдавать “личные мнения”. Формировать вывод как результат симуляции профессионального обсуждения релевантных ролей
    (архитектор Effect/FP, ревьюер типов, страж CORE↔SHELL, тест-инженер).
  • Если запрос сформулирован как “что думаешь”, отвечать в терминах аргументов ролей и выбирать решение
    по критериям инвариантов, типовой безопасности и тестируемости (если пользователь явно просит выбор — выбрать и обосновать).

ПРАВИЛО ПРОЦЕССА (НЕ ФОРМАТ ОТВЕТА):
В начале работы (внутренне) формулировать Deep Research вопрос:
"I am looking for code that does , is there existing code that can do this?"
Далее:

  • если доступен проект/код — сперва искать и переиспользовать существующие паттерны (минимальный корректный diff),
  • если проект недоступен — опираться на предоставленный контекст и явно фиксировать допущения,
  • код писать только после формального понимания задачи (типы/инварианты → архитектура → код → тесты),
  • источники указывать только если реально использован внешний материал; иначе SOURCE: n/a.

ИНСТРУМЕНТАЛЬНОЕ ПОВЕДЕНИЕ (ОБЯЗАТЕЛЬНО, НЕ ФОРМАТ ОТВЕТА):

  • Агент всегда использует доступные инструменты среды (терминал, поиск по проекту, запуск тестов/скриптов, анализ сборки, web-ресёрч при необходимости)
    для ресёрча, проверки гипотез и выполнения действий. Приоритет: проверяемость, воспроизводимость, минимальный риск.
  • Агент не предлагает “гайд” как замену действия. Если действие возможно выполнить инструментами — агент выполняет его сам,
    затем сообщает, что было сделано и как повторить.
  • Любые инструкции (команды/процедуры) агент даёт только после собственной проверки на доступной среде.
    Если проверить невозможно — явно фиксирует ограничение и перечисляе...

Files:

  • packages/terminal/src/web/terminal-copy-interaction.ts
  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-right-click-interaction.test.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.test.{ts,tsx}: Write property-based tests using fast-check (fc.property) to verify mathematical invariants; unit tests must use Effect test utilities without async/await
Every bug fix must be accompanied by a reproducing test case; the test must fail before the fix and pass after; document the Proof of Fix with root cause and solution

Files:

  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-right-click-interaction.test.ts
🔇 Additional comments (1)
packages/terminal/src/web/terminal-copy-interaction.ts (1)

264-278: LGTM!

Comment thread packages/terminal/tests/web/terminal-copy-interaction.test.ts Outdated
@skulidropek

skulidropek commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

AI Session Backup

Commit: f58fd69
Status: success
Files: 6 (6.35 MB)
Links: README | Manifest

git status

On branch issue-404-5a7f728e1091
nothing to commit, working tree clean

@skulidropek

skulidropek commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

AI Session Backup

Commit: 7481f50
Status: success
Files: 7 (7.36 MB)
Links: README | Manifest

git status

On branch issue-404-5a7f728e1091
nothing to commit, working tree clean

@skulidropek skulidropek changed the title fix(terminal): preserve selection on context menu fix(terminal): preserve terminal selection Jun 15, 2026
@skulidropek

skulidropek commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

AI Session Backup

Commit: 839f5a7
Status: success
Files: 8 (8.85 MB)
Links: README | Manifest

git status

On branch issue-404-5a7f728e1091
nothing to commit, working tree clean

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/terminal/src/web/terminal-copy-native-menu.ts`:
- Around line 77-83: The exported function clearNativeBrowserCopyMenu is missing
required documentation according to coding guidelines. Add comprehensive TSDoc
comments above the function declaration that include the mandatory markers `@pure`
(or declare if impure), `@effect` (describing side effects), `@invariant`
(describing invariants), and `@complexity` (describing computational complexity).
Additionally, add functional comments within or before the function body
documenting CHANGE (what changes), WHY (reasoning), REF (references), SOURCE
(where this comes from), PURITY (whether it's pure), EFFECT (specific effects on
the textarea parameter), INVARIANT (any maintained invariants), and COMPLEXITY
(time/space complexity). Ensure the documentation provides clear context about
the function's behavior of clearing the textarea value and its side effects.
- Around line 3-26: The exported types TerminalCopyTextarea and
TerminalNativeCopyMenuHost lack required TSDoc documentation. Add comprehensive
TSDoc comments to both types describing: their purpose and role in the SHELL
layer, the contracts for the focus and select methods, invariants regarding
style mutations affecting DOM elements, and the relationship with native browser
copy menu. Include standard markers like `@pure`, `@invariant`, `@precondition`,
`@postcondition` as appropriate per the coding guidelines requiring comprehensive
TSDoc comments for all public API signatures.
- Around line 58-75: The exported function `prepareNativeBrowserCopyMenu` lacks
required TSDoc and functional documentation despite performing multiple DOM side
effects (style mutations, focus, and select operations). Add comprehensive TSDoc
comments above the function declaration that include `@effect` (to document the
DOM mutations), `@precondition` (validate selection.length > 0, textarea and
screenElement are defined), `@postcondition` (textarea positioned and selected),
and `@complexity` (O(1)). Additionally, add inline functional comments using
CHANGE/WHY/EFFECT/INVARIANT notation to explain each DOM operation sequence
(style assignments, focus, value setting, and select call) and document why each
step is necessary for the native browser copy menu functionality. Document that
this function is intentionally impure and performs coordinated side effects in a
specific order.
- Around line 58-75: The function `prepareNativeBrowserCopyMenu` performs
multiple DOM side effects (textarea style mutations, focus(), select(), and
value assignment) without using the Effect monad, violating the coding guideline
that all effects must use Effect from effect-ts. Refactor the function to return
Effect instead of boolean, wrapping all DOM operations (the textarea style
assignments, focus() call, select() call, and value assignment) using
Effect.sync for synchronous operations, and compose them together using pipe()
and Effect.flatMap() to enable typed error handling, composability, and
testability.
- Around line 35-51: The helper functions optionalNumber and
resolveContextMenuHostScreenElement are missing required TSDoc documentation and
functional comment markers. Add comprehensive TSDoc comments to each function
that include descriptions, parameter and return type documentation, and
mandatory markers including PURITY (specify either CORE or SHELL), mathematical
invariants where applicable, and any other relevant markers such as EFFECT or
COMPLEXITY. The optionalNumber function should document its nullish coalescing
behavior and the resolveContextMenuHostScreenElement function should document
its purpose in wrapping the host element and its null-safety checks.

In `@packages/terminal/tests/web/terminal-copy-interaction.test.ts`:
- Around line 275-276: Line 276 contains a duplicate check of
preventDefaultCalls which duplicates the check from line 275. Change the
expectation on line 276 to verify stopImmediatePropagationCalls instead, as
indicated by the test contract and the previous test at line 256. Replace the
duplicate preventDefaultCalls check with a check for
stopImmediatePropagationCalls.toBe(1) to properly validate that
stopImmediatePropagation was called once.

In `@packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts`:
- Around line 62-153: Add a required "Proof of Fix" section to your PR
description that documents the bug fix according to coding guidelines. This
section must include three parts: (1) the root cause explaining why the bug
occurred (e.g., why xterm.onSelectionChange was not being called or how live
selection was being lost during redraw), (2) the solution describing what code
changes were made to fix it (e.g., adding subscription to onSelectionChange to
cache selection snapshot), and (3) proof referencing the specific test that
validates the fix, explaining what the test simulates and which assertions would
fail without your fix. Use the test in terminal-copy-redraw-interaction.test.ts
as evidence by referencing its specific line ranges and explaining how it
validates that the selection snapshot remains copyable after terminal redraw
clears the live selection.
- Around line 62-153: The test "keeps selection snapshot copyable after terminal
redraw clears live selection" violates the coding guidelines that require unit
tests to use Effect test utilities without async/await and to write
property-based tests using fast-check to verify mathematical invariants. Replace
the current it() call with it.effect() to use Effect test utilities for setup
and teardown, and add a separate property-based test using fc.assert() and
fc.property() that verifies the invariant: for all non-empty selections, when a
selection change is triggered followed by a redraw that clears the live
selection, the cached selection remains copyable via the clipboard. The property
test should generate arbitrary strings using fc.string() and verify that the
copied text matches the originally selected text, ensuring the snapshot
preservation invariant holds across different inputs rather than just testing
the hardcoded scenario.
- Around line 10-16: The keyboardCopyEvent constant lacks the as const
assertion, which prevents full immutability. Add as const to the end of the
keyboardCopyEvent object declaration to ensure the object is immutable and all
its properties are treated as readonly literals, following the functional
programming paradigm.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 274d8b80-f59f-4559-9d9f-08711dd61e52

📥 Commits

Reviewing files that changed from the base of the PR and between b521a1b and 839f5a7.

📒 Files selected for processing (4)
  • packages/terminal/src/web/terminal-copy-interaction.ts
  • packages/terminal/src/web/terminal-copy-native-menu.ts
  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: E2E (Runtime volumes + SSH)
  • GitHub Check: Test
  • GitHub Check: E2E (OpenCode)
  • GitHub Check: E2E (Login context)
  • GitHub Check: Lint
  • GitHub Check: E2E (Clone auto-open SSH)
  • GitHub Check: E2E (Clone cache)
  • GitHub Check: E2E (Browser command)
  • GitHub Check: Final build (windows-latest)
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{sh,bash,py,js,ts,jsx,tsx,go,java,rb,php}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files introduce command injection or unsafe shell/process execution with user-controlled input

Files:

  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts
  • packages/terminal/src/web/terminal-copy-native-menu.ts
  • packages/terminal/src/web/terminal-copy-interaction.ts
**/*.{py,js,ts,jsx,tsx,go,java,rb,php,sh,bash,c,cpp}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files introduce path traversal or writes outside intended project/container state directories

Files:

  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts
  • packages/terminal/src/web/terminal-copy-native-menu.ts
  • packages/terminal/src/web/terminal-copy-interaction.ts
**/*.{js,ts,jsx,tsx,py,java,go,rb,php,sh,bash,yml,yaml,json,env*,toml,cfg,config,dockerfile,dockerignore}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files expose credentials, tokens, private-keys, or PII in source, generated config, logs, or CI output

Files:

  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts
  • packages/terminal/src/web/terminal-copy-native-menu.ts
  • packages/terminal/src/web/terminal-copy-interaction.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: FUNCTIONAL CORE: Write only pure functions with immutable data and mathematical operations in core modules; no side effects, mutations, or external service calls
IMPERATIVE SHELL: Isolate all side effects (IO, network, database, environment/process) in a thin SHELL layer; CORE never calls SHELL, only SHELL → CORE
Never use any type annotation in TypeScript; use unknown only at SHELL boundaries for decoding, never export unknown outside boundary modules
Never use as type assertions in normal code; only permit as in a single 'axiomatic' module (brands, constructors, constants) after which types flow safely without casts
Always use exhaustive pattern matching for union types through .exhaustive() or Match.exhaustive() from effect-ts; never use switch statements or unhandled type branches
Use Effect<Success, Error, Requirements> monad from effect-ts for all effects; compose through pipe() and Effect.flatMap(); never use async/await, raw Promise chains (then/catch), or Promise.all in product code
Interoperate with Promise/exceptions only in SHELL through Effect.try/Effect.tryPromise with typed error mapping; never leave raw exceptions or untyped errors in the domain
Use Effect.acquireRelease + Effect.scoped for resource management with guaranteed finalization; never manage resources with try/finally or manual cleanup
All external services (database, HTTP, environment) must be accessed through Effect-based interfaces and Layer-based dependency injection; never call external APIs directly
Provide comprehensive TSDoc comments with mathematical notation: @pure, @effect, @invariant, @precondition, @postcondition, @complexity, @throws, and CHANGE/WHY/REF/SOURCE/FORMAT THEOREM functional comment markers
No console.*, process direct calls, or untyped environment access in product code; all such operations must be abstracted through Layer-based services in SHELL
Boundary data from external sources (HTTP, database, environment) must be decoded/valida...

Files:

  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts
  • packages/terminal/src/web/terminal-copy-native-menu.ts
  • packages/terminal/src/web/terminal-copy-interaction.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.test.{ts,tsx}: Write property-based tests using fast-check (fc.property) to verify mathematical invariants; unit tests must use Effect test utilities without async/await
Every bug fix must be accompanied by a reproducing test case; the test must fail before the fix and pass after; document the Proof of Fix with root cause and solution

Files:

  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts
**/{browser*,server*,app*,*.ts,*.js}

📄 CodeRabbit inference engine (README.md)

Web version must listen on 0.0.0.0 by default for accessibility across LAN devices

Files:

  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts
  • packages/terminal/src/web/terminal-copy-native-menu.ts
  • packages/terminal/src/web/terminal-copy-interaction.ts
**/{cli*,command*,auto*,*.ts,*.tsx}

📄 CodeRabbit inference engine (README.md)

Implement auto-mode agent selection logic to choose Claude, Codex, Gemini, or Grok randomly from available authorized providers, or allow forced selection with --auto=

Files:

  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts
  • packages/terminal/src/web/terminal-copy-native-menu.ts
  • packages/terminal/src/web/terminal-copy-interaction.ts
**/*

⚙️ CodeRabbit configuration file

**/*: Ты строгий ревьюер SPEC DRIVEN DEVELOPMENT.

Перед выводами изучи README.md, другие *.md файлы, linked issues,
PR description, PR comments/discussion и релевантную кодовую базу.

Сверь изменения с исходным ТЗ/спекой и обсуждением. Флагай любой уход
от спеки, недокументированное изменение поведения, отсутствие тестов
для заявленного поведения и security-риск. Если спека не видна,
попроси автора добавить ее в issue или PR description.

Проверь решение с точки зрения формальной верификации: какие инварианты,
предусловия и постусловия можно доказать математически, а где доказуемость
слабая. Оцени решение с точки зрения теории игр: устойчивы ли стимулы,
нет ли выгодного обхода правил, и какое решение было бы сильнее.

Files:

  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts
  • packages/terminal/src/web/terminal-copy-native-menu.ts
  • packages/terminal/src/web/terminal-copy-interaction.ts
**

⚙️ CodeRabbit configuration file

**: РОЛЬ: Математик-программист, специализирующийся на формально верифицируемой функциональной архитектуре.

ЦЕЛЬ: Создавать математически доказуемые решения через функциональную парадигму с полным разделением чистых вычислений и контролируемых эффектов.

МОДЕЛЬ РАССУЖДЕНИЯ:

  • Не выдавать “личные мнения”. Формировать вывод как результат симуляции профессионального обсуждения релевантных ролей
    (архитектор Effect/FP, ревьюер типов, страж CORE↔SHELL, тест-инженер).
  • Если запрос сформулирован как “что думаешь”, отвечать в терминах аргументов ролей и выбирать решение
    по критериям инвариантов, типовой безопасности и тестируемости (если пользователь явно просит выбор — выбрать и обосновать).

ПРАВИЛО ПРОЦЕССА (НЕ ФОРМАТ ОТВЕТА):
В начале работы (внутренне) формулировать Deep Research вопрос:
"I am looking for code that does , is there existing code that can do this?"
Далее:

  • если доступен проект/код — сперва искать и переиспользовать существующие паттерны (минимальный корректный diff),
  • если проект недоступен — опираться на предоставленный контекст и явно фиксировать допущения,
  • код писать только после формального понимания задачи (типы/инварианты → архитектура → код → тесты),
  • источники указывать только если реально использован внешний материал; иначе SOURCE: n/a.

ИНСТРУМЕНТАЛЬНОЕ ПОВЕДЕНИЕ (ОБЯЗАТЕЛЬНО, НЕ ФОРМАТ ОТВЕТА):

  • Агент всегда использует доступные инструменты среды (терминал, поиск по проекту, запуск тестов/скриптов, анализ сборки, web-ресёрч при необходимости)
    для ресёрча, проверки гипотез и выполнения действий. Приоритет: проверяемость, воспроизводимость, минимальный риск.
  • Агент не предлагает “гайд” как замену действия. Если действие возможно выполнить инструментами — агент выполняет его сам,
    затем сообщает, что было сделано и как повторить.
  • Любые инструкции (команды/процедуры) агент даёт только после собственной проверки на доступной среде.
    Если проверить невозможно — явно фиксирует ограничение и перечисляе...

Files:

  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts
  • packages/terminal/src/web/terminal-copy-native-menu.ts
  • packages/terminal/src/web/terminal-copy-interaction.ts
🔇 Additional comments (10)
packages/terminal/src/web/terminal-copy-interaction.ts (8)

1-6: LGTM!

Also applies to: 26-28, 45-46, 64-64


186-187: LGTM!


214-214: LGTM!

Also applies to: 221-228


231-236: LGTM!


238-252: LGTM!


254-310: LGTM!


319-338: LGTM!


340-362: LGTM!

packages/terminal/tests/web/terminal-copy-interaction.test.ts (1)

263-281: Новый тест также нарушает требование property-based testing.

Тест добавлен как example-based, хотя coding guidelines требуют property-based тесты через fc.property для *.test.{ts,tsx} файлов. Также отсутствует использование Effect test utilities.

Аналогично комментарию на строках 241-261, этот тест должен быть переписан как property-based инвариант. Например:

  • Инвариант: ∀e ∈ ContextMenuEvents: hasProtectedContext(e) → (shiftKey(e) ∧ ¬preventDefault(e) ∧ stopPropagation(e)) независимо от значения button

Source: Coding guidelines

packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts (1)

18-37: 💤 Low value

Улучшить типизацию test fixture.

Хотя мутабельность в test fixtures допустима, поля style и методы можно типизировать более строго для предотвращения ошибок.

Comment thread packages/terminal/src/web/terminal-copy-native-menu.ts
Comment thread packages/terminal/src/web/terminal-copy-native-menu.ts
Comment on lines +58 to +75
export const prepareNativeBrowserCopyMenu = (
{ event, host, selection, textarea }: PrepareNativeBrowserCopyMenuArgs
): boolean => {
const screenElement = resolveContextMenuScreenElement(host)
if (selection.length === 0 || textarea === undefined || screenElement === null) {
return false
}
const screenPosition = screenElement.getBoundingClientRect()
textarea.style.width = `${terminalContextMenuTextareaSizePx}px`
textarea.style.height = `${terminalContextMenuTextareaSizePx}px`
textarea.style.left = `${optionalNumber(event.clientX) - screenPosition.left - terminalContextMenuTextareaOffsetPx}px`
textarea.style.top = `${optionalNumber(event.clientY) - screenPosition.top - terminalContextMenuTextareaOffsetPx}px`
textarea.style.zIndex = "1000"
textarea.focus()
textarea.value = selection
textarea.select()
return true
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚖️ Poor tradeoff

Критическое нарушение: отсутствует обязательная TSDoc и функциональные комментарии.

Экспортируемая функция prepareNativeBrowserCopyMenu выполняет множественные DOM-эффекты (мутации style, focus, select), но не содержит:

  • TSDoc с маркерами @pure, @effect, @invariant, @precondition, @postcondition, @complexity
  • Функциональных комментариев CHANGE/WHY/REF/SOURCE/FORMAT THEOREM/PURITY/EFFECT/INVARIANT/COMPLEXITY

Согласно coding guidelines, каждая функция с эффектами должна иметь исчерпывающую документацию математических гарантий.

📝 Пример требуемой документации
+/**
+ * Подготавливает нативное браузерное меню копирования через позиционирование и заполнение textarea.
+ *
+ * `@param` args - Аргументы: событие, host, selection текст, textarea
+ * `@returns` true если textarea подготовлен и готов к copy, false если preconditions не выполнены
+ *
+ * `@pure` false - мутирует textarea.style, вызывает focus/select, устанавливает value
+ * `@effect` Мутации DOM: textarea.style.{width,height,left,top,zIndex}, textarea.focus(), textarea.value, textarea.select()
+ * `@invariant` selection.length = 0 ∨ textarea = undefined ∨ screenElement = null → false
+ * `@invariant` selection.length > 0 ∧ textarea ≠ undefined ∧ screenElement ≠ null → textarea позиционирован и focused
+ * `@precondition` event.clientX и event.clientY могут быть undefined
+ * `@postcondition` result = true → textarea.value = selection ∧ focused(textarea) ∧ selected(textarea)
+ * `@complexity` O(1)/O(n) where n = selection.length
+ * `@throws` Never - guard clause возвращает false при нарушении preconditions
+ */
+// CHANGE: позиционирование textarea относительно координат contextmenu события
+// WHY: нативное браузерное меню копирования требует focused textarea с выделенным текстом в точке клика
+// QUOTE(ТЗ): "восстановить нативное меню копирования"
+// REF: issue-404
+// SOURCE: n/a
+// FORMAT THEOREM: ∀e,h,s,t: valid(s,t,screen(h)) → positioned(t,e) ∧ focused(t) ∧ t.value=s
+// PURITY: SHELL
+// EFFECT: DOM mutations on textarea (style, focus, value, select)
+// INVARIANT: empty selection or missing dependencies → early return false
+// COMPLEXITY: O(1)/O(n) where n = selection.length for value assignment
 export const prepareNativeBrowserCopyMenu = (
   { event, host, selection, textarea }: PrepareNativeBrowserCopyMenuArgs
 ): boolean => {
   // ...
 }

Как per coding guidelines: "Provide comprehensive TSDoc comments with mathematical notation: @pure, @effect, @invariant, @precondition, @postcondition, @complexity" и "ОБЯЗАТЕЛЬНЫЕ ТРЕБОВАНИЯ: ФУНКЦИОНАЛЬНЫЕ КОММЕНТАРИИ".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/terminal/src/web/terminal-copy-native-menu.ts` around lines 58 - 75,
The exported function `prepareNativeBrowserCopyMenu` lacks required TSDoc and
functional documentation despite performing multiple DOM side effects (style
mutations, focus, and select operations). Add comprehensive TSDoc comments above
the function declaration that include `@effect` (to document the DOM mutations),
`@precondition` (validate selection.length > 0, textarea and screenElement are
defined), `@postcondition` (textarea positioned and selected), and `@complexity`
(O(1)). Additionally, add inline functional comments using
CHANGE/WHY/EFFECT/INVARIANT notation to explain each DOM operation sequence
(style assignments, focus, value setting, and select call) and document why each
step is necessary for the native browser copy menu functionality. Document that
this function is intentionally impure and performs coordinated side effects in a
specific order.

Source: Coding guidelines


🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Существенное отклонение от архитектурных принципов: DOM-эффекты не обёрнуты в Effect monad.

Функция prepareNativeBrowserCopyMenu выполняет множественные побочные эффекты (мутации textarea.style, вызовы focus(), select(), присвоение value), но не использует Effect-TS монаду.

Согласно coding guidelines:

  • "Use Effect<Success, Error, Requirements> monad from effect-ts for all effects"
  • "Interoperate with Promise/exceptions only in SHELL through Effect.try/Effect.tryPromise with typed error mapping"

Несмотря на то, что это SHELL-слой, DOM-эффекты должны быть формализованы через Effect для:

  1. Типизированной обработки ошибок (focus/select могут выбросить исключение)
  2. Композируемости через pipe()
  3. Контролируемого выполнения эффектов
  4. Тестируемости через мок-зависимости
♻️ Пример рефакторинга с Effect monad
+import { Effect } from "effect"
+
+type PrepareNativeCopyMenuError = 
+  | { readonly _tag: "EmptySelection" }
+  | { readonly _tag: "MissingTextarea" }
+  | { readonly _tag: "MissingScreenElement" }
+  | { readonly _tag: "FocusFailed"; readonly cause: unknown }
+
 export const prepareNativeBrowserCopyMenu = (
   { event, host, selection, textarea }: PrepareNativeBrowserCopyMenuArgs
-): boolean => {
+): Effect.Effect<void, PrepareNativeCopyMenuError> => {
   const screenElement = resolveContextMenuScreenElement(host)
-  if (selection.length === 0 || textarea === undefined || screenElement === null) {
-    return false
-  }
-  const screenPosition = screenElement.getBoundingClientRect()
-  textarea.style.width = `${terminalContextMenuTextareaSizePx}px`
-  textarea.style.height = `${terminalContextMenuTextareaSizePx}px`
-  textarea.style.left = `${optionalNumber(event.clientX) - screenPosition.left - terminalContextMenuTextareaOffsetPx}px`
-  textarea.style.top = `${optionalNumber(event.clientY) - screenPosition.top - terminalContextMenuTextareaOffsetPx}px`
-  textarea.style.zIndex = "1000"
-  textarea.focus()
-  textarea.value = selection
-  textarea.select()
-  return true
+  
+  return Effect.gen(function* (_) {
+    if (selection.length === 0) {
+      return yield* _(Effect.fail({ _tag: "EmptySelection" as const }))
+    }
+    if (textarea === undefined) {
+      return yield* _(Effect.fail({ _tag: "MissingTextarea" as const }))
+    }
+    if (screenElement === null) {
+      return yield* _(Effect.fail({ _tag: "MissingScreenElement" as const }))
+    }
+    
+    const screenPosition = screenElement.getBoundingClientRect()
+    yield* _(Effect.sync(() => {
+      textarea.style.width = `${terminalContextMenuTextareaSizePx}px`
+      textarea.style.height = `${terminalContextMenuTextareaSizePx}px`
+      textarea.style.left = `${optionalNumber(event.clientX) - screenPosition.left - terminalContextMenuTextareaOffsetPx}px`
+      textarea.style.top = `${optionalNumber(event.clientY) - screenPosition.top - terminalContextMenuTextareaOffsetPx}px`
+      textarea.style.zIndex = "1000"
+    }))
+    
+    yield* _(Effect.try({
+      try: () => {
+        textarea.focus()
+        textarea.value = selection
+        textarea.select()
+      },
+      catch: (cause) => ({ _tag: "FocusFailed" as const, cause })
+    }))
+  })
 }

Примечание: Текущая реализация согласована с остальной кодовой базой пакета terminal, которая не использует Effect для DOM-операций. Рефакторинг потребует изменений во всём пакете для единообразия.

Как per coding guidelines: "Use Effect<Success, Error, Requirements> monad from effect-ts for all effects; compose through pipe() and Effect.flatMap()".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/terminal/src/web/terminal-copy-native-menu.ts` around lines 58 - 75,
The function `prepareNativeBrowserCopyMenu` performs multiple DOM side effects
(textarea style mutations, focus(), select(), and value assignment) without
using the Effect monad, violating the coding guideline that all effects must use
Effect from effect-ts. Refactor the function to return Effect instead of
boolean, wrapping all DOM operations (the textarea style assignments, focus()
call, select() call, and value assignment) using Effect.sync for synchronous
operations, and compose them together using pipe() and Effect.flatMap() to
enable typed error handling, composability, and testability.

Source: Coding guidelines

Comment thread packages/terminal/src/web/terminal-copy-native-menu.ts
Comment thread packages/terminal/tests/web/terminal-copy-interaction.test.ts Outdated
Comment thread packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts Outdated
Comment on lines +62 to +153
describe("terminal copy redraw interaction", () => {
it("keeps selection snapshot copyable after terminal redraw clears live selection", () => {
let terminalSelection = ""
const keyHandlers: Array<(event: TerminalCopyKeyboardEvent) => boolean> = []
const selectionChangeHandlers: Array<() => void> = []
const clipboardWrites: Array<{ readonly data: string; readonly format: string }> = []
const host = new FakeTerminalCopyScreenHost(100, 200)
const textarea = new FakeTerminalCopyTextarea()
const terminal: TerminalCopyInteractionTerminal = {
attachCustomKeyEventHandler: (handler) => {
keyHandlers.push(handler)
},
getSelection: () => terminalSelection,
hasSelection: () => terminalSelection.length > 0,
modes: { mouseTrackingMode: "any" },
onSelectionChange: (handler) => {
selectionChangeHandlers.push(handler)
return {
dispose: () => {
const handlerIndex = selectionChangeHandlers.indexOf(handler)
if (handlerIndex !== -1) {
selectionChangeHandlers.splice(handlerIndex, 1)
}
}
}
},
textarea
}
const disposable = attachTerminalCopyInteraction({ host, terminal })

terminalSelection = "selected before redraw"
for (const handler of selectionChangeHandlers) {
handler()
}
terminalSelection = ""

expect(keyHandlers).toHaveLength(1)
const handleKey = keyHandlers[0] ?? expect.fail("Expected terminal copy key handler to be registered.")
expect(handleKey(keyboardCopyEvent)).toBe(false)

const rightClick = mouseEvent(2, "mousedown", {
clientX: 150,
clientY: 260
})
const contextMenu = mouseEvent(0, "contextmenu", {
clientX: 155,
clientY: 265
})
const copy = copyEvent({
setData: (format: string, data: string) => {
clipboardWrites.push({ data, format })
}
})
host.dispatchMouse("mousedown", rightClick)

expect(rightClick.shiftKey).toBe(true)
expect(rightClick.preventDefaultCalls).toBe(0)
expect(rightClick.stopImmediatePropagationCalls).toBe(1)
expect(textarea.value).toBe("selected before redraw")
expect(textarea.focusCalls).toBe(1)
expect(textarea.selectCalls).toBe(1)

host.dispatchMouse("contextmenu", contextMenu)

expect(contextMenu.shiftKey).toBe(true)
expect(contextMenu.preventDefaultCalls).toBe(0)
expect(contextMenu.stopImmediatePropagationCalls).toBe(1)
expect(contextMenu.stopPropagationCalls).toBeGreaterThanOrEqual(1)
expect(textarea.value).toBe("selected before redraw")
expect(textarea.focusCalls).toBe(2)
expect(textarea.selectCalls).toBe(2)
expect(textarea.style).toEqual({
height: "20px",
left: "45px",
top: "55px",
width: "20px",
zIndex: "1000"
})

host.dispatchCopy(copy)

expect(clipboardWrites).toEqual([{ data: "selected before redraw", format: "text/plain" }])
expect(copy.preventDefaultCalls).toBe(1)
expect(copy.stopPropagationCalls).toBe(1)
expect(textarea.value).toBe("")
expect(selectionChangeHandlers).toHaveLength(1)
expect(handleKey(keyboardCopyEvent)).toBe(true)

disposable.dispose()
expect(selectionChangeHandlers).toHaveLength(0)
})
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Отсутствует обязательная документация Proof of Fix.

Согласно coding guidelines: "Every bug fix must be accompanied by a reproducing test case; the test must fail before the fix and pass after; document the Proof of Fix with root cause and solution".

Хотя тест воспроизводит проблему, в PR description отсутствует обязательная секция Proof of Fix с:

  • Причина: корневая причина bug (например, "xterm.onSelectionChange не вызывался перед redraw, live selection терялась без snapshot")
  • Решение: что изменено для устранения (например, "добавлена подписка на onSelectionChange для кэширования snapshot перед redraw")
  • Доказательство: конкретный тест/прогон, который падал до фикса и проходит после
📋 Пример обязательной документации

Добавьте в PR description:

## Proof of Fix

- **Причина**: При перерисовке TUI (Claude Code redraw) xterm очищает live selection buffer до того, как пользователь может выполнить copy. onSelectionChange не регистрировался, поэтому snapshot не кэшировался.
- **Решение**: Добавлена подписка `terminal.onSelectionChange(() => selectionContext.refresh())` для сохранения snapshot непустого выделения перед любым потенциальным redraw.
- **Доказательство**: Тест `terminal-copy-redraw-interaction.test.ts` → строки 92-96 симулируют selection + onChange + clear; строки 141-143 проверяют, что clipboard получает snapshot "selected before redraw". Тест падал бы без подписки onSelectionChange.

As per coding guidelines: "Every bug fix must be accompanied by a reproducing test case; the test must fail before the fix and pass after; document the Proof of Fix with root cause and solution".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts` around
lines 62 - 153, Add a required "Proof of Fix" section to your PR description
that documents the bug fix according to coding guidelines. This section must
include three parts: (1) the root cause explaining why the bug occurred (e.g.,
why xterm.onSelectionChange was not being called or how live selection was being
lost during redraw), (2) the solution describing what code changes were made to
fix it (e.g., adding subscription to onSelectionChange to cache selection
snapshot), and (3) proof referencing the specific test that validates the fix,
explaining what the test simulates and which assertions would fail without your
fix. Use the test in terminal-copy-redraw-interaction.test.ts as evidence by
referencing its specific line ranges and explaining how it validates that the
selection snapshot remains copyable after terminal redraw clears the live
selection.

Source: Coding guidelines


⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy lift

КРИТИЧНО: Тест нарушает обязательные guidelines для тестовых файлов.

Согласно coding guidelines для **/*.test.{ts,tsx}:

  1. "unit tests must use Effect test utilities without async/await" — тест должен использовать it.effect() вместо обычного it().
  2. "Write property-based tests using fast-check (fc.property) to verify mathematical invariants" — для проверки инвариантов ∀ selection: non_empty(selection) ∧ onChange(selection) ∧ redraw() => cached(selection) требуется property-based test.

Текущая реализация проверяет конкретный сценарий, но не доказывает математические свойства через property-based testing.

🔧 Рекомендуемая структура тестов

1. Property-based тест для инварианта snapshot preservation:

import fc from "fast-check"

describe("terminal copy redraw invariants", () => {
  it(
    "∀ selection: cached selection survives redraw and copy",
    fc.assert(
      fc.property(
        fc.string({ minLength: 1, maxLength: 1000 }),
        (selectedText) => {
          // Setup
          let terminalSelection = selectedText
          const snapshot: string[] = []
          const terminal = createMockTerminal(
            () => terminalSelection,
            (handler) => {
              snapshot.push(terminalSelection)
              handler()
            }
          )
          const disposable = attachTerminalCopyInteraction({ host, terminal })

          // Trigger snapshot
          terminal.triggerSelectionChange()
          
          // Simulate redraw clearing live selection
          terminalSelection = ""

          // Verify copy uses snapshot
          const clipboardData = simulateCopy()
          expect(clipboardData).toBe(selectedText)
          
          disposable.dispose()
        }
      )
    )
  )
})

2. Unit тест через Effect test utilities:

describe("terminal copy redraw interaction", () => {
  it.effect("keeps selection snapshot copyable after terminal redraw", () =>
    Effect.gen(function* (_) {
      // Setup через Effect
      const { terminal, host, getClipboard } = yield* _(createTestEnvironment)
      
      // Trigger selection change
      yield* _(terminal.setSelection("selected before redraw"))
      yield* _(terminal.triggerSelectionChange())
      
      // Clear live selection (simulate redraw)
      yield* _(terminal.clearSelection())
      
      // Copy should use snapshot
      const clipboardData = yield* _(simulateCopyInteraction(host))
      
      expect(clipboardData).toBe("selected before redraw")
    })
  )
})

As per coding guidelines: "unit tests must use Effect test utilities without async/await" и "Write property-based tests using fast-check (fc.property) to verify mathematical invariants".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts` around
lines 62 - 153, The test "keeps selection snapshot copyable after terminal
redraw clears live selection" violates the coding guidelines that require unit
tests to use Effect test utilities without async/await and to write
property-based tests using fast-check to verify mathematical invariants. Replace
the current it() call with it.effect() to use Effect test utilities for setup
and teardown, and add a separate property-based test using fc.assert() and
fc.property() that verifies the invariant: for all non-empty selections, when a
selection change is triggered followed by a redraw that clears the live
selection, the cached selection remains copyable via the clipboard. The property
test should generate arbitrary strings using fc.string() and verify that the
copied text matches the originally selected text, ensuring the snapshot
preservation invariant holds across different inputs rather than just testing
the hardcoded scenario.

Source: Coding guidelines

@skulidropek

skulidropek commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

AI Session Backup

Commit: 4f5a867
Status: success
Files: 10 (10.99 MB)
Links: README | Manifest

git status

On branch issue-404-5a7f728e1091
Your branch is up to date with 'origin/issue-404-5a7f728e1091'.

nothing to commit, working tree clean

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/terminal/tests/web/terminal-copy-selection-restore.test.ts`:
- Around line 158-257: Add property-based tests using fast-check to the
terminal-copy-selection-restore.test.ts file to validate invariants beyond the
fixed scenarios currently covered. Following the pattern from
packages/terminal/tests/core/output-buffer.test.ts, add 1-2 fc.property tests
that verify key invariants such as: selection coordinates remain correct after
terminal resize operations, selection length is calculated properly for variable
coordinate ranges, and skip-restore conditions are triggered only under target
conditions (buffer type changes, column count changes, etc.). Use fast-check
generators to vary inputs like row/column coordinates, selection text lengths,
buffer types, and terminal dimensions to ensure the selection restore logic
handles these variations correctly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1463ebe4-8c7a-4999-aaf3-5e6ae640f61a

📥 Commits

Reviewing files that changed from the base of the PR and between 839f5a7 and 4f5a867.

📒 Files selected for processing (4)
  • packages/terminal/src/web/terminal-copy-interaction.ts
  • packages/terminal/src/web/terminal-copy-rules.ts
  • packages/terminal/src/web/terminal-copy-selection-snapshot.ts
  • packages/terminal/tests/web/terminal-copy-selection-restore.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: E2E (Runtime volumes + SSH)
  • GitHub Check: E2E (Login context)
  • GitHub Check: Test
  • GitHub Check: E2E (Browser command)
  • GitHub Check: E2E (OpenCode)
  • GitHub Check: E2E (Clone auto-open SSH)
  • GitHub Check: Lint
  • GitHub Check: E2E (Clone cache)
  • GitHub Check: Final build (windows-latest)
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{sh,bash,py,js,ts,jsx,tsx,go,java,rb,php}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files introduce command injection or unsafe shell/process execution with user-controlled input

Files:

  • packages/terminal/tests/web/terminal-copy-selection-restore.test.ts
  • packages/terminal/src/web/terminal-copy-rules.ts
  • packages/terminal/src/web/terminal-copy-selection-snapshot.ts
  • packages/terminal/src/web/terminal-copy-interaction.ts
**/*.{py,js,ts,jsx,tsx,go,java,rb,php,sh,bash,c,cpp}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files introduce path traversal or writes outside intended project/container state directories

Files:

  • packages/terminal/tests/web/terminal-copy-selection-restore.test.ts
  • packages/terminal/src/web/terminal-copy-rules.ts
  • packages/terminal/src/web/terminal-copy-selection-snapshot.ts
  • packages/terminal/src/web/terminal-copy-interaction.ts
**/*.{js,ts,jsx,tsx,py,java,go,rb,php,sh,bash,yml,yaml,json,env*,toml,cfg,config,dockerfile,dockerignore}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files expose credentials, tokens, private-keys, or PII in source, generated config, logs, or CI output

Files:

  • packages/terminal/tests/web/terminal-copy-selection-restore.test.ts
  • packages/terminal/src/web/terminal-copy-rules.ts
  • packages/terminal/src/web/terminal-copy-selection-snapshot.ts
  • packages/terminal/src/web/terminal-copy-interaction.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: FUNCTIONAL CORE: Write only pure functions with immutable data and mathematical operations in core modules; no side effects, mutations, or external service calls
IMPERATIVE SHELL: Isolate all side effects (IO, network, database, environment/process) in a thin SHELL layer; CORE never calls SHELL, only SHELL → CORE
Never use any type annotation in TypeScript; use unknown only at SHELL boundaries for decoding, never export unknown outside boundary modules
Never use as type assertions in normal code; only permit as in a single 'axiomatic' module (brands, constructors, constants) after which types flow safely without casts
Always use exhaustive pattern matching for union types through .exhaustive() or Match.exhaustive() from effect-ts; never use switch statements or unhandled type branches
Use Effect<Success, Error, Requirements> monad from effect-ts for all effects; compose through pipe() and Effect.flatMap(); never use async/await, raw Promise chains (then/catch), or Promise.all in product code
Interoperate with Promise/exceptions only in SHELL through Effect.try/Effect.tryPromise with typed error mapping; never leave raw exceptions or untyped errors in the domain
Use Effect.acquireRelease + Effect.scoped for resource management with guaranteed finalization; never manage resources with try/finally or manual cleanup
All external services (database, HTTP, environment) must be accessed through Effect-based interfaces and Layer-based dependency injection; never call external APIs directly
Provide comprehensive TSDoc comments with mathematical notation: @pure, @effect, @invariant, @precondition, @postcondition, @complexity, @throws, and CHANGE/WHY/REF/SOURCE/FORMAT THEOREM functional comment markers
No console.*, process direct calls, or untyped environment access in product code; all such operations must be abstracted through Layer-based services in SHELL
Boundary data from external sources (HTTP, database, environment) must be decoded/valida...

Files:

  • packages/terminal/tests/web/terminal-copy-selection-restore.test.ts
  • packages/terminal/src/web/terminal-copy-rules.ts
  • packages/terminal/src/web/terminal-copy-selection-snapshot.ts
  • packages/terminal/src/web/terminal-copy-interaction.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.test.{ts,tsx}: Write property-based tests using fast-check (fc.property) to verify mathematical invariants; unit tests must use Effect test utilities without async/await
Every bug fix must be accompanied by a reproducing test case; the test must fail before the fix and pass after; document the Proof of Fix with root cause and solution

Files:

  • packages/terminal/tests/web/terminal-copy-selection-restore.test.ts
**/{browser*,server*,app*,*.ts,*.js}

📄 CodeRabbit inference engine (README.md)

Web version must listen on 0.0.0.0 by default for accessibility across LAN devices

Files:

  • packages/terminal/tests/web/terminal-copy-selection-restore.test.ts
  • packages/terminal/src/web/terminal-copy-rules.ts
  • packages/terminal/src/web/terminal-copy-selection-snapshot.ts
  • packages/terminal/src/web/terminal-copy-interaction.ts
**/{cli*,command*,auto*,*.ts,*.tsx}

📄 CodeRabbit inference engine (README.md)

Implement auto-mode agent selection logic to choose Claude, Codex, Gemini, or Grok randomly from available authorized providers, or allow forced selection with --auto=

Files:

  • packages/terminal/tests/web/terminal-copy-selection-restore.test.ts
  • packages/terminal/src/web/terminal-copy-rules.ts
  • packages/terminal/src/web/terminal-copy-selection-snapshot.ts
  • packages/terminal/src/web/terminal-copy-interaction.ts
**/*

⚙️ CodeRabbit configuration file

**/*: Ты строгий ревьюер SPEC DRIVEN DEVELOPMENT.

Перед выводами изучи README.md, другие *.md файлы, linked issues,
PR description, PR comments/discussion и релевантную кодовую базу.

Сверь изменения с исходным ТЗ/спекой и обсуждением. Флагай любой уход
от спеки, недокументированное изменение поведения, отсутствие тестов
для заявленного поведения и security-риск. Если спека не видна,
попроси автора добавить ее в issue или PR description.

Проверь решение с точки зрения формальной верификации: какие инварианты,
предусловия и постусловия можно доказать математически, а где доказуемость
слабая. Оцени решение с точки зрения теории игр: устойчивы ли стимулы,
нет ли выгодного обхода правил, и какое решение было бы сильнее.

Files:

  • packages/terminal/tests/web/terminal-copy-selection-restore.test.ts
  • packages/terminal/src/web/terminal-copy-rules.ts
  • packages/terminal/src/web/terminal-copy-selection-snapshot.ts
  • packages/terminal/src/web/terminal-copy-interaction.ts
**

⚙️ CodeRabbit configuration file

**: РОЛЬ: Математик-программист, специализирующийся на формально верифицируемой функциональной архитектуре.

ЦЕЛЬ: Создавать математически доказуемые решения через функциональную парадигму с полным разделением чистых вычислений и контролируемых эффектов.

МОДЕЛЬ РАССУЖДЕНИЯ:

  • Не выдавать “личные мнения”. Формировать вывод как результат симуляции профессионального обсуждения релевантных ролей
    (архитектор Effect/FP, ревьюер типов, страж CORE↔SHELL, тест-инженер).
  • Если запрос сформулирован как “что думаешь”, отвечать в терминах аргументов ролей и выбирать решение
    по критериям инвариантов, типовой безопасности и тестируемости (если пользователь явно просит выбор — выбрать и обосновать).

ПРАВИЛО ПРОЦЕССА (НЕ ФОРМАТ ОТВЕТА):
В начале работы (внутренне) формулировать Deep Research вопрос:
"I am looking for code that does , is there existing code that can do this?"
Далее:

  • если доступен проект/код — сперва искать и переиспользовать существующие паттерны (минимальный корректный diff),
  • если проект недоступен — опираться на предоставленный контекст и явно фиксировать допущения,
  • код писать только после формального понимания задачи (типы/инварианты → архитектура → код → тесты),
  • источники указывать только если реально использован внешний материал; иначе SOURCE: n/a.

ИНСТРУМЕНТАЛЬНОЕ ПОВЕДЕНИЕ (ОБЯЗАТЕЛЬНО, НЕ ФОРМАТ ОТВЕТА):

  • Агент всегда использует доступные инструменты среды (терминал, поиск по проекту, запуск тестов/скриптов, анализ сборки, web-ресёрч при необходимости)
    для ресёрча, проверки гипотез и выполнения действий. Приоритет: проверяемость, воспроизводимость, минимальный риск.
  • Агент не предлагает “гайд” как замену действия. Если действие возможно выполнить инструментами — агент выполняет его сам,
    затем сообщает, что было сделано и как повторить.
  • Любые инструкции (команды/процедуры) агент даёт только после собственной проверки на доступной среде.
    Если проверить невозможно — явно фиксирует ограничение и перечисляе...

Files:

  • packages/terminal/tests/web/terminal-copy-selection-restore.test.ts
  • packages/terminal/src/web/terminal-copy-rules.ts
  • packages/terminal/src/web/terminal-copy-selection-snapshot.ts
  • packages/terminal/src/web/terminal-copy-interaction.ts

Comment thread packages/terminal/tests/web/terminal-copy-selection-restore.test.ts
@skulidropek

skulidropek commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

AI Session Backup

Commit: e555130
Status: success
Files: 10 (12.35 MB)
Links: README | Manifest

git status

On branch issue-404-5a7f728e1091
Your branch is up to date with 'origin/issue-404-5a7f728e1091'.

nothing to commit, working tree clean

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/terminal/tests/web/terminal-copy-selection-restore.test.ts (1)

266-279: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Переведите unit-тесты на it.effect(...) для соблюдения тестового контракта.

На Line 266 и Line 281 используются обычные it(...), что расходится с обязательным правилом для **/*.test.ts. Здесь нужно использовать Effect test utilities (например, it.effect(..., () => Effect.sync(...))) без async/await.

As per coding guidelines, "**/*.test.{ts,tsx}: ... unit tests must use Effect test utilities without async/await".

Also applies to: 281-314

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/terminal/tests/web/terminal-copy-selection-restore.test.ts` around
lines 266 - 279, The test functions use regular `it(...)` syntax instead of the
required Effect test utilities. Per the coding guidelines for `**/*.test.ts`
files, convert all unit tests to use `it.effect(...)` with `Effect.sync(...)`
instead of regular `it(...)`. For the test at line 266 "does not restore xterm
selection after intentional keyboard input clears the snapshot" and the other
test(s) starting at line 281, replace the test callback function body with
`Effect.sync(() => { /* test body here */ })` and use `it.effect(...)` instead
of `it(...)`, removing any async/await if present.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/terminal/tests/web/terminal-copy-selection-restore.test.ts`:
- Around line 170-194: The test cleanup using harness.disposable.dispose() is
only executed in the happy path, which means if fc.assert or the expect
statement within the fc.property function throws an error, the disposal is
skipped and resources leak into subsequent test cases, causing instability. Wrap
the entire fc.assert call (including the fc.property logic) in a try-finally
block to guarantee that harness.disposable.dispose() is executed regardless of
whether the property-based test passes or fails. This same pattern needs to be
applied at all affected test locations: the main test case with
createSelectionRestoreHarness (starting at the described location), and the two
additional test cases at lines 196-229 and 231-264 that follow the same
structure.

---

Outside diff comments:
In `@packages/terminal/tests/web/terminal-copy-selection-restore.test.ts`:
- Around line 266-279: The test functions use regular `it(...)` syntax instead
of the required Effect test utilities. Per the coding guidelines for
`**/*.test.ts` files, convert all unit tests to use `it.effect(...)` with
`Effect.sync(...)` instead of regular `it(...)`. For the test at line 266 "does
not restore xterm selection after intentional keyboard input clears the
snapshot" and the other test(s) starting at line 281, replace the test callback
function body with `Effect.sync(() => { /* test body here */ })` and use
`it.effect(...)` instead of `it(...)`, removing any async/await if present.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0b6cb8b5-8a5c-4830-b139-4f6e6a1a8301

📥 Commits

Reviewing files that changed from the base of the PR and between 4f5a867 and e555130.

📒 Files selected for processing (5)
  • packages/terminal/src/web/terminal-copy-native-menu.ts
  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-right-click-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-selection-restore.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: E2E (Clone auto-open SSH)
  • GitHub Check: E2E (OpenCode)
  • GitHub Check: Lint
  • GitHub Check: E2E (Browser command)
  • GitHub Check: E2E (Runtime volumes + SSH)
  • GitHub Check: E2E (Clone cache)
  • GitHub Check: E2E (Login context)
  • GitHub Check: Test
  • GitHub Check: Final build (windows-latest)
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{sh,bash,py,js,ts,jsx,tsx,go,java,rb,php}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files introduce command injection or unsafe shell/process execution with user-controlled input

Files:

  • packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-right-click-interaction.test.ts
  • packages/terminal/src/web/terminal-copy-native-menu.ts
  • packages/terminal/tests/web/terminal-copy-selection-restore.test.ts
  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
**/*.{py,js,ts,jsx,tsx,go,java,rb,php,sh,bash,c,cpp}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files introduce path traversal or writes outside intended project/container state directories

Files:

  • packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-right-click-interaction.test.ts
  • packages/terminal/src/web/terminal-copy-native-menu.ts
  • packages/terminal/tests/web/terminal-copy-selection-restore.test.ts
  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
**/*.{js,ts,jsx,tsx,py,java,go,rb,php,sh,bash,yml,yaml,json,env*,toml,cfg,config,dockerfile,dockerignore}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files expose credentials, tokens, private-keys, or PII in source, generated config, logs, or CI output

Files:

  • packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-right-click-interaction.test.ts
  • packages/terminal/src/web/terminal-copy-native-menu.ts
  • packages/terminal/tests/web/terminal-copy-selection-restore.test.ts
  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: FUNCTIONAL CORE: Write only pure functions with immutable data and mathematical operations in core modules; no side effects, mutations, or external service calls
IMPERATIVE SHELL: Isolate all side effects (IO, network, database, environment/process) in a thin SHELL layer; CORE never calls SHELL, only SHELL → CORE
Never use any type annotation in TypeScript; use unknown only at SHELL boundaries for decoding, never export unknown outside boundary modules
Never use as type assertions in normal code; only permit as in a single 'axiomatic' module (brands, constructors, constants) after which types flow safely without casts
Always use exhaustive pattern matching for union types through .exhaustive() or Match.exhaustive() from effect-ts; never use switch statements or unhandled type branches
Use Effect<Success, Error, Requirements> monad from effect-ts for all effects; compose through pipe() and Effect.flatMap(); never use async/await, raw Promise chains (then/catch), or Promise.all in product code
Interoperate with Promise/exceptions only in SHELL through Effect.try/Effect.tryPromise with typed error mapping; never leave raw exceptions or untyped errors in the domain
Use Effect.acquireRelease + Effect.scoped for resource management with guaranteed finalization; never manage resources with try/finally or manual cleanup
All external services (database, HTTP, environment) must be accessed through Effect-based interfaces and Layer-based dependency injection; never call external APIs directly
Provide comprehensive TSDoc comments with mathematical notation: @pure, @effect, @invariant, @precondition, @postcondition, @complexity, @throws, and CHANGE/WHY/REF/SOURCE/FORMAT THEOREM functional comment markers
No console.*, process direct calls, or untyped environment access in product code; all such operations must be abstracted through Layer-based services in SHELL
Boundary data from external sources (HTTP, database, environment) must be decoded/valida...

Files:

  • packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-right-click-interaction.test.ts
  • packages/terminal/src/web/terminal-copy-native-menu.ts
  • packages/terminal/tests/web/terminal-copy-selection-restore.test.ts
  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.test.{ts,tsx}: Write property-based tests using fast-check (fc.property) to verify mathematical invariants; unit tests must use Effect test utilities without async/await
Every bug fix must be accompanied by a reproducing test case; the test must fail before the fix and pass after; document the Proof of Fix with root cause and solution

Files:

  • packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-right-click-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-selection-restore.test.ts
  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
**/{browser*,server*,app*,*.ts,*.js}

📄 CodeRabbit inference engine (README.md)

Web version must listen on 0.0.0.0 by default for accessibility across LAN devices

Files:

  • packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-right-click-interaction.test.ts
  • packages/terminal/src/web/terminal-copy-native-menu.ts
  • packages/terminal/tests/web/terminal-copy-selection-restore.test.ts
  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
**/{cli*,command*,auto*,*.ts,*.tsx}

📄 CodeRabbit inference engine (README.md)

Implement auto-mode agent selection logic to choose Claude, Codex, Gemini, or Grok randomly from available authorized providers, or allow forced selection with --auto=

Files:

  • packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-right-click-interaction.test.ts
  • packages/terminal/src/web/terminal-copy-native-menu.ts
  • packages/terminal/tests/web/terminal-copy-selection-restore.test.ts
  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
**/*

⚙️ CodeRabbit configuration file

**/*: Ты строгий ревьюер SPEC DRIVEN DEVELOPMENT.

Перед выводами изучи README.md, другие *.md файлы, linked issues,
PR description, PR comments/discussion и релевантную кодовую базу.

Сверь изменения с исходным ТЗ/спекой и обсуждением. Флагай любой уход
от спеки, недокументированное изменение поведения, отсутствие тестов
для заявленного поведения и security-риск. Если спека не видна,
попроси автора добавить ее в issue или PR description.

Проверь решение с точки зрения формальной верификации: какие инварианты,
предусловия и постусловия можно доказать математически, а где доказуемость
слабая. Оцени решение с точки зрения теории игр: устойчивы ли стимулы,
нет ли выгодного обхода правил, и какое решение было бы сильнее.

Files:

  • packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-right-click-interaction.test.ts
  • packages/terminal/src/web/terminal-copy-native-menu.ts
  • packages/terminal/tests/web/terminal-copy-selection-restore.test.ts
  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
**

⚙️ CodeRabbit configuration file

**: РОЛЬ: Математик-программист, специализирующийся на формально верифицируемой функциональной архитектуре.

ЦЕЛЬ: Создавать математически доказуемые решения через функциональную парадигму с полным разделением чистых вычислений и контролируемых эффектов.

МОДЕЛЬ РАССУЖДЕНИЯ:

  • Не выдавать “личные мнения”. Формировать вывод как результат симуляции профессионального обсуждения релевантных ролей
    (архитектор Effect/FP, ревьюер типов, страж CORE↔SHELL, тест-инженер).
  • Если запрос сформулирован как “что думаешь”, отвечать в терминах аргументов ролей и выбирать решение
    по критериям инвариантов, типовой безопасности и тестируемости (если пользователь явно просит выбор — выбрать и обосновать).

ПРАВИЛО ПРОЦЕССА (НЕ ФОРМАТ ОТВЕТА):
В начале работы (внутренне) формулировать Deep Research вопрос:
"I am looking for code that does , is there existing code that can do this?"
Далее:

  • если доступен проект/код — сперва искать и переиспользовать существующие паттерны (минимальный корректный diff),
  • если проект недоступен — опираться на предоставленный контекст и явно фиксировать допущения,
  • код писать только после формального понимания задачи (типы/инварианты → архитектура → код → тесты),
  • источники указывать только если реально использован внешний материал; иначе SOURCE: n/a.

ИНСТРУМЕНТАЛЬНОЕ ПОВЕДЕНИЕ (ОБЯЗАТЕЛЬНО, НЕ ФОРМАТ ОТВЕТА):

  • Агент всегда использует доступные инструменты среды (терминал, поиск по проекту, запуск тестов/скриптов, анализ сборки, web-ресёрч при необходимости)
    для ресёрча, проверки гипотез и выполнения действий. Приоритет: проверяемость, воспроизводимость, минимальный риск.
  • Агент не предлагает “гайд” как замену действия. Если действие возможно выполнить инструментами — агент выполняет его сам,
    затем сообщает, что было сделано и как повторить.
  • Любые инструкции (команды/процедуры) агент даёт только после собственной проверки на доступной среде.
    Если проверить невозможно — явно фиксирует ограничение и перечисляе...

Files:

  • packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts
  • packages/terminal/tests/web/terminal-copy-right-click-interaction.test.ts
  • packages/terminal/src/web/terminal-copy-native-menu.ts
  • packages/terminal/tests/web/terminal-copy-selection-restore.test.ts
  • packages/terminal/tests/web/terminal-copy-interaction.test.ts
🔇 Additional comments (4)
packages/terminal/src/web/terminal-copy-native-menu.ts (1)

3-230: LGTM!

packages/terminal/tests/web/terminal-copy-interaction.test.ts (1)

2-3: LGTM!

Also applies to: 34-35, 245-280

packages/terminal/tests/web/terminal-copy-right-click-interaction.test.ts (1)

2-2: LGTM!

Also applies to: 128-155, 211-239

packages/terminal/tests/web/terminal-copy-redraw-interaction.test.ts (1)

2-2: LGTM!

Also applies to: 17-17, 64-154

Comment thread packages/terminal/tests/web/terminal-copy-selection-restore.test.ts
@skulidropek

skulidropek commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

AI Session Backup

Commit: 6a33842
Status: success
Files: 10 (12.67 MB)
Links: README | Manifest

git status

On branch issue-404-5a7f728e1091
Your branch is up to date with 'origin/issue-404-5a7f728e1091'.

nothing to commit, working tree clean

@skulidropek skulidropek merged commit c5e3073 into main Jun 15, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Почему терминал не способен скролиться?

1 participant