Feat/screenshare app audio sharing#33044
Feat/screenshare app audio sharing#33044JoaoCostaIFG wants to merge 4 commits intoelement-hq:developfrom
Conversation
Integrate @vencord/venmic for application audio capture during screen sharing on Linux via PipeWire. - Add audio picker dialog for selecting apps/system audio to share - Support both Wayland (native portal) and X11 (Electron picker) paths - Inject getDisplayMedia patch for Element Call iframe compatibility - Build venmic as a hak native module dependency - Add Compound Design Tokens for audio picker UI theming
Remove duplicate package declarations for matrix-seshat and @vencord/venmic that were already managed via hakDependencies. The optionalDependencies, pnpm overrides, and root dependencies entries were unnecessary duplicates causing build complexity. Also re-add New Vector Ltd. copyright to displayMediaCallback.ts. Signed-off-by: JoaoCostaIFG <me@joaocosta.dev>
…and iframe injection - Extract shared patch logic into venmic-patch.ts - preload.cts now imports and calls applyVenmicPatch() - venmic-inject.ts now imports VENMIC_PATCH_SCRIPT constant - Add cleanup handlers (stopVenmic) in iframe injection when window.electron is available - Document cross-origin iframe limitation where venmic cannot auto-stop
t3chguy
left a comment
There was a problem hiding this comment.
Do you think it is feasible to add a playwright screenshot test for the additional UI, maybe using https://github.com/spaceagetv/electron-playwright-helpers to just fire the IPC to open it
| /> | ||
| <link rel="stylesheet" href="compound/compound-design-tokens.css" /> | ||
| <title id="page-title">Share Audio</title> | ||
| <style> |
There was a problem hiding this comment.
Styles should be in a separate CSS file referenced in
| <button type="button" class="btn-primary" id="share" disabled></button> | ||
| </div> | ||
|
|
||
| <script> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
| <meta | ||
| http-equiv="Content-Security-Policy" | ||
| content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';" |
There was a problem hiding this comment.
We should harden the CSP by extracting the CSS & JS to their own files
| node["application.name"] || | ||
| node["application.process.binary"] || | ||
| node["node.name"] || | ||
| "Unknown Application" |
| content.innerHTML = | ||
| '<div class="error">' + | ||
| escapeHtml( | ||
| (strings.errorLoadPrefix || "Failed to load audio sources:") + " " + err.message, |
| window.audioPickerAPI | ||
| .getConfig() | ||
| .then(function (config) { | ||
| // Apply Compound theme class immediately for correct token values | ||
| document.documentElement.classList.add("cpd-theme-" + (config.theme || "dark")); | ||
| return window.audioPickerAPI.getStrings(); | ||
| }) | ||
| .then(function (s) { | ||
| strings = s; | ||
|
|
||
| // Populate static UI elements with translated strings | ||
| document.getElementById("page-title").textContent = strings.title; | ||
| document.getElementById("heading").textContent = strings.title; | ||
| document.getElementById("subtitle").textContent = strings.subtitle; | ||
| document.getElementById("loading-text").textContent = strings.loading; | ||
| cancelBtn.textContent = strings.cancel; | ||
| shareBtn.textContent = strings.share; | ||
|
|
||
| return window.audioPickerAPI.getSources(); | ||
| }) | ||
| .then(function (sources) { | ||
| if (sources.ok) { | ||
| renderOptions(sources); | ||
| } else { | ||
| content.className = ""; | ||
| content.innerHTML = | ||
| '<div class="error">' + | ||
| escapeHtml(strings.errorLoadPrefix) + | ||
| " " + | ||
| (sources.isGlibcOutdated | ||
| ? escapeHtml(strings.errorGlibc) | ||
| : escapeHtml(strings.errorPipewire)) + | ||
| "</div>"; | ||
| shareBtn.disabled = false; | ||
| } | ||
| }) | ||
| .catch(function (err) { | ||
| content.className = ""; | ||
| content.innerHTML = | ||
| '<div class="error">' + | ||
| escapeHtml( | ||
| (strings.errorLoadPrefix || "Failed to load audio sources:") + " " + err.message, |
| } | ||
| } | ||
| } else { | ||
| console.warn("Compound Design Tokens CSS not found at " + CPD_CSS_SRC + ", skipping copy"); |
There was a problem hiding this comment.
shouldn't this be an error which fails the build?
| function getAudioPickerHtmlPath(): string { | ||
| const candidates = [ | ||
| // Development: src -> build/audio-picker.html | ||
| join(__dirname, "..", "build", "audio-picker.html"), | ||
| // Packaged app: resources/build/audio-picker.html | ||
| join(app.getAppPath(), "..", "build", "audio-picker.html"), | ||
| // Packaged app alternative: app.asar.unpacked or similar | ||
| join(app.getAppPath(), "build", "audio-picker.html"), | ||
| ]; | ||
|
|
||
| for (const candidate of candidates) { | ||
| if (existsSync(candidate)) { | ||
| return candidate; | ||
| } | ||
| } | ||
|
|
||
| // Fallback to first candidate (will error if not found) | ||
| console.warn("audio-picker: could not find audio-picker.html, tried:", candidates); | ||
| return candidates[0]; | ||
| } |
|
With the provided PR description it is hard to follow this PR without being familiar with the venmic approach. To move this forward efficiently, the PR description (or maybe even as part of the source) needs a detailed architectural description on this approach. |
|
Is it solving the same issue this tries to solve? https://github.com/alectrocute/electron-audio-loopback |
Checklist
public/exportedsymbols have accurate TSDoc documentation.Description
Will help implement the feature requested in this issue: element-hq/element-call #3657.
Problems
pnpm run hakwill fail on non-linux systems:Next steps (outside the scope of this PR)
Screenshots
Audio sharing app picker: