Skip to content

feat: tool-window drag and transparent key colors - #105

Merged
pountzas merged 3 commits into
mainfrom
dev
Aug 12, 2026
Merged

pountzas merged 3 commits into
mainfrom
dev

Conversation

@pountzas

@pountzas pountzas commented Aug 12, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Long-press tool-window headers (Settings, Macro Builder, Head Tracking) to move undecorated windows; main window stays docked.
  • Add transparent keyboard outline color cycle (white / dark gray / silver), shown on the mini-mode toolbar only when transparent mode is on, with a matching Settings control.

Commit message

feat: tool-window drag and transparent key colors

Related issue

N/A

Accessibility impact

Tool windows can be repositioned without a native title bar. Transparent keyboard outline color can be switched for contrast against different backgrounds.

Test plan

  • Ran npm run tauri dev locally
  • Long-press Settings / Macro Builder / Head Tracking headers → window moves; close button still works; main window unchanged
  • Enable mini-mode transparent keyboard → color swatch appears; cycle white → dark gray → silver updates key outlines/labels
  • Color control hidden when transparent is off; Settings select matches when transparent is on
  • Updated docs if user-facing behavior changed

Screenshots (if UI changed)

N/A

Summary by CodeRabbit

  • New Features
    • Added configurable transparent keyboard key colors: white, dark gray, and silver.
    • Added settings controls and quick cycling for transparent key colors.
    • Added consistent styling across transparent keyboard controls, suggestions, and dictation.
    • Improved tool-window headers with localized close labels and long-press dragging.
    • Added localized transparent-key color options across supported languages.
  • Bug Fixes
    • Corrected transparent text and outline colors to match the selected key color.

…mponents

- Added support for transparent key colors in the keyboard and mini mode components.
- Updated SuggestionsBar, DictationButton, and Keyboard components to utilize the new transparent key palette.
- Refactored HeadTrackingWizard and MacroBuilder to use ToolWindowHeader for consistent UI.
- Enhanced SettingsPanel to allow users to select transparent key colors with appropriate translations.
- Updated internationalization files to include new keys for transparent key colors.
- Added tests for transparent key color functionality and palette mapping.
@coderabbitai

coderabbitai Bot commented Aug 12, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

The PR adds configurable transparent key colors with shared palette styling and cyclic selection. It introduces ToolWindowHeader with long-press dragging and localized close labels. Settings and translations expose the color selector across supported locales.

Transparent mini-mode color foundation

Layer / File(s) Summary
Color contracts and palette utilities
src/lib/types.ts, src/lib/miniMode.ts, src/lib/miniMode.test.ts
Adds transparent key color types, defaults, palette resolution, cyclic selection, outline styling, and tests.
Shared window header and drag behavior
src/hooks/useLongPressWindowDrag.ts, src/components/common/ToolWindowHeader.tsx, src/components/head-tracking/HeadTrackingWizard.tsx, src/components/macros/MacroBuilder.tsx, src/components/settings/SettingsPanel.tsx
Adds reusable draggable headers and replaces inline headers in tool windows.
Transparent keyboard rendering and controls
src/components/keyboard/*, src/components/common/SuggestionsBar.tsx
Applies selected palette colors to keys, controls, dictation, and suggestion chips. Adds toolbar color cycling.
Settings and localization
src/components/settings/SettingsPanel.tsx, src/i18n/*
Adds the translated transparent key color selector and locale entries.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant SettingsPanel
  participant updateSettings
  participant KeyboardSection
  participant Keyboard
  participant transparentKeyPalette

  User->>SettingsPanel: select transparent key color
  SettingsPanel->>updateSettings: save transparentKeyColor
  updateSettings-->>KeyboardSection: provide updated settings
  KeyboardSection->>transparentKeyPalette: resolve selected palette
  Keyboard->>transparentKeyPalette: resolve key text and outline colors
  transparentKeyPalette-->>Keyboard: return palette colors
Loading

Possibly related PRs

  • pountzas/reach-Panel#102: Extends the earlier transparent mini-mode implementation in the same keyboard and settings components.
  • pountzas/reach-Panel#86: Shares keyboard, settings, dictation, and type changes for configurable keyboard behavior.
  • pountzas/reach-Panel#18: Shares changes to keyboard components, settings, types, and localization files.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.08% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title clearly summarizes both primary changes: tool-window dragging and transparent key color support.
Description check ✅ Passed The description includes all template sections and provides clear scope, accessibility impact, testing details, and screenshot status.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

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.

@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: 4

🤖 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 `@src/components/head-tracking/HeadTrackingWizard.tsx`:
- Line 73: Translate both tool-window titles: in
src/components/macros/MacroBuilder.tsx:110, pass t("macroBuilder") to
ToolWindowHeader; in src/components/head-tracking/HeadTrackingWizard.tsx:73,
pass t("headTrackingCalibration") instead of the hardcoded title, and add the
headTrackingCalibration key to every locale.

In `@src/components/keyboard/KeyboardSection.tsx`:
- Around line 132-158: Pass the selected palette border color to each affected
ModeToggleGroup so the enclosing frame matches the active palette instead of
remaining white. Update the transparent-toggle group in
src/components/keyboard/KeyboardSection.tsx lines 114-120, the color-control
group in src/components/keyboard/KeyboardSection.tsx lines 132-158, and the
dictation group in src/components/keyboard/DictationButton.tsx lines 49-60; use
the existing palette border value in each component.

In `@src/hooks/useLongPressWindowDrag.ts`:
- Around line 9-18: Update the useLongPressWindowDrag hook to import useEffect
and register an unmount cleanup effect that calls the existing clear callback,
ensuring the pending timer and start position are reset before ToolWindowHeader
unmounts.
- Around line 20-30: Update onPointerDown and the related pointer handlers in
useLongPressWindowDrag to track the active event.pointerId with the pending
gesture. Ignore additional pointerdown events while active, cancel any replaced
timer, and ensure pointermove, pointerup, pointerleave, and pointercancel only
affect the matching pointer before allowing startDragging().
🪄 Autofix

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 87e0b2bf-c318-48a4-a77c-15f106e9ee24

📥 Commits

Reviewing files that changed from the base of the PR and between d1bc3ba and 400dba2.

📒 Files selected for processing (20)
  • src/components/common/SuggestionsBar.tsx
  • src/components/common/ToolWindowHeader.tsx
  • src/components/head-tracking/HeadTrackingWizard.tsx
  • src/components/keyboard/DictationButton.tsx
  • src/components/keyboard/KeyButton.tsx
  • src/components/keyboard/Keyboard.tsx
  • src/components/keyboard/KeyboardSection.tsx
  • src/components/macros/MacroBuilder.tsx
  • src/components/settings/SettingsPanel.tsx
  • src/hooks/useLongPressWindowDrag.ts
  • src/i18n/de.ts
  • src/i18n/el.ts
  • src/i18n/en.ts
  • src/i18n/es.ts
  • src/i18n/fr.ts
  • src/i18n/it.ts
  • src/i18n/pt.ts
  • src/lib/miniMode.test.ts
  • src/lib/miniMode.ts
  • src/lib/types.ts

Close
</button>
</div>
title="Head Tracking Calibration"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate current title strings and translation records.
rg -n -C 2 '"Head Tracking Calibration"|"Macro Builder"|dragToMove|close' src

Repository: pountzas/reach-Panel

Length of output: 12475


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- component imports and title usage ---'
sed -n '1,135p' src/components/head-tracking/HeadTrackingWizard.tsx
sed -n '1,145p' src/components/macros/MacroBuilder.tsx

printf '%s\n' '--- translation definitions and types ---'
fd -i 'i18n|translation|locale' src
rg -n 'export type|interface|macroBuilder|headTracking|dragToMove|useTranslation' src/i18n src/components/common/ToolWindowHeader.tsx src/components/head-tracking src/components/macros

Repository: pountzas/reach-Panel

Length of output: 11534


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- locale files ---'
git ls-files 'src/i18n/*'
for f in src/i18n/*.ts; do
  printf '\n--- %s ---\n' "$f"
  rg -n -C 2 'macroBuilder|headTracking|export|satisfies|Translation|Locale' "$f"
done

printf '%s\n' '--- shared header implementation ---'
cat -n src/components/common/ToolWindowHeader.tsx

Repository: pountzas/reach-Panel

Length of output: 3953


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- tool-window title contract and callers ---'
cat -n src/lib/toolWindows.ts | sed -n '1,70p'
rg -n -C 4 '<ToolWindowHeader|title=' src/components src/lib
printf '%s\n' '--- translation hook and locale completeness ---'
cat -n src/hooks/useTranslation.ts
cat -n src/i18n/index.ts
python3 - <<'PY'
from pathlib import Path
import re

en = Path("src/i18n/en.ts").read_text()
keys = re.findall(r'^\s{2}([A-Za-z0-9_]+):', en, re.M)
print("en_keys", len(keys), keys[-10:])
for path in sorted(Path("src/i18n").glob("*.ts")):
    if path.name in {"en.ts", "index.ts"}:
        continue
    text = path.read_text()
    present = re.findall(r'^\s{2}([A-Za-z0-9_]+):', text, re.M)
    missing = [k for k in keys if k not in present]
    print(path, "keys", len(present), "missing", missing)
PY

Repository: pountzas/reach-Panel

Length of output: 18276


Translate the tool-window titles.

Pass t("macroBuilder") to ToolWindowHeader in src/components/macros/MacroBuilder.tsx. Add a headTrackingCalibration key to every locale and pass t("headTrackingCalibration") in src/components/head-tracking/HeadTrackingWizard.tsx.

📍 Affects 2 files
  • src/components/head-tracking/HeadTrackingWizard.tsx#L73-L73 (this comment)
  • src/components/macros/MacroBuilder.tsx#L110-L110
🤖 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 `@src/components/head-tracking/HeadTrackingWizard.tsx` at line 73, Translate
both tool-window titles: in src/components/macros/MacroBuilder.tsx:110, pass
t("macroBuilder") to ToolWindowHeader; in
src/components/head-tracking/HeadTrackingWizard.tsx:73, pass
t("headTrackingCalibration") instead of the hardcoded title, and add the
headTrackingCalibration key to every locale.

Comment thread src/components/keyboard/KeyboardSection.tsx
Comment thread src/hooks/useLongPressWindowDrag.ts
Comment thread src/hooks/useLongPressWindowDrag.ts Outdated
Comment on lines +20 to +30
const onPointerDown = useCallback(
(event: ReactPointerEvent<HTMLElement>) => {
if (event.button !== 0) return;
if ((event.target as HTMLElement).closest(".section-no-drag")) return;

startRef.current = { x: event.clientX, y: event.clientY };
timerRef.current = window.setTimeout(() => {
timerRef.current = null;
startRef.current = null;
void getCurrentWindow().startDragging();
}, HOLD_MS);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

file="$(fd -t f 'useLongPressWindowDrag\.ts$' . | head -n 1)"
printf '%s\n' "== candidate: $file =="
cat -n "$file"

printf '%s\n' '== references =='
rg -n -C 4 'useLongPressWindowDrag|onPointer(Down|Move|Up|Cancel|Leave)' . -g '*.ts' -g '*.tsx' -g '*.js' -g '*.jsx'

Repository: pountzas/reach-Panel

Length of output: 23972


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '== ToolWindowHeader =='
cat -n src/components/common/ToolWindowHeader.tsx

printf '%s\n' '== all hook consumers =='
rg -n -C 8 'useLongPressWindowDrag\(\)|\.\.\.dragHandlers|dragHandlers\.' src

printf '%s\n' '== relevant pointer and touch configuration =='
rg -n -C 3 'ToolWindowHeader|touch-action|touchAction|pointer-events|section-no-drag' src -g '*.tsx' -g '*.ts' -g '*.css'

Repository: pountzas/reach-Panel

Length of output: 23229


🏁 Script executed:

#!/bin/bash
set -eu

python3 - <<'PY'
class Model:
    def __init__(self):
        self.timer_ref = None
        self.start_ref = None
        self.next_timer = 0
        self.started = []

    def down(self, pointer_id, x=0, y=0):
        self.start_ref = (pointer_id, x, y)
        self.next_timer += 1
        self.timer_ref = self.next_timer
        return self.next_timer

    def clear(self):
        self.timer_ref = None
        self.start_ref = None

    def move(self, pointer_id, x, y):
        if self.start_ref is None or self.timer_ref is None:
            return
        _, sx, sy = self.start_ref
        if abs(x - sx) > 8 or abs(y - sy) > 8:
            self.clear()

    def terminate(self, pointer_id):
        self.clear()

    def timer_fires(self, timer_id):
        self.timer_ref = None
        self.start_ref = None
        self.started.append(timer_id)

m = Model()
first_timer = m.down(1)
second_timer = m.down(2)
m.timer_fires(first_timer)
m.timer_fires(second_timer)
print("two pointerdowns startDragging calls:", m.started)

m = Model()
m.down(1)
m.down(2)
m.terminate(2)
print("pointer 2 termination leaves pointer 1 timer:", m.timer_ref is not None)

m = Model()
m.down(1, 0, 0)
m.down(2, 100, 100)
m.move(2, 120, 100)
print("pointer 2 movement clears pointer 1 timer:", m.timer_ref is None)
PY

Repository: pountzas/reach-Panel

Length of output: 303


Track the active pointer before starting the timer.

Store event.pointerId with the pending gesture. Ignore additional pointerdown events while a gesture is active, or cancel the previous timer before replacing it. Ignore pointermove, pointerup, pointerleave, and pointercancel events from other pointers. Otherwise, a second pointer can cause multiple startDragging() calls or cancel the active gesture.

🤖 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 `@src/hooks/useLongPressWindowDrag.ts` around lines 20 - 30, Update
onPointerDown and the related pointer handlers in useLongPressWindowDrag to
track the active event.pointerId with the pending gesture. Ignore additional
pointerdown events while active, cancel any replaced timer, and ensure
pointermove, pointerup, pointerleave, and pointercancel only affect the matching
pointer before allowing startDragging().

- Implemented long-press functionality for dragging tool-window headers, allowing users to reposition windows without a title bar.
- Added a color cycling feature for the transparent keyboard outline, enhancing visibility in mini-mode.
- Updated related components and hooks to support new dragging behavior and color options.
- Adjusted accessibility features to improve user experience with the new functionalities.
@pountzas
pountzas merged commit f773c80 into main Aug 12, 2026
2 of 3 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