Skip to content

test: 100% branch coverage for renderer modules - #24

Merged
Roboroads merged 48 commits into
mainfrom
feat/test-coverage
Jun 10, 2026
Merged

test: 100% branch coverage for renderer modules#24
Roboroads merged 48 commits into
mainfrom
feat/test-coverage

Conversation

@Roboroads

@Roboroads Roboroads commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Add test coverage across all renderer modules (Encoders, Deck, Queue, Microphone, LocalOutput, StatusBar, forms) using v8 ignore annotations for Electron IPC/jsdom-incompatible paths and by extracting Svelte compiler-generated dirty-check branches to $derived helpers. Covers Svelte 5 ?? '' null-guard branches, class array && expressions, and drag-and-drop event handlers.

Summary by CodeRabbit

Release Notes

  • Tests

    • Added comprehensive test coverage for core modules, components, and utilities across the application.
  • Documentation

    • Added GitHub Actions workflow for automated test execution on push to main and pull requests.
  • Chores

    • Added Vitest coverage configuration and dependencies to support test coverage analysis.
    • Added code coverage ignore directives for environment-specific code paths.

Roboroads added 29 commits June 2, 2026 14:55
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin Roboroads Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin Roboroads Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin Roboroads Schepers <robbin@deschepers.nl>
… gaps

Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
…aveformDisplay handleSeekBarClick

Signed-off-by: Robbin Roboroads Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin Roboroads Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin Roboroads Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin Roboroads Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin Roboroads Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

✅ All compliance checks are now passing — thanks for updating your PR!

Roboroads added 8 commits June 3, 2026 12:52
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin Roboroads Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin Roboroads Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin Roboroads Schepers <robbin@deschepers.nl>
…utput

Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
Use v8 ignore start/stop around keyed {#each} blocks whose compiler-generated
UPDATE path is unreachable in jsdom; fix LocalOutput ignore count off-by-one.

Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
@Roboroads
Roboroads force-pushed the feat/test-coverage branch from 77b9cd7 to 7ce68bb Compare June 4, 2026 10:11
The electron package requires a downloaded binary; loading it in Node/jsdom
crashes without it. Mocking at the module level prevents the binary lookup.

Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
@Roboroads
Roboroads marked this pull request as ready for review June 4, 2026 11:32
@Roboroads
Roboroads enabled auto-merge (squash) June 4, 2026 11:44
@coderabbitai

coderabbitai Bot commented Jun 4, 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
  • ✅ Review completed - (🔄 Check again to review again)
📝 Walkthrough

Walkthrough

Adds CI test execution, Vitest coverage configuration, renderer and main-process tests, and coverage-ignore annotations across audio, UI, windowing, IPC, and library code.

Changes

Unified test and coverage expansion

Layer / File(s) Summary
CI and coverage foundation
.github/workflows/test.yml, .idea/vcs.xml, package.json, packages/audio-worklet/package.json, packages/audio-worklet/vitest.config.mts, packages/shared/package.json, packages/shared/vitest.config.mts, packages/streamline/package.json, packages/streamline/vitest.config.mts
Adds the new CI test workflow and monorepo Vitest coverage dependencies/configuration.
Renderer components, forms, and audio primitives
packages/streamline/renderer/src/audio/*, packages/streamline/renderer/src/components/*, packages/streamline/renderer/src/components/forms/*, packages/streamline/renderer/src/drag-drop/song-drag.spec.ts, packages/streamline/renderer/src/first-run/setup.spec.ts, packages/streamline/renderer/src/hotkeys/*, packages/streamline/renderer/src/layout/*, packages/streamline/renderer/src/modules/*, packages/streamline/renderer/src/windows/*
Adds renderer tests, derived UI state, and coverage annotations across shared components, forms, hotkeys, layout, and module UIs.
Window manager and interaction infrastructure
packages/streamline/renderer/src/windows/*
Updates window wrapper and interact.js-backed behaviors, including style derivation and window interaction tests.
Main-process audio and encoder services
packages/streamline/src/audio/*, packages/streamline/src/db/migrate.spec.ts, packages/streamline/src/encoders/*
Adds backend audio transport and encoder process/manager tests, ffmpeg path handling, and related coverage annotations.
IPC handlers, library workers, and update checks
packages/streamline/src/ipc/handlers/*, packages/streamline/src/library/*, packages/streamline/src/update-check.*
Adds IPC handler tests, library scan/watch worker coverage, update-check validation, and worker/runtime coverage annotations.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

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

Caution

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

⚠️ Outside diff range comments (2)
packages/shared/package.json (1)

23-31: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Pin @vitest/coverage-v8 to the same vitest version

packages/shared/package.json has @vitest/coverage-v8 at ^4.1.8 while vitest is ^2.0.0 (also mismatched in packages/audio-worklet/package.json). Vitest requires these packages to be kept at the exact same version; otherwise coverage collection can fail at runtime.

"`@vitest/coverage-v8`": "^4.1.8",
"vitest": "^2.0.0"

Update @vitest/coverage-v8 to match vitest (as packages/streamline/package.json already does with ^2.0.0) and refresh the lockfile.

🤖 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/shared/package.json` around lines 23 - 31, The dependency
"`@vitest/coverage-v8`" in packages/shared/package.json is pinned to "^4.1.8"
while "vitest" is "^2.0.0"; update the "`@vitest/coverage-v8`" entry to the same
version spec as "vitest" (e.g., "^2.0.0") so both packages match (also check
packages/audio-worklet/package.json for the same mismatch), then run your
package manager to regenerate the lockfile (install/update) so the lockfile
reflects the change.
packages/streamline/renderer/src/audio/mixer-bridge.ts (1)

64-71: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Prevent duplicate destination connections when toggling soft clip.

Line 70 reconnects tapNode to audioCtx.destination every call, but this path is never disconnected here. Repeated setSoftClipEnabled(...) calls can accumulate connections and double-route audio. Add a targeted disconnect (or connect once during init) before reconnecting.

Proposed fix
 export function setSoftClipEnabled(enabled: boolean): void {
 	if (!masterBus || !softClipper || !tapNode) return;
@@
 	if (enabled) {
 		masterBus.connect(softClipper);
 		softClipper.connect(tapNode);
 	} else {
 		masterBus.connect(tapNode);
 	}
+	try {
+		tapNode.disconnect(getAudioContext().destination);
+	} catch {
+		/* not connected */
+	}
 	tapNode.connect(getAudioContext().destination);
 }
🤖 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/streamline/renderer/src/audio/mixer-bridge.ts` around lines 64 - 71,
The tapNode is being reconnected to the audio destination every time soft
clipping is toggled which can create duplicate connections; modify the toggle
logic around masterBus, softClipper and tapNode so you first remove any existing
connection from tapNode to getAudioContext().destination (e.g. call a targeted
disconnect on tapNode for that destination) before re-routing
(masterBus.connect(softClipper) / softClipper.connect(tapNode) or
masterBus.connect(tapNode)), or alternatively move the
tapNode.connect(getAudioContext().destination) to the one-time initialization
path so it is only connected once; locate and update the code that performs
these connects involving masterBus, softClipper, tapNode and getAudioContext()
to ensure a single destination connection.
🧹 Nitpick comments (2)
packages/streamline/renderer/src/hotkeys/reset-to-default.ts (1)

39-40: 🏗️ Heavy lift

Avoid blanket coverage ignore on the full reset flow.

Line 39 suppresses coverage for a large stateful path (resetToDefault) that is mock-testable. Prefer testing this flow and limiting ignore pragmas to genuinely unreachable branches.

🤖 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/streamline/renderer/src/hotkeys/reset-to-default.ts` around lines 39
- 40, The coverage suppression currently applied over the entire resetToDefault
function hides a mock-testable stateful flow; remove or narrow the /* v8 ignore
next 32 */ pragma so only truly unreachable branches are ignored, and add unit
tests that mock the IPC channel and the involved stores to exercise
resetToDefault; specifically, extract any complex sub-steps inside
resetToDefault (e.g., IPC call handlers, store reset helpers) into smaller
functions (helpers referenced by resetToDefault) so you can unit-test those
pieces, then write tests that simulate successful and error IPC responses and
verify store resets and side effects rather than suppressing coverage for the
whole function.
packages/streamline/renderer/src/windows/WindowManager.component.spec.ts (1)

35-64: ⚡ Quick win

Move window.streamline setup/cleanup to hooks to avoid leakage on failed assertions.

Right now cleanup is inline in each test; if a test throws before delete, subsequent tests can inherit stale global state.

♻️ Suggested refactor
 describe('WindowManager', () => {
+  beforeEach(() => {
+    allMapMock.clear();
+    (window as unknown as Record<string, unknown>).streamline = {
+      api: {
+        layout: {
+          list: vi.fn().mockResolvedValue([]),
+          load: vi.fn().mockResolvedValue(null)
+        }
+      }
+    };
+  });
+
+  afterEach(() => {
+    delete (window as unknown as Record<string, unknown>).streamline;
+  });
+
   it('renders without crashing when instanceStore is empty', () => {
-    allMapMock.clear();
-    (window as unknown as Record<string, unknown>).streamline = {
-      api: {
-        layout: {
-          list: vi.fn().mockResolvedValue([]),
-          load: vi.fn().mockResolvedValue(null)
-        }
-      }
-    };
     const { container } = render(WindowManager);
     expect(container.querySelector('div.absolute')).toBeTruthy();
-    delete (window as unknown as Record<string, unknown>).streamline;
   });

   it('renders wrapper container', () => {
-    allMapMock.clear();
-    (window as unknown as Record<string, unknown>).streamline = {
-      api: {
-        layout: {
-          list: vi.fn().mockResolvedValue([]),
-          load: vi.fn().mockResolvedValue(null)
-        }
-      }
-    };
     const { container } = render(WindowManager);
     const root = container.firstElementChild;
     expect(root?.className).toContain('absolute');
-    delete (window as unknown as Record<string, unknown>).streamline;
   });
 });
🤖 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/streamline/renderer/src/windows/WindowManager.component.spec.ts`
around lines 35 - 64, The tests set and delete the global (window as unknown as
Record<string, unknown>).streamline inline which can leak state if a test
throws; move the setup into a beforeEach that calls allMapMock.clear() and
assigns window.streamline.api.layout.list and .load to
vi.fn().mockResolvedValue(...) and move the deletion into an afterEach that
deletes window.streamline so each spec (e.g., the two tests referencing
WindowManager) runs with a fresh global and always cleans up; keep test bodies
focused on rendering assertions and reuse the same mock functions created in the
hooks.
🤖 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 @.github/workflows/test.yml:
- Around line 12-14: The workflow uses mutable action tags and leaves checkout
credentials persisted; update the three actions referenced (actions/checkout,
pnpm/action-setup, actions/setup-node) to be pinned to their respective full
commit SHAs instead of tags and set persist-credentials: false on the
actions/checkout step; locate the checkout step named actions/checkout@v4 and
add persist-credentials: false, and replace the tag strings for
pnpm/action-setup@v4 and actions/setup-node@v4 with their exact commit SHA pins.

In `@packages/audio-worklet/package.json`:
- Line 31: The package.json has a major-version mismatch between vitest and
`@vitest/coverage-v8`: the dependency "`@vitest/coverage-v8`" is ^4.1.8 while
"vitest" is ^2.0.0; update them to be compatible by either upgrading "vitest" to
a v4.x release (e.g., ^4.1.8) to match "`@vitest/coverage-v8`" or downgrade/pin
"`@vitest/coverage-v8`" to a v2-compatible release so their major versions align;
modify the package.json dependency entries for "vitest" and/or
"`@vitest/coverage-v8`" accordingly and run install to verify CI coverage works.

In `@packages/audio-worklet/vitest.config.mts`:
- Line 9: Update the coverage thresholds object so it enforces non-zero gates
instead of disabling them; replace the current thresholds: { lines: 0,
functions: 0, branches: 0, statements: 0 } with stricter values (at minimum set
branches: 100, and ideally set lines, functions, and statements to 100) in the
vitest configuration where the thresholds object is defined so the coverage gate
will fail on regressions.

In
`@packages/streamline/renderer/src/components/forms/NumberField.component.spec.ts`:
- Around line 22-27: The test "clamps to min on input" in
NumberField.component.spec.ts currently asserts the raw typed value ('2')
instead of the expected clamped minimum; update the assertion to expect the min
value (e.g., '10') after firing the input event on the rendered NumberField
(render(NumberField, { label: 'L', value: 50, min: 10 })) using the same input
element variable so the test verifies that NumberField enforces the min clamp.

In `@packages/streamline/renderer/src/hotkeys/binder.spec.ts`:
- Around line 36-40: The test should assert teardown removes the exact function
that was registered, not any Function; spy on window.addEventListener when
calling initHotkeyBinder(), capture the handler argument from the
addEventListener call (from the call args for 'keydown'), then call the returned
teardown and assert window.removeEventListener was called with 'keydown' and
that same captured handler (use the exact reference from the add spy) instead of
expect.any(Function); reference initHotkeyBinder and the teardown variable in
the assertion.

In `@packages/streamline/renderer/src/hotkeys/reset-to-default.ts`:
- Around line 74-75: The call to resetToDefault() can reject and currently is
invoked without a rejection handler; update the invocation of resetToDefault
(the reset-to-default IPC flow) to handle promise failures—either await it
inside a try/catch or append a .catch(err => /* handle/log error */) (e.g. void
resetToDefault().catch(err => processLogger.error("resetToDefault failed",
err))); ensure the catch logs or otherwise handles the error to prevent
unhandled promise rejections.

In `@packages/streamline/renderer/src/modules/context.spec.ts`:
- Around line 30-52: The tests create spies on console methods inside each it
block (vi.spyOn(console, 'info'|'warn'|'error')) but call spy.mockRestore()
inside the test body which can be skipped on failures; move restoration to a
shared afterEach to guarantee cleanup. Update the spec to keep references to the
spies (or re-spy per test) and add an afterEach that calls mockRestore() for any
active spies, or alternatively use vi.restoreAllMocks() in afterEach; apply this
change for the tests that use createModuleContext and ctx.log (the tests
referencing console.info/warn/error and createModuleContext).

In `@packages/streamline/renderer/src/modules/deck/Deck.component.spec.ts`:
- Around line 303-314: The test in Deck.component.spec.ts is race-prone because
it queries the close button globally and asserts dialog removal synchronously;
instead, scope the queries to the opened dialog and wait for its removal: after
opening the modal via render(Deck) and clicking the gearButton, locate the
dialog element (e.g., via getByRole('dialog') or
document.querySelector('[role="dialog"]')), use within(dialog) to find the close
button rather than a global query (closeButton), click that scoped close button,
and then use waitFor or waitForElementToBeRemoved on the dialog element to
assert the modal is removed.

In `@packages/streamline/renderer/src/modules/deck/Deck.svelte`:
- Around line 384-389: The test-only global window.__deck_setArtwork assigned in
Deck.svelte (the block that sets (window as unknown as { __deck_setArtwork?:
(url: string | null) => void }).__deck_setArtwork = (url) => { artworkDataUrl =
url; }) must be cleared during component teardown; add an onDestroy handler
(imported from 'svelte' if needed) that, when import.meta.env.MODE === 'test',
removes or sets window.__deck_setArtwork = undefined (or delete
window.__deck_setArtwork) to avoid leaking the closure across tests and ensure
artworkDataUrl is not referenced after destroy.

In `@packages/streamline/src/encoders/encoder-process.spec.ts`:
- Around line 157-161: The test emits stderr data but never asserts the logging
side-effect; update the test to spy/mock the logger and assert a debug call when
stderr data is emitted: create a spy for the logger's debug method (or a mock
logger object) and pass it into EncoderProcess (the same constructor used in the
test), call encoderProcess.start(), emit on (fakeProcess.stderr as EventEmitter)
the Buffer.from('ffmpeg output'), then assert the spy was called with a message
containing 'ffmpeg output' (or otherwise matches the expected log string) to
verify EncoderProcess handles stderr events and logs via its logger.

In `@packages/streamline/src/encoders/ffmpeg-path.spec.ts`:
- Around line 24-29: The test currently only compares two identical return
values which would pass even without caching; modify the spec for getFFmpegPath
so it proves caching by making the mocked ffmpeg-static change (or its factory
record calls) after the first invocation and asserting the second
getFFmpegPath() still returns the original value and that the ffmpeg-static
mock/factory was not invoked again. Concretely, adjust the vi.doMock for
'ffmpeg-static' to expose a mutable return or a call counter, call
getFFmpegPath() once to capture the initial path, change the mock’s return (or
assert the counter) and then call getFFmpegPath() again expecting the initial
cached path and no additional mock invocation. Ensure you reference
getFFmpegPath and the 'ffmpeg-static' mock in the updated assertions.

In `@packages/streamline/src/encoders/ffmpeg-path.ts`:
- Around line 9-13: The code currently calls ffmpegStatic!.replace(...) which
will throw if ffmpegStatic is null; update the logic around ffmpegStatic and
_path to first check if ffmpegStatic is non-null (e.g., if (!ffmpegStatic) throw
new Error('ffmpeg-static binary not found') or return a sensible fallback), then
perform the replace for the app.asar path only when ffmpegStatic is a string;
ensure all uses of ffmpegStatic and _path (the variables in this block) handle
the null case rather than force-unwrapping with !.

In `@packages/streamline/src/encoders/manager.spec.ts`:
- Around line 119-132: The test currently only asserts mockEncoderStart was
invoked; update it to also validate the password callback passed by startEncoder
returns null for configs without passwordRef: after calling
startEncoder(fileConfig, fakeWindow) and asserting mockEncoderStart was called,
inspect the arguments of mockEncoderStart (via its mock.calls), locate the
password callback argument supplied to the encoder constructor invoked by
startEncoder, call that callback and assert it returns null. Ensure you
reference startEncoder and mockEncoderStart when locating and testing the
callback.

In `@packages/streamline/src/ipc/handlers/encoder.spec.ts`:
- Around line 48-52: The tests mutate shared encoder window state via
setEncoderWindow(fakeWindow) but the beforeEach only clears mocks; update
beforeEach in encoder.spec.ts to also reset the encoder window state (either
call a provided reset function or call setEncoderWindow(undefined) / null)
before each test so registerEncoderHandlers() runs against a clean state;
specifically add a call to reset the encoder window state alongside
vi.clearAllMocks() and before registerEncoderHandlers() to avoid order-dependent
tests involving setEncoderWindow and registerEncoderHandlers.

In `@packages/streamline/src/ipc/handlers/hotkeys.spec.ts`:
- Around line 41-57: Tests call the IPC handlers without awaiting their
promises, which can let async failures go unnoticed; make the two tests async
and await the handler invocations returned by getHandler('hotkey:save') and
getHandler('hotkey:delete') respectively (i.e., change the test callbacks to
async and prefix getHandler(...)(...) with await) so mockDb assertions run after
the handler completes.

In `@packages/streamline/src/ipc/handlers/settings.spec.ts`:
- Around line 32-60: Tests share mocked state across cases causing flaky
expectations; add test isolation by clearing/resetting mocks before each test.
In the spec add a beforeEach/afterEach that calls vi.resetAllMocks() or
vi.clearAllMocks() and explicitly clears any mocked ipcMain.handle and mockDb
methods (e.g., vi.mocked(ipcMain.handle).mockClear(), mockDb.get.mockClear(),
mockDb.values.mockClear(), mockDb.run.mockClear()) so registerSettingsHandlers,
getHandler, and mockDb call counts are fresh for every it block.

In `@packages/streamline/src/library/watcher.spec.ts`:
- Line 22: The test variable chokidarInstance should be made optional and
explicitly reset in a beforeEach to avoid order-dependent failures: change its
declaration (chokidarInstance) to an optional type (e.g., add | undefined) and
add a beforeEach that sets chokidarInstance = undefined (and any mock reset you
need) so each test starts with a clean watcher state; update any places that
assume non-null to handle the optional (or assert existence in the specific
tests that require it).

---

Outside diff comments:
In `@packages/shared/package.json`:
- Around line 23-31: The dependency "`@vitest/coverage-v8`" in
packages/shared/package.json is pinned to "^4.1.8" while "vitest" is "^2.0.0";
update the "`@vitest/coverage-v8`" entry to the same version spec as "vitest"
(e.g., "^2.0.0") so both packages match (also check
packages/audio-worklet/package.json for the same mismatch), then run your
package manager to regenerate the lockfile (install/update) so the lockfile
reflects the change.

In `@packages/streamline/renderer/src/audio/mixer-bridge.ts`:
- Around line 64-71: The tapNode is being reconnected to the audio destination
every time soft clipping is toggled which can create duplicate connections;
modify the toggle logic around masterBus, softClipper and tapNode so you first
remove any existing connection from tapNode to getAudioContext().destination
(e.g. call a targeted disconnect on tapNode for that destination) before
re-routing (masterBus.connect(softClipper) / softClipper.connect(tapNode) or
masterBus.connect(tapNode)), or alternatively move the
tapNode.connect(getAudioContext().destination) to the one-time initialization
path so it is only connected once; locate and update the code that performs
these connects involving masterBus, softClipper, tapNode and getAudioContext()
to ensure a single destination connection.

---

Nitpick comments:
In `@packages/streamline/renderer/src/hotkeys/reset-to-default.ts`:
- Around line 39-40: The coverage suppression currently applied over the entire
resetToDefault function hides a mock-testable stateful flow; remove or narrow
the /* v8 ignore next 32 */ pragma so only truly unreachable branches are
ignored, and add unit tests that mock the IPC channel and the involved stores to
exercise resetToDefault; specifically, extract any complex sub-steps inside
resetToDefault (e.g., IPC call handlers, store reset helpers) into smaller
functions (helpers referenced by resetToDefault) so you can unit-test those
pieces, then write tests that simulate successful and error IPC responses and
verify store resets and side effects rather than suppressing coverage for the
whole function.

In `@packages/streamline/renderer/src/windows/WindowManager.component.spec.ts`:
- Around line 35-64: The tests set and delete the global (window as unknown as
Record<string, unknown>).streamline inline which can leak state if a test
throws; move the setup into a beforeEach that calls allMapMock.clear() and
assigns window.streamline.api.layout.list and .load to
vi.fn().mockResolvedValue(...) and move the deletion into an afterEach that
deletes window.streamline so each spec (e.g., the two tests referencing
WindowManager) runs with a fresh global and always cleans up; keep test bodies
focused on rendering assertions and reuse the same mock functions created in the
hooks.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1effd62e-e16b-4eaa-b25b-26c4adb87e0e

📥 Commits

Reviewing files that changed from the base of the PR and between 7cb1947 and 1d50892.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (97)
  • .github/workflows/test.yml
  • .idea/vcs.xml
  • package.json
  • packages/audio-worklet/package.json
  • packages/audio-worklet/vitest.config.mts
  • packages/shared/package.json
  • packages/shared/vitest.config.mts
  • packages/streamline/package.json
  • packages/streamline/renderer/src/audio/context.spec.ts
  • packages/streamline/renderer/src/audio/context.ts
  • packages/streamline/renderer/src/audio/mixer-bridge.spec.ts
  • packages/streamline/renderer/src/audio/mixer-bridge.ts
  • packages/streamline/renderer/src/audio/port.spec.ts
  • packages/streamline/renderer/src/components/DbMeter.component.spec.ts
  • packages/streamline/renderer/src/components/IconButton.component.spec.ts
  • packages/streamline/renderer/src/components/Modal.component.spec.ts
  • packages/streamline/renderer/src/components/Modal.svelte
  • packages/streamline/renderer/src/components/OnAirIndicator.component.spec.ts
  • packages/streamline/renderer/src/components/StatusBar.component.spec.ts
  • packages/streamline/renderer/src/components/StatusBar.svelte
  • packages/streamline/renderer/src/components/Toast.component.spec.ts
  • packages/streamline/renderer/src/components/ToastContainer.component.spec.ts
  • packages/streamline/renderer/src/components/forms/BitrateField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/ComboField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/ComboField.svelte
  • packages/streamline/renderer/src/components/forms/FilenameField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/FolderField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/FormField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/NumberField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/NumberField.svelte
  • packages/streamline/renderer/src/components/forms/SampleRateField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/SelectField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/TextField.component.spec.ts
  • packages/streamline/renderer/src/drag-drop/song-drag.spec.ts
  • packages/streamline/renderer/src/first-run/setup.spec.ts
  • packages/streamline/renderer/src/hotkeys/binder.spec.ts
  • packages/streamline/renderer/src/hotkeys/reset-to-default.spec.ts
  • packages/streamline/renderer/src/hotkeys/reset-to-default.ts
  • packages/streamline/renderer/src/hotkeys/store.svelte.spec.ts
  • packages/streamline/renderer/src/layout/persistence.svelte.spec.ts
  • packages/streamline/renderer/src/layout/persistence.svelte.ts
  • packages/streamline/renderer/src/layout/store.svelte.spec.ts
  • packages/streamline/renderer/src/modules/context.spec.ts
  • packages/streamline/renderer/src/modules/crossfader/Crossfader.component.spec.ts
  • packages/streamline/renderer/src/modules/crossfader/Crossfader.svelte
  • packages/streamline/renderer/src/modules/deck/Deck.component.spec.ts
  • packages/streamline/renderer/src/modules/deck/Deck.svelte
  • packages/streamline/renderer/src/modules/deck/DeckSettingsModal.component.spec.ts
  • packages/streamline/renderer/src/modules/deck/WaveformDisplay.component.spec.ts
  • packages/streamline/renderer/src/modules/deck/WaveformDisplay.svelte
  • packages/streamline/renderer/src/modules/deck/deck-audio.ts
  • packages/streamline/renderer/src/modules/deck/waveform-worker.spec.ts
  • packages/streamline/renderer/src/modules/deck/waveform-worker.ts
  • packages/streamline/renderer/src/modules/encoders/EncoderModal.component.spec.ts
  • packages/streamline/renderer/src/modules/encoders/EncoderModal.svelte
  • packages/streamline/renderer/src/modules/encoders/Encoders.component.spec.ts
  • packages/streamline/renderer/src/modules/encoders/Encoders.svelte
  • packages/streamline/renderer/src/modules/instance-store.svelte.spec.ts
  • packages/streamline/renderer/src/modules/local-output/LocalOutput.component.spec.ts
  • packages/streamline/renderer/src/modules/local-output/LocalOutput.svelte
  • packages/streamline/renderer/src/modules/microphone/Microphone.component.spec.ts
  • packages/streamline/renderer/src/modules/microphone/Microphone.svelte
  • packages/streamline/renderer/src/modules/queue/Queue.component.spec.ts
  • packages/streamline/renderer/src/modules/queue/Queue.svelte
  • packages/streamline/renderer/src/modules/queue/QueueSettingsModal.component.spec.ts
  • packages/streamline/renderer/src/modules/queue/rotation.ts
  • packages/streamline/renderer/src/modules/register-builtins.spec.ts
  • packages/streamline/renderer/src/windows/WindowManager.component.spec.ts
  • packages/streamline/renderer/src/windows/WindowManager.svelte
  • packages/streamline/renderer/src/windows/WindowWrapper.component.spec.ts
  • packages/streamline/renderer/src/windows/WindowWrapper.svelte
  • packages/streamline/renderer/src/windows/use-interact.spec.ts
  • packages/streamline/renderer/src/windows/use-interact.ts
  • packages/streamline/src/audio/pcm-receiver.spec.ts
  • packages/streamline/src/audio/pcm-receiver.ts
  • packages/streamline/src/audio/port.spec.ts
  • packages/streamline/src/db/migrate.spec.ts
  • packages/streamline/src/encoders/encoder-process.spec.ts
  • packages/streamline/src/encoders/encoder-process.ts
  • packages/streamline/src/encoders/ffmpeg-path.spec.ts
  • packages/streamline/src/encoders/ffmpeg-path.ts
  • packages/streamline/src/encoders/manager.spec.ts
  • packages/streamline/src/encoders/manager.ts
  • packages/streamline/src/ipc/handlers/encoder.spec.ts
  • packages/streamline/src/ipc/handlers/hotkeys.spec.ts
  • packages/streamline/src/ipc/handlers/layout.spec.ts
  • packages/streamline/src/ipc/handlers/library.spec.ts
  • packages/streamline/src/ipc/handlers/secret.spec.ts
  • packages/streamline/src/ipc/handlers/settings.spec.ts
  • packages/streamline/src/ipc/handlers/system.spec.ts
  • packages/streamline/src/ipc/handlers/system.ts
  • packages/streamline/src/library/scan-worker.ts
  • packages/streamline/src/library/scanner.spec.ts
  • packages/streamline/src/library/watcher.spec.ts
  • packages/streamline/src/update-check.spec.ts
  • packages/streamline/src/update-check.ts
  • packages/streamline/vitest.config.mts
💤 Files with no reviewable changes (1)
  • .idea/vcs.xml

Comment thread .github/workflows/test.yml Outdated
Comment thread packages/audio-worklet/package.json Outdated
Comment thread packages/audio-worklet/vitest.config.mts Outdated
Comment thread packages/streamline/renderer/src/hotkeys/binder.spec.ts
Comment thread packages/streamline/src/encoders/manager.spec.ts
Comment thread packages/streamline/src/ipc/handlers/encoder.spec.ts
Comment thread packages/streamline/src/ipc/handlers/hotkeys.spec.ts Outdated
Comment thread packages/streamline/src/ipc/handlers/settings.spec.ts
Comment thread packages/streamline/src/library/watcher.spec.ts Outdated
…icate audio connection

Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>

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

Caution

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

⚠️ Outside diff range comments (2)
packages/streamline/renderer/src/audio/mixer-bridge.ts (1)

50-69: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Prevent duplicate softClipper → tapNode connections when toggling soft clip repeatedly.

Repeated setSoftClipEnabled(true) calls can create parallel duplicate edges because softClipper.disconnect(tapNode) is never done before reconnecting, which can increase output level unexpectedly.

Suggested fix
 export function setSoftClipEnabled(enabled: boolean): void {
 	if (!masterBus || !softClipper || !tapNode) return;
 	// Use targeted disconnects to avoid severing LocalOutput gainNode connections
 	try {
 		masterBus.disconnect(softClipper);
 	} catch {
 		/* not connected */
 	}
 	try {
 		masterBus.disconnect(tapNode);
 	} catch {
 		/* not connected */
 	}
+	try {
+		softClipper.disconnect(tapNode);
+	} catch {
+		/* not connected */
+	}
 	if (enabled) {
 		masterBus.connect(softClipper);
 		softClipper.connect(tapNode);
 	} else {
 		masterBus.connect(tapNode);
 	}
 }
🤖 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/streamline/renderer/src/audio/mixer-bridge.ts` around lines 50 - 69,
setSoftClipEnabled currently reconnects softClipper → tapNode on repeated
enables without ever disconnecting that edge, causing duplicate parallel
connections; update setSoftClipEnabled to explicitly disconnect softClipper from
tapNode (wrapped in a try/catch like the other disconnects) before calling
softClipper.connect(tapNode) when enabled, and ensure you likewise disconnect
softClipper from tapNode when disabling so only one path (masterBus→tapNode or
masterBus→softClipper→tapNode) exists; reference the setSoftClipEnabled function
and the masterBus, softClipper, and tapNode nodes when making this change.
packages/streamline/renderer/src/modules/deck/WaveformDisplay.svelte (1)

54-65: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard zero-width click geometry before computing seek ratio.

When rect.width is 0, both click handlers can emit invalid seek values. Add a width guard before dividing.

Suggested fix
 function handleCanvasClick(e: MouseEvent) {
 	if (!canvas || duration === 0) return;
 	const rect = canvas.getBoundingClientRect();
+	if (rect.width <= 0) return;
 	onSeek(((e.clientX - rect.left) / rect.width) * duration);
 }

 function handleSeekBarClick(e: MouseEvent) {
 	if (duration === 0) return;
 	const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
+	if (rect.width <= 0) return;
 	onSeek(((e.clientX - rect.left) / rect.width) * duration);
 }
🤖 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/streamline/renderer/src/modules/deck/WaveformDisplay.svelte` around
lines 54 - 65, Both click handlers compute a seek ratio by dividing by
rect.width without guarding for zero; update handleCanvasClick and
handleSeekBarClick to early-return when rect.width is 0 (or <= 0) before
performing the division so you never pass NaN/Infinity into onSeek.
Specifically, after obtaining rect via getBoundingClientRect() in
handleCanvasClick and handleSeekBarClick, check if rect.width <= 0 and return
early (preserving existing duration checks) to avoid invalid seek values.
♻️ Duplicate comments (2)
packages/streamline/renderer/src/modules/deck/Deck.component.spec.ts (1)

303-315: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Wait for dialog removal after clicking Close.

Line 314 asserts synchronously; this can still race modal teardown and intermittently fail.

Suggested fix
 		await fireEvent.click(closeButton);
-		expect(document.querySelector('[role="dialog"]')).toBeNull();
+		await waitFor(() => {
+			expect(document.querySelector('[role="dialog"]')).toBeNull();
+		});
 	});
🤖 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/streamline/renderer/src/modules/deck/Deck.component.spec.ts` around
lines 303 - 315, The test currently clicks the Close button and then asserts
synchronously that the dialog is gone, which can race; after locating
closeButton (from dialog.querySelector) and firing the click, change the final
assertion to wait for the dialog to be removed (e.g., use waitFor or
waitForElementToBeRemoved) targeting the same selector '[role="dialog"]' so the
test awaits modal teardown instead of asserting immediately.
packages/streamline/src/encoders/ffmpeg-path.spec.ts (1)

24-29: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Strengthen the cache test so it actually proves memoization.

Line 28 compares two identical calls and still passes if caching is removed. Mutate a mocked input between calls and assert the second result stays pinned to the first value.

Suggested fix
 	it('caches the result on repeated calls', async () => {
-		vi.doMock('electron', () => ({ app: { isPackaged: false } }));
-		vi.doMock('ffmpeg-static', () => ({ default: '/path/ffmpeg' }));
+		const appMock = { isPackaged: true };
+		vi.doMock('electron', () => ({ app: appMock }));
+		vi.doMock('ffmpeg-static', () => ({
+			default: '/app/app.asar/node_modules/ffmpeg-static/bin/ffmpeg'
+		}));
 		const { getFFmpegPath } = await import('./ffmpeg-path');
-		expect(getFFmpegPath()).toBe(getFFmpegPath());
+		const first = getFFmpegPath();
+		appMock.isPackaged = false;
+		expect(getFFmpegPath()).toBe(first);
 	});
🤖 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/streamline/src/encoders/ffmpeg-path.spec.ts` around lines 24 - 29,
The test currently calls getFFmpegPath() twice with the same mocked input so it
passes even without memoization; change the test to mock 'ffmpeg-static' to one
value, import getFFmpegPath and call it once to capture the first result, then
re-mock 'ffmpeg-static' to a different value and call getFFmpegPath() again and
assert the second call equals the first. Reference getFFmpegPath and the
'ffmpeg-static' mock so the test proves the function caches/memoizes its result.
🧹 Nitpick comments (3)
packages/streamline/renderer/src/windows/WindowManager.component.spec.ts (1)

35-48: ⚡ Quick win

Move window.streamline setup/teardown into test hooks for isolation.

delete window.streamline is inside each test body, so a failing assertion can leak global state into subsequent tests. Use beforeEach/afterEach to make cleanup unconditional.

Suggested refactor
 describe('WindowManager', () => {
+  beforeEach(() => {
+    (window as unknown as Record<string, unknown>).streamline = {
+      api: {
+        layout: {
+          list: vi.fn().mockResolvedValue([]),
+          load: vi.fn().mockResolvedValue(null)
+        }
+      }
+    };
+  });
+
+  afterEach(() => {
+    delete (window as unknown as Record<string, unknown>).streamline;
+  });
+
 	it('renders without crashing when instanceStore is empty', () => {
 		allMapMock.clear();
-		(window as unknown as Record<string, unknown>).streamline = {
-			api: {
-				layout: {
-					list: vi.fn().mockResolvedValue([]),
-					load: vi.fn().mockResolvedValue(null)
-				}
-			}
-		};
 		const { container } = render(WindowManager);
 		expect(container.querySelector('div.absolute')).toBeTruthy();
-		delete (window as unknown as Record<string, unknown>).streamline;
 	});
 
 	it('renders wrapper container', () => {
 		allMapMock.clear();
-		(window as unknown as Record<string, unknown>).streamline = {
-			api: {
-				layout: {
-					list: vi.fn().mockResolvedValue([]),
-					load: vi.fn().mockResolvedValue(null)
-				}
-			}
-		};
 		const { container } = render(WindowManager);
 		const root = container.firstElementChild;
 		expect(root?.className).toContain('absolute');
-		delete (window as unknown as Record<string, unknown>).streamline;
 	});
 });

Also applies to: 50-64

🤖 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/streamline/renderer/src/windows/WindowManager.component.spec.ts`
around lines 35 - 48, The tests in WindowManager.component.spec.ts set
window.streamline inside individual it blocks and delete it at the end, which
can leak global state on failed assertions; move the setup into a beforeEach
that assigns (window as unknown as Record<string, unknown>).streamline = { api:
{ layout: { list: vi.fn().mockResolvedValue([]), load:
vi.fn().mockResolvedValue(null) } } } and move the cleanup into an afterEach
that unconditionally deletes (window as unknown as Record<string,
unknown>).streamline; update both the test at 'renders without crashing when
instanceStore is empty' and the similar test around lines 50-64 to remove inline
setup/teardown so they rely on the shared hooks and keep assertions unchanged.
packages/streamline/renderer/src/windows/WindowWrapper.component.spec.ts (1)

45-161: ⚡ Quick win

Reset shared mocks between tests to keep assertions independent.

updateMock, bringToFrontMock, and updateInstanceMock are shared across cases. Add a beforeEach(() => vi.clearAllMocks()) in this suite to prevent state bleed as tests grow.

🤖 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/streamline/renderer/src/windows/WindowWrapper.component.spec.ts`
around lines 45 - 161, Tests share mocks (updateMock, bringToFrontMock,
updateInstanceMock) causing state bleed; add a beforeEach in the WindowWrapper
suite that calls vi.clearAllMocks() to reset these mocks between tests so each
it block starts with clean mock state—insert a beforeEach(() =>
vi.clearAllMocks()) inside the describe('WindowWrapper', ...) block near the top
of the file.
package.json (1)

22-22: ⚡ Quick win

Align Vitest coverage provider major versions across the workspace.

Root now brings in @vitest/coverage-v8@^4.1.8 while the app package is on Vitest 2 + coverage-v8 2. Keeping these aligned (or removing the root copy if unused) reduces dependency drift and avoids accidental toolchain mismatch later.

#!/bin/bash
# Verify Vitest + coverage provider versions across workspace package manifests.
fd -a "package.json" | xargs -I{} sh -c '
  printf "\n== %s ==\n" "{}"
  jq -r '"'"'
    .devDependencies // {} |
    {
      vitest: .vitest,
      "`@vitest/coverage-v8`": .["`@vitest/coverage-v8`"]
    } |
    to_entries[] |
    select(.value != null) |
    "\(.key)=\(.value)"
  '"'"' "{}"
'
🤖 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 `@package.json` at line 22, The root package.json currently lists
"`@vitest/coverage-v8`": "^4.1.8" which mismatches the app package's Vitest +
coverage provider major version; either update the root dependency to match the
app's vitest major (e.g., align "`@vitest/coverage-v8`" to the same major as the
app) or remove the root entry if it's unused. Locate the root package.json
dependency "`@vitest/coverage-v8`" and either change its version string to the
app's matching major version alongside "vitest" or delete the entry, then run
yarn/npm install and re-run the workspace version-check script to confirm
consistency.
🤖 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/shared/vitest.config.mts`:
- Line 9: The coverage gate is effectively disabled because the thresholds
object in vitest.config.mts (the thresholds: { lines: 0, functions: 0, branches:
0, statements: 0 } entry) uses zeros; update that thresholds object to non-zero
enforced values (for example set branches: 100 and sensible minima for lines,
functions, and statements like 80–90 depending on policy) so CI fails on
coverage regressions—locate the thresholds object in the exported config and
replace the zero values with the agreed enforcement numbers.

In `@packages/streamline/renderer/src/windows/use-interact.ts`:
- Around line 17-22: Remove the misleading "v8 ignore next — fallback
unreachable" comments from the width/height getters in use-interact.ts so the
default fallbacks (opts.minWidth ?? 200 and opts.minHeight ?? 150) are treated
as covered logic; update tests to assert the fallback clamp behavior when
InteractOptions omits minWidth/minHeight. Locate the getters named width and
height in the useInteract hook (they reference opts.minWidth and opts.minHeight)
and simply delete the ignore annotations and add/adjust unit tests to validate
the 200x150 default clamp.

In `@packages/streamline/src/ipc/handlers/secret.spec.ts`:
- Around line 57-61: The test for SECRET_SET currently toggles encryption
availability with isAvailableMock.mockReturnValue(false) and asserts
encryptStringMock was called but never asserts the warning branch; update the
test (the it block using getHandler('secret:set')) to also assert that the
mocked warning logger was invoked (e.g., expect(warnMock).toHaveBeenCalled() or
expect(warnMock).toHaveBeenCalledWith(expect.stringContaining('encryption')/the
exact message used in the handler)) so the test fails if the warning emission
regresses; keep the existing encryptStringMock assertion and add the warning
assertion after invoking getHandler('secret:set')(null, 'ref', 'val').

---

Outside diff comments:
In `@packages/streamline/renderer/src/audio/mixer-bridge.ts`:
- Around line 50-69: setSoftClipEnabled currently reconnects softClipper →
tapNode on repeated enables without ever disconnecting that edge, causing
duplicate parallel connections; update setSoftClipEnabled to explicitly
disconnect softClipper from tapNode (wrapped in a try/catch like the other
disconnects) before calling softClipper.connect(tapNode) when enabled, and
ensure you likewise disconnect softClipper from tapNode when disabling so only
one path (masterBus→tapNode or masterBus→softClipper→tapNode) exists; reference
the setSoftClipEnabled function and the masterBus, softClipper, and tapNode
nodes when making this change.

In `@packages/streamline/renderer/src/modules/deck/WaveformDisplay.svelte`:
- Around line 54-65: Both click handlers compute a seek ratio by dividing by
rect.width without guarding for zero; update handleCanvasClick and
handleSeekBarClick to early-return when rect.width is 0 (or <= 0) before
performing the division so you never pass NaN/Infinity into onSeek.
Specifically, after obtaining rect via getBoundingClientRect() in
handleCanvasClick and handleSeekBarClick, check if rect.width <= 0 and return
early (preserving existing duration checks) to avoid invalid seek values.

---

Duplicate comments:
In `@packages/streamline/renderer/src/modules/deck/Deck.component.spec.ts`:
- Around line 303-315: The test currently clicks the Close button and then
asserts synchronously that the dialog is gone, which can race; after locating
closeButton (from dialog.querySelector) and firing the click, change the final
assertion to wait for the dialog to be removed (e.g., use waitFor or
waitForElementToBeRemoved) targeting the same selector '[role="dialog"]' so the
test awaits modal teardown instead of asserting immediately.

In `@packages/streamline/src/encoders/ffmpeg-path.spec.ts`:
- Around line 24-29: The test currently calls getFFmpegPath() twice with the
same mocked input so it passes even without memoization; change the test to mock
'ffmpeg-static' to one value, import getFFmpegPath and call it once to capture
the first result, then re-mock 'ffmpeg-static' to a different value and call
getFFmpegPath() again and assert the second call equals the first. Reference
getFFmpegPath and the 'ffmpeg-static' mock so the test proves the function
caches/memoizes its result.

---

Nitpick comments:
In `@package.json`:
- Line 22: The root package.json currently lists "`@vitest/coverage-v8`": "^4.1.8"
which mismatches the app package's Vitest + coverage provider major version;
either update the root dependency to match the app's vitest major (e.g., align
"`@vitest/coverage-v8`" to the same major as the app) or remove the root entry if
it's unused. Locate the root package.json dependency "`@vitest/coverage-v8`" and
either change its version string to the app's matching major version alongside
"vitest" or delete the entry, then run yarn/npm install and re-run the workspace
version-check script to confirm consistency.

In `@packages/streamline/renderer/src/windows/WindowManager.component.spec.ts`:
- Around line 35-48: The tests in WindowManager.component.spec.ts set
window.streamline inside individual it blocks and delete it at the end, which
can leak global state on failed assertions; move the setup into a beforeEach
that assigns (window as unknown as Record<string, unknown>).streamline = { api:
{ layout: { list: vi.fn().mockResolvedValue([]), load:
vi.fn().mockResolvedValue(null) } } } and move the cleanup into an afterEach
that unconditionally deletes (window as unknown as Record<string,
unknown>).streamline; update both the test at 'renders without crashing when
instanceStore is empty' and the similar test around lines 50-64 to remove inline
setup/teardown so they rely on the shared hooks and keep assertions unchanged.

In `@packages/streamline/renderer/src/windows/WindowWrapper.component.spec.ts`:
- Around line 45-161: Tests share mocks (updateMock, bringToFrontMock,
updateInstanceMock) causing state bleed; add a beforeEach in the WindowWrapper
suite that calls vi.clearAllMocks() to reset these mocks between tests so each
it block starts with clean mock state—insert a beforeEach(() =>
vi.clearAllMocks()) inside the describe('WindowWrapper', ...) block near the top
of the file.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a39eae30-d6ad-4ae3-8dbe-71b649aee595

📥 Commits

Reviewing files that changed from the base of the PR and between 7cb1947 and 090e3e8.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (99)
  • .github/workflows/test.yml
  • .idea/vcs.xml
  • package.json
  • packages/audio-worklet/package.json
  • packages/audio-worklet/src/tap-processor.ts
  • packages/audio-worklet/vitest.config.mts
  • packages/shared/package.json
  • packages/shared/vitest.config.mts
  • packages/streamline/package.json
  • packages/streamline/renderer/src/audio/context.spec.ts
  • packages/streamline/renderer/src/audio/context.ts
  • packages/streamline/renderer/src/audio/mixer-bridge.spec.ts
  • packages/streamline/renderer/src/audio/mixer-bridge.ts
  • packages/streamline/renderer/src/audio/port.spec.ts
  • packages/streamline/renderer/src/components/DbMeter.component.spec.ts
  • packages/streamline/renderer/src/components/IconButton.component.spec.ts
  • packages/streamline/renderer/src/components/Modal.component.spec.ts
  • packages/streamline/renderer/src/components/Modal.svelte
  • packages/streamline/renderer/src/components/OnAirIndicator.component.spec.ts
  • packages/streamline/renderer/src/components/StatusBar.component.spec.ts
  • packages/streamline/renderer/src/components/StatusBar.svelte
  • packages/streamline/renderer/src/components/Toast.component.spec.ts
  • packages/streamline/renderer/src/components/ToastContainer.component.spec.ts
  • packages/streamline/renderer/src/components/forms/BitrateField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/ComboField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/ComboField.svelte
  • packages/streamline/renderer/src/components/forms/FilenameField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/FolderField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/FormField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/NumberField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/NumberField.svelte
  • packages/streamline/renderer/src/components/forms/SampleRateField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/SelectField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/TextField.component.spec.ts
  • packages/streamline/renderer/src/drag-drop/song-drag.spec.ts
  • packages/streamline/renderer/src/first-run/setup.spec.ts
  • packages/streamline/renderer/src/hotkeys/binder.spec.ts
  • packages/streamline/renderer/src/hotkeys/reset-to-default.spec.ts
  • packages/streamline/renderer/src/hotkeys/reset-to-default.ts
  • packages/streamline/renderer/src/hotkeys/store.svelte.spec.ts
  • packages/streamline/renderer/src/layout/persistence.svelte.spec.ts
  • packages/streamline/renderer/src/layout/persistence.svelte.ts
  • packages/streamline/renderer/src/layout/store.svelte.spec.ts
  • packages/streamline/renderer/src/modules/context.spec.ts
  • packages/streamline/renderer/src/modules/crossfader/Crossfader.component.spec.ts
  • packages/streamline/renderer/src/modules/crossfader/Crossfader.svelte
  • packages/streamline/renderer/src/modules/deck/Deck.component.spec.ts
  • packages/streamline/renderer/src/modules/deck/Deck.svelte
  • packages/streamline/renderer/src/modules/deck/DeckSettingsModal.component.spec.ts
  • packages/streamline/renderer/src/modules/deck/WaveformDisplay.component.spec.ts
  • packages/streamline/renderer/src/modules/deck/WaveformDisplay.svelte
  • packages/streamline/renderer/src/modules/deck/deck-audio.ts
  • packages/streamline/renderer/src/modules/deck/waveform-worker.spec.ts
  • packages/streamline/renderer/src/modules/deck/waveform-worker.ts
  • packages/streamline/renderer/src/modules/encoders/EncoderModal.component.spec.ts
  • packages/streamline/renderer/src/modules/encoders/EncoderModal.svelte
  • packages/streamline/renderer/src/modules/encoders/Encoders.component.spec.ts
  • packages/streamline/renderer/src/modules/encoders/Encoders.svelte
  • packages/streamline/renderer/src/modules/instance-store.svelte.spec.ts
  • packages/streamline/renderer/src/modules/local-output/LocalOutput.component.spec.ts
  • packages/streamline/renderer/src/modules/local-output/LocalOutput.svelte
  • packages/streamline/renderer/src/modules/microphone/Microphone.component.spec.ts
  • packages/streamline/renderer/src/modules/microphone/Microphone.svelte
  • packages/streamline/renderer/src/modules/queue/Queue.component.spec.ts
  • packages/streamline/renderer/src/modules/queue/Queue.svelte
  • packages/streamline/renderer/src/modules/queue/QueueSettingsModal.component.spec.ts
  • packages/streamline/renderer/src/modules/queue/rotation.ts
  • packages/streamline/renderer/src/modules/register-builtins.spec.ts
  • packages/streamline/renderer/src/windows/WindowManager.component.spec.ts
  • packages/streamline/renderer/src/windows/WindowManager.svelte
  • packages/streamline/renderer/src/windows/WindowWrapper.component.spec.ts
  • packages/streamline/renderer/src/windows/WindowWrapper.svelte
  • packages/streamline/renderer/src/windows/use-interact.spec.ts
  • packages/streamline/renderer/src/windows/use-interact.ts
  • packages/streamline/src/audio/pcm-receiver.spec.ts
  • packages/streamline/src/audio/pcm-receiver.ts
  • packages/streamline/src/audio/port.spec.ts
  • packages/streamline/src/db/migrate.spec.ts
  • packages/streamline/src/encoders/encoder-process.spec.ts
  • packages/streamline/src/encoders/encoder-process.ts
  • packages/streamline/src/encoders/ffmpeg-path.spec.ts
  • packages/streamline/src/encoders/ffmpeg-path.ts
  • packages/streamline/src/encoders/manager.spec.ts
  • packages/streamline/src/encoders/manager.ts
  • packages/streamline/src/ipc/handlers/encoder.spec.ts
  • packages/streamline/src/ipc/handlers/encoder.ts
  • packages/streamline/src/ipc/handlers/hotkeys.spec.ts
  • packages/streamline/src/ipc/handlers/layout.spec.ts
  • packages/streamline/src/ipc/handlers/library.spec.ts
  • packages/streamline/src/ipc/handlers/secret.spec.ts
  • packages/streamline/src/ipc/handlers/settings.spec.ts
  • packages/streamline/src/ipc/handlers/system.spec.ts
  • packages/streamline/src/ipc/handlers/system.ts
  • packages/streamline/src/library/scan-worker.ts
  • packages/streamline/src/library/scanner.spec.ts
  • packages/streamline/src/library/watcher.spec.ts
  • packages/streamline/src/update-check.spec.ts
  • packages/streamline/src/update-check.ts
  • packages/streamline/vitest.config.mts
💤 Files with no reviewable changes (1)
  • .idea/vcs.xml

Comment thread packages/shared/vitest.config.mts Outdated
Comment thread packages/streamline/renderer/src/windows/use-interact.ts Outdated
Comment thread packages/streamline/src/ipc/handlers/secret.spec.ts
… correctness, defensive guards

Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>

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

Caution

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

⚠️ Outside diff range comments (1)
packages/streamline/renderer/src/modules/encoders/EncoderModal.svelte (1)

72-100: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Incomplete v8 ignore directive — else block not covered.

The coverage ignore at line 72 specifies next 11, which covers only lines 73–83 (the if (type === 'file') branch). The else branch (lines 85–100) also calls onSave() with network encoder parameters and is equally untestable in jsdom, yet it remains uncovered by the directive.

🔧 Proposed fix

Extend the ignore range to cover the entire if/else block:

 	const sampleRateOut = snapSampleRate(sampleRate);
-	/* v8 ignore next 11 — save() requires Electron IPC (safeStorage); not testable in jsdom */
+	/* v8 ignore next 28 — save() requires Electron IPC (safeStorage); not testable in jsdom */
 	if (type === 'file') {
 		const pathTemplate = composePathTemplate(folder, filename);
 		onSave({

This will correctly exclude lines 73–100 (both the if and else branches) from coverage.

🤖 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/streamline/renderer/src/modules/encoders/EncoderModal.svelte` around
lines 72 - 100, The v8 coverage ignore currently only covers the `if (type ===
'file')` branch; extend the ignore directive so it spans the entire if/else
block that calls `onSave` (including the `composePathTemplate` usage and the
`else` branch that passes `host`, `port`, `mount`, `username`, and
`passwordRef`) so both branches are excluded from coverage; update the `/* v8
ignore next N */` count to cover all lines from the `if (type === 'file')` start
through the end of the `else` block (the lines that invoke `onSave` in both
branches).
♻️ Duplicate comments (2)
packages/streamline/src/ipc/handlers/hotkeys.spec.ts (1)

41-57: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Await async handler invocations in save/delete tests.

Line 48 and Line 54 should be awaited so async handler failures cannot be skipped.

Suggested fix
-	it('HOTKEY_SAVE upserts binding', () => {
+	it('HOTKEY_SAVE upserts binding', async () => {
 		const binding: HotkeyBinding = {
 			id: 'h1',
 			instanceId: 'i1',
 			action: 'play',
 			accelerator: 'F1'
 		};
-		getHandler('hotkey:save')(null, binding);
+		await getHandler('hotkey:save')(null, binding);
 		expect(mockDb.insert).toHaveBeenCalledOnce();
 		expect(mockDb.run).toHaveBeenCalledOnce();
 	});

-	it('HOTKEY_DELETE deletes by id', () => {
-		getHandler('hotkey:delete')(null, 'h1');
+	it('HOTKEY_DELETE deletes by id', async () => {
+		await getHandler('hotkey:delete')(null, 'h1');
 		expect(mockDb.delete).toHaveBeenCalledOnce();
 		expect(mockDb.run).toHaveBeenCalledOnce();
 	});
🤖 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/streamline/src/ipc/handlers/hotkeys.spec.ts` around lines 41 - 57,
The tests "HOTKEY_SAVE upserts binding" and "HOTKEY_DELETE deletes by id" call
async IPC handlers via getHandler('hotkey:save') and getHandler('hotkey:delete')
but do not await them; update both tests to be async (add async to the it
callback) and await the invocation of the handler (await getHandler(...)(null,
...)) so any thrown/rejected errors are surfaced and the subsequent expectations
(mockDb.insert/delete and mockDb.run) run after the handler completes.
packages/streamline/renderer/src/hotkeys/reset-to-default.ts (1)

74-75: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Do not silently swallow reset failures.

Line 75 suppresses all resetToDefault() errors with an empty catch, so reset failures become invisible to users and debugging.

Suggested failure handling
 	const onKeyDown = createTripleTapHandler(() => {
 		/* v8 ignore next — resetToDefault IPC flow not exercised in unit tests */
-		void resetToDefault().catch(() => {});
+		void resetToDefault().catch((error) => {
+			console.error('resetToDefault failed', error);
+			eventBus.emit('toast:show', {
+				message: 'Failed to reset layout and hotkeys',
+				type: 'error'
+			});
+		});
 	});
🤖 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/streamline/renderer/src/hotkeys/reset-to-default.ts` around lines 74
- 75, The current call to resetToDefault() swallows all errors with an empty
catch; change this so failures are surfaced: replace the empty catch at the
reset-to-default call with a catch that logs the error (e.g., console.error or
the renderer logging utility) and surfaces user-visible feedback (e.g., dispatch
an IPC/error-notification or call an existing showErrorNotification function)
and/or rethrow so upstream can handle it; update the catch block around
resetToDefault() in reset-to-default.ts to log the error and trigger a UI
notification or IPC event named for reset failures.
🧹 Nitpick comments (4)
packages/streamline/src/ipc/handlers/system.spec.ts (1)

92-96: ⚡ Quick win

Test coverage gap: selectFolder non-canceled path is untested.

The test suite covers the canceled path for system:selectFolder (lines 92–96) but does not exercise the success path where a folder is selected. This mirrors the pattern used for system:selectFile (lines 81–90), which tests both canceled and non-canceled outcomes.

While the implementation in system.ts line 33 marks this branch with /* v8 ignore next */, adding a test for the non-canceled path would increase confidence that the handler behaves correctly when users actually select a folder.

🧪 Suggested test case
 	it('SYSTEM_SELECT_FOLDER returns null when canceled', async () => {
 		dialogMock.showOpenDialog.mockResolvedValueOnce({ canceled: true, filePaths: [] });
 		const result = await (getHandler('system:selectFolder')(null) as Promise<unknown>);
 		expect(result).toBeNull();
 	});
+
+	it('SYSTEM_SELECT_FOLDER returns folder path when not canceled', async () => {
+		dialogMock.showOpenDialog.mockResolvedValueOnce({ canceled: false, filePaths: ['/chosen/folder'] });
+		const result = await (getHandler('system:selectFolder')(null) as Promise<unknown>);
+		expect(result).toBe('/chosen/folder');
+	});
 
 	it('SYSTEM_GET_DEFAULT_RECORDINGS_FOLDER returns path with StreamlineRecordings', () => {
🤖 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/streamline/src/ipc/handlers/system.spec.ts` around lines 92 - 96,
Add a unit test for the non-canceled path of the "system:selectFolder" handler:
mock dialogMock.showOpenDialog to resolve to { canceled: false, filePaths:
['/some/path'] }, call getHandler('system:selectFolder')(null) and assert the
result equals the selected folder path (e.g., filePaths[0]); mirror the pattern
used in the "system:selectFile" tests so the success branch in the selectFolder
handler (referenced by getHandler and the "system:selectFolder" handler in
system.spec.ts) is covered.
packages/streamline/renderer/src/audio/mixer-bridge.spec.ts (1)

10-38: ⚡ Quick win

Remove or document the orphaned tanh curve tests.

The buildTanhCurve helper and its test suite are no longer connected to the production code. The actual tanh curve is built inline in mixer-bridge.ts (lines 28–34) and excluded from coverage (/* v8 ignore next 27 */ at line 21). These tests validate a local copy of the algorithm that doesn't match the production code path, adding no coverage and potentially causing confusion.

🧹 Recommended cleanup

Either:

  1. Remove the dead tests (preferred if the production code will remain coverage-ignored):
-function buildTanhCurve(size = 256): Float32Array {
-	const curve = new Float32Array(size);
-	for (let i = 0; i < size; i++) {
-		const x = (i * 2) / (size - 1) - 1;
-		curve[i] = Math.tanh(x * 2);
-	}
-	return curve;
-}
-
-describe('soft-clip tanh curve', () => {
-	it('is monotonically increasing', () => {
-		const curve = buildTanhCurve();
-		for (let i = 1; i < curve.length; i++) {
-			expect(curve[i]).toBeGreaterThanOrEqual(curve[i - 1]);
-		}
-	});
-
-	it('clamps output to [-1, 1]', () => {
-		const curve = buildTanhCurve();
-		expect(curve[0]).toBeGreaterThanOrEqual(-1);
-		expect(curve[curve.length - 1]).toBeLessThanOrEqual(1);
-	});
-
-	it('maps center to 0', () => {
-		const curve = buildTanhCurve();
-		const mid = Math.floor(curve.length / 2);
-		expect(Math.abs(curve[mid])).toBeLessThan(0.01);
-	});
-});
-
 describe('mixer-bridge module', () => {
  1. Or extract and export buildTanhCurve from mixer-bridge.ts so it can be unit tested independently (if you want coverage of the curve logic).
🤖 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/streamline/renderer/src/audio/mixer-bridge.spec.ts` around lines 10
- 38, The test helper buildTanhCurve and its suite are orphaned because the real
tanh curve is generated inline in mixer-bridge.ts (inside the production
function) and excluded from coverage; either delete the test file (remove
buildTanhCurve and the "soft-clip tanh curve" describe block) if you intend to
keep the production curve coverage-ignored, or refactor the production code to
extract and export a reusable function (e.g., export buildTanhCurve or export
getTanhCurve) from mixer-bridge.ts and update the tests to import and assert
that exported function so the test covers the actual implementation.
packages/streamline/renderer/src/modules/queue/QueueSettingsModal.component.spec.ts (1)

77-80: 💤 Low value

Consider adding a length check before array access.

The test accesses checkboxes[0] without verifying the array is non-empty. In this test's context (one deck is rendered), this is safe, but a defensive check would make the test more robust.

🛡️ Optional defensive check
 const checkboxes = container.querySelectorAll(
   'input[type="checkbox"]'
 ) as NodeListOf<HTMLInputElement>;
+expect(checkboxes.length).toBeGreaterThan(0);
 expect(checkboxes[0].checked).toBe(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
`@packages/streamline/renderer/src/modules/queue/QueueSettingsModal.component.spec.ts`
around lines 77 - 80, Add a defensive length check before accessing
checkboxes[0] in the QueueSettingsModal.component.spec.ts test: verify the
NodeList returned by container.querySelectorAll('input[type="checkbox"]') has
length > 0 (or use expect(checkboxes.length).toBeGreaterThan(0)) and then assert
on checkboxes[0].checked; update the test around the checkboxes variable to
guard the array access and keep the existing expectation of true for the first
checkbox.
packages/streamline/renderer/src/modules/local-output/LocalOutput.svelte (1)

88-90: ⚡ Quick win

Remove unnecessary passthrough helper.

The passthrough<T>(value: T): T function is an identity function used only once (line 103). Svelte can bind selectedDeviceId directly without wrapping it.

♻️ Simplify by removing passthrough
-	function passthrough<T>(value: T): T {
-		return value;
-	}
-
 	/* v8 ignore next 2 — instanceId prop is always a non-empty string; ?? '' branch unreachable */

And update line 103:

 		<select
 			id={deviceSelectId}
 			class="rounded border border-primary-700 bg-primary-800 px-2 py-1 text-sm text-primary-100"
-			value={passthrough(selectedDeviceId)}
+			value={selectedDeviceId}
 			onchange={(e) => changeDevice((e.target as HTMLSelectElement).value)}
 		>

Also applies to: 103-103

🤖 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/streamline/renderer/src/modules/local-output/LocalOutput.svelte`
around lines 88 - 90, Remove the unnecessary identity helper
passthrough<T>(value: T): T and its single usage; replace the call that wraps
selectedDeviceId with a direct binding to selectedDeviceId (remove
passthrough(...) wherever used, e.g. the instance referencing selectedDeviceId)
and delete the passthrough function declaration (function passthrough) from the
module to simplify the code.
🤖 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/audio-worklet/vitest.config.mts`:
- Around line 9-11: The config currently sets 100% coverage thresholds while
only including src/tap-processor.ts, but that file is fully wrapped in V8 ignore
comments (the v8 ignore start/stop at the top and bottom of tap-processor.ts),
so coverage will be missing or inconsistent; fix by either removing
'src/tap-processor.ts' from the vitest config include array (or lowering
thresholds in the thresholds config) if the file can't be run in Node, or by
editing tap-processor.ts to narrow the /* v8 ignore start/stop */ blocks to only
the AudioWorklet registration bits so meaningful code remains testable and
coverage can be collected against the current thresholds.

In `@packages/streamline/renderer/src/hotkeys/reset-to-default.spec.ts`:
- Around line 151-157: The test currently asserts teardown removes a keydown
listener with expect.any(Function) which can yield false positives; change it to
capture the actual callback passed to window.addEventListener (by stubbing
window.addEventListener and reading its first call's second argument), then call
initResetToDefaultHotkey(), call teardown(), and assert
window.removeEventListener was called with 'keydown' and that exact captured
callback; reference the initResetToDefaultHotkey function and the
window.addEventListener/removeEventListener stubs to locate the code to update.

In
`@packages/streamline/renderer/src/modules/deck/WaveformDisplay.component.spec.ts`:
- Around line 101-113: The test "does not seek when seekbar width is zero" for
WaveformDisplay should explicitly mock the seek bar's bounding rectangle to
width: 0 before firing the click; locate the seekBar element (queried by
container.querySelector('[role="slider"]') in the test), stub its
getBoundingClientRect to return an object with width: 0 (and any required x/left
values), then fire the click and assert onSeek was not called; ensure the mock
is set up on the seekBar instance used by the test so the width check in the
component uses the mocked zero width.

In `@packages/streamline/src/update-check.ts`:
- Around line 28-30: Remove the "/* v8 ignore next 2 */" annotation that skips
the major/minor version checks in update-check.ts and add unit tests in
update-check.spec.ts (the "resolveUpdate — stable" suite) that exercise those
branches: add cases using resolveUpdate (or the same exported comparator) for
major divergence (e.g., "v1.0.0" vs "v0.0.1") and minor divergence (e.g.,
"v0.1.0" vs "v0.0.1") and assert the expected true/false outcomes so the rMaj
!== cMaj and rMin !== cMin paths are covered.
- Around line 48-49: Add a unit test in the "resolveUpdate — nightly" suite
(update-check.spec.ts) that supplies a ReleaseInfo where name: null and asserts
resolveUpdate behavior for that branch (so the null-name fallback is exercised),
then remove the /* v8 ignore next — null branch for release.name not exercised
in unit tests */ annotation on the line that computes remoteSha (the ternary
using release.name ? parseNightlySha(release.name) : null) so the branch is
covered by tests.

---

Outside diff comments:
In `@packages/streamline/renderer/src/modules/encoders/EncoderModal.svelte`:
- Around line 72-100: The v8 coverage ignore currently only covers the `if (type
=== 'file')` branch; extend the ignore directive so it spans the entire if/else
block that calls `onSave` (including the `composePathTemplate` usage and the
`else` branch that passes `host`, `port`, `mount`, `username`, and
`passwordRef`) so both branches are excluded from coverage; update the `/* v8
ignore next N */` count to cover all lines from the `if (type === 'file')` start
through the end of the `else` block (the lines that invoke `onSave` in both
branches).

---

Duplicate comments:
In `@packages/streamline/renderer/src/hotkeys/reset-to-default.ts`:
- Around line 74-75: The current call to resetToDefault() swallows all errors
with an empty catch; change this so failures are surfaced: replace the empty
catch at the reset-to-default call with a catch that logs the error (e.g.,
console.error or the renderer logging utility) and surfaces user-visible
feedback (e.g., dispatch an IPC/error-notification or call an existing
showErrorNotification function) and/or rethrow so upstream can handle it; update
the catch block around resetToDefault() in reset-to-default.ts to log the error
and trigger a UI notification or IPC event named for reset failures.

In `@packages/streamline/src/ipc/handlers/hotkeys.spec.ts`:
- Around line 41-57: The tests "HOTKEY_SAVE upserts binding" and "HOTKEY_DELETE
deletes by id" call async IPC handlers via getHandler('hotkey:save') and
getHandler('hotkey:delete') but do not await them; update both tests to be async
(add async to the it callback) and await the invocation of the handler (await
getHandler(...)(null, ...)) so any thrown/rejected errors are surfaced and the
subsequent expectations (mockDb.insert/delete and mockDb.run) run after the
handler completes.

---

Nitpick comments:
In `@packages/streamline/renderer/src/audio/mixer-bridge.spec.ts`:
- Around line 10-38: The test helper buildTanhCurve and its suite are orphaned
because the real tanh curve is generated inline in mixer-bridge.ts (inside the
production function) and excluded from coverage; either delete the test file
(remove buildTanhCurve and the "soft-clip tanh curve" describe block) if you
intend to keep the production curve coverage-ignored, or refactor the production
code to extract and export a reusable function (e.g., export buildTanhCurve or
export getTanhCurve) from mixer-bridge.ts and update the tests to import and
assert that exported function so the test covers the actual implementation.

In `@packages/streamline/renderer/src/modules/local-output/LocalOutput.svelte`:
- Around line 88-90: Remove the unnecessary identity helper
passthrough<T>(value: T): T and its single usage; replace the call that wraps
selectedDeviceId with a direct binding to selectedDeviceId (remove
passthrough(...) wherever used, e.g. the instance referencing selectedDeviceId)
and delete the passthrough function declaration (function passthrough) from the
module to simplify the code.

In
`@packages/streamline/renderer/src/modules/queue/QueueSettingsModal.component.spec.ts`:
- Around line 77-80: Add a defensive length check before accessing checkboxes[0]
in the QueueSettingsModal.component.spec.ts test: verify the NodeList returned
by container.querySelectorAll('input[type="checkbox"]') has length > 0 (or use
expect(checkboxes.length).toBeGreaterThan(0)) and then assert on
checkboxes[0].checked; update the test around the checkboxes variable to guard
the array access and keep the existing expectation of true for the first
checkbox.

In `@packages/streamline/src/ipc/handlers/system.spec.ts`:
- Around line 92-96: Add a unit test for the non-canceled path of the
"system:selectFolder" handler: mock dialogMock.showOpenDialog to resolve to {
canceled: false, filePaths: ['/some/path'] }, call
getHandler('system:selectFolder')(null) and assert the result equals the
selected folder path (e.g., filePaths[0]); mirror the pattern used in the
"system:selectFile" tests so the success branch in the selectFolder handler
(referenced by getHandler and the "system:selectFolder" handler in
system.spec.ts) is covered.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b4280f91-445a-4c04-9963-7ae6f4be5de6

📥 Commits

Reviewing files that changed from the base of the PR and between 7cb1947 and bd62031.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (98)
  • .github/workflows/test.yml
  • .idea/vcs.xml
  • package.json
  • packages/audio-worklet/package.json
  • packages/audio-worklet/src/tap-processor.ts
  • packages/audio-worklet/vitest.config.mts
  • packages/shared/package.json
  • packages/shared/vitest.config.mts
  • packages/streamline/package.json
  • packages/streamline/renderer/src/audio/context.spec.ts
  • packages/streamline/renderer/src/audio/context.ts
  • packages/streamline/renderer/src/audio/mixer-bridge.spec.ts
  • packages/streamline/renderer/src/audio/mixer-bridge.ts
  • packages/streamline/renderer/src/audio/port.spec.ts
  • packages/streamline/renderer/src/components/DbMeter.component.spec.ts
  • packages/streamline/renderer/src/components/IconButton.component.spec.ts
  • packages/streamline/renderer/src/components/Modal.component.spec.ts
  • packages/streamline/renderer/src/components/Modal.svelte
  • packages/streamline/renderer/src/components/OnAirIndicator.component.spec.ts
  • packages/streamline/renderer/src/components/StatusBar.component.spec.ts
  • packages/streamline/renderer/src/components/StatusBar.svelte
  • packages/streamline/renderer/src/components/Toast.component.spec.ts
  • packages/streamline/renderer/src/components/ToastContainer.component.spec.ts
  • packages/streamline/renderer/src/components/forms/BitrateField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/ComboField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/ComboField.svelte
  • packages/streamline/renderer/src/components/forms/FilenameField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/FolderField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/FormField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/NumberField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/NumberField.svelte
  • packages/streamline/renderer/src/components/forms/SampleRateField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/SelectField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/TextField.component.spec.ts
  • packages/streamline/renderer/src/drag-drop/song-drag.spec.ts
  • packages/streamline/renderer/src/first-run/setup.spec.ts
  • packages/streamline/renderer/src/hotkeys/binder.spec.ts
  • packages/streamline/renderer/src/hotkeys/reset-to-default.spec.ts
  • packages/streamline/renderer/src/hotkeys/reset-to-default.ts
  • packages/streamline/renderer/src/hotkeys/store.svelte.spec.ts
  • packages/streamline/renderer/src/layout/persistence.svelte.spec.ts
  • packages/streamline/renderer/src/layout/persistence.svelte.ts
  • packages/streamline/renderer/src/layout/store.svelte.spec.ts
  • packages/streamline/renderer/src/modules/context.spec.ts
  • packages/streamline/renderer/src/modules/crossfader/Crossfader.component.spec.ts
  • packages/streamline/renderer/src/modules/crossfader/Crossfader.svelte
  • packages/streamline/renderer/src/modules/deck/Deck.component.spec.ts
  • packages/streamline/renderer/src/modules/deck/Deck.svelte
  • packages/streamline/renderer/src/modules/deck/DeckSettingsModal.component.spec.ts
  • packages/streamline/renderer/src/modules/deck/WaveformDisplay.component.spec.ts
  • packages/streamline/renderer/src/modules/deck/WaveformDisplay.svelte
  • packages/streamline/renderer/src/modules/deck/deck-audio.ts
  • packages/streamline/renderer/src/modules/deck/waveform-worker.spec.ts
  • packages/streamline/renderer/src/modules/deck/waveform-worker.ts
  • packages/streamline/renderer/src/modules/encoders/EncoderModal.component.spec.ts
  • packages/streamline/renderer/src/modules/encoders/EncoderModal.svelte
  • packages/streamline/renderer/src/modules/encoders/Encoders.component.spec.ts
  • packages/streamline/renderer/src/modules/encoders/Encoders.svelte
  • packages/streamline/renderer/src/modules/instance-store.svelte.spec.ts
  • packages/streamline/renderer/src/modules/local-output/LocalOutput.component.spec.ts
  • packages/streamline/renderer/src/modules/local-output/LocalOutput.svelte
  • packages/streamline/renderer/src/modules/microphone/Microphone.component.spec.ts
  • packages/streamline/renderer/src/modules/microphone/Microphone.svelte
  • packages/streamline/renderer/src/modules/queue/Queue.component.spec.ts
  • packages/streamline/renderer/src/modules/queue/Queue.svelte
  • packages/streamline/renderer/src/modules/queue/QueueSettingsModal.component.spec.ts
  • packages/streamline/renderer/src/modules/queue/rotation.ts
  • packages/streamline/renderer/src/modules/register-builtins.spec.ts
  • packages/streamline/renderer/src/windows/WindowManager.component.spec.ts
  • packages/streamline/renderer/src/windows/WindowManager.svelte
  • packages/streamline/renderer/src/windows/WindowWrapper.component.spec.ts
  • packages/streamline/renderer/src/windows/WindowWrapper.svelte
  • packages/streamline/renderer/src/windows/use-interact.spec.ts
  • packages/streamline/src/audio/pcm-receiver.spec.ts
  • packages/streamline/src/audio/pcm-receiver.ts
  • packages/streamline/src/audio/port.spec.ts
  • packages/streamline/src/db/migrate.spec.ts
  • packages/streamline/src/encoders/encoder-process.spec.ts
  • packages/streamline/src/encoders/encoder-process.ts
  • packages/streamline/src/encoders/ffmpeg-path.spec.ts
  • packages/streamline/src/encoders/ffmpeg-path.ts
  • packages/streamline/src/encoders/manager.spec.ts
  • packages/streamline/src/encoders/manager.ts
  • packages/streamline/src/ipc/handlers/encoder.spec.ts
  • packages/streamline/src/ipc/handlers/encoder.ts
  • packages/streamline/src/ipc/handlers/hotkeys.spec.ts
  • packages/streamline/src/ipc/handlers/layout.spec.ts
  • packages/streamline/src/ipc/handlers/library.spec.ts
  • packages/streamline/src/ipc/handlers/secret.spec.ts
  • packages/streamline/src/ipc/handlers/settings.spec.ts
  • packages/streamline/src/ipc/handlers/system.spec.ts
  • packages/streamline/src/ipc/handlers/system.ts
  • packages/streamline/src/library/scan-worker.ts
  • packages/streamline/src/library/scanner.spec.ts
  • packages/streamline/src/library/watcher.spec.ts
  • packages/streamline/src/update-check.spec.ts
  • packages/streamline/src/update-check.ts
  • packages/streamline/vitest.config.mts
💤 Files with no reviewable changes (1)
  • .idea/vcs.xml

Comment thread packages/audio-worklet/vitest.config.mts Outdated
Comment thread packages/streamline/renderer/src/hotkeys/reset-to-default.spec.ts
Comment thread packages/streamline/src/update-check.ts Outdated
Comment thread packages/streamline/src/update-check.ts Outdated
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>

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

♻️ Duplicate comments (1)
packages/audio-worklet/vitest.config.mts (1)

7-10: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Missing coverage thresholds defeats the 100% coverage goal.

The configuration enables V8 coverage but doesn't set any thresholds. Without explicit thresholds, the coverage gate won't enforce the PR's stated goal of "100% branch coverage." Regressions will pass silently.

📊 Proposed fix to add coverage thresholds
 		coverage: {
 			provider: 'v8',
+			thresholds: { lines: 100, functions: 100, branches: 100, statements: 100 },
 			exclude: ['src/index.ts']
 		}
🤖 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/audio-worklet/vitest.config.mts` around lines 7 - 10, The coverage
block currently sets provider and exclude but lacks enforcement thresholds; add
a thresholds object to the coverage config (under the existing coverage object)
that enforces 100% for branches (and optionally lines, functions, statements)
globally and/or per-file. Update the coverage config to include a thresholds key
with values such as global: { branches: 100, lines: 100, functions: 100,
statements: 100 } so the V8 coverage provider actually fails on regressions
while keeping the existing exclude array intact.
🧹 Nitpick comments (6)
packages/streamline/src/audio/pcm-receiver.spec.ts (1)

64-66: ⚡ Quick win

Weak assertion — test does not verify dropout count behavior.

The test only checks the return type, not the actual count value or behavior. Consider extending this test to verify that getDropoutCount() returns 0 initially, or merge this assertion into the existing dropout increment test (lines 51-62) which already calls getDropoutCount().

♻️ Suggested consolidation
 	it('increments dropoutCount when target has no consumer', () => {
 		const before = getDropoutCount();
 		const msg: PcmMessage = {
 			buffer: new ArrayBuffer(100),
 			frames: 10,
 			sampleRate: 48000,
 			channels: 2,
 			encoderTargets: ['no-such-enc']
 		};
 		handlePcmMessage(msg);
 		expect(getDropoutCount()).toBe(before + 1);
+		expect(typeof getDropoutCount()).toBe('number');
 	});
-
-	it('getDropoutCount returns current dropout count', () => {
-		expect(typeof getDropoutCount()).toBe('number');
-	});
🤖 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/streamline/src/audio/pcm-receiver.spec.ts` around lines 64 - 66, The
test for getDropoutCount currently only asserts the return type; update it to
assert actual behavior by verifying getDropoutCount() returns 0 initially (or
move this assertion into the existing dropout increment test that exercises the
increment behavior on lines 51-62) so the suite confirms both the initial state
and that getDropoutCount() increases after invoking the dropout increment logic;
locate and modify the test that references getDropoutCount() (or merge into the
existing increment test) to include an explicit numeric expectation rather than
just typeof.
packages/streamline/renderer/src/modules/encoders/EncoderModal.svelte (1)

72-100: ⚡ Quick win

Overly broad v8 ignore suppresses coverage for testable code.

The ignore comment on Line 72 covers the entire save payload construction (lines 73–100), but neither the file-type branch (lines 73–84) nor the network-type branch (lines 86–100) make IPC calls—they only build config objects and invoke onSave. The accompanying test suite (EncoderModal.component.spec.ts lines 123–147, 149–164, 181–199) successfully exercises these paths by mocking window.streamline.api and clicking the Save button, confirming this code is testable in jsdom.

The password-related IPC calls (lines 58–61 and 66–69) already have targeted ignore comments. Consider removing the ignore directive on Line 72 to restore coverage for the payload-construction logic, which does not depend on Electron-specific APIs.

🤖 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/streamline/renderer/src/modules/encoders/EncoderModal.svelte` around
lines 72 - 100, Remove the broad v8 ignore that currently spans the payload
construction and onSave invocation; this code (composePathTemplate and both
branches that call onSave) is testable in jsdom. Narrow or delete the ignore so
only the actual IPC-dependent safeStorage calls remain ignored (keep the
targeted ignores around the password-related IPC calls), leaving the
composePathTemplate + onSave branches uncovered so tests can measure coverage.
packages/streamline/renderer/src/modules/microphone/Microphone.svelte (1)

205-207: Clarify or update the v8 ignore comment.

Similar to LocalOutput.svelte, the comment states "instanceId prop is always a non-empty string; ?? '' branch unreachable," but the visible code shows simple string concatenation without a ?? operator. If this refers to Svelte compiler-generated code, consider clarifying the comment to prevent confusion.

🤖 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/streamline/renderer/src/modules/microphone/Microphone.svelte` around
lines 205 - 207, Update the confusing v8 ignore comment above the
micDeviceSelectId and micVolSliderId declarations: either remove the "?? ''
branch unreachable" phrase or reword it to explicitly state that instanceId is
guaranteed non-empty by the Svelte component (or that the comment refers to
Svelte-compiled code which inserts a fallback), matching the style used in
LocalOutput.svelte; reference the symbols micDeviceSelectId, micVolSliderId and
the instanceId prop so reviewers can locate and confirm the clarification.
packages/streamline/renderer/src/modules/encoders/Encoders.component.spec.ts (1)

157-172: ⚡ Quick win

Strengthen the partial-order test assertion.

The test verifies that configs missing from the stored order JSON are placed at the end, but only checks the first item's position. To fully validate the behavior, assert that all missing configs (enc-a and enc-c) appear after the ordered config (enc-b).

✅ Recommended assertion improvements
 	const names = getAllByText(/Encoder [ABC]/);
 	expect(names[0].textContent).toBe('Encoder B');
+	// Verify missing configs appear after ordered ones
+	expect(names[1].textContent).toMatch(/Encoder [AC]/);
+	expect(names[2].textContent).toMatch(/Encoder [AC]/);
🤖 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/streamline/renderer/src/modules/encoders/Encoders.component.spec.ts`
around lines 157 - 172, The test only asserts the first item is 'Encoder B' but
should verify the full ordering; after rendering (using makeStreamline and
importing Encoders.svelte) capture the elements via getAllByText (as assigned to
names) and assert their sequence includes 'Encoder B' first and that 'Encoder A'
and 'Encoder C' follow it (e.g. compare names.map(n => n.textContent) against
the expected ordered array or assert indexOf('Encoder B') < indexOf('Encoder A')
and < indexOf('Encoder C')). Ensure you reference the existing variables
configA/configB/configC, makeStreamline, Encoders.svelte import, and the
getAllByText/names usage so the new assertions are added immediately after the
existing expect(names[0].textContent).toBe('Encoder B') check.
packages/streamline/renderer/src/modules/local-output/LocalOutput.svelte (1)

92-94: Clarify or update the v8 ignore comment.

The comment states "instanceId prop is always a non-empty string; ?? '' branch unreachable," but the code shows simple string concatenation without a visible ?? operator. If this refers to Svelte compiler-generated null-guard code, consider clarifying the comment to mention "compiler-generated" to avoid confusion.

🤖 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/streamline/renderer/src/modules/local-output/LocalOutput.svelte`
around lines 92 - 94, Update the V8 ignore comment above the deviceSelectId and
volSliderId declarations to explicitly reference the Svelte/compiler-generated
null-guard rather than implying a visible "??" in this code; e.g. state that
instanceId is always a non-empty string and any compiler-generated nullish
fallback (e.g. "?? ''") is unreachable. Target the comment near the
deviceSelectId and volSliderId lines and mention instanceId and the Svelte
compiler to avoid confusion.
packages/streamline/renderer/src/modules/encoders/Encoders.svelte (1)

145-146: Clarify the necessity of v8 ignore for activeLiveText.

The derived value activeLiveText is a simple string interpolation of activeCount (which is always a number from .length). The v8 ignore comment suggests this guards against a null branch, but activeLiveText is actively used in the template at line 237. If the derived value is reached in tests, this annotation may be unnecessary.

🤖 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/streamline/renderer/src/modules/encoders/Encoders.svelte` around
lines 145 - 146, Remove or replace the ambiguous "v8 ignore next 2" comment
before the activeLiveText declaration: either delete the v8-ignore lines if they
are unnecessary, or replace them with a concise comment that states "activeCount
is always a number (derived from .length) and activeLiveText is always a string
used in the template" so the intent is clear; ensure you touch the const
activeLiveText = $derived(`${activeCount} live`) declaration (and adjust tests
if needed) so the code comment accurately reflects why no null-branch exists.
🤖 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/streamline/renderer/src/modules/crossfader/Crossfader.svelte`:
- Around line 98-102: Remove the `{!true /* v8 ignore ... */}` interpolations
inside the <select> (they render the literal "false" between options) and
instead place the v8-ignore comment where it won't emit template text — e.g.,
move the `/* v8 ignore next 2 — deckLabel/deckId ... */` comment into the
<script> block immediately above the deckLabel/deckId function definitions (or
use a non-rendering mechanism such as a JS comment in the script) so the
coverage directive still applies but no runtime text node is produced in the
template.

In
`@packages/streamline/renderer/src/modules/deck/DeckSettingsModal.component.spec.ts`:
- Around line 18-27: The tests in DeckSettingsModal.component.spec.ts use
container.querySelector('input[type="checkbox"]') with a type assertion, which
can be null and gives poor failure messages; update both tests (the one calling
render(DeckSettingsModal, { sendMetadata: false, ... }) and the similar
sendMetadata: true test) to first assert the element exists (e.g.,
expect(element).not.toBeNull()) or throw a clear error if null before accessing
.checked, locating the element via
container.querySelector('input[type="checkbox"]') so the failure message
explicitly states the checkbox was not found instead of causing a runtime null
access.

---

Duplicate comments:
In `@packages/audio-worklet/vitest.config.mts`:
- Around line 7-10: The coverage block currently sets provider and exclude but
lacks enforcement thresholds; add a thresholds object to the coverage config
(under the existing coverage object) that enforces 100% for branches (and
optionally lines, functions, statements) globally and/or per-file. Update the
coverage config to include a thresholds key with values such as global: {
branches: 100, lines: 100, functions: 100, statements: 100 } so the V8 coverage
provider actually fails on regressions while keeping the existing exclude array
intact.

---

Nitpick comments:
In `@packages/streamline/renderer/src/modules/encoders/EncoderModal.svelte`:
- Around line 72-100: Remove the broad v8 ignore that currently spans the
payload construction and onSave invocation; this code (composePathTemplate and
both branches that call onSave) is testable in jsdom. Narrow or delete the
ignore so only the actual IPC-dependent safeStorage calls remain ignored (keep
the targeted ignores around the password-related IPC calls), leaving the
composePathTemplate + onSave branches uncovered so tests can measure coverage.

In
`@packages/streamline/renderer/src/modules/encoders/Encoders.component.spec.ts`:
- Around line 157-172: The test only asserts the first item is 'Encoder B' but
should verify the full ordering; after rendering (using makeStreamline and
importing Encoders.svelte) capture the elements via getAllByText (as assigned to
names) and assert their sequence includes 'Encoder B' first and that 'Encoder A'
and 'Encoder C' follow it (e.g. compare names.map(n => n.textContent) against
the expected ordered array or assert indexOf('Encoder B') < indexOf('Encoder A')
and < indexOf('Encoder C')). Ensure you reference the existing variables
configA/configB/configC, makeStreamline, Encoders.svelte import, and the
getAllByText/names usage so the new assertions are added immediately after the
existing expect(names[0].textContent).toBe('Encoder B') check.

In `@packages/streamline/renderer/src/modules/encoders/Encoders.svelte`:
- Around line 145-146: Remove or replace the ambiguous "v8 ignore next 2"
comment before the activeLiveText declaration: either delete the v8-ignore lines
if they are unnecessary, or replace them with a concise comment that states
"activeCount is always a number (derived from .length) and activeLiveText is
always a string used in the template" so the intent is clear; ensure you touch
the const activeLiveText = $derived(`${activeCount} live`) declaration (and
adjust tests if needed) so the code comment accurately reflects why no
null-branch exists.

In `@packages/streamline/renderer/src/modules/local-output/LocalOutput.svelte`:
- Around line 92-94: Update the V8 ignore comment above the deviceSelectId and
volSliderId declarations to explicitly reference the Svelte/compiler-generated
null-guard rather than implying a visible "??" in this code; e.g. state that
instanceId is always a non-empty string and any compiler-generated nullish
fallback (e.g. "?? ''") is unreachable. Target the comment near the
deviceSelectId and volSliderId lines and mention instanceId and the Svelte
compiler to avoid confusion.

In `@packages/streamline/renderer/src/modules/microphone/Microphone.svelte`:
- Around line 205-207: Update the confusing v8 ignore comment above the
micDeviceSelectId and micVolSliderId declarations: either remove the "?? ''
branch unreachable" phrase or reword it to explicitly state that instanceId is
guaranteed non-empty by the Svelte component (or that the comment refers to
Svelte-compiled code which inserts a fallback), matching the style used in
LocalOutput.svelte; reference the symbols micDeviceSelectId, micVolSliderId and
the instanceId prop so reviewers can locate and confirm the clarification.

In `@packages/streamline/src/audio/pcm-receiver.spec.ts`:
- Around line 64-66: The test for getDropoutCount currently only asserts the
return type; update it to assert actual behavior by verifying getDropoutCount()
returns 0 initially (or move this assertion into the existing dropout increment
test that exercises the increment behavior on lines 51-62) so the suite confirms
both the initial state and that getDropoutCount() increases after invoking the
dropout increment logic; locate and modify the test that references
getDropoutCount() (or merge into the existing increment test) to include an
explicit numeric expectation rather than just typeof.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5dc548f8-0ae0-4175-bb97-b0c63f34afdd

📥 Commits

Reviewing files that changed from the base of the PR and between 7cb1947 and 3a2bb05.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (98)
  • .github/workflows/test.yml
  • .idea/vcs.xml
  • package.json
  • packages/audio-worklet/package.json
  • packages/audio-worklet/src/tap-processor.ts
  • packages/audio-worklet/vitest.config.mts
  • packages/shared/package.json
  • packages/shared/vitest.config.mts
  • packages/streamline/package.json
  • packages/streamline/renderer/src/audio/context.spec.ts
  • packages/streamline/renderer/src/audio/context.ts
  • packages/streamline/renderer/src/audio/mixer-bridge.spec.ts
  • packages/streamline/renderer/src/audio/mixer-bridge.ts
  • packages/streamline/renderer/src/audio/port.spec.ts
  • packages/streamline/renderer/src/components/DbMeter.component.spec.ts
  • packages/streamline/renderer/src/components/IconButton.component.spec.ts
  • packages/streamline/renderer/src/components/Modal.component.spec.ts
  • packages/streamline/renderer/src/components/Modal.svelte
  • packages/streamline/renderer/src/components/OnAirIndicator.component.spec.ts
  • packages/streamline/renderer/src/components/StatusBar.component.spec.ts
  • packages/streamline/renderer/src/components/StatusBar.svelte
  • packages/streamline/renderer/src/components/Toast.component.spec.ts
  • packages/streamline/renderer/src/components/ToastContainer.component.spec.ts
  • packages/streamline/renderer/src/components/forms/BitrateField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/ComboField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/ComboField.svelte
  • packages/streamline/renderer/src/components/forms/FilenameField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/FolderField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/FormField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/NumberField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/NumberField.svelte
  • packages/streamline/renderer/src/components/forms/SampleRateField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/SelectField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/TextField.component.spec.ts
  • packages/streamline/renderer/src/drag-drop/song-drag.spec.ts
  • packages/streamline/renderer/src/first-run/setup.spec.ts
  • packages/streamline/renderer/src/hotkeys/binder.spec.ts
  • packages/streamline/renderer/src/hotkeys/reset-to-default.spec.ts
  • packages/streamline/renderer/src/hotkeys/reset-to-default.ts
  • packages/streamline/renderer/src/hotkeys/store.svelte.spec.ts
  • packages/streamline/renderer/src/layout/persistence.svelte.spec.ts
  • packages/streamline/renderer/src/layout/persistence.svelte.ts
  • packages/streamline/renderer/src/layout/store.svelte.spec.ts
  • packages/streamline/renderer/src/modules/context.spec.ts
  • packages/streamline/renderer/src/modules/crossfader/Crossfader.component.spec.ts
  • packages/streamline/renderer/src/modules/crossfader/Crossfader.svelte
  • packages/streamline/renderer/src/modules/deck/Deck.component.spec.ts
  • packages/streamline/renderer/src/modules/deck/Deck.svelte
  • packages/streamline/renderer/src/modules/deck/DeckSettingsModal.component.spec.ts
  • packages/streamline/renderer/src/modules/deck/WaveformDisplay.component.spec.ts
  • packages/streamline/renderer/src/modules/deck/WaveformDisplay.svelte
  • packages/streamline/renderer/src/modules/deck/deck-audio.ts
  • packages/streamline/renderer/src/modules/deck/waveform-worker.spec.ts
  • packages/streamline/renderer/src/modules/deck/waveform-worker.ts
  • packages/streamline/renderer/src/modules/encoders/EncoderModal.component.spec.ts
  • packages/streamline/renderer/src/modules/encoders/EncoderModal.svelte
  • packages/streamline/renderer/src/modules/encoders/Encoders.component.spec.ts
  • packages/streamline/renderer/src/modules/encoders/Encoders.svelte
  • packages/streamline/renderer/src/modules/instance-store.svelte.spec.ts
  • packages/streamline/renderer/src/modules/local-output/LocalOutput.component.spec.ts
  • packages/streamline/renderer/src/modules/local-output/LocalOutput.svelte
  • packages/streamline/renderer/src/modules/microphone/Microphone.component.spec.ts
  • packages/streamline/renderer/src/modules/microphone/Microphone.svelte
  • packages/streamline/renderer/src/modules/queue/Queue.component.spec.ts
  • packages/streamline/renderer/src/modules/queue/Queue.svelte
  • packages/streamline/renderer/src/modules/queue/QueueSettingsModal.component.spec.ts
  • packages/streamline/renderer/src/modules/queue/rotation.ts
  • packages/streamline/renderer/src/modules/register-builtins.spec.ts
  • packages/streamline/renderer/src/windows/WindowManager.component.spec.ts
  • packages/streamline/renderer/src/windows/WindowManager.svelte
  • packages/streamline/renderer/src/windows/WindowWrapper.component.spec.ts
  • packages/streamline/renderer/src/windows/WindowWrapper.svelte
  • packages/streamline/renderer/src/windows/use-interact.spec.ts
  • packages/streamline/src/audio/pcm-receiver.spec.ts
  • packages/streamline/src/audio/pcm-receiver.ts
  • packages/streamline/src/audio/port.spec.ts
  • packages/streamline/src/db/migrate.spec.ts
  • packages/streamline/src/encoders/encoder-process.spec.ts
  • packages/streamline/src/encoders/encoder-process.ts
  • packages/streamline/src/encoders/ffmpeg-path.spec.ts
  • packages/streamline/src/encoders/ffmpeg-path.ts
  • packages/streamline/src/encoders/manager.spec.ts
  • packages/streamline/src/encoders/manager.ts
  • packages/streamline/src/ipc/handlers/encoder.spec.ts
  • packages/streamline/src/ipc/handlers/encoder.ts
  • packages/streamline/src/ipc/handlers/hotkeys.spec.ts
  • packages/streamline/src/ipc/handlers/layout.spec.ts
  • packages/streamline/src/ipc/handlers/library.spec.ts
  • packages/streamline/src/ipc/handlers/secret.spec.ts
  • packages/streamline/src/ipc/handlers/settings.spec.ts
  • packages/streamline/src/ipc/handlers/system.spec.ts
  • packages/streamline/src/ipc/handlers/system.ts
  • packages/streamline/src/library/scan-worker.ts
  • packages/streamline/src/library/scanner.spec.ts
  • packages/streamline/src/library/watcher.spec.ts
  • packages/streamline/src/update-check.spec.ts
  • packages/streamline/src/update-check.ts
  • packages/streamline/vitest.config.mts
💤 Files with no reviewable changes (1)
  • .idea/vcs.xml

Comment thread packages/streamline/renderer/src/modules/crossfader/Crossfader.svelte Outdated
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>

@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

♻️ Duplicate comments (1)
packages/streamline/renderer/src/modules/crossfader/Crossfader.svelte (1)

98-102: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove {!true /* v8 ignore ... */} interpolations that render literal "false" text nodes.

The {!true /* v8 ignore start */} and {!true /* v8 ignore stop */} interpolations (lines 98, 102, 114, 118) will render the string "false" as text nodes inside the <select> elements. In Svelte, mustache-style interpolation {expression} outputs text; {!true} evaluates to false and renders as a text node.

The coverage directive for deckLabel/deckId is already correctly placed in the script block (line 28). Remove the {!true ...} interpolations from the template to avoid rendering unwanted text nodes.

Proposed fix
 			<option value="">-- None --</option>
-			<!-- prettier-ignore -->
-			{!true /* v8 ignore start */}
 			{`#each` decks as d (d.id)}<option value={deckId(d.id)} label={deckLabel(d.title, d.id)}
 				></option>{/each}
-			<!-- prettier-ignore -->
-			{!true /* v8 ignore stop */}
 		</select>

Apply the same fix to both select elements (lines 98-102 and 114-118).

Also applies to: 114-118

🤖 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/streamline/renderer/src/modules/crossfader/Crossfader.svelte` around
lines 98 - 102, Remove the stray Svelte interpolations `{!true /* v8 ignore
start */}` and `{!true /* v8 ignore stop */}` that are rendering the literal
"false" text nodes inside the <select> blocks; locate the select blocks
iterating `decks` (the each block using `deckId(d.id)` and `deckLabel(d.title,
d.id)`) and simply delete those `{!true ...}` expressions (they are redundant
because the coverage directives for `deckLabel`/`deckId` live in the script),
applying the same removal to both places where they appear.
🧹 Nitpick comments (3)
packages/streamline/vitest.config.mts (1)

19-19: ⚡ Quick win

Exclude **/*.test.ts from coverage to match existing test/spec pattern parity

vitest.config.mts coverage excludes only **/*.spec.ts while coverage still includes all src/**/*.ts and renderer/src/**/*.ts. There are currently no *.test.ts files under packages/streamline/src or packages/streamline/renderer/src, so the 100% gate won’t break today—but excluding **/*.test.ts prevents future additions from being counted as source.

Proposed config tweak
 		coverage: {
 			provider: 'v8',
 			thresholds: { lines: 100, functions: 100, branches: 100, statements: 100 },
 			include: ['src/**/*.ts', 'renderer/src/**/*.ts', 'renderer/src/**/*.svelte'],
 			exclude: [
 				'**/*.spec.ts',
+				'**/*.test.ts',
 				'src/main.ts',
🤖 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/streamline/vitest.config.mts` at line 19, In vitest.config.mts
update the coverage.exclude list used by Vitest (the array that currently
contains '**/*.spec.ts') to also exclude '**/*.test.ts' so test files with
either .spec.ts or .test.ts aren't treated as source; locate the
coverage.exclude array in the config and add the '**/*.test.ts' pattern
alongside '**/*.spec.ts' to maintain parity with the existing test naming
convention.
.github/workflows/test.yml (1)

10-10: ⚡ Quick win

Add a job timeout to prevent stuck CI runs.

If tests hang, this job can run indefinitely and delay feedback. Add timeout-minutes at the job level.

Suggested patch
 jobs:
   test:
     runs-on: ubuntu-latest
+    timeout-minutes: 20
     steps:
🤖 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 @.github/workflows/test.yml at line 10, The GitHub Actions job currently
lacks a timeout and can hang indefinitely; update the workflow job that contains
"runs-on: ubuntu-latest" to include a job-level timeout by adding a
`timeout-minutes` key (for example `timeout-minutes: 30`) under the same job
mapping so the runner will cancel stuck runs after the configured time; ensure
the new key is placed at the same indentation level as `runs-on` within the job
definition.
packages/streamline/renderer/src/modules/deck/Deck.component.spec.ts (1)

74-94: 💤 Low value

Consider adding cleanup for test globals.

While beforeEach sets up window.streamline before each test, adding an afterEach hook to delete window.streamline and __deckAudio would improve test hygiene and prevent potential cross-suite pollution.

🧹 Optional cleanup pattern
 	beforeEach(() => {
 		stateEvents = [];
 		layoutUpdateInstance.mockReset();
 		instanceUpdate.mockReset();
 		eventBus.on('deck:d1:state', (payload) => {
 			stateEvents.push(payload as DeckStatePayload);
 		});
 		(window as unknown as { streamline: unknown }).streamline = {
 			api: {
 				library: {
 					readAudioFile: vi.fn().mockResolvedValue(new ArrayBuffer(0)),
 					getCoverArt: vi.fn().mockResolvedValue(null),
 					getSongByPath: vi.fn().mockResolvedValue(null),
 					getFileMetadata: vi.fn().mockResolvedValue(null),
 					loadWaveform: vi.fn().mockResolvedValue(null),
 					saveWaveform: vi.fn()
 				}
 			},
 			getPathForFile: vi.fn()
 		};
 	});
+
+	afterEach(() => {
+		delete (window as unknown as Record<string, unknown>).streamline;
+		delete (globalThis as unknown as Record<string, unknown>).__deckAudio;
+	});
🤖 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/streamline/renderer/src/modules/deck/Deck.component.spec.ts` around
lines 74 - 94, Add an afterEach cleanup that removes the test globals set in
beforeEach: delete (window as unknown as { streamline?: unknown }).streamline
and delete (window as unknown as { __deckAudio?: unknown }).__deckAudio (or the
actual global name used) to avoid cross-test pollution; place this afterEach
alongside the existing beforeEach in Deck.component.spec.ts so that the mocked
window.streamline and any __deckAudio created during tests are removed after
each spec.
🤖 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/streamline/src/audio/port.spec.ts`:
- Around line 26-41: Reset the didFinishLoad callback in the test setup so it
can't hold a listener from a prior test: in the beforeEach that sets up
mockWebContents (where you call vi.clearAllMocks(), vi.resetModules(),
channelRef.current = makeChannel(), and create mockWebContents/postMessage),
explicitly reassign didFinishLoad to undefined (or a no-op) before registering
the new mock on handler; this ensures any subsequent test calls to
didFinishLoad!() only invoke the callback installed in that test via
mockWebContents.on.

---

Duplicate comments:
In `@packages/streamline/renderer/src/modules/crossfader/Crossfader.svelte`:
- Around line 98-102: Remove the stray Svelte interpolations `{!true /* v8
ignore start */}` and `{!true /* v8 ignore stop */}` that are rendering the
literal "false" text nodes inside the <select> blocks; locate the select blocks
iterating `decks` (the each block using `deckId(d.id)` and `deckLabel(d.title,
d.id)`) and simply delete those `{!true ...}` expressions (they are redundant
because the coverage directives for `deckLabel`/`deckId` live in the script),
applying the same removal to both places where they appear.

---

Nitpick comments:
In @.github/workflows/test.yml:
- Line 10: The GitHub Actions job currently lacks a timeout and can hang
indefinitely; update the workflow job that contains "runs-on: ubuntu-latest" to
include a job-level timeout by adding a `timeout-minutes` key (for example
`timeout-minutes: 30`) under the same job mapping so the runner will cancel
stuck runs after the configured time; ensure the new key is placed at the same
indentation level as `runs-on` within the job definition.

In `@packages/streamline/renderer/src/modules/deck/Deck.component.spec.ts`:
- Around line 74-94: Add an afterEach cleanup that removes the test globals set
in beforeEach: delete (window as unknown as { streamline?: unknown }).streamline
and delete (window as unknown as { __deckAudio?: unknown }).__deckAudio (or the
actual global name used) to avoid cross-test pollution; place this afterEach
alongside the existing beforeEach in Deck.component.spec.ts so that the mocked
window.streamline and any __deckAudio created during tests are removed after
each spec.

In `@packages/streamline/vitest.config.mts`:
- Line 19: In vitest.config.mts update the coverage.exclude list used by Vitest
(the array that currently contains '**/*.spec.ts') to also exclude
'**/*.test.ts' so test files with either .spec.ts or .test.ts aren't treated as
source; locate the coverage.exclude array in the config and add the
'**/*.test.ts' pattern alongside '**/*.spec.ts' to maintain parity with the
existing test naming convention.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 91905201-cdb7-473a-878f-a76e32ba04a9

📥 Commits

Reviewing files that changed from the base of the PR and between 7cb1947 and 4ca35c5.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (98)
  • .github/workflows/test.yml
  • .idea/vcs.xml
  • package.json
  • packages/audio-worklet/package.json
  • packages/audio-worklet/src/tap-processor.ts
  • packages/audio-worklet/vitest.config.mts
  • packages/shared/package.json
  • packages/shared/vitest.config.mts
  • packages/streamline/package.json
  • packages/streamline/renderer/src/audio/context.spec.ts
  • packages/streamline/renderer/src/audio/context.ts
  • packages/streamline/renderer/src/audio/mixer-bridge.spec.ts
  • packages/streamline/renderer/src/audio/mixer-bridge.ts
  • packages/streamline/renderer/src/audio/port.spec.ts
  • packages/streamline/renderer/src/components/DbMeter.component.spec.ts
  • packages/streamline/renderer/src/components/IconButton.component.spec.ts
  • packages/streamline/renderer/src/components/Modal.component.spec.ts
  • packages/streamline/renderer/src/components/Modal.svelte
  • packages/streamline/renderer/src/components/OnAirIndicator.component.spec.ts
  • packages/streamline/renderer/src/components/StatusBar.component.spec.ts
  • packages/streamline/renderer/src/components/StatusBar.svelte
  • packages/streamline/renderer/src/components/Toast.component.spec.ts
  • packages/streamline/renderer/src/components/ToastContainer.component.spec.ts
  • packages/streamline/renderer/src/components/forms/BitrateField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/ComboField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/ComboField.svelte
  • packages/streamline/renderer/src/components/forms/FilenameField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/FolderField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/FormField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/NumberField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/NumberField.svelte
  • packages/streamline/renderer/src/components/forms/SampleRateField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/SelectField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/TextField.component.spec.ts
  • packages/streamline/renderer/src/drag-drop/song-drag.spec.ts
  • packages/streamline/renderer/src/first-run/setup.spec.ts
  • packages/streamline/renderer/src/hotkeys/binder.spec.ts
  • packages/streamline/renderer/src/hotkeys/reset-to-default.spec.ts
  • packages/streamline/renderer/src/hotkeys/reset-to-default.ts
  • packages/streamline/renderer/src/hotkeys/store.svelte.spec.ts
  • packages/streamline/renderer/src/layout/persistence.svelte.spec.ts
  • packages/streamline/renderer/src/layout/persistence.svelte.ts
  • packages/streamline/renderer/src/layout/store.svelte.spec.ts
  • packages/streamline/renderer/src/modules/context.spec.ts
  • packages/streamline/renderer/src/modules/crossfader/Crossfader.component.spec.ts
  • packages/streamline/renderer/src/modules/crossfader/Crossfader.svelte
  • packages/streamline/renderer/src/modules/deck/Deck.component.spec.ts
  • packages/streamline/renderer/src/modules/deck/Deck.svelte
  • packages/streamline/renderer/src/modules/deck/DeckSettingsModal.component.spec.ts
  • packages/streamline/renderer/src/modules/deck/WaveformDisplay.component.spec.ts
  • packages/streamline/renderer/src/modules/deck/WaveformDisplay.svelte
  • packages/streamline/renderer/src/modules/deck/deck-audio.ts
  • packages/streamline/renderer/src/modules/deck/waveform-worker.spec.ts
  • packages/streamline/renderer/src/modules/deck/waveform-worker.ts
  • packages/streamline/renderer/src/modules/encoders/EncoderModal.component.spec.ts
  • packages/streamline/renderer/src/modules/encoders/EncoderModal.svelte
  • packages/streamline/renderer/src/modules/encoders/Encoders.component.spec.ts
  • packages/streamline/renderer/src/modules/encoders/Encoders.svelte
  • packages/streamline/renderer/src/modules/instance-store.svelte.spec.ts
  • packages/streamline/renderer/src/modules/local-output/LocalOutput.component.spec.ts
  • packages/streamline/renderer/src/modules/local-output/LocalOutput.svelte
  • packages/streamline/renderer/src/modules/microphone/Microphone.component.spec.ts
  • packages/streamline/renderer/src/modules/microphone/Microphone.svelte
  • packages/streamline/renderer/src/modules/queue/Queue.component.spec.ts
  • packages/streamline/renderer/src/modules/queue/Queue.svelte
  • packages/streamline/renderer/src/modules/queue/QueueSettingsModal.component.spec.ts
  • packages/streamline/renderer/src/modules/queue/rotation.ts
  • packages/streamline/renderer/src/modules/register-builtins.spec.ts
  • packages/streamline/renderer/src/windows/WindowManager.component.spec.ts
  • packages/streamline/renderer/src/windows/WindowManager.svelte
  • packages/streamline/renderer/src/windows/WindowWrapper.component.spec.ts
  • packages/streamline/renderer/src/windows/WindowWrapper.svelte
  • packages/streamline/renderer/src/windows/use-interact.spec.ts
  • packages/streamline/src/audio/pcm-receiver.spec.ts
  • packages/streamline/src/audio/pcm-receiver.ts
  • packages/streamline/src/audio/port.spec.ts
  • packages/streamline/src/db/migrate.spec.ts
  • packages/streamline/src/encoders/encoder-process.spec.ts
  • packages/streamline/src/encoders/encoder-process.ts
  • packages/streamline/src/encoders/ffmpeg-path.spec.ts
  • packages/streamline/src/encoders/ffmpeg-path.ts
  • packages/streamline/src/encoders/manager.spec.ts
  • packages/streamline/src/encoders/manager.ts
  • packages/streamline/src/ipc/handlers/encoder.spec.ts
  • packages/streamline/src/ipc/handlers/encoder.ts
  • packages/streamline/src/ipc/handlers/hotkeys.spec.ts
  • packages/streamline/src/ipc/handlers/layout.spec.ts
  • packages/streamline/src/ipc/handlers/library.spec.ts
  • packages/streamline/src/ipc/handlers/secret.spec.ts
  • packages/streamline/src/ipc/handlers/settings.spec.ts
  • packages/streamline/src/ipc/handlers/system.spec.ts
  • packages/streamline/src/ipc/handlers/system.ts
  • packages/streamline/src/library/scan-worker.ts
  • packages/streamline/src/library/scanner.spec.ts
  • packages/streamline/src/library/watcher.spec.ts
  • packages/streamline/src/update-check.spec.ts
  • packages/streamline/src/update-check.ts
  • packages/streamline/vitest.config.mts
💤 Files with no reviewable changes (1)
  • .idea/vcs.xml

Comment thread packages/streamline/src/audio/port.spec.ts Outdated
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>

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

Caution

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

⚠️ Outside diff range comments (1)
packages/streamline/renderer/src/layout/persistence.svelte.ts (1)

7-18: ⚠️ Potential issue | 🟠 Major

Fix off-by-one in v8 ignore next 10 for the $effect block

/* v8 ignore next 10 */ on line 7 ignores lines 8–17 of packages/streamline/renderer/src/layout/persistence.svelte.ts, but the $effect callback closes on line 18 (});), so that line may still count toward coverage. Change to v8 ignore next 11 or wrap the block with v8 ignore start/v8 ignore stop.

🤖 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/streamline/renderer/src/layout/persistence.svelte.ts` around lines 7
- 18, The v8 coverage pragma currently uses "v8 ignore next 10" but the $effect
block spans 11 lines (closing on the line after the ignored range), so update
the pragma to "v8 ignore next 11" or replace it with a paired "v8 ignore start"
/ "v8 ignore stop" around the entire $effect block; locate the block by the
$effect(...) callback that references layoutStore.active,
$state.snapshot(layout), DEBOUNCE_MS, and the
window.streamline.api.layout.save(...) call and adjust the v8 ignore directives
to cover the full callback including its closing "});".
♻️ Duplicate comments (1)
packages/streamline/renderer/src/modules/crossfader/Crossfader.svelte (1)

98-102: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Remove {!true /* v8 ignore ... */} from <select> markup.

Line 98 and Line 114 use boolean interpolation as a coverage marker inside <select>. This is a risky pattern in Svelte templates and can introduce unintended text nodes instead of remaining non-rendering metadata.

Suggested minimal patch
-				<!-- prettier-ignore -->
-				{!true /* v8 ignore start */}
-				{`#each` decks as d (d.id)}<option value={deckId(d.id)} label={deckLabel(d.title, d.id)}
-					></option>{/each}
-				<!-- prettier-ignore -->
-				{!true /* v8 ignore stop */}
+				{`#each` decks as d (d.id)}
+					<option value={deckId(d.id)} label={deckLabel(d.title, d.id)}></option>
+				{/each}
...
-				<!-- prettier-ignore -->
-				{!true /* v8 ignore start */}
-				{`#each` decks as d (d.id)}<option value={deckId(d.id)} label={deckLabel(d.title, d.id)}
-					></option>{/each}
-				<!-- prettier-ignore -->
-				{!true /* v8 ignore stop */}
+				{`#each` decks as d (d.id)}
+					<option value={deckId(d.id)} label={deckLabel(d.title, d.id)}></option>
+				{/each}
In Svelte template markup, what is rendered by `{false}` or `{!true}` inside normal element content (not attributes)?

Also applies to: 114-118

🤖 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/streamline/renderer/src/modules/crossfader/Crossfader.svelte` around
lines 98 - 102, Remove the boolean interpolations `{!true /* v8 ignore start
*/}` and `{!true /* v8 ignore stop */}` inside the <select> markup in
Crossfader.svelte (they can create real text nodes); instead delete those
expressions and, if you need non-rendering markers, replace them with HTML
comments like `<!-- v8 ignore start -->` / `<!-- v8 ignore stop -->` around the
`{`#each` decks as d (d.id)}` block that renders `<option value={deckId(d.id)}
label={deckLabel(d.title, d.id)}></option>`, leaving the decks/ deckId /
deckLabel logic unchanged.
🧹 Nitpick comments (6)
packages/streamline/renderer/src/modules/queue/Queue.component.spec.ts (1)

423-438: ⚡ Quick win

Strengthen the dedup test so it actually proves deduplication.

expect(loadSongCalls).toEqual([]) can still pass when duplicates are not deduplicated, so this test may miss regressions in the dedup path. Add an assertion for the single-deck outcome (e.g., the “Autoplay needs a second linked deck” warning) to make the behavior unambiguous.

♻️ Suggested assertion tightening
 it('deduplicates linked deck IDs when filtering active decks', () => {
   settingsHolder.current = JSON.stringify({ autoplay: true, linkedDeckIds: ['d1', 'd1'] });
   layoutInstancesHolder.current = [{ id: 'd1', moduleId: 'deck' }];
   const loadSongCalls: unknown[] = [];
+  const toasts: Array<{ message: string; type: string }> = [];
   eventBus.on('deck:d1:load-song', () => loadSongCalls.push('d1'));
+  eventBus.on('toast:show', (payload) =>
+    toasts.push(payload as { message: string; type: string })
+  );

   render(Queue, { instanceId: 'q-dedup' });
   queueAddSong()(makeSong());

   eventBus.emit('deck:d1:state', { state: 'unloaded' as DeckState });
   eventBus.emit('deck:d1:ended', undefined);

   expect(loadSongCalls).toEqual([]);
+  expect(toasts).toEqual([{ message: 'Autoplay needs a second linked deck', type: 'warning' }]);
 });
🤖 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/streamline/renderer/src/modules/queue/Queue.component.spec.ts`
around lines 423 - 438, The test currently only checks loadSongCalls and can
pass even if deduplication is broken; update the "deduplicates linked deck IDs
when filtering active decks" test that renders Queue and calls queueAddSong() to
also assert the single-deck outcome so dedup is actually verified: after
emitting deck:d1:state and deck:d1:ended, assert that the component/emitter
emitted the "Autoplay needs a second linked deck" warning (or the equivalent
message/event produced by Queue when only one linked deck remains) and/or that
the internal single-deck flag/state is set; reference the same test setup
(settingsHolder.current, layoutInstancesHolder.current, loadSongCalls, eventBus,
Queue, queueAddSong) and add an expectation for the warning/event in addition to
expect(loadSongCalls).toEqual([]).
packages/streamline/src/audio/port.spec.ts (1)

57-64: ⚡ Quick win

Add assertion before invoking didFinishLoad!() to catch registration failures.

The past review recommended adding expect(didFinishLoad).toBeTypeOf('function') before calling didFinishLoad!() to ensure the listener was actually registered. This assertion is still missing. Without it, if createAudioPort silently fails to register the listener, the test will crash with a confusing error instead of a clear assertion failure.

🛡️ Recommended assertion
 	it('starts port1 and transfers port2 on did-finish-load', async () => {
 		const { createAudioPort } = await import('./port');
 		createAudioPort(mockWindow as unknown as import('electron').BrowserWindow);
 		const channel = channelRef.current as ReturnType<typeof makeChannel>;
+		expect(didFinishLoad).toBeTypeOf('function');
 		didFinishLoad!();
 		expect(channel.port1.start).toHaveBeenCalledOnce();
 		expect(mockWebContents.postMessage).toHaveBeenCalledWith('audio:port', null, expect.any(Array));
 	});

Apply the same pattern at lines 66-75, 77-88, and 90-99.

🤖 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/streamline/src/audio/port.spec.ts` around lines 57 - 64, Add an
explicit assertion that the didFinishLoad listener was registered before
invoking it: in the test case that calls createAudioPort(mockWindow...), assert
expect(typeof didFinishLoad).toBe('function') immediately before calling
didFinishLoad!(); do the same in the other related test blocks (the ones
covering the other ranges) so that createAudioPort and the listener registration
(didFinishLoad) are validated before invocation.
packages/streamline/src/audio/pcm-receiver.ts (1)

7-8: Tighten the PcmMessage guard: buffer null seems impossible; focus on frames===0.

  • PcmMessage is only constructed with buffer: new ArrayBuffer(...) in packages/streamline/src/audio/pcm-receiver.spec.ts and from packages/streamline/renderer/src/audio/port.ts via sendPcm(buffer: ArrayBuffer, frames: number, ...), which always passes a non-null ArrayBuffer into { buffer, frames, ... }. This makes the !msg.buffer portion of packages/streamline/src/audio/pcm-receiver.ts’s guard likely redundant.
  • frames === 0 is the only remaining meaningful edge case (existing tests use positive frames), so either add a unit test for frames=0 or remove the guard if upstream guarantees non-empty chunks.
🤖 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/streamline/src/audio/pcm-receiver.ts` around lines 7 - 8, The guard
in pcm-receiver.ts currently checks "!msg.buffer || msg.frames === 0" but
PcmMessage is always constructed with a non-null ArrayBuffer (see PcmMessage
usage and sendPcm), so tighten the check to only guard the meaningful edge case:
replace the guard with a single check for msg.frames === 0 (e.g., if (msg.frames
=== 0) return;) or alternatively assert/throw if buffer is unexpectedly null
using a non-null assertion for PcmMessage; update or add a unit test in
pcm-receiver.spec.ts for frames === 0 if you remove the buffer null branch.
packages/streamline/src/ipc/handlers/system.spec.ts (1)

70-74: ⚡ Quick win

Tighten log-folder assertion to the exact expected path.

stringContaining('logs') can pass even when the handler uses the wrong base directory.

Proposed assertion improvement
 import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
+import { join } from 'node:path';
@@
 	it('SYSTEM_OPEN_LOG_FOLDER calls shell.openPath with logs dir', async () => {
 		appMock.getPath.mockReturnValue('/fake/userData');
 		await getHandler('system:openLogFolder')(null);
-		expect(shellMock.openPath).toHaveBeenCalledWith(expect.stringContaining('logs'));
+		expect(shellMock.openPath).toHaveBeenCalledWith(join('/fake/userData', 'logs'));
 	});
🤖 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/streamline/src/ipc/handlers/system.spec.ts` around lines 70 - 74,
The test currently uses a loose assertion expect.stringContaining('logs');
update the assertion in the "SYSTEM_OPEN_LOG_FOLDER calls shell.openPath with
logs dir" test to assert the exact expected path produced from appMock.getPath
plus the "logs" folder (use the same path-joining logic as the handler, e.g.,
path.join(appMock.getPath(), 'logs')) and assert shellMock.openPath was called
with that exact normalized path (reference: getHandler('system:openLogFolder'),
appMock.getPath, shellMock.openPath).
packages/streamline/src/ipc/handlers/layout.spec.ts (1)

112-119: ⚡ Quick win

Assert imported instance-to-layout linkage explicitly.

This test can still pass if imported instances keep the old layoutId, which would break parent-child linkage when persisted.

Proposed test hardening
 	it('LAYOUT_IMPORT assigns new UUIDs and returns layout', () => {
 		const original = makeLayout();
 		const json = JSON.stringify(original);
 		const result = getHandler('layout:import')(null, json) as Layout;
 		expect(result.id).not.toBe(original.id);
 		expect(result.instances[0].id).not.toBe(original.instances[0].id);
+		expect(result.instances[0].layoutId).toBe(result.id);
 		expect(result.isActive).toBe(false);
 	});
🤖 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/streamline/src/ipc/handlers/layout.spec.ts` around lines 112 - 119,
The test currently checks that imported layout and instance IDs change but
doesn't assert that each imported instance's layoutId was updated to the new
layout id; update the LAYOUT_IMPORT spec (the getHandler('layout:import') result
handling) to assert that each instance's layoutId equals result.id (and does not
equal original.id), e.g., iterate result.instances and check instance.layoutId
=== result.id (or at least assert result.instances[0].layoutId === result.id) to
ensure parent-child linkage is preserved after import.
packages/streamline/renderer/src/layout/store.svelte.spec.ts (1)

32-32: ⚡ Quick win

Type cast bypasses type safety.

The cast null as unknown as Layout forces null into a Layout-typed parameter, which is a type lie. If layoutStore.set() is designed to accept null (as evidenced by the test on lines 51–53 verifying null handling), the type signature should be Layout | null instead of requiring a cast.

♻️ Update the store type signature

Update layoutStore.set() signature to accept Layout | null:

-set(layout: Layout): void {
+set(layout: Layout | null): void {

Then remove the cast in the test:

-layoutStore.set(null as unknown as Layout);
+layoutStore.set(null);
🤖 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/streamline/renderer/src/layout/store.svelte.spec.ts` at line 32, The
test is using a type-unsafe cast (`null as unknown as Layout`) because the store
API requires a non-null Layout; change the store's type signature so its setter
accepts `Layout | null` (update the exported/store type where `layoutStore.set`
is declared) and then remove the cast in the spec so the test can call
`layoutStore.set(null)` directly; ensure any related types or interfaces
referencing Layout are adjusted to accommodate null where appropriate.
🤖 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/streamline/renderer/src/modules/deck/deck-audio.ts`:
- Line 38: The v8 ignore count at the top of deck-audio.ts is too short and
currently skips lines 39–139 but needs to include getPosition() through line 140
and the blank line 141; update the comment on line 38 from "/* v8 ignore next
101 */" to "/* v8 ignore next 103 */" to cover the extra two lines, then re-run
tests/lint to ensure no overlap with getDuration() (which starts at
getDuration()) and the subsequent "/* v8 ignore next 3 */" at the next block.

In
`@packages/streamline/renderer/src/modules/local-output/LocalOutput.component.spec.ts`:
- Around line 31-41: Tests mutate navigator.mediaDevices without restoring it,
causing cross-suite leakage; capture the original descriptor (via
Object.getOwnPropertyDescriptor or store navigator.mediaDevices) before you
redefine it in the spec (the block that sets Object.defineProperty on navigator,
and any similar mocks at lines referenced), and restore the original descriptor
in an afterEach/afterAll cleanup so the global navigator is returned to its
prior state; update the LocalOutput.component.spec.ts to save the original
value/descriptor, replace the mediaDevices for the test, and restore it in
afterEach to avoid flakiness across suites.

---

Outside diff comments:
In `@packages/streamline/renderer/src/layout/persistence.svelte.ts`:
- Around line 7-18: The v8 coverage pragma currently uses "v8 ignore next 10"
but the $effect block spans 11 lines (closing on the line after the ignored
range), so update the pragma to "v8 ignore next 11" or replace it with a paired
"v8 ignore start" / "v8 ignore stop" around the entire $effect block; locate the
block by the $effect(...) callback that references layoutStore.active,
$state.snapshot(layout), DEBOUNCE_MS, and the
window.streamline.api.layout.save(...) call and adjust the v8 ignore directives
to cover the full callback including its closing "});".

---

Duplicate comments:
In `@packages/streamline/renderer/src/modules/crossfader/Crossfader.svelte`:
- Around line 98-102: Remove the boolean interpolations `{!true /* v8 ignore
start */}` and `{!true /* v8 ignore stop */}` inside the <select> markup in
Crossfader.svelte (they can create real text nodes); instead delete those
expressions and, if you need non-rendering markers, replace them with HTML
comments like `<!-- v8 ignore start -->` / `<!-- v8 ignore stop -->` around the
`{`#each` decks as d (d.id)}` block that renders `<option value={deckId(d.id)}
label={deckLabel(d.title, d.id)}></option>`, leaving the decks/ deckId /
deckLabel logic unchanged.

---

Nitpick comments:
In `@packages/streamline/renderer/src/layout/store.svelte.spec.ts`:
- Line 32: The test is using a type-unsafe cast (`null as unknown as Layout`)
because the store API requires a non-null Layout; change the store's type
signature so its setter accepts `Layout | null` (update the exported/store type
where `layoutStore.set` is declared) and then remove the cast in the spec so the
test can call `layoutStore.set(null)` directly; ensure any related types or
interfaces referencing Layout are adjusted to accommodate null where
appropriate.

In `@packages/streamline/renderer/src/modules/queue/Queue.component.spec.ts`:
- Around line 423-438: The test currently only checks loadSongCalls and can pass
even if deduplication is broken; update the "deduplicates linked deck IDs when
filtering active decks" test that renders Queue and calls queueAddSong() to also
assert the single-deck outcome so dedup is actually verified: after emitting
deck:d1:state and deck:d1:ended, assert that the component/emitter emitted the
"Autoplay needs a second linked deck" warning (or the equivalent message/event
produced by Queue when only one linked deck remains) and/or that the internal
single-deck flag/state is set; reference the same test setup
(settingsHolder.current, layoutInstancesHolder.current, loadSongCalls, eventBus,
Queue, queueAddSong) and add an expectation for the warning/event in addition to
expect(loadSongCalls).toEqual([]).

In `@packages/streamline/src/audio/pcm-receiver.ts`:
- Around line 7-8: The guard in pcm-receiver.ts currently checks "!msg.buffer ||
msg.frames === 0" but PcmMessage is always constructed with a non-null
ArrayBuffer (see PcmMessage usage and sendPcm), so tighten the check to only
guard the meaningful edge case: replace the guard with a single check for
msg.frames === 0 (e.g., if (msg.frames === 0) return;) or alternatively
assert/throw if buffer is unexpectedly null using a non-null assertion for
PcmMessage; update or add a unit test in pcm-receiver.spec.ts for frames === 0
if you remove the buffer null branch.

In `@packages/streamline/src/audio/port.spec.ts`:
- Around line 57-64: Add an explicit assertion that the didFinishLoad listener
was registered before invoking it: in the test case that calls
createAudioPort(mockWindow...), assert expect(typeof
didFinishLoad).toBe('function') immediately before calling didFinishLoad!(); do
the same in the other related test blocks (the ones covering the other ranges)
so that createAudioPort and the listener registration (didFinishLoad) are
validated before invocation.

In `@packages/streamline/src/ipc/handlers/layout.spec.ts`:
- Around line 112-119: The test currently checks that imported layout and
instance IDs change but doesn't assert that each imported instance's layoutId
was updated to the new layout id; update the LAYOUT_IMPORT spec (the
getHandler('layout:import') result handling) to assert that each instance's
layoutId equals result.id (and does not equal original.id), e.g., iterate
result.instances and check instance.layoutId === result.id (or at least assert
result.instances[0].layoutId === result.id) to ensure parent-child linkage is
preserved after import.

In `@packages/streamline/src/ipc/handlers/system.spec.ts`:
- Around line 70-74: The test currently uses a loose assertion
expect.stringContaining('logs'); update the assertion in the
"SYSTEM_OPEN_LOG_FOLDER calls shell.openPath with logs dir" test to assert the
exact expected path produced from appMock.getPath plus the "logs" folder (use
the same path-joining logic as the handler, e.g., path.join(appMock.getPath(),
'logs')) and assert shellMock.openPath was called with that exact normalized
path (reference: getHandler('system:openLogFolder'), appMock.getPath,
shellMock.openPath).
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 186b2815-0ebe-4e17-8981-93005a2cddfe

📥 Commits

Reviewing files that changed from the base of the PR and between 7cb1947 and 2f7fc38.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (98)
  • .github/workflows/test.yml
  • .idea/vcs.xml
  • package.json
  • packages/audio-worklet/package.json
  • packages/audio-worklet/src/tap-processor.ts
  • packages/audio-worklet/vitest.config.mts
  • packages/shared/package.json
  • packages/shared/vitest.config.mts
  • packages/streamline/package.json
  • packages/streamline/renderer/src/audio/context.spec.ts
  • packages/streamline/renderer/src/audio/context.ts
  • packages/streamline/renderer/src/audio/mixer-bridge.spec.ts
  • packages/streamline/renderer/src/audio/mixer-bridge.ts
  • packages/streamline/renderer/src/audio/port.spec.ts
  • packages/streamline/renderer/src/components/DbMeter.component.spec.ts
  • packages/streamline/renderer/src/components/IconButton.component.spec.ts
  • packages/streamline/renderer/src/components/Modal.component.spec.ts
  • packages/streamline/renderer/src/components/Modal.svelte
  • packages/streamline/renderer/src/components/OnAirIndicator.component.spec.ts
  • packages/streamline/renderer/src/components/StatusBar.component.spec.ts
  • packages/streamline/renderer/src/components/StatusBar.svelte
  • packages/streamline/renderer/src/components/Toast.component.spec.ts
  • packages/streamline/renderer/src/components/ToastContainer.component.spec.ts
  • packages/streamline/renderer/src/components/forms/BitrateField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/ComboField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/ComboField.svelte
  • packages/streamline/renderer/src/components/forms/FilenameField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/FolderField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/FormField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/NumberField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/NumberField.svelte
  • packages/streamline/renderer/src/components/forms/SampleRateField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/SelectField.component.spec.ts
  • packages/streamline/renderer/src/components/forms/TextField.component.spec.ts
  • packages/streamline/renderer/src/drag-drop/song-drag.spec.ts
  • packages/streamline/renderer/src/first-run/setup.spec.ts
  • packages/streamline/renderer/src/hotkeys/binder.spec.ts
  • packages/streamline/renderer/src/hotkeys/reset-to-default.spec.ts
  • packages/streamline/renderer/src/hotkeys/reset-to-default.ts
  • packages/streamline/renderer/src/hotkeys/store.svelte.spec.ts
  • packages/streamline/renderer/src/layout/persistence.svelte.spec.ts
  • packages/streamline/renderer/src/layout/persistence.svelte.ts
  • packages/streamline/renderer/src/layout/store.svelte.spec.ts
  • packages/streamline/renderer/src/modules/context.spec.ts
  • packages/streamline/renderer/src/modules/crossfader/Crossfader.component.spec.ts
  • packages/streamline/renderer/src/modules/crossfader/Crossfader.svelte
  • packages/streamline/renderer/src/modules/deck/Deck.component.spec.ts
  • packages/streamline/renderer/src/modules/deck/Deck.svelte
  • packages/streamline/renderer/src/modules/deck/DeckSettingsModal.component.spec.ts
  • packages/streamline/renderer/src/modules/deck/WaveformDisplay.component.spec.ts
  • packages/streamline/renderer/src/modules/deck/WaveformDisplay.svelte
  • packages/streamline/renderer/src/modules/deck/deck-audio.ts
  • packages/streamline/renderer/src/modules/deck/waveform-worker.spec.ts
  • packages/streamline/renderer/src/modules/deck/waveform-worker.ts
  • packages/streamline/renderer/src/modules/encoders/EncoderModal.component.spec.ts
  • packages/streamline/renderer/src/modules/encoders/EncoderModal.svelte
  • packages/streamline/renderer/src/modules/encoders/Encoders.component.spec.ts
  • packages/streamline/renderer/src/modules/encoders/Encoders.svelte
  • packages/streamline/renderer/src/modules/instance-store.svelte.spec.ts
  • packages/streamline/renderer/src/modules/local-output/LocalOutput.component.spec.ts
  • packages/streamline/renderer/src/modules/local-output/LocalOutput.svelte
  • packages/streamline/renderer/src/modules/microphone/Microphone.component.spec.ts
  • packages/streamline/renderer/src/modules/microphone/Microphone.svelte
  • packages/streamline/renderer/src/modules/queue/Queue.component.spec.ts
  • packages/streamline/renderer/src/modules/queue/Queue.svelte
  • packages/streamline/renderer/src/modules/queue/QueueSettingsModal.component.spec.ts
  • packages/streamline/renderer/src/modules/queue/rotation.ts
  • packages/streamline/renderer/src/modules/register-builtins.spec.ts
  • packages/streamline/renderer/src/windows/WindowManager.component.spec.ts
  • packages/streamline/renderer/src/windows/WindowManager.svelte
  • packages/streamline/renderer/src/windows/WindowWrapper.component.spec.ts
  • packages/streamline/renderer/src/windows/WindowWrapper.svelte
  • packages/streamline/renderer/src/windows/use-interact.spec.ts
  • packages/streamline/src/audio/pcm-receiver.spec.ts
  • packages/streamline/src/audio/pcm-receiver.ts
  • packages/streamline/src/audio/port.spec.ts
  • packages/streamline/src/db/migrate.spec.ts
  • packages/streamline/src/encoders/encoder-process.spec.ts
  • packages/streamline/src/encoders/encoder-process.ts
  • packages/streamline/src/encoders/ffmpeg-path.spec.ts
  • packages/streamline/src/encoders/ffmpeg-path.ts
  • packages/streamline/src/encoders/manager.spec.ts
  • packages/streamline/src/encoders/manager.ts
  • packages/streamline/src/ipc/handlers/encoder.spec.ts
  • packages/streamline/src/ipc/handlers/encoder.ts
  • packages/streamline/src/ipc/handlers/hotkeys.spec.ts
  • packages/streamline/src/ipc/handlers/layout.spec.ts
  • packages/streamline/src/ipc/handlers/library.spec.ts
  • packages/streamline/src/ipc/handlers/secret.spec.ts
  • packages/streamline/src/ipc/handlers/settings.spec.ts
  • packages/streamline/src/ipc/handlers/system.spec.ts
  • packages/streamline/src/ipc/handlers/system.ts
  • packages/streamline/src/library/scan-worker.ts
  • packages/streamline/src/library/scanner.spec.ts
  • packages/streamline/src/library/watcher.spec.ts
  • packages/streamline/src/update-check.spec.ts
  • packages/streamline/src/update-check.ts
  • packages/streamline/vitest.config.mts
💤 Files with no reviewable changes (1)
  • .idea/vcs.xml

Comment thread packages/streamline/renderer/src/modules/deck/deck-audio.ts Outdated
…te ignore hacks

Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
Signed-off-by: Robbin "Roboroads" Schepers <robbin@deschepers.nl>
@Roboroads
Roboroads merged commit bc65616 into main Jun 10, 2026
5 of 6 checks passed
@Roboroads
Roboroads deleted the feat/test-coverage branch June 10, 2026 18:11
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