diff --git a/.github/workflows/dev-release.yml b/.github/workflows/dev-release.yml
index 96bb469..4f68997 100644
--- a/.github/workflows/dev-release.yml
+++ b/.github/workflows/dev-release.yml
@@ -42,11 +42,19 @@ jobs:
python3 -m ensurepip --upgrade
python3 -m pip install --upgrade pip setuptools
+ - name: Setup LXD for Snapcraft
+ if: matrix.os == 'ubuntu-latest'
+ uses: canonical/setup-lxd@v0.1.2
+ with:
+ channel: latest/stable
+
- name: Install Linux build deps
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update
- sudo apt-get install -y libx11-dev libxext-dev libxtst-dev libxkbcommon-x11-dev libxkbfile-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxt-dev
+ sudo apt-get install -y libx11-dev libxext-dev libxtst-dev libxkbcommon-x11-dev libxkbfile-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxt-dev libpipewire-0.3-dev libpulse-dev
+ sudo snap install core22
+ sudo snap install snapcraft --classic --channel=7.x/stable
- name: Install dependencies
run: npm install
@@ -54,6 +62,11 @@ jobs:
- name: Build Electron App
shell: bash
run: |
+ if [ "${{ matrix.name }}" = "nsis" ]; then
+ export WIN_CSC_LINK="${{ secrets.WIN_CERTS_BASE64 }}"
+ export WIN_CSC_KEY_PASSWORD="${{ secrets.WIN_CERTS_PASSWORD }}"
+ fi
+
if [ "${{ matrix.name }}" = "linux" ]; then
npm run build:linux
elif [ "${{ matrix.name }}" = "nsis" ]; then
@@ -89,52 +102,52 @@ jobs:
}
shell: powershell
- # ✅ Windows için eSigner ile imzala - NSIS Installer
- - name: Sign NSIS Installer
- if: matrix.os == 'windows-latest' && matrix.name == 'nsis'
- uses: sslcom/esigner-codesign@develop
- with:
- command: sign
- username: ${{ secrets.ESIGNER_USERNAME }}
- password: ${{ secrets.ESIGNER_PASSWORD }}
- credential_id: ${{ secrets.ESIGNER_CREDENTIAL_ID }}
- totp_secret: ${{ secrets.ESIGNER_TOTP }}
- file_path: dist/Topluyo-Setup-${{ steps.package-version.outputs.version }}.exe
- malware_block: false
- override: true
- clean_logs: false
-
- - name: Update latest.yml after signing (NSIS)
- if: matrix.os == 'windows-latest' && matrix.name == 'nsis'
- run: |
- $exeFile = "dist/Topluyo-Setup-${{ steps.package-version.outputs.version }}.exe"
- $version = "${{ steps.package-version.outputs.version }}"
-
- # İmzalanmış dosyanın yeni SHA512 değerini hesapla
- $fileHash = Get-FileHash -Path $exeFile -Algorithm SHA512
- # Hex string'i byte array'e çevir ve sonra Base64'e çevir
- $hexBytes = [byte[]] -split ($fileHash.Hash -replace '..', '0x$& ')
- $sha512 = [System.Convert]::ToBase64String($hexBytes)
-
- # Dosya boyutunu al
- $fileSize = (Get-Item $exeFile).Length
-
- # Yeni latest.yml içeriğini oluştur
- $currentDate = (Get-Date).ToString("yyyy-MM-ddTHH:mm:ss.fffZ")
- $latestYml = "version: $version`n" +
- "files:`n" +
- " - url: Topluyo-Setup-$version.exe`n" +
- " sha512: $sha512`n" +
- " size: $fileSize`n" +
- "path: Topluyo-Setup-$version.exe`n" +
- "sha512: $sha512`n" +
- "releaseDate: '$currentDate'"
-
- # latest.yml dosyasını güncelle (BOM olmadan UTF8)
- [System.IO.File]::WriteAllText("dist/latest.yml", $latestYml, [System.Text.UTF8Encoding]::new($false))
- Write-Host "latest.yml updated with new SHA512: $sha512"
- Write-Host "File size: $fileSize bytes"
- shell: powershell
+ # # ✅ Windows için eSigner ile imzala - NSIS Installer
+ # - name: Sign NSIS Installer
+ # if: matrix.os == 'windows-latest' && matrix.name == 'nsis'
+ # uses: sslcom/esigner-codesign@develop
+ # with:
+ # command: sign
+ # username: ${{ secrets.ESIGNER_USERNAME }}
+ # password: ${{ secrets.ESIGNER_PASSWORD }}
+ # credential_id: ${{ secrets.ESIGNER_CREDENTIAL_ID }}
+ # totp_secret: ${{ secrets.ESIGNER_TOTP }}
+ # file_path: dist/Topluyo-Setup-${{ steps.package-version.outputs.version }}.exe
+ # malware_block: false
+ # override: true
+ # clean_logs: false
+
+ # - name: Update latest.yml after signing (NSIS)
+ # if: matrix.os == 'windows-latest' && matrix.name == 'nsis'
+ # run: |
+ # $exeFile = "dist/Topluyo-Setup-${{ steps.package-version.outputs.version }}.exe"
+ # $version = "${{ steps.package-version.outputs.version }}"
+ #
+ # # İmzalanmış dosyanın yeni SHA512 değerini hesapla
+ # $fileHash = Get-FileHash -Path $exeFile -Algorithm SHA512
+ # # Hex string'i byte array'e çevir ve sonra Base64'e çevir
+ # $hexBytes = [byte[]] -split ($fileHash.Hash -replace '..', '0x$& ')
+ # $sha512 = [System.Convert]::ToBase64String($hexBytes)
+ #
+ # # Dosya boyutunu al
+ # $fileSize = (Get-Item $exeFile).Length
+ #
+ # # Yeni latest.yml içeriğini oluştur
+ # $currentDate = (Get-Date).ToString("yyyy-MM-ddTHH:mm:ss.fffZ")
+ # $latestYml = "version: $version`n" +
+ # "files:`n" +
+ # " - url: Topluyo-Setup-$version.exe`n" +
+ # " sha512: $sha512`n" +
+ # " size: $fileSize`n" +
+ # "path: Topluyo-Setup-$version.exe`n" +
+ # "sha512: $sha512`n" +
+ # "releaseDate: '$currentDate'"
+ #
+ # # latest.yml dosyasını güncelle (BOM olmadan UTF8)
+ # [System.IO.File]::WriteAllText("dist/latest.yml", $latestYml, [System.Text.UTF8Encoding]::new($false))
+ # Write-Host "latest.yml updated with new SHA512: $sha512"
+ # Write-Host "File size: $fileSize bytes"
+ # shell: powershell
- name: Upload Build Artifacts
uses: actions/upload-artifact@v4
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 91da778..3e2ee36 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -44,11 +44,19 @@ jobs:
python3 -m ensurepip --upgrade
python3 -m pip install --upgrade pip setuptools
+ - name: Setup LXD for Snapcraft
+ if: matrix.os == 'ubuntu-latest'
+ uses: canonical/setup-lxd@v0.1.2
+ with:
+ channel: latest/stable
+
- name: Install Linux build deps
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update
- sudo apt-get install -y libx11-dev libxext-dev libxtst-dev libxkbcommon-x11-dev libxkbfile-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxt-dev
+ sudo apt-get install -y libx11-dev libxext-dev libxtst-dev libxkbcommon-x11-dev libxkbfile-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libxt-dev libpipewire-0.3-dev libpulse-dev
+ sudo snap install core22
+ sudo snap install snapcraft --classic --channel=7.x/stable
- name: Install dependencies
run: npm install
@@ -56,6 +64,11 @@ jobs:
- name: Build Electron App
shell: bash
run: |
+ if [ "${{ matrix.name }}" = "nsis" ]; then
+ export WIN_CSC_LINK="${{ secrets.WIN_CERTS_BASE64 }}"
+ export WIN_CSC_KEY_PASSWORD="${{ secrets.WIN_CERTS_PASSWORD }}"
+ fi
+
if [ "${{ matrix.name }}" = "linux" ]; then
npm run build:linux
elif [ "${{ matrix.name }}" = "nsis" ]; then
@@ -91,52 +104,52 @@ jobs:
}
shell: powershell
- # ✅ Windows için eSigner ile imzala - NSIS Installer
- - name: Sign NSIS Installer
- if: matrix.os == 'windows-latest' && matrix.name == 'nsis'
- uses: sslcom/esigner-codesign@develop
- with:
- command: sign
- username: ${{ secrets.ESIGNER_USERNAME }}
- password: ${{ secrets.ESIGNER_PASSWORD }}
- credential_id: ${{ secrets.ESIGNER_CREDENTIAL_ID }}
- totp_secret: ${{ secrets.ESIGNER_TOTP }}
- file_path: dist/Topluyo-Setup-${{ steps.package-version.outputs.version }}.exe
- malware_block: false
- override: true
- clean_logs: false
-
- - name: Update latest.yml after signing (NSIS)
- if: matrix.os == 'windows-latest' && matrix.name == 'nsis'
- run: |
- $exeFile = "dist/Topluyo-Setup-${{ steps.package-version.outputs.version }}.exe"
- $version = "${{ steps.package-version.outputs.version }}"
-
- # İmzalanmış dosyanın yeni SHA512 değerini hesapla
- $fileHash = Get-FileHash -Path $exeFile -Algorithm SHA512
- # Hex string'i byte array'e çevir ve sonra Base64'e çevir
- $hexBytes = [byte[]] -split ($fileHash.Hash -replace '..', '0x$& ')
- $sha512 = [System.Convert]::ToBase64String($hexBytes)
-
- # Dosya boyutunu al
- $fileSize = (Get-Item $exeFile).Length
-
- # Yeni latest.yml içeriğini oluştur
- $currentDate = (Get-Date).ToString("yyyy-MM-ddTHH:mm:ss.fffZ")
- $latestYml = "version: $version`n" +
- "files:`n" +
- " - url: Topluyo-Setup-$version.exe`n" +
- " sha512: $sha512`n" +
- " size: $fileSize`n" +
- "path: Topluyo-Setup-$version.exe`n" +
- "sha512: $sha512`n" +
- "releaseDate: '$currentDate'"
-
- # latest.yml dosyasını güncelle (BOM olmadan UTF8)
- [System.IO.File]::WriteAllText("dist/latest.yml", $latestYml, [System.Text.UTF8Encoding]::new($false))
- Write-Host "latest.yml updated with new SHA512: $sha512"
- Write-Host "File size: $fileSize bytes"
- shell: powershell
+ # # ✅ Windows için eSigner ile imzala - NSIS Installer
+ # - name: Sign NSIS Installer
+ # if: matrix.os == 'windows-latest' && matrix.name == 'nsis'
+ # uses: sslcom/esigner-codesign@develop
+ # with:
+ # command: sign
+ # username: ${{ secrets.ESIGNER_USERNAME }}
+ # password: ${{ secrets.ESIGNER_PASSWORD }}
+ # credential_id: ${{ secrets.ESIGNER_CREDENTIAL_ID }}
+ # totp_secret: ${{ secrets.ESIGNER_TOTP }}
+ # file_path: dist/Topluyo-Setup-${{ steps.package-version.outputs.version }}.exe
+ # malware_block: false
+ # override: true
+ # clean_logs: false
+
+ # - name: Update latest.yml after signing (NSIS)
+ # if: matrix.os == 'windows-latest' && matrix.name == 'nsis'
+ # run: |
+ # $exeFile = "dist/Topluyo-Setup-${{ steps.package-version.outputs.version }}.exe"
+ # $version = "${{ steps.package-version.outputs.version }}"
+ #
+ # # İmzalanmış dosyanın yeni SHA512 değerini hesapla
+ # $fileHash = Get-FileHash -Path $exeFile -Algorithm SHA512
+ # # Hex string'i byte array'e çevir ve sonra Base64'e çevir
+ # $hexBytes = [byte[]] -split ($fileHash.Hash -replace '..', '0x$& ')
+ # $sha512 = [System.Convert]::ToBase64String($hexBytes)
+ #
+ # # Dosya boyutunu al
+ # $fileSize = (Get-Item $exeFile).Length
+ #
+ # # Yeni latest.yml içeriğini oluştur
+ # $currentDate = (Get-Date).ToString("yyyy-MM-ddTHH:mm:ss.fffZ")
+ # $latestYml = "version: $version`n" +
+ # "files:`n" +
+ # " - url: Topluyo-Setup-$version.exe`n" +
+ # " sha512: $sha512`n" +
+ # " size: $fileSize`n" +
+ # "path: Topluyo-Setup-$version.exe`n" +
+ # "sha512: $sha512`n" +
+ # "releaseDate: '$currentDate'"
+ #
+ # # latest.yml dosyasını güncelle (BOM olmadan UTF8)
+ # [System.IO.File]::WriteAllText("dist/latest.yml", $latestYml, [System.Text.UTF8Encoding]::new($false))
+ # Write-Host "latest.yml updated with new SHA512: $sha512"
+ # Write-Host "File size: $fileSize bytes"
+ # shell: powershell
- name: Delete builder-debug.yml cross-platform
shell: pwsh
diff --git a/ScreenShare.html b/ScreenShare.html
index d4b6cb0..6f3664c 100644
--- a/ScreenShare.html
+++ b/ScreenShare.html
@@ -299,10 +299,13 @@
Ekranını Paylaş
@@ -313,6 +316,23 @@ Ekranını Paylaş
async function initialize() {
allSources = await window.stream.getSources();
renderSources();
+
+ try {
+ const status = await window.stream.isNativeAudioAvailable();
+ const warningEl = document.getElementById("audio-warning");
+
+ if (!status.available) {
+ warningEl.style.display = "block";
+ warningEl.style.color = "#ff4d4f";
+ warningEl.innerText = "⚠️ Sistem uyumlu değil, ses yankı yapabilir.";
+ } else if (status.backend) {
+ warningEl.style.display = "block";
+ warningEl.style.color = "#52c41a";
+ warningEl.innerText = `✅ Ses İzolasyonu Aktif (${status.backend})`;
+ }
+ } catch (err) {
+ console.error("isNativeAudioAvailable failed:", err);
+ }
}
function renderSources() {
diff --git a/Windows.js b/Windows.js
index 13c0ace..2ad1caa 100644
--- a/Windows.js
+++ b/Windows.js
@@ -30,8 +30,8 @@ function createMainWindow(windowstate, url) {
icon: path.join(__dirname, "topluyo.png"),
webPreferences: {
devTools: process.env.NODE_ENV === "development",
- contextIsolation: false,
- nodeIntegration: true,
+ contextIsolation: true,
+ nodeIntegration: false,
nodeIntegrationInSubFrames: true,
preload: path.join(__dirname, "preloads/main.js"),
},
diff --git a/electron-builder.config.js b/electron-builder.config.js
index 89e8c1f..dc0b05c 100644
--- a/electron-builder.config.js
+++ b/electron-builder.config.js
@@ -76,7 +76,9 @@ const config = {
"maintainer": "Hasan Delibaş "
},
"snap": {
+ "base": "core22",
"confinement": "classic",
+ "useTemplateApp": false,
"publish": false
},
"mac": {
diff --git a/main.js b/main.js
index d3c28f1..6aeda52 100644
--- a/main.js
+++ b/main.js
@@ -144,6 +144,10 @@ if (!gotLock) {
}
app.whenReady().then(() => {
+ // Cloudflare vb. bot korumalarını aşmak için User-Agent'tan Electron ve uygulama adını temizliyoruz
+ const { session } = require("electron");
+ app.userAgentFallback = session.defaultSession.getUserAgent().replace(/Electron\/\S+\s?/, "").replace(/topluyo\/\S+\s?/, "");
+
//* Load the previous state with fallback to defaults
const mainWindowState = windowStateKeeper({
defaultWidth: 800,
@@ -656,6 +660,13 @@ ipcMain.on("close", () => {
if (win && !win.isDestroyed()) win.close();
});
+ipcMain.handle("isNativeAudioAvailable", () => {
+ if (!captureModule) return { available: false, backend: null };
+ const available = captureModule.isAvailable ? captureModule.isAvailable() : false;
+ const backend = captureModule.getLinuxAudioBackend ? captureModule.getLinuxAudioBackend() : null;
+ return { available, backend };
+});
+
ipcMain.handle("start-native-audio", (event) => {
const sourceId = global.lastSelectedSource || "";
console.log("start-native-audio invoked. Selected source:", sourceId);
diff --git a/package-lock.json b/package-lock.json
index bb840fd..c0674c7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,16 +1,16 @@
{
"name": "topluyo",
- "version": "2026.7.623",
+ "version": "2026.7.1018",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "topluyo",
- "version": "2026.7.623",
+ "version": "2026.7.1018",
"license": "MIT",
"dependencies": {
"electron-log": "^5.4.1",
- "electron-native-screenshare": "1.1.0",
+ "electron-native-screenshare": "1.2.0",
"electron-updater": "^6.3.9",
"electron-window-state": "^5.0.3",
"uiohook-napi": "^1.5.5"
@@ -21,9 +21,9 @@
}
},
"node_modules/@electron-internal/extract-zip": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@electron-internal/extract-zip/-/extract-zip-1.0.4.tgz",
- "integrity": "sha512-Zr1Vs7E9tpCNhZHDAbFVXc2gEVCG9RqPDjrno5+bdgB6LRAuvgyMHJut4NCVyYwtAieapMzc3fiQ3CSTi75ARg==",
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@electron-internal/extract-zip/-/extract-zip-1.0.5.tgz",
+ "integrity": "sha512-+bqFCP98pLI0Tt0XQo1TmlXtwjWchISndDOxCkEcIuUgXWpBnLyRI+2DU+mesvnMMX6L1XDqYNA0lXNDHd/yiA==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
@@ -56,9 +56,9 @@
"license": "MIT"
},
"node_modules/@electron/asar/node_modules/brace-expansion": {
- "version": "1.1.15",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
- "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
+ "version": "1.1.18",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
+ "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -124,9 +124,9 @@
}
},
"node_modules/@electron/get": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/@electron/get/-/get-5.0.0.tgz",
- "integrity": "sha512-pjoBpru1KdEtcExBnuHAP1cAc/5faoedw0hzJkL3o4/IJp7HNF1+fbrdxT3gMYRX2oJfvnA/WXeCTVQpYYxyJA==",
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@electron/get/-/get-5.1.0.tgz",
+ "integrity": "sha512-3kSBtG8ObcTVfXanm5vVJ6UnBLEVmVsRk1M+vGqCuMBV+XLCbJYuWQful+yIy0GQDsSlK0kHEriEHn7SPk4EnA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -224,9 +224,9 @@
}
},
"node_modules/@electron/rebuild": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-4.1.0.tgz",
- "integrity": "sha512-GFky+1JeO8fpSqtZCh+2wFRN/MVbAhm7tXI2M7BA1Os79OR8LEoxRtAbRPyxvmKWhEMF/p10rNJkkgP1klI13g==",
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-4.2.0.tgz",
+ "integrity": "sha512-RKL/O+jGoXJMxrx/5771y1n0xTKmFuOYGO3gMmwypBM6rsH0kou0mswwdXA2JrhIkE4xyC7v9vGk0n6NPzgOxQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -271,9 +271,9 @@
"license": "MIT"
},
"node_modules/@electron/universal/node_modules/brace-expansion": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz",
- "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==",
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz",
+ "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -281,9 +281,9 @@
}
},
"node_modules/@electron/universal/node_modules/fs-extra": {
- "version": "11.3.6",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.6.tgz",
- "integrity": "sha512-w8ZNZr2mKIc7qeNaQ9AVPT1+iFaI+Avd4xudVOvdDJ8VytREi1Ft5Ih7hd9jjehod8vAM5GMsfQ/TpPf4EyoEA==",
+ "version": "11.4.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.4.0.tgz",
+ "integrity": "sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -324,6 +324,60 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/@electron/windows-sign": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@electron/windows-sign/-/windows-sign-1.2.2.tgz",
+ "integrity": "sha512-dfZeox66AvdPtb2lD8OsIIQh12Tp0GNCRUDfBHIKGpbmopZto2/A8nSpYYLoedPIHpqkeblZ/k8OV0Gy7PYuyQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "cross-dirname": "^0.1.0",
+ "debug": "^4.3.4",
+ "fs-extra": "^11.1.1",
+ "minimist": "^1.2.8",
+ "postject": "^1.0.0-alpha.6"
+ },
+ "bin": {
+ "electron-windows-sign": "bin/electron-windows-sign.js"
+ },
+ "engines": {
+ "node": ">=14.14"
+ }
+ },
+ "node_modules/@electron/windows-sign/node_modules/fs-extra": {
+ "version": "11.4.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.4.0.tgz",
+ "integrity": "sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=14.14"
+ }
+ },
+ "node_modules/@electron/windows-sign/node_modules/jsonfile": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
+ "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
"node_modules/@isaacs/fs-minipass": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
@@ -406,9 +460,9 @@
}
},
"node_modules/@noble/hashes": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.2.0.tgz",
- "integrity": "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.3.0.tgz",
+ "integrity": "sha512-oN+QwyX7VSHotibwubG3kpzbwKrfnyR6OOO+3Nk/53ADL7FmgHHz4TgrbaYKvvOw09u6QTx0oiH1cNCIOuN0CQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -554,9 +608,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "24.13.2",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.2.tgz",
- "integrity": "sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==",
+ "version": "24.13.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz",
+ "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -909,16 +963,16 @@
"optional": true
},
"node_modules/brace-expansion": {
- "version": "5.0.7",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
- "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
+ "version": "5.0.9",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz",
+ "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^4.0.2"
},
"engines": {
- "node": "18 || 20 || >=22"
+ "node": "20 || >=22"
}
},
"node_modules/buffer-from": {
@@ -1165,6 +1219,15 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/cross-dirname": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/cross-dirname/-/cross-dirname-0.1.0.tgz",
+ "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true
+ },
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -1334,9 +1397,9 @@
"license": "MIT"
},
"node_modules/dir-compare/node_modules/brace-expansion": {
- "version": "1.1.15",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
- "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
+ "version": "1.1.18",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
+ "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1441,9 +1504,9 @@
}
},
"node_modules/electron": {
- "version": "43.0.0",
- "resolved": "https://registry.npmjs.org/electron/-/electron-43.0.0.tgz",
- "integrity": "sha512-PV60GsWU6qufhuOhw3n+Yix3WPDcqDtBqE8orbEQGQGHEkgp9o/JCPgb7L4vIL0r1HnfPdqSRtboOTqbDkcFDQ==",
+ "version": "43.3.0",
+ "resolved": "https://registry.npmjs.org/electron/-/electron-43.3.0.tgz",
+ "integrity": "sha512-nLlvu0WFjftWsSaTkV2B/c4NDuJBspTyXu8vKSQ6vLvFt8uG3NgN49LLKcXddwX0GqVvAQDhciWp+4xOdTdhew==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1508,9 +1571,9 @@
}
},
"node_modules/electron-native-screenshare": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/electron-native-screenshare/-/electron-native-screenshare-1.1.0.tgz",
- "integrity": "sha512-NRsrOhveT1uIQMoBJVnHqWE/jKbQ/RUD7khKgvntMtAiM5uHtcmirFnhsToEwcwd1S0Ir5dzq+pLV0u2B2Lxsg==",
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/electron-native-screenshare/-/electron-native-screenshare-1.2.0.tgz",
+ "integrity": "sha512-O7m+/AeE0UWFq8Z18THGSrPHRIxYiYtjdMKtVhGcNRE/sUN6Fj9EgXDOmmJnORCh303NWaNgmAnNz+JbeLa48Q==",
"hasInstallScript": true,
"license": "MIT",
"os": [
@@ -1766,9 +1829,9 @@
"license": "MIT"
},
"node_modules/fast-uri": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.3.tgz",
- "integrity": "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==",
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz",
+ "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==",
"dev": true,
"funding": [
{
@@ -1818,9 +1881,9 @@
"license": "MIT"
},
"node_modules/filelist/node_modules/brace-expansion": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz",
- "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==",
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz",
+ "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1995,9 +2058,9 @@
"license": "MIT"
},
"node_modules/glob/node_modules/brace-expansion": {
- "version": "1.1.15",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
- "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
+ "version": "1.1.18",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
+ "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2316,9 +2379,9 @@
}
},
"node_modules/js-yaml": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
- "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz",
+ "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",
"funding": [
{
"type": "github",
@@ -2511,13 +2574,13 @@
}
},
"node_modules/minimatch": {
- "version": "10.2.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
- "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+ "version": "10.2.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz",
+ "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
- "brace-expansion": "^5.0.5"
+ "brace-expansion": "^5.0.8"
},
"engines": {
"node": "18 || 20 || >=22"
@@ -2577,9 +2640,9 @@
"license": "MIT"
},
"node_modules/node-abi": {
- "version": "4.32.0",
- "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-4.32.0.tgz",
- "integrity": "sha512-2cvOMwK7aQ8eQhHTG9DBK/akKfNaJDwtbxgUaA1u3BHGpd6cbUvWCbA0u3aKan+s7Nl60tMGx4tn11+VS1fiUA==",
+ "version": "4.33.0",
+ "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-4.33.0.tgz",
+ "integrity": "sha512-vLBWCKb+7LWsX+TbfzWOkw0W81m377tyx3hOweBTjO43CXZnRGS1/JPWs20fr0PgZyDXk6ROYrylsEycK8raDA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2662,9 +2725,9 @@
}
},
"node_modules/node-gyp/node_modules/undici": {
- "version": "6.27.0",
- "resolved": "https://registry.npmjs.org/undici/-/undici-6.27.0.tgz",
- "integrity": "sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg==",
+ "version": "6.28.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-6.28.0.tgz",
+ "integrity": "sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2813,9 +2876,9 @@
"license": "ISC"
},
"node_modules/picomatch": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
- "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
+ "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2871,6 +2934,36 @@
"node": ">=10.4.0"
}
},
+ "node_modules/postject": {
+ "version": "1.0.0-alpha.6",
+ "resolved": "https://registry.npmjs.org/postject/-/postject-1.0.0-alpha.6.tgz",
+ "integrity": "sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "commander": "^9.4.0"
+ },
+ "bin": {
+ "postject": "dist/cli.js"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/postject/node_modules/commander": {
+ "version": "9.5.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
+ "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "engines": {
+ "node": "^12.20.0 || >=14"
+ }
+ },
"node_modules/proc-log": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz",
@@ -2946,9 +3039,9 @@
}
},
"node_modules/pvutils": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.5.tgz",
- "integrity": "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==",
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.2.0.tgz",
+ "integrity": "sha512-BbubeCEyTuQjVMakvJQ/Sxbc93F2pwmbsxONT/ZRrwU7Ua38d8unYTwXpTVLAKJ4BDuH9IGztCjQcd/N/39Dvg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3117,9 +3210,9 @@
}
},
"node_modules/sax": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz",
- "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==",
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz",
+ "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==",
"license": "BlueOak-1.0.0",
"engines": {
"node": ">=11.0.0"
@@ -3310,9 +3403,9 @@
}
},
"node_modules/tar": {
- "version": "7.5.19",
- "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.19.tgz",
- "integrity": "sha512-4LeEWl96twnS2Q7Bz4MGqgazLqO+hJN63GZxXoIqh1T3VweYD997gbU1ItNsQafqqXTXd5WFyFdReLtwvRBNiw==",
+ "version": "7.5.22",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.22.tgz",
+ "integrity": "sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==",
"dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
@@ -3470,9 +3563,9 @@
}
},
"node_modules/undici": {
- "version": "7.28.0",
- "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz",
- "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==",
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz",
+ "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==",
"dev": true,
"license": "MIT",
"optional": true,
diff --git a/package.json b/package.json
index 87dc1f0..725df0c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,7 @@
{
"name": "topluyo",
- "version": "2026.7.1018",
+ "desktopName": "Topluyo",
+ "version": "2026.8.1616",
"description": "Topluyo",
"main": "main.js",
"scripts": {
@@ -25,7 +26,7 @@
},
"dependencies": {
"electron-log": "^5.4.1",
- "electron-native-screenshare": "1.1.0",
+ "electron-native-screenshare": "1.2.0",
"electron-updater": "^6.3.9",
"electron-window-state": "^5.0.3",
"uiohook-napi": "^1.5.5"
@@ -114,7 +115,9 @@
"maintainer": "Hasan Delibaş "
},
"snap": {
+ "base": "core22",
"confinement": "classic",
+ "useTemplateApp": false,
"publish": false
},
"mac": {
@@ -147,7 +150,7 @@
]
},
"allowScripts": {
- "electron-native-screenshare@1.0.1": true,
+ "electron-native-screenshare@1.2.0": true,
"electron-winstaller@5.4.0": true,
"uiohook-napi@1.5.5": true
}
diff --git a/preloads/loading.js b/preloads/loading.js
index b270252..52fba12 100644
--- a/preloads/loading.js
+++ b/preloads/loading.js
@@ -1,9 +1,8 @@
// preloads/loading.js
-const { ipcRenderer } = require("electron");
+const { ipcRenderer, contextBridge } = require("electron");
window.addEventListener("DOMContentLoaded", () => {
- window.closeWindow = () => {
- ipcRenderer.send("close");
- };
document.body.classList.add("electron-app");
});
+
+contextBridge.exposeInMainWorld("closeWindow", () => ipcRenderer.send("close"));
diff --git a/preloads/main.js b/preloads/main.js
index 9321f0c..b6b15fc 100644
--- a/preloads/main.js
+++ b/preloads/main.js
@@ -1,256 +1,250 @@
// preloads/main.js
-const { ipcRenderer, contextBridge } = require("electron");
+const { ipcRenderer, contextBridge, webFrame } = require("electron");
window.addEventListener("DOMContentLoaded", () => {
- window.closeWindow = () => {
- ipcRenderer.send("close");
- };
+ document.body.classList.add("electron-app");
+ document.body.classList.add("Electron");
+});
- window.minimizeWindow = () => {
- ipcRenderer.send("minimize");
- };
- window.maximizeWindow = () => {
- ipcRenderer.send("maximize");
- };
+// Expose standard window controls
+contextBridge.exposeInMainWorld("closeWindow", () => ipcRenderer.send("close"));
+contextBridge.exposeInMainWorld("minimizeWindow", () => ipcRenderer.send("minimize"));
+contextBridge.exposeInMainWorld("maximizeWindow", () => ipcRenderer.send("maximize"));
+contextBridge.exposeInMainWorld("ossOpen", () => ipcRenderer.send("open-oss"));
- window.ossOpen = () => {
- ipcRenderer.send("open-oss");
- };
+contextBridge.exposeInMainWorld("stream", {
+ getSources: () => ipcRenderer.invoke("getSources"),
+ setSource: (data) =>
+ ipcRenderer.invoke("setSource", { id: data.id, isAudioEnabled: data.audio }),
+});
- document.body.classList.add("electron-app");
- document.body.classList.add("Electron");
+contextBridge.exposeInMainWorld("Electron", {
+ onUpdateMessage: (callback) => ipcRenderer.on("update-message", (e, msg) => callback({}, msg)),
+ onProgress: (callback) => ipcRenderer.on("download-progress", (e, obj) => callback({}, obj)),
+ getOSSLibraries: () => ipcRenderer.invoke("get-oss-libraries"),
+ openExternal: (url) => ipcRenderer.invoke("open-external", url),
+ SetPTTKey: (key) => ipcRenderer.invoke("set-ptt-key", key),
+ NotificationIFRAME: (iframeUrl, force = false) => ipcRenderer.send('notification:iframe', { iframeUrl, force }),
+ NotificationOS: (obj) => ipcRenderer.send('notification:os', obj),
+ version: ipcRenderer.sendSync('get-app-version-sync'),
+ setStartup: (checked) => ipcRenderer.send("set-Startup", checked)
+});
- if (typeof documenter !== 'undefined') {
- documenter.on("input", "#run-on-startup", function () {
- ipcRenderer.send("set-Startup", this.checked);
- });
+// IPC for Native Audio Capture
+contextBridge.exposeInMainWorld("nativeAudioIPC", {
+ start: () => ipcRenderer.invoke('start-native-audio'),
+ stop: () => ipcRenderer.invoke('stop-native-audio'),
+ onData: (callback) => {
+ ipcRenderer.removeAllListeners('native-audio-data');
+ ipcRenderer.on('native-audio-data', (event, buffer, meta) => callback(buffer, meta));
+ },
+ offData: () => ipcRenderer.removeAllListeners('native-audio-data')
+});
+
+// Setup webFrame injections for callbacks that require global variables
+ipcRenderer.on("electron-error", (event, errorData) => {
+ let osInfo = `${errorData.os} ${errorData.osRelease} (${errorData.arch})`;
+ if (errorData.os === 'win32') {
+ osInfo += errorData.isWindowsStore ? ' [Microsoft Store]' : ' [Stand-alone]';
}
+ const extraInfo = errorData.extra ? `\nExtra: ${errorData.extra}` : '';
- ipcRenderer.on("electron-error", (event, errorData) => {
- let osInfo = `${errorData.os} ${errorData.osRelease} (${errorData.arch})`;
- if (errorData.os === 'win32') {
- osInfo += errorData.isWindowsStore ? ' [Microsoft Store]' : ' [Stand-alone]';
- }
-
- const extraInfo = errorData.extra ? `\nExtra: ${errorData.extra}` : '';
-
- const errorText = `**Desktop App Error**
-Type: ${errorData.type}
-Message: ${errorData.message}
-OS: ${osInfo}
-App Version: ${errorData.appVersion}
-Electron: ${errorData.electronVersion}${extraInfo}
-Stack:
-\`\`\`
-${errorData.stack || 'No stack trace'}
-\`\`\``.substring(0, 3000);
+ const errorText = `**Desktop App Error**\nType: ${errorData.type}\nMessage: ${errorData.message}\nOS: ${osInfo}\nApp Version: ${errorData.appVersion}\nElectron: ${errorData.electronVersion}${extraInfo}\nStack:\n\`\`\`\n${errorData.stack || 'No stack trace'}\n\`\`\``.substring(0, 3000);
+ webFrame.executeJavaScript(`
if (typeof Route !== 'undefined' && Route.api) {
- Route.api({
- api: "/!api/post/add",
- data: { channel_id: 33591, code: "", text: errorText }
- });
+ Route.api({ api: "/!api/post/add", data: { channel_id: 33591, code: "", text: ${JSON.stringify(errorText)} } });
} else if (window.Route && window.Route.api) {
- window.Route.api({
- api: "/!api/post/add",
- data: { channel_id: 33591, code: "", text: errorText }
- });
+ window.Route.api({ api: "/!api/post/add", data: { channel_id: 33591, code: "", text: ${JSON.stringify(errorText)} } });
}
- });
+ `).catch(console.error);
+});
- ipcRenderer.on("ptt-status-change", (event, status) => {
+ipcRenderer.on("ptt-status-change", (event, status) => {
+ webFrame.executeJavaScript(`
if (typeof Topluyo !== 'undefined' && typeof Topluyo.Microphone === 'function') {
- Topluyo.Microphone(status);
+ Topluyo.Microphone(${status});
} else if (window.Topluyo && typeof window.Topluyo.Microphone === 'function') {
- window.Topluyo.Microphone(status);
+ window.Topluyo.Microphone(${status});
}
- });
+ `).catch(console.error);
+});
- // Notification Response Listener
- ipcRenderer.on('notification:response', (event, obj) => {
+ipcRenderer.on('notification:response', (event, obj) => {
+ webFrame.executeJavaScript(`
if (typeof Topluyo !== 'undefined' && typeof Topluyo.NotificationResponse === 'function') {
- Topluyo.NotificationResponse(obj);
+ Topluyo.NotificationResponse(${JSON.stringify(obj)});
} else if (window.Topluyo && typeof window.Topluyo.NotificationResponse === 'function') {
- window.Topluyo.NotificationResponse(obj);
+ window.Topluyo.NotificationResponse(${JSON.stringify(obj)});
}
- });
+ `).catch(console.error);
});
-// Override MediaDevices.prototype to ensure we catch ALL calls in this frame
-if (typeof MediaDevices !== 'undefined' && MediaDevices.prototype.getDisplayMedia) {
- const originalGetDisplayMedia = MediaDevices.prototype.getDisplayMedia;
- MediaDevices.prototype.getDisplayMedia = async function (constraints) {
- console.log("[NativeCapture] getDisplayMedia intercepted! Constraints:", constraints);
-
- // Call the original Electron implementation
- const stream = await originalGetDisplayMedia.call(this, constraints);
-
- const audioTracks = stream.getAudioTracks();
- console.log("[NativeCapture] Original stream audio tracks:", audioTracks.length);
-
- if (audioTracks.length > 0) {
- console.log("[NativeCapture] Requesting WASAPI start from main process...");
- try {
- const started = await ipcRenderer.invoke('start-native-audio');
- console.log("[NativeCapture] WASAPI start response:", started);
-
- if (started && started.platform === 'linux') {
- console.log("[NativeCapture] PulseAudio Linux routing started:", started.sinkName);
- audioTracks[0].stop();
- stream.removeTrack(audioTracks[0]);
-
- try {
- const paStream = await navigator.mediaDevices.getUserMedia({
- audio: {
- deviceId: started.sinkName
- }
- });
- const paAudioTrack = paStream.getAudioTracks()[0];
-
- const stopNativeCapture = () => {
- console.log("[NativeCapture] Stopping Linux capture...");
- ipcRenderer.invoke('stop-native-audio');
- paAudioTrack.stop();
- };
-
- const originalTrackStop = paAudioTrack.stop.bind(paAudioTrack);
- paAudioTrack.stop = () => {
- stopNativeCapture();
- originalTrackStop();
- };
-
- const videoTracks = stream.getVideoTracks();
- if (videoTracks.length > 0) {
- videoTracks[0].addEventListener('ended', () => {
- stopNativeCapture();
- });
- }
-
- stream.addTrack(paAudioTrack);
- console.log("[NativeCapture] Added PulseAudio virtual track to stream!");
- } catch (e) {
- console.error("[NativeCapture] Failed to capture PulseAudio sink via getUserMedia:", e);
- }
- } else if (started) {
- console.log("[NativeCapture] Native WASAPI started successfully. Stopping default loopback track to prevent echo.");
- audioTracks[0].stop();
- stream.removeTrack(audioTracks[0]);
-
- console.log("[NativeCapture] Creating WebCodecs MediaStreamTrackGenerator.");
- const generator = new MediaStreamTrackGenerator({ kind: 'audio' });
- const writer = generator.writable.getWriter();
-
- let timestamp = 0; // Microseconds
- let packetCount = 0;
-
- ipcRenderer.removeAllListeners('native-audio-data');
- ipcRenderer.on('native-audio-data', (event, buffer, meta) => {
+// Documenter logic injection
+webFrame.executeJavaScript(`
+ if (typeof documenter !== 'undefined') {
+ documenter.on("input", "#run-on-startup", function () {
+ if (window.Electron && window.Electron.setStartup) {
+ window.Electron.setStartup(this.checked);
+ }
+ });
+ }
+`).catch(e => { });
+
+// Inject MediaDevices override synchronously into the main world using a script tag
+const injectScript = document.createElement('script');
+injectScript.textContent = `
+ try {
+ const patchMediaDevices = () => {
+ const mediaDevicesProto = Object.getPrototypeOf(navigator.mediaDevices) || MediaDevices.prototype;
+ if (mediaDevicesProto && mediaDevicesProto.getDisplayMedia && !navigator.mediaDevices.__isNativeCapturePatched) {
+ navigator.mediaDevices.__isNativeCapturePatched = true;
+
+ const originalGetDisplayMedia = mediaDevicesProto.getDisplayMedia;
+ mediaDevicesProto.getDisplayMedia = async function (constraints) {
+
+ const stream = await originalGetDisplayMedia.call(this, constraints);
+ const audioTracks = stream.getAudioTracks();
+
+ if (audioTracks.length > 0) {
try {
- packetCount++;
- if (packetCount % 100 === 0) {
- console.log(`[NativeCapture] Received 100 audio packets. Latest meta:`, meta, `Buffer size: ${buffer.byteLength}`);
- }
-
- const arrayBuffer = buffer.buffer || buffer;
- const byteOffset = buffer.byteOffset || 0;
- const byteLength = buffer.byteLength || buffer.length;
-
- const isFloat = meta.isFloat;
- let typedData;
-
- if (isFloat) {
- typedData = new Float32Array(arrayBuffer, byteOffset, byteLength / 4);
- } else {
- typedData = new Int16Array(arrayBuffer, byteOffset, byteLength / 2);
+ const started = await window.nativeAudioIPC.start();
+
+ if (started && started.platform === 'linux') {
+ audioTracks[0].stop();
+ stream.removeTrack(audioTracks[0]);
+
+ try {
+ const paStream = await navigator.mediaDevices.getUserMedia({
+ audio: { deviceId: started.sinkName }
+ });
+ const paAudioTrack = paStream.getAudioTracks()[0];
+
+ const stopNativeCapture = () => {
+ window.nativeAudioIPC.stop();
+ paAudioTrack.stop();
+ };
+
+ const originalTrackStop = paAudioTrack.stop.bind(paAudioTrack);
+ paAudioTrack.stop = () => {
+ stopNativeCapture();
+ originalTrackStop();
+ };
+
+ const videoTracks = stream.getVideoTracks();
+ if (videoTracks.length > 0) {
+ videoTracks[0].addEventListener('ended', stopNativeCapture);
+ }
+
+ stream.addTrack(paAudioTrack);
+ } catch (e) {
+ console.error("[NativeCapture] Failed to capture PulseAudio sink via getUserMedia:", e);
+ }
+ } else if (started) {
+ audioTracks[0].stop();
+ stream.removeTrack(audioTracks[0]);
+
+ const generator = new MediaStreamTrackGenerator({ kind: 'audio' });
+ const writer = generator.writable.getWriter();
+
+ let timestamp = 0; // Microseconds
+ let packetCount = 0;
+
+ window.nativeAudioIPC.onData((buffer, meta) => {
+ try {
+ packetCount++;
+
+ const arrayBuffer = buffer.buffer || buffer;
+ const byteOffset = buffer.byteOffset || 0;
+ const byteLength = buffer.byteLength || buffer.length;
+
+ const isFloat = meta.isFloat;
+ let typedData;
+
+ if (isFloat) {
+ typedData = new Float32Array(arrayBuffer, byteOffset, byteLength / 4);
+ } else {
+ typedData = new Int16Array(arrayBuffer, byteOffset, byteLength / 2);
+ }
+
+ const frames = typedData.length / meta.channels;
+
+ const audioData = new AudioData({
+ format: isFloat ? 'f32' : 's16',
+ sampleRate: meta.sampleRate,
+ numberOfFrames: frames,
+ numberOfChannels: meta.channels,
+ timestamp: timestamp,
+ data: typedData
+ });
+
+ timestamp += (frames / meta.sampleRate) * 1000000;
+ writer.write(audioData);
+ } catch (e) {
+ console.error("[NativeCapture] Audio insertion error:", e);
+ }
+ });
+
+ const stopNativeCapture = () => {
+ window.nativeAudioIPC.stop();
+ window.nativeAudioIPC.offData();
+ };
+
+ const originalGeneratorStop = generator.stop.bind(generator);
+ generator.stop = () => {
+ stopNativeCapture();
+ originalGeneratorStop();
+ };
+
+ const videoTracks = stream.getVideoTracks();
+ if (videoTracks.length > 0) {
+ videoTracks[0].addEventListener('ended', () => {
+ stopNativeCapture();
+ });
+ }
+
+ stream.addTrack(generator);
}
-
- const frames = typedData.length / meta.channels;
-
- const audioData = new AudioData({
- format: isFloat ? 'f32' : 's16',
- sampleRate: meta.sampleRate,
- numberOfFrames: frames,
- numberOfChannels: meta.channels,
- timestamp: timestamp,
- data: typedData
- });
-
- timestamp += (frames / meta.sampleRate) * 1000000;
- writer.write(audioData);
- } catch (e) {
- console.error("[NativeCapture] Audio insertion error:", e);
+ } catch (err) {
+ console.error("[NativeCapture] IPC error during native capture start:", err);
}
- });
-
- // Hook stopping mechanism
- const stopNativeCapture = () => {
- console.log("[NativeCapture] Stopping WASAPI capture...");
- ipcRenderer.invoke('stop-native-audio');
- ipcRenderer.removeAllListeners('native-audio-data');
- };
-
- const originalGeneratorStop = generator.stop.bind(generator);
- generator.stop = () => {
- stopNativeCapture();
- originalGeneratorStop();
- };
-
- const videoTracks = stream.getVideoTracks();
- if (videoTracks.length > 0) {
- videoTracks[0].addEventListener('ended', () => {
- console.log("[NativeCapture] Video track ended, stopping audio.");
- stopNativeCapture();
- });
}
-
- stream.addTrack(generator);
- console.log("[NativeCapture] Added native audio track to stream!");
- } else {
- console.log("[NativeCapture] Native capture returned false. Falling back to default Electron loopback.");
- }
- } catch (err) {
- console.error("[NativeCapture] IPC error during native capture start:", err);
- console.log("[NativeCapture] Falling back to default Electron loopback.");
+ return stream;
+ };
}
+ };
+
+ if (navigator.mediaDevices) {
+ patchMediaDevices();
+ } else {
+ let attempts = 0;
+ const patchInterval = setInterval(() => {
+ attempts++;
+ if (navigator.mediaDevices) {
+ patchMediaDevices();
+ clearInterval(patchInterval);
+ } else if (attempts > 100) {
+ clearInterval(patchInterval);
+ }
+ }, 50);
+ }
+ } catch (err) {
+ console.error("[NativeCapture] Error during synchronous injection:", err);
+ }
+`;
+
+// Inject into the DOM synchronously before other scripts load
+if (document.documentElement) {
+ document.documentElement.appendChild(injectScript);
+ injectScript.remove();
+} else {
+ // Use MutationObserver to inject as soon as documentElement is available, before page scripts
+ const observer = new MutationObserver(() => {
+ if (document.documentElement) {
+ document.documentElement.appendChild(injectScript);
+ injectScript.remove();
+ observer.disconnect();
}
-
- return stream;
- };
-}
-
-try {
- contextBridge.exposeInMainWorld("stream", {
- getSources: () => ipcRenderer.invoke("getSources"),
- setSource: (data) =>
- ipcRenderer.invoke("setSource", { id: data.id, isAudioEnabled: data.audio }),
- });
-
- contextBridge.exposeInMainWorld("Electron", {
- onUpdateMessage: (callback) => ipcRenderer.on("update-message", callback),
- onProgress: (callback) => ipcRenderer.on("download-progress", callback),
- getOSSLibraries: () => ipcRenderer.invoke("get-oss-libraries"),
- openExternal: (url) => ipcRenderer.invoke("open-external", url),
- SetPTTKey: (key) => ipcRenderer.invoke("set-ptt-key", key),
- NotificationIFRAME: (iframeUrl, force = false) => ipcRenderer.send('notification:iframe', { iframeUrl, force }),
- NotificationOS: (obj) => ipcRenderer.send('notification:os', obj),
- version: ipcRenderer.sendSync('get-app-version-sync')
});
-} catch (e) {
- // If contextIsolation is false, expose directly to window
- window.stream = {
- getSources: () => ipcRenderer.invoke("getSources"),
- setSource: (data) =>
- ipcRenderer.invoke("setSource", { id: data.id, isAudioEnabled: data.audio }),
- };
- window.Electron = {
- ...(window.Electron || {}),
- onUpdateMessage: (callback) => ipcRenderer.on("update-message", callback),
- onProgress: (callback) => ipcRenderer.on("download-progress", callback),
- getOSSLibraries: () => ipcRenderer.invoke("get-oss-libraries"),
- openExternal: (url) => ipcRenderer.invoke("open-external", url),
- SetPTTKey: (key) => ipcRenderer.invoke("set-ptt-key", key),
- NotificationIFRAME: (iframeUrl, force = false) => ipcRenderer.send('notification:iframe', { iframeUrl, force }),
- NotificationOS: (obj) => ipcRenderer.send('notification:os', obj),
- version: ipcRenderer.sendSync('get-app-version-sync')
- };
+ observer.observe(document, { childList: true });
}
diff --git a/preloads/notification.js b/preloads/notification.js
index 823b7b6..fc53a71 100644
--- a/preloads/notification.js
+++ b/preloads/notification.js
@@ -4,10 +4,6 @@ const { ipcRenderer, contextBridge } = require("electron");
let isClosing = false;
window.addEventListener("DOMContentLoaded", () => {
- window.closeWindow = () => {
- isClosing = true;
- ipcRenderer.send("notification:close");
- };
document.body.classList.add("electron-app");
window.addEventListener("mousedown", () => {
@@ -19,24 +15,16 @@ window.addEventListener("DOMContentLoaded", () => {
});
});
-try {
- contextBridge.exposeInMainWorld("NotificationClose", () => {
- isClosing = true;
- ipcRenderer.send('notification:close');
- });
- contextBridge.exposeInMainWorld("closeWindow", () => {
- isClosing = true;
- ipcRenderer.send('notification:close');
- });
- contextBridge.exposeInMainWorld("NotificationResponse", (obj) => ipcRenderer.send('notification:event', obj));
-} catch (e) {
- window.NotificationClose = () => {
- isClosing = true;
- ipcRenderer.send('notification:close');
- };
- window.closeWindow = () => {
- isClosing = true;
- ipcRenderer.send('notification:close');
- };
- window.NotificationResponse = (obj) => ipcRenderer.send('notification:event', obj);
-}
+contextBridge.exposeInMainWorld("NotificationClose", () => {
+ isClosing = true;
+ ipcRenderer.send('notification:close');
+});
+
+contextBridge.exposeInMainWorld("closeWindow", () => {
+ isClosing = true;
+ ipcRenderer.send('notification:close');
+});
+
+contextBridge.exposeInMainWorld("NotificationResponse", (obj) => {
+ ipcRenderer.send('notification:event', obj);
+});
diff --git a/preloads/oss.js b/preloads/oss.js
index d2a1184..1fd4e6f 100644
--- a/preloads/oss.js
+++ b/preloads/oss.js
@@ -2,20 +2,12 @@
const { ipcRenderer, contextBridge } = require("electron");
window.addEventListener("DOMContentLoaded", () => {
- window.closeWindow = () => {
- ipcRenderer.send("close");
- };
document.body.classList.add("electron-app");
});
-try {
- contextBridge.exposeInMainWorld("electronAPI", {
- getOSSLibraries: () => ipcRenderer.invoke("get-oss-libraries"),
- openExternal: (url) => ipcRenderer.invoke("open-external", url)
- });
-} catch (e) {
- window.electronAPI = {
- getOSSLibraries: () => ipcRenderer.invoke("get-oss-libraries"),
- openExternal: (url) => ipcRenderer.invoke("open-external", url)
- };
-}
+contextBridge.exposeInMainWorld("closeWindow", () => ipcRenderer.send("close"));
+
+contextBridge.exposeInMainWorld("electronAPI", {
+ getOSSLibraries: () => ipcRenderer.invoke("get-oss-libraries"),
+ openExternal: (url) => ipcRenderer.invoke("open-external", url)
+});
diff --git a/preloads/screenshare.js b/preloads/screenshare.js
index 13005dc..fd16be2 100644
--- a/preloads/screenshare.js
+++ b/preloads/screenshare.js
@@ -2,22 +2,14 @@
const { ipcRenderer, contextBridge } = require("electron");
window.addEventListener("DOMContentLoaded", () => {
- window.closeWindow = () => {
- ipcRenderer.send("close");
- };
document.body.classList.add("electron-app");
});
-try {
- contextBridge.exposeInMainWorld("stream", {
- getSources: () => ipcRenderer.invoke("getSources"),
- setSource: (data) =>
- ipcRenderer.invoke("setSource", { id:data.id, isAudioEnabled:data.audio }),
- });
-} catch (e) {
- window.stream = {
- getSources: () => ipcRenderer.invoke("getSources"),
- setSource: (data) =>
- ipcRenderer.invoke("setSource", { id:data.id, isAudioEnabled:data.audio }),
- };
-}
+contextBridge.exposeInMainWorld("closeWindow", () => ipcRenderer.send("close"));
+
+contextBridge.exposeInMainWorld("stream", {
+ getSources: () => ipcRenderer.invoke("getSources"),
+ setSource: (data) =>
+ ipcRenderer.invoke("setSource", { id:data.id, isAudioEnabled:data.audio }),
+ isNativeAudioAvailable: () => ipcRenderer.invoke("isNativeAudioAvailable"),
+});
diff --git a/utils.js b/utils.js
index 24af082..e0a6642 100644
--- a/utils.js
+++ b/utils.js
@@ -36,7 +36,13 @@ const getAllSources = async () => {
}
};
+let currentStreamWin = null;
async function createStreamWindow(_, callback) {
+ if (currentStreamWin && !currentStreamWin.isDestroyed()) {
+ currentStreamWin.focus();
+ return;
+ }
+
let callbackcalled = false;
const win = new BrowserWindow({
width: 900,
@@ -58,6 +64,7 @@ async function createStreamWindow(_, callback) {
win.setAlwaysOnTop(true, "screen-saver");
}
+ currentStreamWin = win;
win.loadFile("ScreenShare.html");
const allSources = await getAllSources();
@@ -93,15 +100,19 @@ async function createStreamWindow(_, callback) {
win.on("close", () => {
console.log("Stream penceresi kapatılıyor");
if (callbackcalled) return;
+ if (callbackcalled) {
+ currentStreamWin = null;
+ return;
+ }
callbackcalled = true;
try {
ipcMain.removeHandler("getSources");
ipcMain.removeHandler("setSource");
callback(null);
} catch (e) {
- console.error("Pencere kapatma hatası:", e);
- throw e;
+ console.error(e);
}
+ currentStreamWin = null;
});
}
@@ -177,7 +188,13 @@ const openExternalLinks = (url) => {
};
+let currentOssWin = null;
function ossWindow(){
+ if (currentOssWin && !currentOssWin.isDestroyed()) {
+ currentOssWin.focus();
+ return;
+ }
+
let ossWin = new BrowserWindow({
width: 800,
height: 600,
@@ -197,8 +214,11 @@ function ossWindow(){
return { action: "deny" };
})
ossWin.on("closed", () => {
+ currentOssWin = null;
ossWin = null;
});
+
+ currentOssWin = ossWin;
}
module.exports = { isSafeUrl, mediaHandler, openExternalLinks, ossWindow };