Skip to content

feat(launch): add capture-excluded notes window for recordings#43

Merged
EtienneLescot merged 15 commits into
getopenscreen:mainfrom
Itzadetunji:main
Jul 4, 2026
Merged

feat(launch): add capture-excluded notes window for recordings#43
EtienneLescot merged 15 commits into
getopenscreen:mainfrom
Itzadetunji:main

Conversation

@Itzadetunji

@Itzadetunji Itzadetunji commented Jun 28, 2026

Copy link
Copy Markdown

Notes window

Recording-time plain-text notes in a separate Electron window, opened from the HUD. The window uses content protection so it is excluded from most screen capture pipelines.

Notes is Windows and macOS only
This is due to linux not supporting "setContentProtection" from electron


Pull request summary

Use the section below as the PR description (gh pr create --body-file docs/features/notes-window.md — remove everything above this heading first, or copy from Summary onward).

Summary

Adds a dedicated Notes window for taking plain-text notes while recording. Users open it from a new notepad button on the recording HUD. Notes live in a separate BrowserWindow (not the HUD overlay) with setContentProtection(true) so the window is excluded from most screen capture pipelines.

Implementation follows the existing multi-window pattern: main-process factory in electron/windows.ts, lifecycle wrapper in electron/main.ts, IPC via open-notes / electronAPI.openNotes(), and renderer routing with the ?showNotes=true query param to render NotesWindow. Re-opening notes focuses the existing window instead of creating duplicates. Note content is persisted locally via localStorage.

Related issue

Fixes #

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

Testing

Manual

  1. npm run dev
  2. Open the HUD and click the Notes (notepad) button on the tray.
  3. Confirm a separate framed window opens titled OpenScreen - Notes with a full-height textarea.
  4. Type notes, close the window, reopen via the HUD button — content should persist (localStorage).
  5. Click Notes again while the window is already open — the same window should be focused (no second instance).
  6. Start a screen recording and confirm the notes window is not visible in the captured output (content protection behavior varies by OS/capture method — verify on target platform).

Commands

  • npm run lint
  • npx tsc --noEmit
  • npm run test
  • npm run i18n:check

Platforms tested

  • Windows
  • macOS
  • Linux

Architecture

OpenScreen uses one Vite/React entry (index.html) for every window. The notes window is a second BrowserWindow that loads the same app with ?showNotes=true. App.tsx renders <NotesWindow /> when that param is present.

HUD (LaunchWindow)
  └─ click Notes button
       └─ electronAPI.openNotes()
            └─ IPC open-notes (handlers.ts)
                 └─ createNotesWindowWrapper() (main.ts)
                      └─ createNotesWindow() (windows.ts)
                           └─ loadURL(?showNotes=true)
                                └─ App.tsx → NotesWindow.tsx
Layer File Role
Window factory electron/windows.ts createNotesWindow() — size, title, setContentProtection, load URL
Lifecycle electron/main.ts notesWindow ref, createNotesWindowWrapper(), closed handler
IPC electron/ipc/handlers.ts open-notes — focus existing or create
Preload electron/preload.ts openNotes()
Types electron/electron-env.d.ts openNotes return type
Routing src/App.tsx, src/main.tsx showNotes query param
UI src/components/launch/NotesWindow.tsx Textarea + localStorage
HUD entry src/components/launch/LaunchWindow.tsx Notepad button
i18n src/i18n/locales/en/launch.json tooltips.openNotes

Window options

The notes window is a standard framed, resizable window:

  • Size: 400×540 default; min 320×400; max 640×720
  • Always on top: yes (stays usable while recording)
  • Content protection: enabled (excluded from capture where the OS supports it)
  • Taskbar / Dock: visible (skipTaskbar: false)

Persistence

Notes are stored in renderer localStorage under the key "notes". This is global to the app install, not tied to a specific recording or project.

Known limitations (v1)

  • Plain textarea only — no rich text formatting
  • Single global note — not per-recording or per-project
  • tooltips.openNotes added to English locale only; other locales need updating before npm run i18n:check passes
  • Content protection effectiveness depends on OS and capture method — manual verification required on Windows and macOS
  • HUD button focuses an open notes window; it does not close it (close via window chrome)

Release note (changelog)

Notes window: Take plain-text notes in a separate always-on-top window from the recording HUD. The notes window uses content protection so it stays out of screen recordings.

Summary by CodeRabbit

  • New Features
    • Added a “Notes” window from the launch screen via an “Open Notes” button, with new tooltip text and a “Take notes here...” placeholder.
    • Notes auto-save to local storage as you type and are restored when reopened.
    • Notes are shown in a dedicated view when launched with showNotes=true.
  • Bug Fixes
    • Improved Notes window lifecycle: the app reuses and focuses an existing Notes window when available, otherwise creates it; the Notes view updates correctly when the window closes.

@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a notes window flow: Electron opens and manages a notes BrowserWindow, the renderer exposes an open action, and the app renders a notes UI when showNotes=true.

Changes

Notes Window Feature

Layer / File(s) Summary
Electron window factory and IPC handler
electron/windows.ts, electron/main.ts, electron/ipc/handlers.ts, electron/preload.ts, electron/electron-env.d.ts
Creates the notes window factory, tracks notes window state in main process, registers open-notes, exposes openNotes() in preload, and adds the corresponding renderer type.
Notes UI and launch entry points
src/components/launch/NotesWindow.tsx, src/App.tsx, src/main.tsx, src/components/launch/LaunchWindow.tsx, src/i18n/locales/en/launch.json
Adds the notes editor UI with localStorage persistence, routes App into notes mode from showNotes, enables transparent rendering for that mode, adds the launch HUD control, updates tooltip strings, and applies the renderer transparency condition.

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

Sequence Diagram(s)

sequenceDiagram
  participant Renderer
  participant preload
  participant ipcMain
  participant main
  participant NotesWindow
  Renderer->>preload: openNotes()
  preload->>ipcMain: invoke("open-notes")
  ipcMain->>main: getNotesWindow()
  alt window exists
    main->>NotesWindow: focus()
  else
    main->>main: createNotesWindowWrapper()
    main->>NotesWindow: createNotesWindow()
  end
  ipcMain-->>Renderer: { opened: true }
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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 the main change: adding a capture-excluded notes window for recordings.
Description check ✅ Passed The description covers the required sections and includes summary, type, impact, testing, and platform notes, though some items are incomplete.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@Itzadetunji

Copy link
Copy Markdown
Author

@EtienneLescot, I would love to get feedback on this PR so I know what to improve upon

@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 `@electron/windows.ts`:
- Around line 324-346: The Notes window is still using the default OS window
chrome because the BrowserWindow created in createNotesWindow() does not disable
the frame. Update the BrowserWindow options in createNotesWindow() to make it
frameless by setting the appropriate frame/title bar option so the window
renders without OS controls.

In `@src/App.tsx`:
- Around line 23-24: App currently mounts NotesWindow through two separate
conditions, so when windowType=notes and showNotes=true are both set it renders
twice and competes for the same notes localStorage state. Update App’s render
logic so NotesWindow is created through a single mutually exclusive path, using
the existing windowType/showNotes checks to choose only one branch and prevent
duplicate mounts; adjust the related NotesWindow render sites in App
accordingly.

In `@src/components/launch/LaunchWindow.tsx`:
- Around line 1048-1055: The new Notes icon-only button in LaunchWindow needs an
accessible name because the Tooltip content does not label the trigger. Update
the button that calls window.electronAPI.openNotes() to expose a clear label for
assistive tech, using the existing t("tooltips.openNotes") text as the button’s
accessible name while keeping the Tooltip for hover behavior.

In `@src/components/launch/NotesWindow.tsx`:
- Around line 17-20: The NotesWindow textarea is using hardcoded user-facing
placeholder text, so it bypasses the app’s localization flow. Update the
placeholder in NotesWindow to use the existing i18n/translation mechanism used
elsewhere in the launch UI, and ensure the new key is added to the locale
resources instead of leaving "Take notes here..." inline.
🪄 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: 5a24189e-3595-4780-8f6b-fa7b7d89cd0e

📥 Commits

Reviewing files that changed from the base of the PR and between f6b81e7 and 3dad308.

📒 Files selected for processing (10)
  • electron/electron-env.d.ts
  • electron/ipc/handlers.ts
  • electron/main.ts
  • electron/preload.ts
  • electron/windows.ts
  • src/App.tsx
  • src/components/launch/LaunchWindow.tsx
  • src/components/launch/NotesWindow.tsx
  • src/i18n/locales/en/launch.json
  • src/main.tsx

Comment thread electron/windows.ts
Comment thread src/App.tsx
Comment thread src/components/launch/LaunchWindow.tsx Outdated
Comment thread src/components/launch/NotesWindow.tsx Outdated

@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 `@src/App.tsx`:
- Around line 117-120: The notes render path in App.tsx is not exclusive, so
when showNotes is true the NotesWindow renders but content still falls through
to the default Openscreen panel. Update the render logic around TooltipProvider,
NotesWindow, and the content branch so the notes route short-circuits the
default panel and only one view is shown. Use the existing showNotes and
windowType checks to make the NotesWindow path mutually exclusive from the
fallback content.
🪄 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: fd684725-4cb6-4f12-b5d4-1eae404e9272

📥 Commits

Reviewing files that changed from the base of the PR and between d1ff93e and b280512.

📒 Files selected for processing (1)
  • src/App.tsx

Comment thread src/App.tsx Outdated
@EtienneLescot

Copy link
Copy Markdown
Collaborator

@Itzadetunji thank you for this PR, could you provide us with screenshots of the feature ?

@Itzadetunji

Copy link
Copy Markdown
Author

@Itzadetunji thank you for this PR, could you provide us with screenshots of the feature ?

Here is a screenshot of the toolbar

Screenshot 2026-06-29 at 13 49 37

Screenshotting will not work as native screenshot on mac will not even be able to pick it up
So here is a picture from my phone

IMG_6571

@EtienneLescot

Copy link
Copy Markdown
Collaborator

@Itzadetunji thanks for the screenshot.
did you test with and without native capture (mouse icon on/off) and on all platforms (windows, mac) ?
not available on linux?

@Itzadetunji

Copy link
Copy Markdown
Author

@Itzadetunji thanks for the screenshot. did you test with and without native capture (mouse icon on/off) and on all platforms (windows, mac) ? not available on linux?

Thanks for the prompt reply, I have tested it on windows and Mac and both don’t show the notes window when screenshotting or sharing screen

Attached below is a picture of a windows computer sharing screen and as you can see the notes don’t come up (it was the same when using the open screen recorder)

I also did test with and without native capture and no issues came up

IMG_6572

@Itzadetunji

Copy link
Copy Markdown
Author

@Itzadetunji thanks for the screenshot. did you test with and without native capture (mouse icon on/off) and on all platforms (windows, mac) ? not available on linux?

Thanks for the prompt reply, I have tested it on windows and Mac and both don’t show the notes window when screenshotting or sharing screen

Attached below is a picture of a windows computer sharing screen and as you can see the notes don’t come up (it was the same when using the open screen recorder)

I also did test with and without native capture and no issues came up

IMG_6572

Also, no linux support was considered

@EtienneLescot

Copy link
Copy Markdown
Collaborator

@Itzadetunji

Okay, when it is possible we always try to keep features cross-platform.
So in order to merge this pull request, we need to check whether :

  1. the feature is supported on linux
  2. the feature is not portable on linux due to documented technical limitation

also please check #43 (comment)

Thanks !

Changed the query parameter from "windowType=notes" to "showNotes=true" in both the Electron window creation and the React app. This enhances clarity and consistency in how the notes window is displayed based on URL parameters.
Modified the Electron notes window to enhance its appearance and functionality by adjusting dimensions, background color, and removing unnecessary properties. Updated the React NotesWindow component to ensure it occupies the full screen height and width, improving user experience for note-taking.
Updated the dimensions of the Electron notes window for better usability and added max width and height constraints. Enhanced the React NotesWindow component to store and retrieve notes from local storage, improving the user experience by preserving notes across sessions.
Rearranged the order of window creation to ensure the countdown overlay is displayed correctly. Updated the title of the NotesWindow to "OpenScreen - Notes" for better branding and clarity.
Updated the NotesWindow component to utilize useLayoutEffect instead of useEffect for setting initial notes from local storage, improving performance and ensuring the notes are rendered correctly before the browser paints the UI.
Eliminated the allowScripts configuration for specific dependencies in package.json, streamlining the file and removing unnecessary entries.
Removed the conditional rendering of NotesWindow from the main App component and integrated it directly within the return statement when showNotes is true. Added aria-label for the open notes button in LaunchWindow for better accessibility. Updated the placeholder text in NotesWindow to utilize localized strings for improved user experience.
Removed the conditional block for rendering NotesWindow and integrated it directly within the return statement, improving code clarity. This change ensures that NotesWindow is displayed when showNotes is true, streamlining the component structure.

@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.

🧹 Nitpick comments (1)
electron/ipc/handlers.ts (1)

1484-1494: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Misleading variable name notesSelectorWin.

This holds the notes BrowserWindow, not a "selector" — looks like it was copy-pasted from the open-source-selector handler above (Line 1475's sourceSelectorWin). Rename for clarity.

✏️ Proposed rename
 	ipcMain.handle("open-notes", async () => {
-		const notesSelectorWin = getNotesWindow();
-		if (notesSelectorWin) {
-			notesSelectorWin.focus();
+		const notesWin = getNotesWindow();
+		if (notesWin) {
+			notesWin.focus();
 			return { opened: true };
 		}
🤖 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 `@electron/ipc/handlers.ts` around lines 1484 - 1494, The open-notes IPC
handler uses a misleading copied variable name for the window returned by
getNotesWindow(), so rename notesSelectorWin to a name that clearly reflects the
notes BrowserWindow (for example, notesWin) and update the same identifier
throughout the open-notes handler. Keep the behavior unchanged; this is just a
clarity fix aligned with the nearby open-source-selector handler’s naming
pattern.
🤖 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.

Nitpick comments:
In `@electron/ipc/handlers.ts`:
- Around line 1484-1494: The open-notes IPC handler uses a misleading copied
variable name for the window returned by getNotesWindow(), so rename
notesSelectorWin to a name that clearly reflects the notes BrowserWindow (for
example, notesWin) and update the same identifier throughout the open-notes
handler. Keep the behavior unchanged; this is just a clarity fix aligned with
the nearby open-source-selector handler’s naming pattern.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d1e1dae0-63eb-4e46-81d9-9a15dd549fcb

📥 Commits

Reviewing files that changed from the base of the PR and between b280512 and 2c9f093.

📒 Files selected for processing (10)
  • electron/electron-env.d.ts
  • electron/ipc/handlers.ts
  • electron/main.ts
  • electron/preload.ts
  • electron/windows.ts
  • src/App.tsx
  • src/components/launch/LaunchWindow.tsx
  • src/components/launch/NotesWindow.tsx
  • src/i18n/locales/en/launch.json
  • src/main.tsx
🚧 Files skipped from review as they are similar to previous changes (9)
  • electron/preload.ts
  • src/main.tsx
  • src/components/launch/NotesWindow.tsx
  • src/App.tsx
  • src/i18n/locales/en/launch.json
  • electron/windows.ts
  • electron/main.ts
  • electron/electron-env.d.ts
  • src/components/launch/LaunchWindow.tsx

@Itzadetunji

Copy link
Copy Markdown
Author

@EtienneLescot Linux update:

Notes window: works on Linux (open, type, persist, focus).
Capture exclusion: not supported. Linux does not natively support blocking window screen capture via standard Electron APIs - win.setContentProtection(true) is Windows/macOS only and does not affect Linux

Also fixed the comment from Coderabbit:
The notes window now renders only NotesWindow (showNotes ? : content)

Ps: I have updated the languages

@EtienneLescot

Copy link
Copy Markdown
Collaborator

Hey @Itzadetunji — nice work, this is a solid feature and users actually want it (Loom ships 'Speaker Notes', plokhovd/cloak-note does the same content-protection trick as a standalone app).

A few small things before I'd be comfortable merging:

  1. Cross-platform story. setContentProtection is a no-op on Linux, so the icon should be hidden there. I just pushed a small change on this branch to gate the button behind the existing isLinuxHud check and to throw early in createNotesWindow / open-notes on linux. Please pull it in and add a one-liner in the PR description: 'Notes is Windows and macOS only.'

  2. Markdown, not plain text. A <textarea> is the wrong primitive for the real use case — the user reading this is reading talking points, not journaling, and the natural shape is structured (headers, lists, code blocks for tutorials). Cloak Note and Loom both went markdown. Can you swap to a markdown editor (e.g. a simple textarea with a markdown preview pane, or a lightweight editor like @uiw/react-md-editor) and render the formatted view to the user? Plain text doesn't earn its keep here.

  3. Test matrix. The PR's own test plan says 'manual verification required on Windows and macOS' for capture exclusion. Please actually run a screen share from a third-party tool (Discord / OBS / Zoom) on both, not just the system screenshot — that's where setContentProtection fails most often.

And on a forward-looking note: an automatic teleprompter mode (scrolls at speaking pace, mirror-flip for glass teleprompters, optionally font-size tied to distance from camera) would be a great v2 on top of the markdown notes window. Worth opening a tracking issue for it so it doesn't get lost.

Overall: cool feature, merge-worthy with the Linux gate + markdown + a real capture test. Nice work. 👍

Updated package versions to 1.6.0-rc.1 and integrated Tiptap for the NotesWindow component, replacing the previous textarea with a rich text editor. Improved local storage handling for notes and added styles for better UI. Enhanced CSS to hide scrollbars for a cleaner appearance.
Modified the minimum width of the Electron notes window for improved usability. Removed unused CSS styles from NotesWindow.module.css and updated the NotesWindow component to utilize Tailwind CSS for layout and styling, enhancing the overall user interface.
Updated the NotesToolbar component to include tooltips for each formatting button, improving user experience. Integrated localization for toolbar button labels using the useScopedT hook, ensuring accessibility for multiple languages. Added corresponding translations for toolbar actions in various locale files.
Simplified the notes content escaping logic in the NotesWindow component by formatting the code for better readability. This change enhances maintainability while ensuring that notes saved as plain text are correctly wrapped for Tiptap parsing.
@Itzadetunji

Copy link
Copy Markdown
Author

Hi @EtienneLescot,

I’ve reviewed all the points you raised.

  1. Platform support

    • I updated the PR description to clarify that Notes is currently a Windows/macOS-only feature, since Linux does not support setContentProtection.
  2. Markdown editor choice

    • I implemented Markdown support with Tiptap because it has a relatively small bundle size, strong adoption, and active weekly community updates.
    • It also provides a solid WYSIWYG experience out of the box with minimal setup.
b-1
  1. Testing status
    • I tested the feature on both Windows and macOS.

    • Windows: windows-obs-footage.webm (In the recording, you can see that OBS is not picking up the notes window)

    • macOS: Loom recording
      (The Grammarly icon position is the indicator that the notes window is present; Loom does not capture the protected notes window directly.)

PS: I added language support for the toolbar for the WYSIWYG editor

Also, for the issue you raised that should be created, I will look into it however I will need a bit more clarity here

@EtienneLescot

Copy link
Copy Markdown
Collaborator

Perfect, this is exactly what I had in mind — Tiptap is a great pick, the OBS footage proves the capture exclusion works in practice, and the platform note is now visible upfront.

For the v2 issue, what I have in mind is something like:

Notes window v2: teleprompter mode
Build on top of the markdown notes window. Add a presentation mode that auto-scrolls the markdown at the user's speaking pace, supports mirror-flip (for glass teleprompters in front of a webcam), and lets the user set a font size. Optional: scroll speed tied to reading speed or detected speech pauses. The capture-excluded window we just shipped is the foundation — this is the UX on top.

Open it as a new issue and link it from this PR's 'Future work' section so it doesn't get lost. Approving on my side once the merge conflicts are resolved — nice work.

@EtienneLescot EtienneLescot merged commit 34d687c into getopenscreen:main Jul 4, 2026
11 checks passed
@Itzadetunji

Copy link
Copy Markdown
Author

Perfect, this is exactly what I had in mind — Tiptap is a great pick, the OBS footage proves the capture exclusion works in practice, and the platform note is now visible upfront.

For the v2 issue, what I have in mind is something like:

Notes window v2: teleprompter mode
Build on top of the markdown notes window. Add a presentation mode that auto-scrolls the markdown at the user's speaking pace, supports mirror-flip (for glass teleprompters in front of a webcam), and lets the user set a font size. Optional: scroll speed tied to reading speed or detected speech pauses. The capture-excluded window we just shipped is the foundation — this is the UX on top.

Open it as a new issue and link it from this PR's 'Future work' section so it doesn't get lost. Approving on my side once the merge conflicts are resolved — nice work.

Makes alot of sense, working on the issue now [Future work] will be the starting title

@Itzadetunji

Copy link
Copy Markdown
Author

Here is the link
#67

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.

2 participants