diff --git a/src/cli/CliRecordRunner.test.tsx b/src/cli/CliRecordRunner.test.tsx
index 3e01a0d47..04c0029ea 100644
--- a/src/cli/CliRecordRunner.test.tsx
+++ b/src/cli/CliRecordRunner.test.tsx
@@ -52,6 +52,8 @@ describe("CliRecordRunner", () => {
recorder.startRecordingImmediately.mockClear();
recorder.toggleRecording.mockClear();
window.electronAPI = {
+ getPlatform: vi.fn(() => "win32"),
+ isNativeLinuxCaptureAvailable: vi.fn(async () => ({ success: true, available: false })),
cliGetRequest: vi.fn(async () => request),
cliLog: vi.fn(),
cliDone: vi.fn(async () => undefined),
@@ -81,4 +83,45 @@ describe("CliRecordRunner", () => {
await waitFor(() => expect(recorder.startRecordingImmediately).toHaveBeenCalledTimes(1));
expect(window.electronAPI.cliDone).not.toHaveBeenCalled();
});
+
+ it("keeps source selection for Linux without the native helper", async () => {
+ vi.mocked(window.electronAPI.getPlatform).mockReturnValue("linux");
+ render();
+ await waitFor(() => expect(recorder.startRecordingImmediately).toHaveBeenCalledTimes(1));
+ expect(window.electronAPI.getSources).toHaveBeenCalledTimes(1);
+ expect(window.electronAPI.selectSource).toHaveBeenCalledWith(screenSource, { persist: false });
+ });
+
+ it("reports missing sources in the browser fallback", async () => {
+ vi.mocked(window.electronAPI.getPlatform).mockReturnValue("linux");
+ vi.mocked(window.electronAPI.getSources).mockResolvedValueOnce([]);
+ render();
+ await waitFor(() =>
+ expect(window.electronAPI.cliDone).toHaveBeenCalledWith(
+ expect.objectContaining({
+ success: false,
+ error: expect.stringContaining("Display index 0 not found"),
+ }),
+ ),
+ );
+ expect(recorder.startRecordingImmediately).not.toHaveBeenCalled();
+ });
+
+ it("lets the native Linux portal choose the source without Chromium enumeration", async () => {
+ vi.mocked(window.electronAPI.getPlatform).mockReturnValue("linux");
+ vi.mocked(window.electronAPI.isNativeLinuxCaptureAvailable).mockResolvedValue({
+ success: true,
+ available: true,
+ });
+ vi.mocked(window.electronAPI.getSources).mockResolvedValueOnce([]);
+ render();
+ await waitFor(() => expect(recorder.startRecordingImmediately).toHaveBeenCalledTimes(1));
+ expect(window.electronAPI.getSources).not.toHaveBeenCalled();
+ expect(window.electronAPI.selectSource).not.toHaveBeenCalled();
+ expect(window.electronAPI.cliDone).not.toHaveBeenCalled();
+ expect(window.electronAPI.cliLog).toHaveBeenCalledWith(
+ "info",
+ expect.stringContaining("--window do not apply"),
+ );
+ });
});
diff --git a/src/cli/CliRecordRunner.tsx b/src/cli/CliRecordRunner.tsx
index b7325120b..d585d855d 100644
--- a/src/cli/CliRecordRunner.tsx
+++ b/src/cli/CliRecordRunner.tsx
@@ -9,6 +9,7 @@ import {
} from "@/components/video-editor/projectPersistence";
import { useScreenRecorder } from "@/hooks/useScreenRecorder";
import type { CliRecordRequest } from "@/lib/cliContracts";
+import { portalOwnsSourceSelection } from "@/lib/nativeLinuxRecording";
import { DEFAULT_PROJECT_APPEARANCE } from "@/lib/projectDefaults";
type Phase = "init" | "recording" | "stopping" | "done";
@@ -164,15 +165,24 @@ export function CliRecordRunner() {
}
requestRef.current = request;
- const source = await pickSource(request);
- const selected = await window.electronAPI.selectSource(source, { persist: false });
- if (!selected) {
- throw new Error(
- `Recording source "${source.name}" (${source.id}) is no longer available. ` +
- "Re-list sources with `openscreen sources` and pick one that is currently shared.",
+ // The native Linux helper asks the portal to choose a source. Enumerating
+ // Chromium sources first opens a redundant picker and can return no screens.
+ if (await portalOwnsSourceSelection(window.electronAPI)) {
+ window.electronAPI.cliLog(
+ "info",
+ "Recording source: chosen in the system picker (--display and --window do not apply)",
);
+ } else {
+ const source = await pickSource(request);
+ const selected = await window.electronAPI.selectSource(source, { persist: false });
+ if (!selected) {
+ throw new Error(
+ `Recording source "${source.name}" (${source.id}) is no longer available. ` +
+ "Re-list sources with `openscreen sources` and pick one that is currently shared.",
+ );
+ }
+ window.electronAPI.cliLog("info", `Recording source: ${selected.name}`);
}
- window.electronAPI.cliLog("info", `Recording source: ${selected.name}`);
setMicrophoneEnabled(Boolean(request.mic));
if (request.mic) {