Skip to content

feat: add Notes teleprompter mode#152

Open
My-Denia wants to merge 2 commits into
getopenscreen:mainfrom
My-Denia:feat/notes-teleprompter-67
Open

feat: add Notes teleprompter mode#152
My-Denia wants to merge 2 commits into
getopenscreen:mainfrom
My-Denia:feat/notes-teleprompter-67

Conversation

@My-Denia

@My-Denia My-Denia commented Jul 25, 2026

Copy link
Copy Markdown

Summary

This PR adds a manual teleprompter mode to the existing Notes window.

  • Auto-scrolls Notes content with pause and resume controls.
  • Adjusts scroll speed from 10 to 100 px/s.
  • Adjusts the Notes font size from 14 to 48 px.
  • Mirrors the Notes content horizontally for glass teleprompter setups without
    mirroring the toolbar.
  • Persists speed, font size, and mirror preferences while always starting
    playback paused.
  • Keeps controls accessible at narrow window widths and adds focused unit,
    component, and browser coverage.

Speech-adaptive scrolling, speech recognition, and speed presets are optional
enhancements and are intentionally not included in this MVP.

Related issue

Fixes #67

Type of change

  • Bug fix
  • Feature
  • Enhancement
  • Documentation
  • Refactor / maintenance
  • Performance
  • Security

Release impact

  • Patch
  • Minor
  • Major / breaking change
  • No release note needed

Desktop impact

  • Windows
  • macOS
  • Linux
  • Installer / packaging
  • Not platform-specific

Screenshots / video

Not included.

Testing

  • Focused unit and component tests: 18 passed.
  • Full unit suite: 445 passed.
  • Browser suite: 9 passed, including 160 px toolbar overflow and pointer/keyboard reachability.
  • npx tsc --noEmit
  • Biome check on the exact changed-file set.
  • npm run build-vite
  • git diff --check

The project-wide npm run i18n:check still reports 259 pre-existing
cross-locale discrepancies. The discrepancy set is identical to the base
commit, and dedicated validation confirms that all 11 Notes teleprompter
tooltip and unit keys are present and non-empty in all 13 launch locales, with
both unit templates retaining their {{value}} placeholder.

End-to-end tests, native capture checks, and full Electron packaging were not
run because this change is limited to the renderer Notes window.

Summary by CodeRabbit

  • New Features

    • Added teleprompter playback controls to the notes editor.
    • Adjust scrolling speed and font size with bounded controls.
    • Added horizontal mirroring for notes content.
    • Preserved notes and teleprompter settings between sessions, including legacy note content.
    • Added localized tooltips across supported languages.
  • Bug Fixes

    • Improved readability and scaling of formatted notes content.
  • Tests

    • Added coverage for playback, persistence, controls, keyboard navigation, mirroring, and narrow-screen accessibility.

@My-Denia
My-Denia requested a review from EtienneLescot as a code owner July 25, 2026 00:33
Copilot AI review requested due to automatic review settings July 25, 2026 00:33

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds teleprompter mode to the notes window with persisted speed, font size, and mirror settings; animation-driven scrolling; expanded toolbar controls; legacy note migration; presentation styling; localized labels; and unit, component, and browser accessibility tests.

Changes

Notes teleprompter

Layer / File(s) Summary
Teleprompter contracts and persistence
src/components/launch/notesTeleprompter.ts, src/components/launch/notesTeleprompter.test.ts
Adds normalized settings, safe storage helpers, legacy content conversion, and bounded frame/scroll calculations with edge-case tests.
Notes window playback and presentation
src/components/launch/NotesWindow.tsx, src/components/launch/NotesWindow.module.css, src/components/launch/NotesWindow.test.tsx
Adds persisted teleprompter state, animation-frame scrolling, automatic stopping, toolbar wiring, dynamic font sizing, mirroring, and runtime coverage.
Toolbar controls and accessibility
src/components/launch/NotesToolbar.tsx, src/components/launch/NotesToolbar.test.tsx, src/components/launch/NotesToolbar.browser.test.tsx, src/i18n/locales/*/launch.json
Adds playback, speed, font-size, and mirror controls with bounds, ARIA state, keyboard reachability, and localized tooltip labels.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant NotesToolbar
  participant NotesWindow
  participant TipTapEditor
  participant requestAnimationFrame
  NotesToolbar->>NotesWindow: Toggle playback or change settings
  NotesWindow->>requestAnimationFrame: Schedule playback tick
  requestAnimationFrame->>NotesWindow: Return frame timestamp
  NotesWindow->>TipTapEditor: Update scroll position
  NotesWindow->>NotesToolbar: Render current playback state
Loading

Suggested reviewers: etiennelescot

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes implement #67's teleprompter mode with auto-scroll, mirror, font size, and manual pause/speed controls, excluding optional speech features.
Out of Scope Changes check ✅ Passed The added tests, styles, helpers, and locale updates all support the teleprompter feature and stay within the stated scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title clearly summarizes the main change: adding a Notes teleprompter mode.
Description check ✅ Passed The description matches the template and includes summary, issue, type, release impact, desktop impact, screenshots, and testing details.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

🧹 Nitpick comments (5)
src/components/launch/NotesToolbar.browser.test.tsx (2)

56-77: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Stub editor is recreated on every render.

createEditor() runs in the render body, so each state update passes a new object identity and useEditorRevision's effect (deps: [editor]) re-runs off/on on every click. Hold it stable so the harness matches real usage.

♻️ Proposed fix
 function ToolbarHarness() {
+	const [editor] = useState(createEditor);
 	const [isPlaying, setIsPlaying] = useState(false);
 	const [speed, setSpeed] = useState(40);
 	const [fontSize, setFontSize] = useState(16);
 	const [mirrored, setMirrored] = useState(false);
 
 	return (
 		<NotesToolbar
-			editor={createEditor()}
+			editor={editor}
🤖 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/launch/NotesToolbar.browser.test.tsx` around lines 56 - 77,
Update ToolbarHarness to create the editor once and preserve its identity across
state updates, using a stable hook or equivalent initialization outside the
render-time state changes. Pass that stable editor to NotesToolbar instead of
calling createEditor() directly during each render, so useEditorRevision does
not repeatedly rebind listeners after clicks.

116-129: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Positional assumptions make this brittle.

The tab loop requires DOM order to equal tab order and every control to be enabled — it only holds because the harness starts at speed 40 / font 16. Likewise controls.at(-1) assumes mirror is rendered last. Selecting by aria-label would survive reordering and bound changes.

🤖 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/launch/NotesToolbar.browser.test.tsx` around lines 116 - 129,
The NotesToolbar focus test relies on DOM positions and ordering that change
with control state and layout. Update the test to identify controls by their
aria-labels, and verify tab/focus behavior using the expected labeled controls
rather than controls array indices; select the mirror control by its label
instead of controls.at(-1), while preserving the Pause and aria-pressed
assertions.
src/components/launch/NotesToolbar.test.tsx (2)

54-69: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider covering the active states too.

createProps only ever renders isPlaying: false / mirrored: false. A case with both true would pin the Pause label and aria-pressed="true" in the jsdom suite rather than relying solely on the browser test.

As per coding guidelines, "Add a test for every new behavior in the same package as the code under test."

🤖 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/launch/NotesToolbar.test.tsx` around lines 54 - 69, Add a
NotesToolbar test using createProps with both isPlaying and mirrored set to
true, and assert the active-state behavior: the Pause label and
aria-pressed="true". Keep the existing inactive-state coverage unchanged.

Source: Coding guidelines


12-52: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Toolbar test fixtures are copy-pasted between the two suites. The createEditor stub and the i18n label map are byte-identical in both files; when a label key or a TipTap command is added, both copies must be edited or one suite silently drifts. Extract them into a shared fixture module (e.g. notesToolbarTestFixtures.ts next to the component).

  • src/components/launch/NotesToolbar.test.tsx#L12-L52: move the label map and createEditor into the shared fixture and import them here.
  • src/components/launch/NotesToolbar.browser.test.tsx#L10-L54: import the same fixture instead of redeclaring the mock and stub.
🤖 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/launch/NotesToolbar.test.tsx` around lines 12 - 52, Extract
the shared i18n label map and createEditor fixture from
src/components/launch/NotesToolbar.test.tsx lines 12-52 into a shared
notesToolbarTestFixtures module next to the component, then import and use that
fixture here. Update src/components/launch/NotesToolbar.browser.test.tsx lines
10-54 to import the same shared fixture and remove its duplicate mock and editor
stub declarations.
src/components/launch/NotesToolbar.tsx (1)

208-219: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the duplicate toggle state signaling.

ToolbarButton always emits aria-pressed={active}, so this Play/Pause teleprompter control reads as both “Pause” and pressed. Keep a static stateless label like “Auto-scroll” with aria-pressed, or keep the label swap and drop aria-pressed.

🤖 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/launch/NotesToolbar.tsx` around lines 208 - 219, Update the
teleprompter ToolbarButton using active={isPlaying} and onTogglePlaying so it
does not simultaneously expose a changing Play/Pause label and aria-pressed
state; either use a static “Auto-scroll” label while retaining
active/aria-pressed, or preserve the label swap and remove the active prop.
🤖 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/launch/NotesToolbar.tsx`:
- Around line 235-241: Update both readouts in NotesToolbar: remove their
aria-label and redundant aria-live attributes so the dynamic values are
announced directly, relying on the parent group labels. Add localized unit keys
to launch.json for all 13 locales, and use the existing
translation/locale-formatting mechanism to render the speed and font-size values
with translated units instead of hardcoded “px/s” and “px”.

---

Nitpick comments:
In `@src/components/launch/NotesToolbar.browser.test.tsx`:
- Around line 56-77: Update ToolbarHarness to create the editor once and
preserve its identity across state updates, using a stable hook or equivalent
initialization outside the render-time state changes. Pass that stable editor to
NotesToolbar instead of calling createEditor() directly during each render, so
useEditorRevision does not repeatedly rebind listeners after clicks.
- Around line 116-129: The NotesToolbar focus test relies on DOM positions and
ordering that change with control state and layout. Update the test to identify
controls by their aria-labels, and verify tab/focus behavior using the expected
labeled controls rather than controls array indices; select the mirror control
by its label instead of controls.at(-1), while preserving the Pause and
aria-pressed assertions.

In `@src/components/launch/NotesToolbar.test.tsx`:
- Around line 54-69: Add a NotesToolbar test using createProps with both
isPlaying and mirrored set to true, and assert the active-state behavior: the
Pause label and aria-pressed="true". Keep the existing inactive-state coverage
unchanged.
- Around line 12-52: Extract the shared i18n label map and createEditor fixture
from src/components/launch/NotesToolbar.test.tsx lines 12-52 into a shared
notesToolbarTestFixtures module next to the component, then import and use that
fixture here. Update src/components/launch/NotesToolbar.browser.test.tsx lines
10-54 to import the same shared fixture and remove its duplicate mock and editor
stub declarations.

In `@src/components/launch/NotesToolbar.tsx`:
- Around line 208-219: Update the teleprompter ToolbarButton using
active={isPlaying} and onTogglePlaying so it does not simultaneously expose a
changing Play/Pause label and aria-pressed state; either use a static
“Auto-scroll” label while retaining active/aria-pressed, or preserve the label
swap and remove the active prop.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f423f790-e66c-4f39-aa80-533f0695f1a9

📥 Commits

Reviewing files that changed from the base of the PR and between f508a21 and e785295.

📒 Files selected for processing (21)
  • src/components/launch/NotesToolbar.browser.test.tsx
  • src/components/launch/NotesToolbar.test.tsx
  • src/components/launch/NotesToolbar.tsx
  • src/components/launch/NotesWindow.module.css
  • src/components/launch/NotesWindow.test.tsx
  • src/components/launch/NotesWindow.tsx
  • src/components/launch/notesTeleprompter.test.ts
  • src/components/launch/notesTeleprompter.ts
  • src/i18n/locales/ar/launch.json
  • src/i18n/locales/en/launch.json
  • src/i18n/locales/es/launch.json
  • src/i18n/locales/fr/launch.json
  • src/i18n/locales/it/launch.json
  • src/i18n/locales/ja-JP/launch.json
  • src/i18n/locales/ko-KR/launch.json
  • src/i18n/locales/pt-BR/launch.json
  • src/i18n/locales/ru/launch.json
  • src/i18n/locales/tr/launch.json
  • src/i18n/locales/vi/launch.json
  • src/i18n/locales/zh-CN/launch.json
  • src/i18n/locales/zh-TW/launch.json

Comment thread src/components/launch/NotesToolbar.tsx Outdated
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.

[Future Work]: Notes window v2 teleprompter mode

2 participants